Linux设备驱动基础01:Linux设备驱动概述

article/2025/11/10 1:16:01

目录

1. 设备驱动的作用

2. 有无操作系统时的设备驱动

2.1 无操作系统

2.1.1 硬件、驱动和应用程序的关系

2.1.2 单任务软件典型架构

2.2 有操作系统

2.2.1 硬件、驱动、操作系统和应用软件的关系

3. Linux设备分类

3.1 常规分类法

3.1.1 字符设备

3.1.2 块设备

3.1.3 网络设备

3.2 总线分类法

4. Linux设备驱动在整个软硬件系统中的位置

5. 内核空间与用户空间

5.1 硬件基础

5.2 软件使用

5.3 内核态与用户态

6. GNU C对ANSI C的常见扩展

6.1 零长度数组

6.2 case范围

6.3 语句表达式

6.4 typeof关键字

6.5 可变参数宏

6.6 当前函数名宏

6.7 特殊属性声明__attribute__

6.7.1 noreturn

6.7.2 unused

6.7.3 aligned

6.7.4 packed

6.7.5 section

6.7.6 format

6.8 内建函数

6.8.1 __builtin_constant_p

6.8.2 __builtin_expect

7. 内核编程其他主题

7.1 do {} while(0)

7.2 goto语句的使用

7.3 内核中的并发

7.4 当前进程的获取

7.4.1 before 2.6

7.4.2 from 2.6

7.5 浮点工具链

7.6 其他细节


1. 设备驱动的作用

核心:充当硬件和应用软件之间的纽带

具体任务:

① 读写设备寄存器(实现控制的方式)

② 完成设备的轮询、中断处理、DMA通信(CPU与外设通信的方式)

③ 进行物理内存向虚拟内存的映射(在开启硬件MMU的情况下)

2. 有无操作系统时的设备驱动

2.1 无操作系统

2.1.1 硬件、驱动和应用程序的关系

说明1:无操作系统特点

① 驱动包含的接口函数直接与硬件功能吻合,没有任何附加功能(向下)

② 设备驱动的接口被直接提交给应用软件工程师,应用软件直接访问设备驱动的接口(向上)

说明2:无操作系统情况下,两种不合理的驱动架构

缺点:设备驱动和应用软件平等,驱动中包含了业务层面的处理,不符合高内聚,低耦合的要求

缺点:应用软件直接操作硬件寄存器,不单独设计驱动模块,代码不可复用

2.1.2 单任务软件典型架构

在一个无限循环中夹杂着对设备中断的检测或者对设备的轮询(前后台系统)

2.2 有操作系统

2.2.1 硬件、驱动、操作系统和应用软件的关系

说明1:设备驱动的2个任务

① 操作硬件(向下)

② 将驱动融于内核,需要设计面向操作系统内核的接口,这些接口由操作系统定义(向上)

在有操作系统的情况下,驱动的架构由相应的操作系统定义,必须按照相应的架构设计驱动

结果:驱动成为连接硬件内核的桥梁 !!!

说明2:操作系统通过给驱动制造麻烦来给上层应用提供便利

由于驱动都按照操作系统给出独立于设备的接口设计,应用程序可以使用统一的系统调用接口来访问各种设备

e.g. 使用write和read函数可以访问各种字符设备和块设备,而不论设备的具体类型和工作方式

3. Linux设备分类

3.1 常规分类法

3.1.1 字符设备

特点:

① 以串行顺序依次进行访问的设备

② 字符设备不经过系统的快速缓冲

e.g. 触摸屏、鼠标

3.1.2 块设备

特点:

① 可以用任意顺序进行访问,以块为单位进行操作

② 块设备经过系统的快速缓冲

③ 在块设备上可以构建文件系统

e.g. 硬盘、SD卡

说明:块设备以块为最小传输单位,不能按字节处理数据。而Linux则允许块设备传送任意数量的字节,因此块 & 字符设备的区别仅在于内核内部管理数据的方式不同,即内核和驱动之间的软件接口不同

3.1.3 网络设备

特点:

① 网络设备面向数据包的接收和发送设计

网络设备不对应于文件系统的结点

说明:与字符 & 块设备一样,网络设备也可以是一个纯粹的软件设备(e.g. 回环网卡)

3.2 总线分类法

示例:I2C驱动 / USB驱动 / PCI驱动 / LCD驱动

