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
原创粉丝点击