linux學習-訊號相關函式
未決:在訊號產生和遞送之間的時間間隔內,我們稱訊號是未決的。
阻塞訊號遞送:如果為程序產生了一個阻塞訊號,而且對該訊號的動作是預設動作或捕捉該訊號,則為該程序將此訊號保持為未決的,直到程序對此訊號解除阻塞,或者對此訊號的動作更改為忽略。
訊號遮蔽字:每個程序都有一個訊號遮蔽字,它規定了當前要阻塞遞送到該程序的訊號集。
函式kill和raise
#include<signal.h>
int kill(pid_t pid,int signo);
int raise(int signo);
返回值: 成功返回0,失敗返回-1
kill的pid引數 pid>0
1.函式alarm和pause alarm函式可以設定一個鬧鐘時間,超時產生SIGALRM訊號。如忽略或者不捕捉該訊號,系統預設動作是終止呼叫alarm函式的程序。
#include<unistd.h>
unsigned int alarm(unsigned int seconds);
返回值:0,或者是以前設定的鬧鐘時間的剩餘時間
每個程序只能有一個鬧鐘時間。如果在呼叫alarm之前已經為該程序註冊的鬧鐘時間還未超時,則該鬧鐘的剩餘時間作為本次alarm函式呼叫的值返回。以前註冊的鬧鐘時間則被新值代替。如果本次呼叫的seconds值是0,則取消以前的鬧鐘,其剩餘值,作為alarm函式的返回值。 pause函式呼叫程序掛起,直到捕捉到一個訊號
#include<ubistd.h> int paush(void); 返回值:-1;errno設定為EINTR
給出一個用alarm和pause實現sleep的例項
#include<stdio.h>
#include<signal.h>
#include<setjmp.h>
#include<ubistd.h>
static jmp_buf env_alrm;
static void myalrm(int signo) //訊號處理函式
{
longjmp(env_alrm,1); //區域性跳轉
}
int mysleep(int sec)
{
int minsec,ret,tmp=0;
if(signal(SIGALRM,myalrm)==SIG_EER)
return sec;
minsec=alrm(0); //得到上一次未超時定時器所剩的時間
ret=sec>minsec?(sec-minsec):0;
if(setjmp(env_alrm)==0) //區域性跳轉
{
if(minsec) //考慮之前設定了定時器
alarm(minsec<sec?minsec:sec); //用時間短的做定時
else
alarm(sec);
sleep(9);
pause();
}
tmp=alarm(0); //睡眠時,被其他訊號打斷而剩下的未睡眠時間
if(minsec>sec){ //若前面登記的定時器比睡眠時間長,退出後需要復位之前定時器剩下的時間
alarm(minsec-sec);
}
if(tmp)
ret+=tmp;
return ret;
}
int main()
{
int othersec;
alarm(6);
othersec=mysleep(3);
printf("return %d\n",othersec);
return 0;
}
2.函式sigprocmask 呼叫函式sigprocmask可以檢測和更改當前阻塞而不能遞送給程序的訊號集。
#include<signal.h>
int sigprocmask(int how,const sigset_t *restrict set,sigset_t *restrict oset);
返回值:成功返回0,失敗返回-1;
若oset為非空指標,程序當前的訊號遮蔽子通過oset返回。 若set為非空指標,引數how指示進行何種操作。若為空,則不改變當前訊號遮蔽字,how引數也無用。
how | 說明 |
---|---|
SIG_BLOCK | 新set中的訊號加到訊號遮蔽字中 |
SIG_UNBLOCK | 將set中訊號從當前訊號遮蔽子中取出 |
SIG_SETMASK | 該程序新的訊號遮蔽是set指向的值 |
3.函式sigismember 函式sigismember用來測試引數signum 代表的訊號是否已加入至引數set訊號集裡。
int sigismember(const sigset_t *set,int signum)
返回值:有該訊號,返回1,無則返回0;出錯返回-1
4.函式sigpending sigpending函式返回在送往程序的時候被阻塞掛起的訊號集合。這個訊號集合通過引數set返回。
#include<signal.h>
int sigpengding(sigset_t *set);
返回值:成功返回0,出錯返回-1
下面這個例程,用到了上面幾個函式
static void sig_quit(int signo)
{
printf("caught sigquit\n");
}
int main()
{
sigset_t newmak,oldmask,pendmsk;
if(signal(SIGQUIT,sig_quit)==SIG_ERR)
printf("can not catch quit\n");
sigemptyset(&newmask); //訊號集清空函式
sigaddmask(&newmask,SIGQUIT); //將訊號新增到訊號集中
if(sigprocmask(SIG_BLOCK,&newmask,&oldmask)<0) //將訊號SIGQUIT新增到訊號遮蔽字中
printf("err\n");
sleep(5);
if(sigpending(&pendmask)<0) //檢測當前程序中阻塞的訊號,通過pendmask返回
printf("sigpending err\n");
if(sigismember(&pendmask,SIGQUIT)) //檢測SIGQUIT是否在pendmask訊號集中
printf("sigquit pending\n");
if(sigprocmask(SIG_SETMASK,&oldmask,NULL)<0) //將SIGQUIT訊號取消遮蔽
printf("err\n");
sleep(5);
exit(0);
}
5.函式sigaction sigaction函式的功能是檢查或者修改與指定訊號相關聯的動作。此函式取代了早期使用的signal函式。
#include<signal.h>
int sigaction(int signo,conststruct sigaction*restrict act,
struct sigaction*restrict oact);
返回值:成功返回0,失敗返回-1
給訊號signo設定新的訊號處理函式act, 同時保留該訊號原有的訊號處理函式oldact。 此函式用到了一個數據結構
struct sigaction{
void (*sa_handler)(int);
sigset_t sa_mask;
int sa_flag;
void (*sa_sigaction)(int,siginfo_t*,void*);
};
sa_handler欄位:包含一個訊號捕捉函式的地址。
sa_mask欄位:說明了一個訊號集,在呼叫該訊號捕捉函式之前,這一訊號集要加進程序的訊號遮蔽字中。僅當從訊號捕捉函式返回時再將程序的訊號遮蔽字復位為原先值。
sa_flags欄位:指定對訊號進行處理的 各個選項。有張表格,我就不列了,自己百度吧。簡單說明下面程式用到的兩個標誌 SA_NODEFER: 當訊號處理函式正在進行時,不堵塞對於訊號處理函式自身訊號功能。 SA_RESETHAND:當用戶註冊的訊號處理函式被執行過一次後,該訊號的處理函式被設為系統預設的處理函式。
sa_sigaction欄位:是一個替代的訊號處理程式,當sa_flags 為SA_SIGINFO時,使用該訊號處理程式。sa_sigaction和sa_handler,應用只能一次呼叫其中一個。
static void sig_quit(int signo)
{
printf("catch quit\n");
}
int main()
{
struct sigaction act,oldact;
act.sa_handler = sig_quit;
act.sa_flags = SA_NODEFER | SA_RESETHAND;
sigaction(SIGINT,&act,&oldact);
sleep(5);
printf("hello \n");
return 0;
}
6.函式sigsuspend sigsuspend函式和pause函式一樣,可以使程序掛起(進入睡眠狀態),直至有訊號發生。 sigsuspend函式的引數是一個訊號集,這個訊號集是用來遮蔽訊號的,訊號集中存放了要遮蔽的訊號。 如果該訊號集為空的話,sigsuspend就不遮蔽任何訊號,任何訊號都可以使程序從掛起狀態喚醒,這就與pause函式一樣了。
#include<signal.h>
int sigsuspend(const sigset_t *sigmask);
返回值:-1,並將errno設定為EINTR
可以用於保護程式碼臨界區,使其不被特定的訊號中斷
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<stddef.h>
#include<unistd.h>
#include<signal.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/termios.h>
#include<sys/ioctl.h>
#include<pwd.h>
#include<setjmp.h>
#include<time.h>
static void sig_init(int);
int main()
{
sigset_t newmask,oldmask,waitmask;
printf("hello");
if(signal(SIGINT,sig_init)==SIG_ERR)
perror("signal");
sigemptyset(&waitmask);
sigaddset(&waitmask,SIGUSR1);
sigemptyset(&newmask);
sigaddset(&newmask,SIGINT);
if(sigprocmask(SIG_BLOCK,&newmask,&oldmask)<0) //將SIGINT加入到訊號遮蔽字中,以oldmask返回
perror("sigprocmask");
printf("ok");
if(sigsuspend(&waitmask)!=-1) //將sigusr1遮蔽
perror("sigsuspend");
if(sigprocmask(SIG_SETMASK,&oldmask,NULL)<0) //將阻塞訊號SIGINT恢復
perror("sigprocmask");
exit(0);
}
static void sig_init(int signo)
{
printf("how are you\n");
}