UNIX環境高級編程,linux信號(二)--unix環境高級編程讀書筆記

 2023-11-19 阅读 29 评论 0

摘要:???? 1.信號集 ???? 在linux中,可以用一個稱為信號集的數據類型 ?sigset_t,來表示所有的被阻塞信號的一個集合。對這個集合的操作函數有: #include <signal.h>int sigemptyset(sigset_t *set);int sigfillset(sigset_t *set);int sigaddset(sigset

???? 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函數

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  指定的信號屏蔽字作為新的信號屏蔽字

這個函數將老的信號屏蔽字保存在 ?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 ?函數的使用方法和作用。

實現shell?

???? 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);

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 &

會輸出當前的進程 ?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函數

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);

這個函數先將信號屏蔽字設置為參數 ?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函數

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;
}


















版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/3/183710.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息