???? 在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(&set);
*set = 0;
???? sigaddset ?函數用來向信號集中添加一個信號,而 ?sigdelset ?用來從給定的信號集中刪除一個信號。sigismember ?用來測試一個給定的信號是否存在于給定的信號集中。
UNIX環境高級編程????? sigprocmask ?函數用來更改進程的信號屏蔽字,它的函數原型如下:
#include <signal.h>int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
SIG_BLOCK:用來將 set 指定的信號屏蔽字和現在的信號屏蔽字按位或后,得到的結果作為新的信號屏蔽字
SIG_UNBLOCK:將 set 指定的信號屏蔽字從現在的信號屏蔽字中去除之后,得到的結果作為新的信號屏蔽字,這種情況與第一種情況正好相反
SIG_SETMASK:直接將 set 指定的信號屏蔽字作為新的信號屏蔽字
???? 另外,這個函數中,如果 ?set ?參數 ?NULL ?的話,那么將不修改現在的信號屏蔽字,并且可以將現在的信號屏蔽字保存在 ?oldmask ?中。我們也可以只設置信號屏蔽字,而不保存老的信號屏蔽字,只需要將 ?oldset ?設置為NULL即可。
???? sigpending ?函數用來返回對于當前進程阻塞或者是當前未決的信號集,它的函數原型如下:
#include <signal.h>int sigpending(sigset_t *set);
#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
^\
SIGQUIT pending
caught SIGQUIT
SIGQUIT unblocked
實現shell?
???? sigaction ?函數和 ?signal ?函數十分的相似,它也是用來給信號注冊處理函數的,它的函數原型如下:
#include <signal.h>int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
struct sigaction {void (*sa_handler)(int);sigset_t sa_mask;int sa_flags;};
#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 ?信號的話,被打斷的系統調用就不需要再重新啟動,而如果是其他信號打斷的系統調用就需要重新啟動。
? ? ?這兩個函數和??setjmp??及??longjmp??是什么關系呢???sigsetjmp??和??siglongjmp??函數是用在信號處理程序中的。當調用??sigsetjmp??時,它會將當前的信號屏蔽字保存起來,當調用??siglongjmp??返回時,會恢復之前保存的信號屏蔽字。而??setjmp??和longjmp??函數卻沒有說明有這個功能,因此,系統向我們提供了這兩個函數,這兩個函數的函數原型是:
int sigsetjmp(sigjmp_buf env, int savesigs);void siglongjmp(sigjmp_buf env, int val);
linux 安全。
? ? ?當??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 &
[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
linux調試工具gdb、? ? ?sigsuspend ?函數用來將設置信號屏蔽字和 ?pause ?調用結合為一個原子操作,例如下面的代碼:
if(sigprocmask(SIG_SETMASK,&oldmask,NULL)<0)
{exit(-1);
}
pause();
在 ?sigprocmask ?函數和 ?pause ?函數之間發生的信號就會丟失,正是出于這個原因,我們提出了 ?sigsuspend ?函數,這個函數的函數原型如下:
#include <signal.h>int sigsuspend(const sigset_t *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
^Cin sig_int...
SIGINTafter return from sigsuspend...
SIGINTending main...
? ? ?下面我們再看一個,用 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;
}
shell入門。? ? ?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;
}
版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。
工作时间:8:00-18:00
客服电话
电子邮件
admin@qq.com
扫码二维码
获取最新动态