这些驱动都可以归入常规分类法的3个基础类别,但由于这些设备比较复杂,Linux为其定义了各自的驱动体系结构(即内核提供了给定类型设备的附加层我们编写的驱动是和这些附加层一起工作

所谓给定类型设备的附加层,其实就是内核开发者实现了整个设备类型的共有特性,并提供给驱动程序实现者

e.g. USB设备由USB模块驱动,而USB模块和USB子系统一起工作。但USB设备本身在系统中可以表现为一个字符设备(e.g. USB串口)/ 块设备(e.g. USB读卡器)/ 网络设备(e.g. USB网卡)

4. Linux设备驱动在整个软硬件系统中的位置

说明1:除网络设备外,字符设备和块设备都被映射为Linux文件系统中的文件,可以通过文件系统的系统调用接口(open / close / read / write)访问

说明2:对块设备的2种访问方式

① 原始块访问(e.g. dd命令)

② 构建文件系统通过文件访问

说明3:Linux块子系统 & MTD子系统

① MTD子系统面向Nor & Nand Flash工作,在其上可建立Yaffs等文件系统

② Linux块子系统面向磁盘 & MMC/SD工作,在其上可建立FAT/EXT等文件系统

5. 内核空间与用户空间

5.1 硬件基础

操作系统能区分为内核空间与用户空间的硬件基础是CPU支持不同的工作模式

ARM:支持usr / fiq / irq / svc / sys / und / abt 七种模式(ARM v6架构)

X86:拥有ring 0 ~ ring 3四种特权等级

5.2 软件使用

Linux利用CPU的这一特性实现内核态和用户态,但他只使用两级

ARM:内核态(svc模式)、用户态(usr模式)

X86:内核态(ring 0)、用户态(ring 3) // ring 1 现在被用于实现虚拟化

说明1:ARM Linux的系统调用实现原理是采用swi软中断从用户态切换至内核态

说明2:X86是通过int 0x80中断进入内核态

5.3 内核态与用户态

内核态:可以进行任何操作

用户态:禁止对硬件的直接访问和对内存的未授权访问

说明:内核态和用户态使用不同的地址空间(即有自己的内存映射),Linux只能通过系统调用硬件中断从用户空间进入内核空间

补充:在进入内核态时,系统调用和硬件中断的不同

执行系统调用的内核代码运行在进程上下文中,他代表调用进程执行操作,因此能够访问进程地址空间的所有数据

处理硬件中断的内核代码运行在中断上下文中,他和进程是异步的,与任何一个特定进程无关

通常,一个驱动程序模块中的某些函数作为系统调用的一部分,而其他函数负责中断处理

6. GNU C对ANSI C的常见扩展

6.1 零长度数组

struct  var_data
{int len;char data[0];
};

说明1:由于没有为data数组分配内存,因此sizeof(struct var_data) = sizeof(int)

说明2:char data[0]意味着通过var_data结构体类型变量的data[i]成员可以访问len之后的第i个地址中的内容

e.g. 假设struct var_data的数据域就保存在struct var_data紧接着的内存区域,那么可以通过如下方式遍历这些数据。

struct var_data s;
...
for (i = 0; i < s.len; ++i) // 此时s.len中保存的就是实际的数据域字节数printf("%x\n", s.data[i]);

典型应用场景:定义变长对象的头结构(e.g. 802.11帧头部,由于Information Elements的存在,帧长度可变~~)

补充:其实只有在数据域紧接着struct var_data分配时,零长度数组才有意义

上机测试:成员数组data的起始地址

6.2 case范围

GNU C 支持case x...y 语法

switch (ch)
{
case '0'...'9':ch -= '0';break;
case 'a'...'f'ch -= 'a' - 10;break;
case 'A'...'F':ch -= 'A' - 10;break;
}

6.3 语句表达式

其实就是花括号中的复合语句,在花括号中可以定义变量

在这个topic下主要想讨论一种避免副作用的宏定义方式,

#definf  MIN(x, y)  ((x) < (y) ? (x) : (y))

这种宏定义方式已经考虑得比较全面,但不能避免调用时的副作用,比如,

int x = 10;
int y = 20;// int z = ((x++) < (y++) ? (x++) : (y++))
// 由于副作用变量错误地累加了2次
int z = MIN(x++, y++);

注意:在实际使用中要避免在调用宏时带副作用

改进方式:在复合语句中定义局部变量

#define MIN(type, x, y) \
({type _x = (x); type _y = (y); _x < _y ? _x : _y})// int z = {int _x=x++; int _y=y++; _x < _y ? _x : _y}; 
int z = MIN(int, x++, y++);

6.4 typeof关键字

 typeof(x)可以获得x的类型,借助这个宏,可以重写上面的MIN 宏(内核代码中的实现)

#define MIN(x, y)  ({ \
const typeof(x) _x = (x); \
const typeof(y) _y = (y); \
(void) (&_x == &_y);       \
_x < _y ? _x : _y})

