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 pending
caught SIGQUIT
SIGQUIT 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_RESTARTact.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_alrm
SIGUSR1
SIGALRMending 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;
}

















												

linux信号(二)--unix环境高级编程读书笔记相关推荐

  1. 文件和目录(二)--unix环境高级编程读书笔记

    在linux中,文件的相关信息都记录在stat这个结构体中,文件长度是记录在stat的st_size成员中.对于普通文件,其长度可以为0,目录的长度一般为1024的倍数,这与linux文件系统中blo ...

  2. linux信号(一)--unix环境高级编程读书笔记

    1.信号的概念 在这里要给出一个信号的准确概念感觉很困难,可以这么说,信号就是进程之间或者内核与进程间异步通信的一种机制,有点类似于中断的性质.在  linux  系统中有  31  种信号,每一种信 ...

  3. linux进程控制(一)--unix环境高级编程读书笔记

    1.进程PID和特殊的3个进程 每一个进程在系统中都有一个唯一的标识,这个标识叫做进程标识符,或者叫  PID(process identity).我们可以通过调用  getpid  函数来获取一个进 ...

  4. linux系统数据文件和信息--unix环境高级编程读书笔记

    linux系统中的数据文件有很多,在这一章里介绍的主要内容是和系统有关的一系列文件,包括passwd,shadow,group,utmp,wtmp以及一些系统的相关信息和时间的相关操作. 1.pass ...

  5. unix进程的环境--unix环境高级编程读书笔记

    1.进程的启动 进程总是从   main   函数开始执行的,main函数的函数原型如下: int main(int argc,char* argv[]); 当内核启动  c   程序时,使用一个   ...

  6. 文件io(一)--unix环境高级编程读书笔记

    unix-like(后面以linux为例)系统中的文件操作只需要五个函数就足够了,open.close.read.write以及lseek.这些操作被称为不带缓存的io,这里有必要说一下带缓存和不带缓 ...

  7. 高级IO(一)--UNIX环境高级编程读书笔记

    在前面学习了文件IO,标准IO和终端IO,现在学习高级IO,UNIX中怎么有这么多的IO. 1.非阻塞IO 可以将系统调用分为两类:低速系统调用和其他.低速系统调用是可能会使进程永远阻塞的一类系统调用 ...

  8. 标准IO库--unix环境高级编程读书笔记

    标准IO库是C语言提供的一个库,不光存在于linux中,在windows中也是有的.标准IO库和文件IO的不同是,标准IO库是对文件IO(即系统调用)的封装,并且在用户层添加了一些缓冲区. 文件IO的 ...

  9. APUE Unix环境高级编程读书笔记

    .. 转载于:https://www.cnblogs.com/solitrarychen/p/5407536.html

最新文章

  1. 小程序判断用户在线状态
  2. 报错:org.apache.hadoop.hive.metastore.HiveMetaException: Failed to get schema version.
  3. Zephyr内核到1.5版本的改进
  4. Centos下搭建ftp服务器
  5. 自动摘要php,修改DEDECMS文章自动摘要长度或者取掉文章摘要
  6. Spark之Master主备切换机制原理
  7. [Unity3D]unity3d5.0简单的调用摄像头
  8. 云栖专辑 | 阿里开发者们的第11个感悟:拥抱变化,用正确的方法对待工作
  9. 数据中台、数据仓库和数据湖传统的区别
  10. Kubernetes学习总结(18)—— Kubernetes 容器网络
  11. hibernate mysql语句_打印hibernate的SQL语句的几种办法
  12. lua脚本的加密和解密(GG游戏脚本)
  13. 系列学习 Gateway 之第 4 篇 —— 网关限流
  14. 计算机共享文件误删怎么恢复,在电脑上不小心把共享里的EXCEL表格数据给删除了,该怎么恢复这个文件的数据啊?,excel表格里误删数据恢复...
  15. 从1,3,5,7,9,11,13,15中选3个数(选择可重复)作和得30
  16. Discourse开源论坛搭建
  17. 各种品牌类型的主板 电脑开机启动选项快捷键
  18. 极点输入法如何关闭单过了模式
  19. java面试宝典(综合知识)
  20. android 打开日历功能,Android使用GridView实现日历的简单功能

热门文章

  1. 狼来了!中国房地产的实质--比喻太生动了
  2. WPF在一个窗口中实现多个视图
  3. [转载] Python字符串操作方法详解
  4. [转载] 生成对角矩阵 numpy.diag
  5. [转载] pandas DataFrame apply()函数(1)
  6. docker容器日志清理
  7. Codeforces 466E Information Graph
  8. python3.7安装wxPython后导入wx报错
  9. 淡定的写代码,淡定的人生
  10. pytorch---之指定GPU