2023年8月2日发(作者:)
第一章 信号
一、 信号的概念
信号机制是进程之间相互传递消息的一种方法,信号全称为软中断信号,也有人称作软中断。从它的命名可以看出,它的实质和使用很象中断。所以,信号可以说是进程控制的一部分。
1. 基本概念
软中断信号(signal,又简称为信号)用来通知进程发生了异步事件。进程之间可以互相通过系统调用kill发送软中断信号。内核也可以因为内部事件而
给进程发送信号,通知进程发生了某个事件。注意,信号只是用来通知某进程发生了什么事件,并不给该进程传递任何数据。
收到信号的进程对各种信号有不同的处理方法。处理方法可以分为三类:第一种是类似中断的处理程序,对于需要处理的信号,进程可以指定处理函数,由该函数来处 理。第二种方法是,忽略某个信号,对该信号不做任何处理,就象未发生过一样。第三种方法是,对该信号的处理保留系统的默认值,这种缺省操作,对大部分的信 号的缺省操作是使得进程终止。进程通过系统调用signal来指定进程对某个信号的处理行为。
在进程表的表项中有一个软中断信号域,该域中每一位对应一个信号,当有信号发送给进程时,对应位置位。由此可以看出,进程对不同的信号可以同时保留,但对于同一个信号,进程并不知道在处理之前来过多少个。
2. 信号的类型
发出信号的原因很多,这里按发出信号的原因简单分类,以了解各种信号:
(1) 与进程终止相关的信号。当进程退出,或者子进程终止时,发出这类信号。
(2) 与进程例外事件相关的信号。如进程越界,或企图写一个只读的内存区域(如程序正文区),或执行一个特权指令及其他各种硬件错误。
(3) 与在系统调用期间遇到不可恢复条件相关的信号。如执行系统调用exec时,原有资源已经释放,而目前系统资源又已经耗尽。
(4) 与执行系统调用时遇到非预测错误条件相关的信号。如执行一个并不存在的系统调用。
(5) 在用户态下的进程发出的信号。如进程调用系统调用kill向其他进程发送信号。
(6) 与终端交互相关的信号。如用户关闭一个终端,或按下break键等情况。
(7) 跟踪进程执行的信号。
Linux支持的信号列表如下。很多信号是与机器的体系结构相关的,首先列出的是POSIX.1中列出的信号:
信号 值 处理动作 发出信号的原因
----------------------------------------------------------------------
SIGHUP 1 A 终端挂起或者控制进程终止
SIGINT 2 A 键盘中断(如break键被按下)
SIGQUIT 3 C 键盘的退出键被按下
SIGILL 4 C 非法指令
SIGABRT 6 C 由abort(3)发出的退出指令
SIGFPE 8 C 浮点异常
SIGKILL 9 AEF Kill信号
SIGSEGV 11 C 无效的内存引用
SIGPIPE 13 A 管道破裂: 写一个没有读端口的管道
SIGALRM 14 A 由alarm(2)发出的信号
SIGTERM 15 A 终止信号
SIGUSR1 30,10,16 A 用户自定义信号1
SIGUSR2 31,12,17 A 用户自定义信号2
SIGCHLD 20,17,18 B 子进程结束信号
SIGCONT 19,18,25 进程继续(曾被停止的进程)
SIGSTOP 17,19,23 DEF 终止进程
SIGTSTP 18,20,24 D 控制终端(tty)上按下停止键
SIGTTIN 21,21,26 D 后台进程企图从控制终端读
SIGTTOU 22,22,27 D 后台进程企图从控制终端写
下面的信号没在POSIX.1中列出,而在SUSv2列出
信号 值 处理动作 发出信号的原因
--------------------------------------------------------------------
SIGBUS 10,7,10 C 总线错误(错误的内存访问)
SIGPOLL A Sys V定义的Pollable事件,与SIGIO同义
SIGPROF 27,27,29 A Profiling定时器到
SIGSYS 12,-,12 C 无效的系统调用 (SVID)
SIGTRAP 5 C 跟踪/断点捕获
SIGURG 16,23,21 B Socket出现紧急条件(4.2 BSD)
SIGVTALRM 26,26,28 A 实际时间报警时钟信号(4.2 BSD)
SIGXCPU 24,24,30 C 超出设定的CPU时间限制(4.2 BSD)
SIGXFSZ 25,25,31 C 超出设定的文件大小限制(4.2 BSD)
(对于SIGSYS,SIGXCPU,SIGXFSZ,以及某些机器体系结构下的SIGBUS,Linux缺省的动作是A (terminate),SUSv2 是C (terminate and dump core))。
下面是其它的一些信号
信号 值 处理动作 发出信号的原因
----------------------------------------------------------------------
SIGIOT 6 C IO捕获指令,与SIGABRT同义
SIGEMT 7,-,7
SIGSTKFLT -,16,- A 协处理器堆栈错误
SIGIO 23,29,22 A 某I/O操作现在可以进行了(4.2 BSD)
SIGCLD -,-,18 A 与SIGCHLD同义
SIGPWR 29,30,19 A 电源故障(System V)
SIGINFO 29,-,- A 与SIGPWR同义
SIGLOST -,-,- A 文件锁丢失
SIGWINCH 28,28,20 B 窗口大小改变(4.3 BSD, Sun)
SIGUNUSED -,31,- A 未使用的信号(will be SIGSYS)
(在这里,- 表示信号没有实现;有三个值给出的含义为,第一个值通常在Alpha和Sparc上有效,中间的值对应i386和ppc以及sh,最后一个值对应 mips。信号29在Alpha上为SIGINFO / SIGPWR ,在Sparc上为SIGLOST。)
处理动作一项中的字母含义如下
A 缺省的动作是终止进程
B 缺省的动作是忽略此信号
C 缺省的动作是终止进程并进行内核映像转储(dump core)
D 缺省的动作是停止进程
E 信号不能被捕获
F 信号不能被忽略
上 面介绍的信号是常见系统所支持的。以表格的形式介绍了各种信号的名称、作用及其在默认情况下的处理动作。各种默认处理动作的含义是:终止程序是指进程退 出;忽略该信号是将该信号丢弃,不做处理;停止程序是指程序挂起,进入停止状况以后还能重新进行下去,一般是在调试的过程中(例如ptrace系统调 用);内核映像转储是指将进程数据在内存的映像和进程在内核结构中的部分内容以一定格式转储到文件系统,并且进程退出执行,这样做的好处是为程序员提 供了方便,使得他们可以得到进程当时执行时的数据值,允许他们确定转储的原因,并且可以调试他们的程序。
注意 信号SIGKILL和SIGSTOP既不能被捕捉,也不能被忽略。信号SIGIOT与SIGABRT是一个信号。可以看出,同一个信号在不同的系统中值可能不一样,所以建议最好使用为信号定义的名字,而不要直接使用信号的值。
3. 常见信号功能说明
1) SIGHUP 本信号在用户终端连接(正常或非正常)结束时发出, 通常是在终端的控制进程结束时, 通知同一session内的各个作业, 这时它们与控制终端不再关联.
2) SIGINT 程序终止(interrupt)信号, 在用户键入INTR字符(通常是Ctrl-C)时发出
3) SIGQUIT 和SIGINT类似, 但由QUIT字符(通常是Ctrl-)来控制. 进程在因收到SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信号.
4) SIGILL 执行了非法指令. 通常是因为可执行文件本身出现错误, 或者试图执行数据段. 堆栈溢出时也有可能产生这个信号.
5) SIGTRAP 由断点指令或其它trap指令产生. 由debugger使用.
6) SIGABRT 程序自己发现错误并调用abort时产生.
7) SIGIOT 在PDP-11上由iot指令产生, 在其它机器上和SIGABRT一样.
8) SIGBUS 非法地址, 包括内存地址对齐(alignment)出错. eg: 访问一个四个字长的整数, 但其地址不是4的倍数.
9) SIGFPE 在发生致命的算术运算错误时发出. 不仅包括浮点运算错误, 还包括溢出及除数为0等其它所有的算术的错误.
10) SIGKILL 用来立即结束程序的运行. 本信号不能被阻塞, 处理和忽略.
11) SIGUSR1 留给用户使用
12) SIGSEGV 试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据.
13) SIGUSR2 留给用户使用
14) SIGPIPE Broken pipe
15) SIGALRM 时钟定时信号, 计算的是实际的时间或时钟时间. alarm函数使用该信号.
16) SIGTERM 程序结束(terminate)信号, 与SIGKILL不同的是该信号可以被阻塞和处理. 通常用来要求程序自己正常退出. shell命令kill缺省产生这个信号.
17) SIGCHLD 子进程结束时, 父进程会收到这个信号.
18) SIGCONT 让一个停止(stopped)的进程继续执行. 本信号不能被阻塞.
可以用一个handler来让程序在由stopped状态变为继续执行时完成特定的工作.
例如, 重新显示提示符
19) SIGSTOP 停止(stopped)进程的执行. 注意它和terminate以及interrupt的区别: 该进程还未结束, 只是暂停执行. 本信号不能被阻塞, 处理或忽略.
20) SIGTSTP 停止进程的运行, 但该信号可以被处理和忽略. 用户键入SUSP字符时(通常是Ctrl-Z)发出这个信号
21) SIGTTIN 当后台作业要从用户终端读数据时, 该作业中的所有进程会收到SIGTTIN信号. 缺省时这些进程会停止执行.
22) SIGTTOU 类似于SIGTTIN, 但在写终端(或修改终端模式)时收到.
23) SIGURG 有紧急数据或out-of-band数据到达socket时产生.
24) SIGXCPU 超过CPU时间资源限制. 这个限制可以由getrlimit/setrlimit来读取/改变 25) SIGXFSZ 超过文件大小资源限制.
26) SIGVTALRM 虚拟时钟信号. 类似于SIGALRM, 但是计算的是该进程占用的CPU时间.
27) SIGPROF 类似于SIGALRM/SIGVTALRM, 但包括该进程用的CPU时间以及系统调用的时间.
28) SIGWINCH 窗口大小改变时发出.
29) SIGIO 文件描述符准备就绪, 可以开始进行输入/输出操作.
30) SIGPWR Power failure
当用户按下ctrl+c时,进程被中断,catch()被执行.中断处理函数处理完毕后,转回断点执行下面的指令.
当编写自己的中断处理函数时,注意下面两点:
1.信号不能打断系统调用.
2.信号不能打断信号处理函数.
Signal机制在Linux中是一个非常常用的进程间通信机制,很多人在使用的时候不会考虑该机制是具体如何实现的。signal机制可以被理解成进程的软中断,因此,在实时性方面还是相对比较高的。
每个进程都会采用一个进程控制块对其进行描述,进程控制块中设计了一个signal的位图信息,其中的每位与具体的signal相对应,这与中断机制是保持一致的。当系统中一个进程A通过signal系统调用向进程B发送signal时,设置进程B的对应signal位图,类似于触发了signal对应中断。发送signal只是“中断”触发的一个过程,具体执行会在两个阶段发生:
1、 system call返回。进程B由于调用了system call后,从内核返回用户态时需要检查他拥有的signal位图信息表,此时是一个执行点。
2、 中断返回。进程被系统中断打断之后,系统将CPU交给进程时,需要检查即将执行进程所拥有的signal位图信息表,此时也是一个执行点。
综上所述,signal的执行点可以理解成从内核态返回用户态时,在返回时,如果发现待执行进程存在被触发的signal,那么在离开内核态之后(也就是将CPU切换到用户模式),执行用户进程为该signal绑定的signal处理函数,从这一点上看,signal处理函数是在用户进程上下文中执行的。当执行完signal处理函数之后,再返回到用户进程被中断或者system call(软中断或者指令陷阱)打断的地方。
Signal机制实现的比较灵活,用户进程由于中断或者system call陷入内核之后,将断点信息都保存到了堆栈中,在内核返回用户态时,如果存在被触发的signal,那么直接将待执行的signal处理函数push到堆栈中,在CPU切换到用户模式之后,直接pop堆栈就可以执行signal处理函数并且返回到用户进程了。Signal处理函数应用了进程上下文,并且应用实际的中断模拟了进程的软中断过程。
4. SIGCLD处理方式
APUE上SIGCLD语义写的有点不清楚,到底我们的系统是如何来处理SIGCLD信号呢?
1) SIG_DFL
默认的处理方式是不理会这个信号,但是也不会丢弃子进行状态,所以如果不用wait,waitpid
对其子进行进行状态信息回收,会产生僵尸进程。
2) SIG_IGN
忽略的处理方式,这个方式和默认的忽略是不一样的语意,暂且我们把忽略定义为SIG_IGN,在这种方式下,子进程状态信息会被丢弃,也就是自动回收了,所以不会产生僵尸进程,但是问题也就来了,wait,waitpid却无法捕捉到子进程状态信息了,如果你随后调用了wait,那么会阻塞到所有的子进程结束,并返回错误ECHILD,也就是没有子进程等待。
APUE中P248叙述SIGCHLD如果配置成SIG_IGN也不会产生僵尸进程。是否系统SIG_IGN配置下,对SIGCLD,SIGCHLD做出的处理方式是相同的。
3) 自定义处理方式
SIGCLD会立即检查是否有子进程准备好被等待,这便是SIGCLD最大漏洞了,一旦在信号处理函数中加入了信号处理方式重建的步骤,那么每次设置SIGCLD处理方式时,都会去检查是否有信号到来,如果此时信号的确到来了,先是调用自定义信号处理函数,然后是调用信号处理方式重建函数,在重建配置的时候,会去检查信号是否到来,此时信号未被处理,会再次触发自定义信号处理函数,一直循环。
所以在处理SIGCLD时,应该先wait处理掉了信号信息后,再进行信号处理方式重建。
SIGCHLD在配置信号处理方式时,是不会立即检查是否有子进程准备好被扽带,也不会在此时调用信号处理函数。
二、 Linux下的信号事件
1. 信号的产生
Linux下的信号可以类比于DOS下的INT或者是Windows下的事件.在有一个信号发生时候相信的信号就会发送给相应的进程.在Linux下的信号有以下几个.
我们使用 kill -l 命令可以得到以下的输出结果:
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7)
SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13)
SIGPIPE 14) SIGALRM 15) SIGTERM 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20)
SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26)
SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
关于这些信号的详细解释请查看man 7 signal的输出结果。
信号事件的发生有两个来源:
一个是硬件的原因(比如我们按下了键盘);
一个是软件的原因(比如我们使用系统函数或者是命令发出信号).
最常用的四个发出信号的系统函数是kill, raise, alarm和setit imer函数. setitimer函数我们在计时器的使用 那一章再学习.
#include
#include
int kill(pid_t pid,int sig);
int raise(int sig);
unisigned int alarm(unsigned int seconds);
1) kill系统调用负责向进程发送信号sig
如果pid是正数,那么信号sig被发送到进程pid。
如果pid等于0,那么信号sig被发送到所有和pid进程在同一个进程组的进程。
如果pid等于-1,那么信号发给所有的进程表中的进程,除了最大的那个进程号。
如果pid小于-1,和0一样,只是发送进程组是-pid。
我们用最多的是第一个情况.还记得我们在守护进程那一节的例子吗?我们那个时候用这个函数杀死了父进程守护进程的创建
2) raise系统调用向自己发送一个sig信号
我们可以用上面那个函数来实现这个功能的.
3) alarm函数
alarm函数和时间有点关系了,这个函数可以在seconds秒后向自己发送一个SIGALRM信号 . 下面这个函数会有什么结果呢?
#include
main()
{
unsigned int i;
alarm(1);
for(i=0;1;i++)
printf("I=%d",i);
}
SIGALRM的缺省操作是结束进程,所以程序在1秒之后结束。
2. 信号操作
有时候我们希望进程正确的执行,而不想进程受到信号的影响,比如我们希望上面那个程序在1秒钟之后不结束.这个时候我们就要进行信号的操作了.
信号操作最常用的方法是信号屏蔽.信号屏蔽要用到下面的几个函数.
#include
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set,int signo);
int sigdelset(sigset_t *set,int signo);
int sigismember(sigset_t *set,int signo);
int sigprocmask(int how,const sigset_t *set,sigset_t *oset);
1) sigemptyset函数
初始化信号集合set,将set设置为空。
2) sigfillset函数
也初始化信号集合,只是将信号集合设置为所有信号的集合.
3) sigaddset函数
将信号signo加入到信号集合之中。
4) sigdelset函数
将信号从信号集合中删除.
5) sigismember函数
查询信号是否在信号集合之中.
6) sigprocmask函数
是最为关键的一个函数.在使用之前要先设置好信号集合set.这个函数的作用是将指定的信号集合set加入到进程的信号阻塞集合之中去,如果提供了oset那么当前的进程信号阻塞集合将会保存在oset里面.参数how决定函数的操作方式.
SIG_BLOCK:增加一个信号集合到当前进程的阻塞集合之中.
SIG_UNBLOCK:从当前的阻塞集合之中删除一个信号集合.
SIG_SETMASK:将当前的信号集合设置为信号阻塞集合.
以一个实例来解释使用这几个函数.
#include
#include
#include
int main(int argc,char **argv)
{
double y;
sigset_t intmask;
int i,repeat_factor;
if(argc!=2)
{
fprintf(stderr,"Usage:%s repeat_factor n a",argv[0]);
exit(1);
}
if((repeat_factor=atoi(argv[1]))<1)repeat_factor=10;
sigemptyset(&intmask);
sigprocmask(SIG_UNBLOCK,&intmask,NULL);
fprintf(stderr,"SIGINT signal unblocked n");
for(i=0;i fprintf(stderr,"Unblocked calculation is finished n"); } exit(0); } 7) sigaction函数 程序在运行的时候我们要使用Ctrl+C来结束.如果我们在第一计算的时候发出SIGINT信号 ,由于信号已经屏蔽了,所以程序没有反映.只有到信号被取消阻塞的时候程序才会结束. 注意我们只要发出一次SIGINT信号就可以了,因为信号屏蔽只是将信号加入到信号阻塞集合之中,并没有丢弃这个信号.一旦信号屏蔽取消了,这个信号就会发生作用. 有时候我们希望对信号作出及时的反映的,比如当按下Ctrl+C时,我们不想什么事情也不做,我们想告诉用户你的这个操作不好,请不要重试,而不是什么反映也没有的. 这个时候我们要用到sigaction函数. #include int sigaction(int signo,const struct sigaction *act,struct sigaction *oact); struct sigaction { void (*sa_handler)(int signo); void (*sa_sigaction)(int siginfo_t *info,void *act); sigset_t sa_mask; int sa_flags; void (*sa_restore)(void); } 这个函数和结构看起来是不是有点恐怖呢.不要被这个吓着了,其实这个函数的使用相当简单的.我们先解释一下各个参数的含义. signo很简单就是我们要处理的信号了,可以是任何的合法的信号.有两个信号不能够使用(SIGKILL和SIGSTOP). act包含我们要对这个信号进行如何处理的信息. oact更简单了就是以前对这个函数的处理信息了,主要用来保存信息的,一般用NULL就OK了. 信号结构有点复杂.不要紧我们慢慢的学习. sa_handler是一个函数型指针,这个指针指向一个函数,这个函数有一个参数.这个函数就是我们要进行的信号操作的函数. sa_sigaction,sa_restore和sa_handler差不多的,只是参数不同罢了.这两个元素我们很少使用,就不管了. sa_handler有两个特殊的值:SIG_DEL和SIG__DEL是使用缺省的信号操作函数,而SIG_IGN是使用忽略该信号的操作函数. sa_flags用来设置信号操作的各个情况.一般设置为0好了. sa_mask我们已经学习过了在使用的时候我们用sa_handler指向我们的一个信号操作函数,就可以了. 这个函数复杂,我们使用一个实例来说明.下面这个函数可以捕捉用户的CTRL+C信号.并输出一个提示语句. #include #include #include #include #include #define PROMPT "你想终止程序吗?" char *prompt=PROMPT; void ctrl_c_op(int signo) { write(STDERR_FILENO,prompt,strlen(prompt)); } int main() { struct sigaction act; _handler=ctrl_c_op; sigemptyset(&_mask); _flags=0; if(sigaction(SIGINT,&act,NULL)<0) { fprintf(stderr,"Install Signal Action Error:%s n a",strerror(errno)); exit(1); } while(1); } 在上面程序的信号操作函数之中,我们使用了write函数而没有使用fprintf函数.是因为我们要考虑到下面这种情况.如果我们在信号操作的时候又有一个信号发生,那么程序该如何运行呢? 为了处理在信号处理函数运行的时候信号的发生,我们需要设置sa_mask成员. 我们将我们要屏蔽的信号添加到sa_mask结构当中去,这样这些函数在信号处理的时候就会被屏蔽掉的. 3. 其它信号函数 由于信号的操作和处理比较复杂,我们再介绍几个信号操作函数. #include #include int pause(void); int sigsuspend(const sigset_t *sigmask); 1) pause函数 很简单,就是挂起进程直到一个信号发生了. 2) Sigsuspend函数 也是挂起进程只是在调用的时候用sigmask取代当前的信号阻塞集合. #include int sigsetjmp(sigjmp_buf env,int val); void siglongjmp(sigjmp_buf env,int val); 还记得goto函数或者是setjmp和longjmp函数吗.这两个信号跳转函数也可以实现程序的跳转让我们可以从函数之中跳转到我们需要的地方. 由于上面几个函数,我们很少遇到,所以只是说明了一下,详细情况请查看联机帮助. 4. 一个实例 还记得我们在守护进程创建的哪个程序吗?守护进程在这里我们把那个程序加强一下. 下面这个程序会在也可以检查用户的邮件.不过提供了一个开关,如果用户不想程序提示有新的邮件到来,可以向程序发送SIGUSR2信号,如果想程序提供提示可以发送SIGUSR1信号. #include #include #include #include #include #include #include #include #include #define SLEEP_TIME 10 #define MAX_FILENAME 255 unsigned char notifyflag=1; long get_file_size(const char *filename) { struct stat buf; if(stat(filename,&buf)==-1) { if(errno==ENOENT)return 0; else return -1; } return (long)_size; } void send_mail_notify(void) { fprintf(stderr,"New mail has arrived 007 n"); } void turn_on_notify(int signo) { notifyflag=1; } void turn_off_notify(int signo) { notifyflag=0; } int check_mail(const char *filename) { long old_mail_size,new_mail_size; sigset_t blockset,emptyset; sigemptyset(&blockset); sigemptyset(&emptyset); sigaddset(&blockset,SIGUSR1); sigaddset(&blockset,SIGUSR2); old_mail_size=get_file_size(filename); if(old_mail_size<0)return 1; if(old_mail_size>0) send_mail_notify(); sleep(SLEEP_TIME); while(1) { if(sigprocmask(SIG_BLOCK,&blockset,NULL)<0) return 1; while(notifyflag==0) sigsuspend(&emptyset); if(sigprocmask(SIG_SETMASK,&emptyset,NULL)<0) return 1; new_mail_size=get_file_size(filename); if(new_mail_size>old_mail_size) send_mail_notify; old_mail_size=new_mail_size; sleep(SLEEP_TIME); } } int main(void) { char mailfile[MAX_FILENAME]; struct sigaction newact; struct passwd *pw; if((pw=getpwuid(getuid()))==NULL) { fprintf(stderr,"Get Login Name Error:%s n a",strerror(errno)); exit(1); } strcpy(mailfile,MAIL_DIR); strcat(mailfile,pw->pw_name); _handler=turn_on_notify; _flags=0; sigemptyset(&_mask); sigaddset(&_mask,SIGUSR1); sigaddset(&_mask,SIGUSR2); if(sigaction(SIGUSR1,&newact,NULL)<0) fprintf(stderr,"Turn On Error:%s n a",strerror(errno)); _handler=turn_off_notify; if(sigaction(SIGUSR1,&newact,NULL)<0) fprintf(stderr,"Turn Off Error:%s n a",strerror(errno)); check_mail(mailfile); exit(0); } 信号操作是一件非常复杂的事情,比我们想象之中的复杂程度还要复杂,如果你想彻底的弄清楚信号操作的各个问题,那么除了大量的练习以外还要多看联机手册. 三、 常用函数简要说明 函数 raise(signum) Kill(pid,signum) Alarm(seconds) Signal(signum,action) 说明 向本进程发出一个信号signum 向进程pid发出一个信号signum Seconds秒后发出信号SIGALRM 指定signum信号的动作action Sigemptyset(sigset_t *set) Sigaddset(sigset_t *set ,signum) Sigdelset(sigset_t *set ,signum) Sigfillset(sigset_t *set) Sigprocmask(action,sigset_t *set ,sigset_t *oset) 清空信号集set 将信号signum添加到信号集set中 将信号signum从信号集set中删除 将当前的信号集获取到信号集set中 将信号集set中的根据action与进程的信号集组合。 1、 action=SIG_BLOCK, 将信号集set追加到进程阻塞信号集中。 2、 action=SIG_UNBLOCK将信号集set从进程阻塞信号集中删除 3、 action=SIG_SETMASK用信号集set替换当前进程的阻塞信号集。 查询信号signum是否在信号集set中,1-在;0-不在。 sigismember(sigset_t *set,int signum) sigaction(int signum,const struct sigaction *act,struct sigaction *oact) 四、 Lockf()函数 #include int lockf(int fd, int cmd, off_t len); 1. fd 是打开文件的文件描述符。 为通过此函数调用建立锁定,文件描述符必须使用只写权限(O_WRONLY)或读写权限(O_RDWR)打开。如果调用进程是具有PRIV_LOCKRDONLY 权限的组的成员,它也可以使用lockf()来锁定使用只读权限(O_RDONLY)打开的文件。 2. cmd 是指定要采取的操作的控制值,允许的值在中定义。 如下所示: # define F_ULOCK 0 //解锁 # define F_LOCK 1 //互斥锁定区域 # define F_TLOCK 2 //测试互斥锁定区域 # define F_TEST 3 //测试区域 F_TEST 用于检测在指定的区域中是否存在其他进程的锁定。如果该区域可访问,lockf()将返回 0,否则返回−1;在这种情况下,errno 设置为[EACCES]。F_LOCK 和 F_TLOCK 都用于锁定文件的某个区域(如果该区域可用)。F_ULOCK 用于删除文件区域的锁定。 使用 F_LOCK 或 F_TLOCK 锁定的区域可以完全或部分包含同一个进程以前锁定的区域,或被同一个进程以前锁定的区域包含。此时,这些区域将会合并为一个区域。如果请求要求将新元素添加到活动锁定表中,但该表已满,则会返回一个错误,并且不会锁定新区域。 F_LOCK 和 F_TLOCK 请求仅在采取的操作上有所差异(如果资源不可用)。如果区域已被其他进程锁定,F_LOCK 将使调用进程进入休眠状态,直到该资源可用,而 F_TLOCK 则会返回[EACCES]错误。 F_ULOCK 请求可以完全或部分释放由进程控制的一个或多个锁定区域。如果区域未完全释放,剩余的区域仍将被进程锁定。如果该表已满,将会返回[EDEADLK]错误,并且不会释放请求的区域。 3. len是要锁定或解锁的连续字节数。 要锁定的资源从文件中当前偏移量开始,对于正 len 将向前扩展,对于负 len 则向后扩展(直到但不包括当前偏移量的前面的字节数)。如果 len 为零,则锁定从当前偏移量到文件结尾的区域(即从当前偏移量到现有或任何将来的文件结束标志)。要锁定一个区域,不需要将该区域分配到文件中,因为这样的锁定可以在文件结束标志之后存在。 使用 S_ENFMT 文件模式的常规文件(未设置组执行位)将启用强制策略。启用强制策略后,如果清除了 O_NDELAY,访问锁定区域的读取和写入将进入休眠状态,直到整个区域可用为止,但是如果设置了O_NDELAY,将会返回−1并设置 errno。由其他系统函数(如 exec())访问的文件不受强制策略的影响。 4. 返回值 此函数调用成功后,将返回值 0;否则返回−1,并且设置 errno 以表示该错误。 由于当文件的某部分被其他进程锁定后,变量 errno 将会设置为[EAGAIN]而不是[EACCES],因此可移植应用程序应对这两个值进行预计和测试。例如: if (lockf(fd, F_TLOCK, siz) == -1) //测试锁定 if ((errno == EAGAIN) || (errno == EACCES)) „„. else if ... /* check for other errors*/
发布者:admin,转转请注明出处:http://www.yc00.com/xiaochengxu/1690958344a472967.html
评论列表(0条)