说明:(void)(&_x == &_y) 的作用是判断参与比较的两个值类型是否一致

_x 和_y 的地址值当然不可能相同,但是如果两个变量的类型不同,此处进行地址比较就会使得编译器警告:comparison of distinct pointer types lacks a cast

这是因为C语言中的指针包含(地址值 + 基类型)这2个属性

6.5 可变参数宏

#define pr_debug(fmt, args...) printk(fmt, ##args)

说明:pre_debug宏中的args表示其余的参数,可以是零个或多个

pre_debug("%s:%d\n", filename, line);
// 展开为
printk("%s:%d\n", filename, line);

使用##是为了处理args参数个数为零个的情况,此时前面的逗号变得多余,使用##后,GNU C处理器会丢弃前面的逗号

pre_debug("success!\n");
// 展开为
printk("success!\n");

而不是

printk("success!\n",);

说明:##的作用

##的作用是对标记(token)进行连接,在上例中fmt和args均为token。如果token为空,则不进行连接,并且删除掉多余的逗号

此处的关键如何删除掉多余的逗号,作为验证,我们定义如下2个宏,

#define pr_debug_a(fmt, args...) printf(fmt, args)
#define pr_debug_b(fmt, args...) printf(fmt, ##args)// 以如下相同的方式调用上述宏
pr_debug_a("hello");
pr_debug_b("hello");

使用gcc -E选项查看预处理结果如下,

验证结果在预期之中,不加##号的宏会有多余的逗号,编译时将会失败。下面来验证一下是不是##号将多余的逗号删除的,我们定义如下的宏,

#define test(args...) , ##args// 以如下方式调用上述宏
test();
test("hello");

使用gcc -E选项查看预处理结果如下,

可见##号在可变参数args为空的情况下,确实可以删除之前多余的逗号;如果args不为空,宏也能正常工作

脑洞验证再进行一步,##号能删除多个逗号吗 ? 我们定义如下的宏

#define test(args...) ,, ##argstest();

预处理结果如下,可见##号只能删除一个多余逗号

那么##号可以删除其他符号吗 ? 经过验证,如果换成其他符号或token,预处理均会失败

#define test(args...) ; ##args // 预处理失败
#define test(args...) a ##args // 预处理失败

6.6 当前函数名宏

GNU C中使用宏__FUNCTION__ 保存函数在源代码中的名字,C99中新增了__func__ 宏表示当前函数名。

目前建议在Linux编程中使用__func__ 宏

6.7 特殊属性声明__attribute__

用途:声明函数变量类型的特殊属性,以便进行手工的代码优化定制代码检查的方法

语法:在需要修饰的声明后面添加__attribute__((ATTRIBUTE)),其中ATTRIBUTE为属性说明,如果存在多个属性,以逗号分隔

下面列举几个常用的属性:

6.7.1 noreturn

用于函数,表示函数从不返回。编译器可以据此优化代码(e.g. 不为函数返回准备寄存器),并消除不必要的警告信息

6.7.2 unused

用于函数和变量,表示该函数或变量可能不会被用到,可避免编译器产生的警告

6.7.3 aligned

用于变量、结构体或联合体,指定变量、结构体或联合体的对齐方式,以字节为单位

struct  example_struct
{char a;int b;long c;
}__attribute__((aligned(4))); // 以4字节对齐

下面通过一个实例来了解下aligned的作用:

aligned(4)

aligned(16)

分析:如果将__attribute__((aligned(n)))作用于一个类型(n必须为2的幂次方),那么该类型变量在分配地址空间时,其存放的地址一定按照n字节对齐;并且其占用的空间也是n的整数倍

从验证结果看,此处按16B对齐是整个结构体的起始地址,并不是结构体中每个成员

6.7.4 packed

用于变量和类型,用于变量或结构体时表示使用最小可能的对齐;用于枚举、结构体或联合体类型是表示该类型使用最小的内存

