linux信号(二)--unix环境高级编程读书笔记
来源:互联网 发布:淘宝卖家如何代销上货 编辑:程序博客网 时间:2024/05/21 17:36
1.信号集
在linux中,可以用一个称为信号集的数据类型 sigset_t,来表示所有的被阻塞信号的一个集合。对这个集合的操作函数有:
#include <signal.h> int sigemptyset(sigset_t *set); int sigfillset(sigset_t *set); int sigaddset(sigset_t *set, int signum); int sigdelset(sigset_t *set, int signum); int sigismember(const sigset_t *set, int signum);
其中,sigemptyset 和 sigfillset 函数用来对信号集中的数据进行初始化。sigemptyset 函数用来将信号集中的所有位都初始化为0,sigfillset 函数用来将信号集中的所有位都初始化为1。这里需要注意的是下面的语句:
sigemptyset(&set);
不能用
*set = 0;
来代替,因为不能保证后面的做法与给定的系统上信号集的实现相一致。
sigaddset 函数用来向信号集中添加一个信号,而 sigdelset 用来从给定的信号集中删除一个信号。sigismember 用来测试一个给定的信号是否存在于给定的信号集中。
2.sigprocmask函数
sigprocmask 函数用来更改进程的信号屏蔽字,它的函数原型如下:
#include <signal.h> int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
这个函数根据第一个参数的要求来设置新的信号屏蔽字,并且保存老的信号屏蔽字。第一个参数有三种选择,
SIG_BLOCK:用来将 set 指定的信号屏蔽字和现在的信号屏蔽字按位或后,得到的结果作为新的信号屏蔽字SIG_UNBLOCK:将 set 指定的信号屏蔽字从现在的信号屏蔽字中去除之后,得到的结果作为新的信号屏蔽字,这种情况与第一种情况正好相反SIG_SETMASK:直接将 set 指定的信号屏蔽字作为新的信号屏蔽字
这个函数将老的信号屏蔽字保存在 oldset 指定的信号集中。简单的说就是,SIG_BLOCK 是按位或操作,SIG_SETMASK 是直接设置。
另外,这个函数中,如果 set 参数 NULL 的话,那么将不修改现在的信号屏蔽字,并且可以将现在的信号屏蔽字保存在 oldmask 中。我们也可以只设置信号屏蔽字,而不保存老的信号屏蔽字,只需要将 oldset 设置为NULL即可。
3.sigpending函数
sigpending 函数用来返回对于当前进程阻塞或者是当前未决的信号集,它的函数原型如下:
#include <signal.h> int sigpending(sigset_t *set);
该信号集通过 set 参数来返回。下面的程序演示了 sigpending 和 sigprocmask 函数的使用方法
#include <signal.h>#include <stdio.h>#include <stdlib.h>static void sig_quit(int);int main(void){ sigset_t oldmask,newmask,pendmask; if(signal(SIGQUIT,sig_quit)<0) { printf("signal(SIGQUIT) error\n"); return -1; } sigemptyset(&newmask); sigaddset(&newmask,SIGQUIT); if(sigprocmask(SIG_BLOCK,&newmask,&oldmask)<0) { printf("SIG_BLOCK error\n"); return -1; } sleep(5); if(sigpending(&pendmask)<0) { printf("sigpending error\n"); return -1; } if(sigismember(&pendmask,SIGQUIT)) { printf("\nSIGQUIT pending\n"); } if(sigprocmask(SIG_SETMASK,&oldmask,NULL)<0) { printf("SET_MASK error\n"); return -1; } printf("SIGQUIT unblocked\n"); sleep(5); return 0;}static void sig_quit(int signo){ printf("caught SIGQUIT\n"); if(signal(SIGQUIT,SIG_DFL)==SIG_ERR) { printf("SIGQUIT error\n"); exit(-1); }}
将程序编译成 a.out,用下面命令运行程序:
./a.out
输入退出信号,也就是按 CTRL+\(反斜扛) ,即:
^\
可以看到输出的结果为:
SIGQUIT pendingcaught SIGQUITSIGQUIT unblocked
这是因为我们刚开始使用 sigprocmask 函数将退出信号阻塞,然后通过按键向进程发送退出信号,之后我们调用 sigpending 函数用来获取当前未决和阻塞的信号,并且将这些阻塞的信号输出,最后将信号屏蔽字恢复为最初的模样。在这里就可一体会到 sigprocmask 函数的使用方法和作用。
4.sigaction函数
sigaction 函数和 signal 函数十分的相似,它也是用来给信号注册处理函数的,它的函数原型如下:
#include <signal.h> int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
sigaction 的第一个参数是信号的名称,第二个参数用来指定新的信号处理函数,第三个参数用来保存老的信号处理函数。sigaction 结构体的构造如下:
struct sigaction { void (*sa_handler)(int); sigset_t sa_mask; int sa_flags; };
第一个参数用来指定信号处理函数。第二个参数用来指定当执行信号处理函数时,需要阻塞的信号集。需要注意的是,当在执行当前的信号处理程序时,后面再来的信号如果和当前信号相同,同样会被阻塞。第三个参数用来指定一些信号处理的选项。相信通过上面的阅读,你已经知道 sigaction 函数可以用来实现 signal 函数,下面就是实现 signal 函数的代码:
#include <signal.h>#include <stdio.h>#include <stdlib.h>typedef void (Sigfunc) (int);Sigfunc* signal1(int signo,Sigfunc* func){ struct sigaction act,oact; act.sa_handler = func; sigemptyset(&act.sa_mask); act.sa_flags = 0; if(signo==SIGALRM) { #ifdef SA_INTERRUPT act.sa_flags |= SA_INTERRUPT; #endif } else { #ifdef SA_RESTART act.sa_flags |= SA_RESTART; #endif } if(sigaction(signo,&act,&oact)<0) { return SIG_ERR; } return oact.sa_handler;}
上面的对 sa_flags 的设置说明,如果是 SIGALRM 信号的话,被打断的系统调用就不需要再重新启动,而如果是其他信号打断的系统调用就需要重新启动。
5.sigsetjmp和siglongjmp
这两个函数和 setjmp 及 longjmp 是什么关系呢? sigsetjmp 和 siglongjmp 函数是用在信号处理程序中的。当调用 sigsetjmp 时,它会将当前的信号屏蔽字保存起来,当调用 siglongjmp 返回时,会恢复之前保存的信号屏蔽字。而 setjmp 和longjmp 函数却没有说明有这个功能,因此,系统向我们提供了这两个函数,这两个函数的函数原型是:
int sigsetjmp(sigjmp_buf env, int savesigs); void siglongjmp(sigjmp_buf env, int val);
当 sigsetjmp 中的第二个参数 savesigs 不为 0 ,就会将当前的信号屏蔽字保存在第一个参数 sigjmp 中。下面可以通过一个程序来演示一下这两个函数的使用,以及在执行信号处理程序时,系统的信号屏蔽字是如何包括刚刚被捕捉到的信号的。
#include <signal.h>#include <setjmp.h>#include <time.h>#include <stdlib.h>#include <stdio.h>#include <errno.h>static void sig_usr1(int),sig_alrm(int);static sigjmp_buf jmpbuf;static volatile sig_atomic_t canjmp;void pr_mask(const char* str);int main(void){ if(signal(SIGUSR1,sig_usr1)==SIG_ERR) { printf("signal(SIGUSR1) error\n"); return -1; } if(signal(SIGALRM,sig_alrm)==SIG_ERR) { printf("signal(AIGALRM) error\n"); return -1; } pr_mask("starting main..."); if(sigsetjmp(jmpbuf,1)) { pr_mask("ending main..."); exit(0); } canjmp = 1; for(;;) { pause(); } return 0;}void pr_mask(const char* str){ sigset_t sigset; int errno_save; errno_save = errno; if(sigprocmask(0,NULL,&sigset)<0) { printf("sigprocmask error\n"); exit(-1); } printf("%s\n",str); if(sigismember(&sigset,SIGINT)) printf("SIGINT\n"); if(sigismember(&sigset,SIGQUIT)) printf("SIGQUIT\n"); if(sigismember(&sigset,SIGUSR1)) printf("SIGUSR1\n"); if(sigismember(&sigset,SIGALRM)) printf("SIGALRM\n"); printf("\n"); errno = errno_save;}static void sig_usr1(int signo){ long starttime; if(canjmp == 0) { return; } pr_mask("in sig_usr1..."); alarm(3); starttime = time(NULL); for(;;) { if(time(NULL)>starttime+5) { break; } } pr_mask("ending sig_usr1..."); siglongjmp(jmpbuf,1);}static void sig_alrm(int signo){ pr_mask("in sig_alrm");}将程序编译成 a.out,用下面命令在后台运行程序:
./a.out &
会输出当前的进程 PID,
[1] 2928
向这个进程发送信号:
kill -SIGUSR1 2928
得到的输出结果如下:
in sig_usr1...SIGUSR1asus@asus-K43SJ:~/unix/chapter10$ in sig_alrmSIGUSR1SIGALRMending sig_usr1...SIGUSR1ending main...[1]+ Done ./a.out
pr_mask 函数用来打印,当前被阻塞的信号。通过这个例子我们可以清楚的看到每一步的阻塞的信号有哪些。
6.sigsuspend函数
sigsuspend 函数用来将设置信号屏蔽字和 pause 调用结合为一个原子操作,例如下面的代码:
if(sigprocmask(SIG_SETMASK,&oldmask,NULL)<0){ exit(-1);}pause();在 sigprocmask 函数和 pause 函数之间发生的信号就会丢失,正是出于这个原因,我们提出了 sigsuspend 函数,这个函数的函数原型如下:
#include <signal.h> int sigsuspend(const sigset_t *mask);
这个函数先将信号屏蔽字设置为参数 mask 指定的信号屏蔽字,然后去睡眠,直到它等到一个信号把它打断,并且当从这个信号处理函数中返回时,它才返回。当它返回时,仍然把信号屏蔽字设置为调用它之前的信号屏蔽字。下面的程序显示了这个函数的使用方法:
#include <signal.h>#include <stdlib.h>#include <stdio.h>#include <errno.h>static void sig_int(int);void pr_mask(const char* str);int main(void){ sigset_t oldmask,newmask,zeromask; if(signal(SIGINT,sig_int)==SIG_ERR) { printf("signal(SIGINT) error\n"); return -1; } sigemptyset(&zeromask); sigemptyset(&newmask); sigaddset(&newmask,SIGINT); if(sigprocmask(SIG_BLOCK,&newmask,&oldmask)<0) { printf("SIG_BLOCK error\n"); return -1; } pr_mask("in critical region..."); if(sigsuspend(&zeromask)!=-1) { printf("sigsuspend error\n"); return -1; } pr_mask("after return from sigsuspend..."); if(sigprocmask(SIG_SETMASK,&oldmask,NULL)<0) { printf("SIG_SETMASK error\n"); return -1; } pr_mask("ending main..."); return 0;}static void sig_int(int signo){ pr_mask("in sig_int...");}void pr_mask(const char* str){ sigset_t sigset; int errno_save; errno_save = errno; if(sigprocmask(0,NULL,&sigset)<0) { printf("sigprocmask error\n"); exit(-1); } printf("%s\n",str); if(sigismember(&sigset,SIGINT)) printf("SIGINT\n"); if(sigismember(&sigset,SIGQUIT)) printf("SIGQUIT\n"); if(sigismember(&sigset,SIGUSR1)) printf("SIGUSR1\n"); if(sigismember(&sigset,SIGALRM)) printf("SIGALRM\n"); printf("\n"); errno = errno_save;}将程序编译成 a.out,运行程序,输出结果如下:
in critical region...SIGINT
我们输入 CTRL+C,可以看到,程序接着输出:
^Cin sig_int...SIGINTafter return from sigsuspend...SIGINTending main...
这样,我们看到了 sigsuspend 在返回之后,仍然将信号屏蔽字恢复为之前的值。
下面我们再看一个,用 sigsuspend 函数用来同步父子进程的例子:
#include <stdio.h>#include <signal.h>#include <stdlib.h>#include <sys/types.h>static volatile sig_atomic_t sigflag;static sigset_t newmask,oldmask,zeromask;static void sig_usr(int signo){ sigflag = 1;}void TELL_WAIT(void){ if(signal(SIGUSR1,sig_usr)==SIG_ERR) { printf("signal(SIGUSR1) error\n"); exit(-1); } if(signal(SIGUSR2,sig_usr)==SIG_ERR) { printf("signal(SIGUSR2) error\n"); exit(-1); } sigemptyset(&zeromask); sigemptyset(&newmask); sigaddset(&newmask,SIGUSR1); sigaddset(&newmask,SIGUSR2); if(sigprocmask(SIG_BLOCK,&newmask,&oldmask)<0) { printf("SIG_BLOCK error\n"); exit(-1); }}void TELL_PARENT(pid_t pid){ kill(pid,SIGUSR2);}void WAIT_PARENT(void){ while(sigflag==0) { sigsuspend(&zeromask); } sigflag = 0;}void TELL_CHILD(pid_t pid){ kill(pid,SIGUSR1);}void WAIT_CHILD(void){ while(sigflag==0) { sigsuspend(&zeromask); } sigflag = 0;}void charatatime(char* str){ char* ptr; char c; setbuf(stdout,NULL); for(ptr=str;*ptr!='\0';ptr++) { c = *ptr; putc(c,stdout); }}int main(void){ pid_t pid; TELL_WAIT(); if((pid=fork())<0) { printf("fork error\n"); exit(-1); } else if(pid==0) { WAIT_PARENT(); charatatime("output from child\n"); } else { charatatime("output from parent\n"); TELL_CHILD(pid); } return 0;}
我们这个程序编译之后,可以看到总是父进程先执行。如果没有这些同步的话,就可能会造成输出错乱,父子进程交替输出的情况。
7.system函数
system 函数用来在一个程序中调用另一个可执行程序,在 system 函数的实现中,需要在父进程中忽略 SIGINT 和 SIGQUIT 信号,并且阻塞 SIGCHLD 信号。这个函数的实现如下:
int system(const char* cmdstring){ int status; pid_t pid; struct sigaction ignore,saveintr,savequit; sigset_t chldmask,savemask; if(cmdstring==NULL) { return 1; } ignore.sa_handler = SIG_IGN; sigemptyset(&ignore.sa_mask); ignore.sa_flags = 0; if(sigaction(SIGINT,&ignore,&saveintr)<0) { printf("sigaction error\n"); return -1; } if(sigaction(SIGQUIT,&ignore,&savequit)<0) { printf("sigaction error\n"); return -1; } sigemptyset(&chldmask); sigaddset(&chldmask,SIGCHLD); if(sigprocmask(SIG_BLOCK,&chldmask,&savemask)<0) { printf("SIG_BLOCK error\n"); return -1; } if( (pid=fork())<0 ) { printf("fork error\n"); status = -1; } else if(pid==0) { sigaction(SIGQUIT,&savequit,NULL); sigaction(SIGINT,&saveintr,NULL); if(sigprocmask(SIG_SETMASK,&savemask,NULL)<0) { printf("SIG_SETMASK error\n"); return -1; } execl("/bin/bash","bash","-c",cmdstring,(char*)0); _exit(0); } else { while(waitpid(pid,&status,0)<0) { if(errno!=EINTR) { status = -1; break; } } } sigaction(SIGQUIT,&savequit,NULL); sigaction(SIGINT,&saveintr,NULL); if(sigprocmask(SIG_SETMASK,&savemask,NULL)<0) { printf("SIG_SETMASK error\n"); return -1; } return status;}
1 0
- linux信号(二)--unix环境高级编程读书笔记
- linux信号(一)--unix环境高级编程读书笔记
- 《unix环境高级编程》-10.信号-读书笔记
- 《UNIX环境高级编程》十信号读书笔记
- 《UNIX环境高级编程》读书笔记之信号(1)
- 《UNIX环境高级编程》读书笔记之信号(2)
- unix环境高级编程之信号篇(二)
- UNIX环境高级编程-读书笔记-网络编程(二)
- 文件和目录(二)--unix环境高级编程读书笔记
- UNIX环境高级编程-读书笔记-文件操作(二)
- UNIX环境高级编程读书笔记--信号屏蔽字
- UNIX环境高级编程读书笔记--信号屏蔽字
- unix环境高级编程读书笔记:关于信号的理解
- unix环境高级编程-10(信号)
- UNIX环境高级编程--信号(十)
- UNIX环境高级编程(十)信号
- unix环境高级编程-信号(1)
- unix环境高级编程-信号(2)
- 智能电视TV开发---录像
- 食物相克
- Struts2 验证码图片实例
- 如何禁用和启用mysql外键约束
- Code First 与 DataBase First(.edmx)的区别以及 对于EF对非 ms-sql server数据库的支持
- linux信号(二)--unix环境高级编程读书笔记
- hive中实现差集等操作
- 第三周JS接触学习
- linux-3.12.6 移植yaffs2文件系统记录
- 结构体初始化的方法
- boost库 - Windows下VS2008中安装boost库
- jqGrid增删查改弹出页面一闪就自动关闭
- Java编码问题汇总
- cf /376/C