1、alarm與pause
- 每個進程只能有一個alarm,如果之前有個alarm,在本次alarm執行完畢的時候如果還沒有結束,則本次alarm返回上次alarm剩餘的值。如果本次alarm執行的結果爲0,則取消原來的鬧鐘。
- SIGALARM的默認執行動作是終止進程,但是大多數鬧鐘會捕獲該信號,如果要捕獲該信號,應該在alarm執行前,註冊信號處理函數!
1.1 例子分析
2、sigsetjmp、siglongjmp
- sigsetjmp與siglongjmp是爲了解決,當捕捉信號進入信號處理函數時後,該信號被進入信號屏蔽集,但是如果跳轉後,該信號不會自動從信號屏蔽集中清除的問題,而正常的處理函數返回時,會自動從屏蔽集裏,清除該信號。
#include <setjmp.h>
int sigsetjmp(sigjmp_buf env, int savemask);
返回值:若直接調用則返回0,若從siglongjmp調用返回則返回非0值
void siglongjmp(sigjmp_buf env, int val);
函數說明:sigsetjmp()會保存目前堆棧環境,然後將目前的地址作一個記號,
而在程序其他地方調用siglongjmp()時便會直接跳到這個記號位置,然後還原堆棧,繼續程序的執行。
參數env爲用來保存目前堆棧環境,一般聲明爲全局變量
參數savemask若爲非0則代表擱置的信號集合也會一塊保存
當sigsetjmp()返回0時代表已經做好記號上,若返回非0則代表由siglongjmp()跳轉回來。
返回:若直接調用則爲0,若從siglongjmp調用返回則爲非0
在信號處理程序中經常調用longjmp函數以返回到程序的主循環中,而不是從該處理程序返回。
但是,調用longjmp有一個問題。當捕捉到一個信號時,進入信號捕捉函數,此時當前信號被自動地加到進程的信號屏蔽字中。這阻止了後來產生的這種信號中斷該信號處理程序。(僅當從信號捕捉函數返回時再將進程的信號屏蔽字復位爲原先值:鏈接)如果用longjmp跳出信號處理程序,那麼,對此進程的信號屏蔽字會發生什麼呢()?(setjmp和longjmp保存和恢復信號屏蔽字,還是不保存和恢復,不同的實現各有不同。)
爲了允許兩種形式的行爲並存,POSIX.1並沒有說明setjmp和longjmp對信號屏蔽字的作用,而是定義了兩個新函數sigsetjmp和siglongjmp。在信號處理程序中進行非局部轉移時使用這兩個函數。
- 下面例子中在執行完sigsetjmp後,將canjump 賦1,爲了避免信號因爲隨時可能發生,導致先siglongjmp的情況
#include "apue.h"
#include <setjmp.h>
#include <time.h>
static void sig_usr1(int), sig_alrm(int);
static sigjmp_buf jmpbuf;
static volatile sig_atomic_t canjump;
int
main(void)
{
if (signal(SIGUSR1, sig_usr1) == SIG_ERR)
err_sys("signal(SIGUSR1) error");
if (signal(SIGALRM, sig_alrm) == SIG_ERR)
err_sys("signal(SIGALRM) error");
pr_mask("starting main: ");
if (sigsetjmp(jmpbuf, 1))
{
pr_mask("ending main: ");
exit(0);
}
canjump = 1; /* now sigsetjmp() is OK */
for(; ;)
pause();
}
static void
sig_usr1(int signo)
{
time_t starttime;
if (canjump == 0)
return; /* unexpected signal, ignore */
pr_mask("starting sig_usr1: ");
alarm(3); /* SIGALRM in 3 seconds */
starttime = time(NULL);
for(; ;) /* busy wait for 5 seconds */
if (time(NULL) > starttime + 5)
break;
pr_mask("finishing sig_usr1: ");
canjump = 0;
siglongjmp(jmpbuf, 1); /* jump back to main, don't return */
}
static void
sig_alrm(int signo)
{
pr_mask("in sig_alrm: ");
}
3、sigsuspend
相關鏈接
問題引入:
如果在信號阻塞時將其發送給進程,那麼該信號的傳遞就被推遲直到對它解除了阻塞。對應用程序而言,該信號好像發生在解除對SIGINT的阻塞和pause之間。如果發生了這種情況,或者如果在解除阻塞時刻和pause之間確實發生了信號,那麼就產生了問題。因爲我們可能不會再見到該信號,所以從這種意義上而言,在此時間窗口(解除阻塞和pause之間)中發生的信號丟失了,這樣就使pause永遠阻塞。
爲了糾正此問題,需要在一個原子操作中先恢復信號屏蔽字,然後使進程休眠。這種功能是由sigsuspend函數提供的。
sigset_t newmask, oldmask;
sigemptyset(&newmask);
sigaddset(&newmask, SIGINT);
/* block SIGINT and save current signal mask */
if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
err_sys("SIG_BLOCK error");
/* critical region of code */
if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_SETMASK error");
/* window is open */
pause(); /* wait for signal to occur */
/* continue processing */
#include <signal.h>
int sigsuspend( const sigset_t *sigmask );
返回值:-1,並將errno設置爲EINTR
- 在sigprocmask和pause之間發生了信號,進程還未pause,那麼如果後續沒有信號後,進程一直在pause,永久掛起
- sigsuspend是爲了解決前面阻塞了一個信號後,解除阻塞,並且掛起的操作如果進程切換會永久掛起的問題。將其變成原子操作
- sigsuspend是臨時替換進程信號屏蔽集,並將進程掛起,如果接收到信號,且執行完信號處理函數後,會繼續執行進程,且將臨時信號屏蔽集替換爲原信號屏蔽集!
- 將進程的信號屏蔽字設置爲由sigmask指向的值。在捕捉到一個信號或發生了一個會終止該進程的信號之前,該進程被掛起。如果捕捉到一個信號而且從該信號處理程序返回,則sigsuspend返回,並且將該進程的信號屏蔽字設置爲調用sigsuspend之前的值。
- 此函數沒有成功返回值。如果它返回到調用者,則總是返回-1,並將errno設置爲EINTR(表示一個被中斷的系統調用)。
sigsuspend細述
3.1 例子1
#include "apue.h"
static void sig_int(int);
void fun();
void pr_mask(const char *str)
{
sigset_t sigset;
int errno_save;
//errno_save = errno; /* we can be called by signal handlers */
if (sigprocmask(0, NULL, &sigset) < 0)
printf("sigprocmask error");
printf("%s,%d", str,getpid());
if (sigismember(&sigset, SIGINT)) printf("SIGINT ");
if (sigismember(&sigset, SIGQUIT)) printf("SIGQUIT ");
if (sigismember(&sigset, SIGUSR1)) printf("SIGUSR1 ");
if (sigismember(&sigset, SIGALRM)) printf("SIGALRM ");
/* remaining signals can go here */
printf("\n");
//errno = errno_save;
}
int main(void)
{
sigset_t newmask, oldmask, waitmask;
pr_mask("program start: ");
if(signal(SIGINT, sig_int) == SIG_ERR)
printf("signal(SIGINT) error");
sigemptyset(&waitmask);
sigaddset(&waitmask, SIGUSR1);
sigemptyset(&newmask);
sigaddset(&newmask, SIGINT);
// if(signal(SIGUSR2, fun) == SIG_ERR)
// printf("signal(SIGUSR2) error");
signal(SIGUSR2, fun);
/*
* Block SIGINT and save current signal mask.
*/
if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
printf("SIG_BLOCK error: ");
/*
* Critical region of code.
*/
pr_mask("in critical region: ");
/*
* Pause, allowing all signals except SIGUSR1.
*/
if(sigsuspend(&waitmask) != -1)
printf("sigsuspend error");
pr_mask("after return from sigsuspend: ");
/*
* Reset signal mask which unblocks SIGINT.
*/
if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
printf("SIG_SETMASK error");
/*
* And continue processing...
*/
pr_mask("program exit: ");
exit(0);
}
static void
sig_int(int signo)
{
pr_mask("\nin sig_int: ");
}
void fun(int signo)
{
pr_mask("USR2\n");
}
執行結果如下1
lxz@lxz-VirtualBox:~/liuxz/testapue$ gcc sigsuspend.c -o sigsuspend
lxz@lxz-VirtualBox:~/liuxz/testapue$ ./sigsuspend
program start: ,2597
in critical region: ,2597SIGINT //表示SIGINT在sigprocmask後被阻塞
USR2
,2597SIGUSR1
after return from sigsuspend: ,2597SIGINT //表示在sigsuspend執行完畢後,SIGINT恢復阻塞
program exit: ,2597 //解除阻塞
執行結果如下2
lxz@lxz-VirtualBox:~/liuxz/testapue$ ./sigsuspend
program start: ,2642
in critical region: ,2642SIGINT
^C // 執行INT,發現sigsuspend解除了INT的阻塞
in sig_int: ,2642SIGUSR1
after return from sigsuspend: ,2642SIGINT
program exit: ,2642
3.2 例子2
#include "apue.h"
volatile sig_atomic_t quitflag; /* set nonzero by signal handler */
static void
sig_int(int signo) /* one signal handler for SIGINT and SIGQUIT */
{
signal(SIGINT, sig_int); //這裏要注意對於老版本的linux,執行完信號處理函數後,信號處理方式變成SIG_DFL
//但是新版本不會,而需要在sigaction,會SA_RESETHAND
signal(SIGQUIT, sig_int);
if (signo == SIGINT)
printf("\ninterrupt\n");
else if (signo == SIGQUIT)
quitflag = 1; /* set flag for main loop */
}
int
main(void)
{
sigset_t newmask, oldmask, zeromask;
if(signal(SIGINT, sig_int) == SIG_ERR)
printf("signal(SIGINT) error");
if(signal(SIGQUIT, sig_int) == SIG_ERR)
printf("signal(SIGQUIT) error");
sigemptyset(&zeromask);
sigemptyset(&newmask);
sigaddset(&newmask, SIGQUIT);
/*
* Block SIGQUIT and save current signal mask.
*/
if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
printf("SIG_BLOCK error");
while(quitflag == 0)
{
sigsuspend(&zeromask);
}
/*
* SIGQUIT has been caught and is now blocked; do whatever.
*/
quitflag = 0;
/*
* Reset signal mask which unblocks SIGQUIT.
*/
if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
printf("SIG_SETMASK error");
exit(0);
}
結果1,按照上述寫法,老版linux
^C
interrupt
^C
interrupt
^C
interrupt
^C
interrupt
^C
interrupt
^C
interrupt
結果2,沒有在信號處理函數中重新寫signal,老版linux