说明:可见对整个结构体类型使用packed属性后,不再对齐和补齐(但实际编程时不建议使用,因为非对齐的内存访问效率较低)

6.7.5 section

用于函数或数据,表示将其链接到指定的段

__attribute__((section("section_name")))

6.7.6 format

用于函数,表示函数使用printf / scanf风格的参数,指定format属性可以让编译器根据格式串检查参数类型

该属性说明printk的第1个参数是格式串,从第2个参数开始会根据printf函数的格式串规则检查参数

6.8 内建函数

GNU C提供了大量的内建函数,其中大部分是标准C库函数的GNU C编译器内建版本(如memcpy等),他们与对应的标准C库函数功能相同

不属于库函数的其他内建函数的命名通常以__builtin开始

6.8.1 __builtin_constant_p

__builtin_constant_p(EXP)用于判断一个值是否为编译时常数,如果参数EXP的值是常数该函数返回1,否则返回0

6.8.2 __builtin_expect

__builtin_expect(EXP, C)用于为编译器提供分支预测信息,其返回值是整数表达式EXP的值,C的值必须是编译时常数

由于代码中的分支语句会中断流水线,所以可以使用likely & unlikely宏暗示分支容易成立还是不容易成立

说明:可以使用-ansi -pedantic编译选项禁用GNU C语法

7. 内核编程其他主题

7.1 do {} while(0)

使用场景:宏定义

示例:

#define SAFE_FREE(p) do {free(p); p = NULL;} while(0) // 此处没有分号哦~~if (p)SAFE_FREE(p);
else... // do something

说明:此时的宏展开不会有问题。如果仅仅使用花括号,仍可能有潜在风险

#define  SAFE_FREE(p) {free(p); p = NULL;} if (p)SAFE_FREE(p); // 调用后添加分号,是通常的使用习惯
else... // do something

会被展开为,

if (p){free(p); p = NULL;} ;
else... // do something

由于分号的存在,{free(p); p = NULL} ; 表示2 条语句(复合语句 + 空语句),所以else 无法配对,导致编译失败。

问题根源:C 语言中规定语句以分号结束,但有一点例外,就是复合语句是以右花括号结束(})

7.2 goto语句的使用

goto语句在Linux内核源码中一般只用于错误处理

关键:在错误处理时,注销 / 释放资源的顺序和注册 / 申请资源的顺序相反

if (register_a() != 0)goto err;
if (register_b() != 0)goto err1;
if (register_c() != 0)goto err2;//错误处理书写技巧:从err 写起,逐层向上~~
err2:unregister_b();
err1:unregister_a();
err:
return ret;

7.3 内核中的并发

理解关键:内核代码几乎始终不能假定在给定代码段中能够独占CPU

并发原因:

① Linux中的并发进程,可能同时要使用我们的驱动程序

② 中断处理程序

③ 其他异步事件(e.g. 内核定时器)

④ SMP

⑤ 内核抢占

对内核代码的要求:

① 必须可重入,能够同时运行在多个上下文中

② 内核数据结构要保证多个线程能分开执行

③ 访问共享数据的代码必须避免破坏共享数据

7.4 当前进程的获取

如果当前执行的内核操作由某个进程发起,那么可以通过全局项current来获得当前进程

全局项current的实现方式如下,

7.4.1 before 2.6

实现为指向struct task_struct的指针(include/sched.h)

补充:虽然before 2.6版本中的current是一个指向task_struct结构的全局变量,但是内核态在获取当前进程时,依然是通过进程内核栈计算得到,下图为Linux 2.4版本的示例

7.4.2 from 2.6

① 不再是一个全局变量

② 为支持SMP,开发了一种能找到运行在相关CPU上的当前进程的机制

③ 实现时不依赖特定架构,将指向task_struct结构的指针隐藏在内核栈中

说明1:sp变量声明方式

使用register修饰:建议使用寄存器存储该变量

asm("sp"):如果将该变量存储在寄存器中,则使用sp寄存器

该声明起到将sp寄存器的值存储到sp变量的作用

说明2:current_thread_info实现原理

首先看一下内核栈的结构,内核给每个线程分配8KB(THREAD_SIZE)的栈,虽然以联合体的方式存在,但是在栈的低地址处存放的是thread_info

因此内核栈的布局如下图所示,

当内核线程执行到current_thread_info时,其SP堆栈指针指向调用进程所对应的内核线程的栈顶,通过sp & ~(THREAD_SIZE - 1)对齐,就能到达内核栈的低地址处,进而获取thread_info结构的地址

7.5 浮点工具链

Linux的浮点处理有3种方式,其对应的编译选项如下,

① 完全软浮点:-mfloat-abi=soft

② 与软浮点兼容,但是使用FPU硬件:-mfloat-abi=softfp

③ 完全硬浮点:-mfloat-abi=hard

 

说明:由于目前主流的ARM芯片都自带VFP或者NEON等浮点处理单元(FPU),所以对硬浮点的需求更加强烈。在工具链前缀中包含"hf"的为支持完全硬浮点的工具链,比如arm-linux-gnueabihf-gcc

7.6 其他细节

① 内核API中以双下划线开头的函数,是接口的底层组件,应该谨慎使用

② 内核代码不能实现浮点运算(当然内核代码中也不需要~~)


http://chatgpt.dhexx.cn/article/NtHvDKEN.shtml

相关文章

Linux设备驱动模型

目录 一、linux设备驱动模型简介1、什么是设备驱动模型2、为什么需要设备驱动模型3、驱动开发的2个点 二、设备驱动模型的底层架构1、kobject2、kobj_type3、kset 三、总线式设备驱动组织方式1、总线2、设备3、驱动4、类5、总结 四、platform平台总线工作原理1、何为平台总线2、…

【linux内核分析与应用-陈莉君】字符设备驱动

目录 1.什么是字符设备 2.如何来描述字符设备 3 struct cdev与const struct file_operations之间的关系 4.struct file_operations源码 5.字符设备驱动框架 6.编写字符设备驱动的步骤 7.字符设备结构 8.字符设备驱动程序的注册 9.从系统调用到驱动程序 10.用户空间与内…

微信小程序云数据库使用讲解

第一步&#xff1a;注册开通 单击云开发 第二步&#xff1a;创建数据库 选择数据库&#xff0c;并点击号创建一个集合 输入名字 创建完毕后点击添加记录即可添加数据 数据库获取&#xff1a; 查询&#xff1a; 查询指令&#xff1a;

微信小程序云开发入门-数据库插入数据(包含批量)

一、前言 文章将介绍如何在微信小程序云开发中向云开发数据库插入数据&#xff08;单条或批量&#xff09;。 写法有好几种&#xff0c;文章将会一一进行对比&#xff0c;看看每种写法之间有何优缺点&#xff0c;如何让代码看起来更优雅。 为了更加贴合实际的开发逻辑&#xf…

【微信小程序】如何获取微信小程序云开发数据库的数据并渲染到页面?

前言 上一篇博客我把微信小程序云开发数据库操作&#xff08;增删改查&#xff09;的实现方法都已经分享出来啦&#xff0c;可以戳链接进去阅读哦 【微信小程序】小程序云开发实现数据库增删改查(小白速度Get起来&#xff01;&#xff01;一步步教你如何实现) 基于微信小程序…

开发一个可以查询并显示数据库内容的微信小程序

使用微信开发者工具可以创建云数据库&#xff0c;并通过代码可以查询并在客户端显示数据库的内容。 附&#xff1a;小程序一个功能页面有wxml(客户端呈现)&#xff0c;js(功能函数)&#xff0c;json&#xff0c;wxss(个性化处理)&#xff0c;这些是局部的文件。还有全局的文件如…

微信小程序开发---连接云开发数据库,实现数据获取

之前几篇博客里面都详细交代了如何配置云函数&#xff0c;现在就讲一下关于云函数中数据库的使用&#xff0c;主要是讲如何从云开发平台的数据库中调取数据 我们直接来到需要调用数据库数据的页面的js文件&#xff0c;直接设置全局变量&#xff0c;来便于后续调用数据库 const …

微信小程序查询数据库

微信小程序云开发的官方例子&#xff1a; const db wx.cloud.database() //获取数据库的引用 const _ db.command //获取数据库查询及更新指令 db.collection("china") //获取集合china的引用.where({ //查询的条件指令wheregdp: _.gt(3000) …

微信小程序云数据库操作

微信小程序云数据库操作 1、云数据库简介1.1 数据类型Date地理位置Null 1.2 权限控制 2、云数据库操作2.1 查询数据2.1.1 通过collection.doc获取一条记录2.1.2 通过collection.get获取所有记录的数据2.1.3 通过document.get获取某一条记录的数据2.1.4 通过collection.count获取…

微信小程序取本地数据库数据(实测有图)

测试效果如下&#xff1a; 本实验主要分为如下几个步骤&#xff1a; 一、安装数据库 二、安装PHPApache 三、编辑微信小程序代码 前两项的简单介绍在如下连接&#xff1a; PHPApache 四、本文主要介绍第三项的内容 需要用到的文件如下&#xff1a; 1、新建微信小程序工程 2、…

微信小程序云开发(数据库)

开发者可以使用云开发开发微信小程序、小游戏&#xff0c;无需搭建服务器&#xff0c;即可使用云端能力。 云开发为开发者提供完整的云端支持&#xff0c;弱化后端和运维概念&#xff0c;无需搭建服务器&#xff0c;使用平台提供的 API 进行核心业务开发&#xff0c;即可实现快…

微信小程序云开发(云数据库篇)

微信小程序云开发[云数据库篇] 云数据库关系型数据库和 JSON 数据库对比数据类型数据库操作联表查询事务处理 云数据库 云开发提供了一个 JSON 数据库&#xff0c;顾名思义&#xff0c;数据库中的每条记录都是一个 JSON 格式的对象。 一个数据库可以有多个集合&#xff08;相当…

微信小程序云开发 1 - 数据库

微信小程序云开发最重要的有两点&#xff1a; 1、云数据库&#xff1b; 2、云函数&#xff1b; 学会这两点基本就能够进行微信小程序的云开发&#xff1b; 首先&#xff0c;我们先看微信小程序云数据库的基本操作&#xff1a; 1&#xff09;打开微 信开发者工具&#xff0…

微信小程序笔记 -- 数据库

6.15 学习微信小程序 -- 数据库 数据库1. 初始化2. 数据库操作2.1 数据类型2.2 增删查改2.2.1 增加/插入 数据&#xff08;add方法&#xff09;2.2.2 删除数据&#xff08;remove方法&#xff09;2.2.3 查看数据&#xff08;get&#xff0c;where&#xff09;2.2.4 更新数据&am…

nohup忽略SIGHUP信号

今天遇到一个问题&#xff1a;开启终端启动gunicorn进程后台运行&#xff0c;终端不关闭时&#xff0c;可以导入excel&#xff0c;关闭终端后&#xff0c;不能导入excel。原因是&#xff0c;xlrd模块需要向控制台输出内容&#xff0c;终端关闭后&#xff0c;控制台消失&#xf…

sighup问题

2019独角兽企业重金招聘Python工程师标准>>> SIGHUP会在以下3种情况下被发送给相应的进程&#xff1a; 1、终端关闭时&#xff0c;该信号被发送到session首进程以及作为job提交的进程&#xff08;即用 & 符号提交的进程&#xff09; 2、session首进程退出时&…

Linux——1、守护进程、SIGHUP与nohup

一、守护进程 脱离于终端并且在后台运行的进程&#xff0c;用于长期运行&#xff0c;守护自己的职责&#xff08;如&#xff1a;监听端口、服务等&#xff09;。 1、特点&#xff1a; 不受用户登录、注销影响。大多数Linux下的服务器都是利用守护进程实现的&#xff0c;如My…

Failed to ignore SIGHUP: No error

memcache下载:https://www.runoob.com/memcached/window-install-memcached.html 下载下来需要install一下: c:\memcached\memcached.exe -d install c:\memcached\memcached.exe -d start c:\memcached\memcached.exe -d stop 然后报错: 我下载的版本是1.4.5按照网上说的: 以…

安装Memcached:Failed to ignore SIGHUP: No error 解决方式

Memcached安装1.4.5 版本 管理员运行cmd&#xff0c;出现如下错误&#xff1a; 解决方式&#xff1a; 命令行中输入schtasks /create /sc onstart /tn memcached /tr "c:\memcached\memcached.exe -m 512",回车 注意&#xff1a;c:\memcached\memcached.exe替换自己…

系统信号(SIGHUP ,SIGPIPE,SIGURG)

对于信号的介绍&#xff0c;我再前面的一篇博客中做过专门的总结&#xff0c;感兴趣的可以看看。本文主要介绍在网络编程中几个密切相关的函数&#xff1a;SIGUP&#xff0c;SIGPIPE&#xff0c;SIGURG。 SIGHUP信号 在介绍SIGHUP信号之前&#xff0c;先来了解两个概念&#x…