sigal mq_notify
sigal & mq_notify?- [學(xué)習(xí)筆記]
Tag:signal?mq_notify版權(quán)聲明:轉(zhuǎn)載時(shí)請(qǐng)以超鏈接形式標(biāo)明文章原始出處和作者信息及本聲明
http://yangdk.blogbus.com/logs/65056733.html
信號(hào)是軟件中斷,它提供了一種處理異步事件的方法
當(dāng)信號(hào)產(chǎn)生時(shí)有3種方法可以處理這個(gè)信號(hào):
1.忽略,如signal(SIGINT, SIG_IGN)
2.捕捉,如signal(SIGINT, sigint_handler) sigint_handler是處理函數(shù)
3.系統(tǒng)默認(rèn)動(dòng)作,如signal(SIGINT, SIG_DFL)
void (*signal(int signo, void (*func) (int))) (int);
?returns: the older hander if OK, SIG_ERR if error
signo是要捕捉的信號(hào)。
func則是signal handler(信號(hào)處理函數(shù)),
#define SIG_ERR?(void (*) ()) -1
#define SIG_DFL?(void (*) ()) 0
#define SIG_IGN?(void (*) ()) 1
這是<signal.h>頭文件中的定義,也說明了為什么SIG_DFL和SIG_IGN可以作為signal函數(shù)的第二個(gè)參數(shù),SIG_ERR可以是signal函數(shù)的返回值。
fork之后,子進(jìn)程集成父進(jìn)程的信號(hào)處理方式,而調(diào)用exec創(chuàng)建新進(jìn)程之后,新進(jìn)程并沒有繼承父進(jìn)程設(shè)置的信號(hào)處理方式。
在早期的UNIX版本中,信號(hào)是不可靠的(不可靠是指有可能丟失),下面兩個(gè)經(jīng)典實(shí)例將會(huì)說明這個(gè)問題:
實(shí)例1:
int sig_int();?/* my signal handling function */
...
signal(SIGINT, sig_int);?/* estableish handler */
...
sig_int()
{
?signal(SIGINT, sig_int)?/* reestablish handler for next time */
?...???/* process the signal ... */
}
從信號(hào)產(chǎn)生到信號(hào)處理函數(shù)重新設(shè)置信號(hào)處理函數(shù)這段時(shí)間內(nèi)有一個(gè)時(shí)間窗口,如果在這段時(shí)間內(nèi)再次收到信號(hào),程序會(huì)按照SIGINT的默認(rèn)處理,也就是終止進(jìn)程
實(shí)例2:
int sig_int_flag;?/* set nonzero when signal occurs */
main()
{
?int sig_int();?/* my signal handling function */
?...
?signal(SIGINT, sig_int);?/* establish handler */
?while(sig_int_flag == 0)
??pause();??/* goto sleep, waiting for signal */
?...
}
sig_int()
{
?signal(SIGINT, sig_int);?/* reestablish handler for next time */
?sig_int_flag = 1;??/* set flag for main loop to examine */
}
程序的本意是等信號(hào)發(fā)生后,將標(biāo)志設(shè)為1,并把把程序從pause中喚醒,喚醒后程序判斷標(biāo)志已經(jīng)被改變,于是退出while循環(huán),繼續(xù)執(zhí)行下面的操作,可是一旦消息在判斷標(biāo)志是否為0與pause()之間產(chǎn)生,該進(jìn)程將被終止或者是永遠(yuǎn)pause下去(假設(shè)不在產(chǎn)生信號(hào)),這個(gè)信號(hào)也就丟失了。
中斷的系統(tǒng)調(diào)用
早期UNIX的一個(gè)特性是,如果進(jìn)程在執(zhí)行一個(gè)低速系統(tǒng)調(diào)用而阻塞期間捕捉到一個(gè)信號(hào),則該系統(tǒng)調(diào)用就被中斷不再繼續(xù)執(zhí)行。
如若read系統(tǒng)調(diào)用已接受并傳送數(shù)據(jù)至應(yīng)用程序緩沖區(qū),但尚未接收到應(yīng)用程序請(qǐng)求的全部數(shù)據(jù),此時(shí)被中斷,操作系統(tǒng)可以認(rèn)為系統(tǒng)調(diào)用失敗,并將errno設(shè)置為EINTR,另一種處理方法是允許該系統(tǒng)調(diào)用成功返回,返回已接收到的部分?jǐn)?shù)據(jù)量,write同理。
對(duì)某些系統(tǒng)可以通過判斷函數(shù)返回值和錯(cuò)誤碼來判斷是否低速系統(tǒng)調(diào)用被中斷,代碼如下:
again:
?if((n = read(fd, buf, BUFFSIZE)) < 0){
??if(errno == EINTR)
???goto again;?/* just an interrupted system call */
??/* handle other errors */
?}
為了幫助應(yīng)用程序不必處理被中斷的系統(tǒng)調(diào)用,4.2BSD引入了某些被中斷系統(tǒng)調(diào)用的自動(dòng)重啟動(dòng)的功能。自動(dòng)重啟動(dòng)的系統(tǒng)調(diào)用包括ioctl,read,readv,write,writev,wait和waitpid。
系統(tǒng)V的默認(rèn)工作方式是從不重啟系統(tǒng)調(diào)用。而BSD則重啟動(dòng)被信號(hào)中斷的系統(tǒng)調(diào)用。FreeBSD5.2.1, linux 2.4.22和Mac OS X 10.3的默認(rèn)方式是重啟被中斷的系統(tǒng)調(diào)用,而Solaris 9的默認(rèn)方式是出錯(cuò)返回,并將errno設(shè)置為EINTR,這樣看只有在Solaris上才需要上面的代碼。
信號(hào)集
int sigemptyset(sigset_t *set);???/* 清空所有信號(hào) */
int sigfillset(sigset_t *set);???/* 包括所有信號(hào) */
int sigaddset(sigset_t *set, int signo);?
int sigdelset(sigset_t *set, int signo);
?returns: 0 if OK, -1 if error
int sigismember(const sigset_t *set, int signo);
?returns: 1 if TRUE, 0 if FALSE, -1 if error
int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
?returns: 0 if OK, -1 if error
如果oset非空,則oset返回當(dāng)前的信號(hào)屏蔽字
如果set非空,則how的取值決定了該函數(shù)的動(dòng)作
SIG_BLOCK? 使該進(jìn)程的信號(hào)屏蔽字為當(dāng)前屏蔽字與set的并集? set包含了我們希望阻塞的附加信號(hào)
SIG_UNBLOCK? 使該進(jìn)程的信號(hào)屏蔽字為當(dāng)前屏蔽字與set的補(bǔ)集的交集? set包含了我們希望解除阻塞的信號(hào)
SIG_SETMASK? 將set設(shè)置為當(dāng)前信號(hào)屏蔽字
如果set為空,how的值也就沒有意義
int sigpending(sigset_t *set);
?returns: 0 if OK, -1 if error
該函數(shù)通過set返回當(dāng)前未決的信號(hào),即阻塞而沒有遞送的信號(hào)。
int sigaction(int signo, const struct sigaction *restrict act, struct sigaction *restrict oact);
?returns: 0 if OK, -1 if error
如果act非空,則要修改其動(dòng)作
如果oact非空,則返回該信號(hào)的上一動(dòng)作
struct sigaction{
??????? void??????????? (*sa_handler) (int);??? /* addr of signal handler, or SIG_IGN, or SIG_DFL */
??????? sigset_t??????? sa_mask;??????????????? /* or SIG_IGN, or SIG_DFL */
??????? int???????????? sa_flags;?????????????? /* additional signals to block */
????????
??????? /* alternate handler */
??????? void??????????? (*sa_sigaction) (int, sig_info_t, void *);
};
sa_handler為信號(hào)捕捉函數(shù)的地址
sa_mask 在調(diào)用信號(hào)捕捉函數(shù)之前,這個(gè)信號(hào)集要加入到進(jìn)程的信號(hào)屏蔽字中,從信號(hào)捕捉函數(shù)返回后再將進(jìn)程的信號(hào)屏蔽字復(fù)位為原先值。
sa_flags指定對(duì)信號(hào)處理的各個(gè)選項(xiàng)
sa_sigaction 當(dāng)sa_flags為SA_SIGINFO時(shí),sa_sigaction是替代sa_handler成為信號(hào)處理函數(shù)
int sigsuspend(const sigset_t *sigmask);
?return: always be -1, set errno to EINTR
這個(gè)函數(shù)作用是:先將當(dāng)前進(jìn)程的信號(hào)屏蔽字設(shè)為sigmask,然后掛起等待一個(gè)信號(hào),捕捉到信號(hào)后從sigsuspend返回,并恢復(fù)信號(hào)屏蔽字為調(diào)用此函數(shù)之前的狀態(tài)。
該函數(shù)是為了解決下面的問題而產(chǎn)生的:
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 */
/* reset signal mask, which unblocks SIGINT */
if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
??????? err_sys("SIG_SETMASK error");
/* window is open, error is here */
pause();??????? /* wait for signal to occur */
/* continue processing */
如果信號(hào)在sigprocmask(SIG_SETMASK, &oldmask, NULL)與pause之間deliver,那么該信號(hào)就會(huì)丟失,pause函數(shù)有可能永遠(yuǎn)等待下去,sigsuspend就相當(dāng)于把這兩個(gè)函數(shù)合并,成為一個(gè)原子操作。
這個(gè)函數(shù)一般的應(yīng)用場(chǎng)景:
sigset_t newmask, oldmask, waitmask;
sigemptyset(&newmask);
sigemptyset(&waitmask);
sigaddset(&newmask, SIGINT);
sigaddset(&waitmask, SIGUSR1);
/* block SIGINT and save current signal mask */
if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
??????? err_sys("SIG_BLOCK error");
/* critical region of code */
/* pause, allowing all signals except SIGUSR1 */
if(sigsuspend(&waitmask) != -1)
??????? err_sys("sigsuspend error");
/* reset signal mask, which unblocks SIGINT */
if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
??????? err_sys("SIG_SETMASK error");
/* continue processing */
下面是關(guān)于mq_notify和signal的介紹
我們?cè)趯?shí)現(xiàn)mq的接收模塊時(shí),可能會(huì)采用msgrcv函數(shù),但是在沒有消息的時(shí)候就得阻塞在msgrcv函數(shù)上,如果設(shè)置了nonblock標(biāo)識(shí),我們就得不停地調(diào)用msgrcv查看是否有消息,這種輪詢非常地消耗CPU。
Posix message queue提供了這樣一個(gè)功能,當(dāng)有消息被插入到一個(gè)空的mq時(shí),我們可以選擇向一個(gè)我們注冊(cè)的進(jìn)程發(fā)送一個(gè)信號(hào)或者創(chuàng)建一個(gè)線程,這樣就提供了一種異步處理消息的機(jī)制,在沒有消息的時(shí)候,進(jìn)程可以執(zhí)行別的操作,有消息后,mq會(huì)給注冊(cè)進(jìn)程發(fā)送信號(hào),注冊(cè)進(jìn)程調(diào)用信號(hào)處理函數(shù)或接收消息或者新創(chuàng)建的線程接收消息。
int mq_notify(mqd_t, const struct sigevent *notification);
?returns: 0 if OK, -1 on error
以下是幾種典型的利用mq_notify實(shí)現(xiàn)異步事件的例子:
1.????? /* no signals blocked */
??????? sigemptyset(&zeromask);
??????? sigemptyset(&newmask);
??????? sigemptyset(&oldmask);
??????? sigaddset(&newmask, SIGUSR1);
??????? /* establish signal handler, enable notification */
??????? signal(SIGUSR1, sig_usr1);
??????? sigev.sigev_notify = SIGEV_SIGNAL;
??????? sigev.sigev_signo = SIGUSR1;
??????? stat = mq_notify(mqd, &sigev);
??????? if(0 != stat){
??????????????? perror("mq_notify");
??????????????? exit(-1);
??????? }
??????? for( ; ; ){
??????????????? /* block SIGUSR1 阻塞SIGUSR1信號(hào),保護(hù)代碼臨界區(qū) */
??????????????? sigprocmask(SIG_BLOCK, &newmask, &oldmask);
??????????????? while(mqflag == 0){
??????????????????????? sigsuspend(&zeromask);/* 解除對(duì)SIGUSR1的阻塞并等待信號(hào),返回時(shí)對(duì)SIGUSR1繼續(xù)阻塞 */
??????????????? }
??????????????? mqflag = 0;???????????? /*reset flag*/
??????????????? stat = mq_notify(mqd, &sigev);
??????????????? if(0 != stat){
??????????????????????? perror("mq_notify");
??????????????????????? exit(-1);
??????????????? }
??????????????? /* 接收全部消息,否則以后可能永遠(yuǎn)收不到信號(hào) */
??????????????? while((n = mq_receive(mqd, buff, attr.mq_msgsize, NULL)) >= 0){
??????????????????????? printf("read %ld bytes\n", (long) n);
??????????????? };
??????????????? if(errno != EAGAIN){
??????????????????????? perror("mq_receive error");
??????????????????????? exit(-1);
??????????????? }
??????????????? sigprocmask(SIG_UNBLOCK, &newmask, NULL);?????? /* unblock SIGUSR1 */
??????? }
??????? exit(0);
}
static void sig_usr1(int signo)?/* 信號(hào)處理函數(shù)內(nèi)必須用可重入的函數(shù),因此不能在這里面調(diào)用mq_receive接收消息 */
{
??????? signal(SIGUSR1, sig_usr1);
??????? mqflag = 1;
??????? return;
}
2.????? /* no signals blocked */
??????? sigemptyset(&newmask);
??????? sigaddset(&newmask, SIGUSR1);
??????? sigprocmask(SIG_BLOCK, &newmask, NULL);
??????? /* establish signal handler, enable notification */
??????? sigev.sigev_notify = SIGEV_SIGNAL;
??????? sigev.sigev_signo = SIGUSR1;
??????? stat = mq_notify(mqd, &sigev);
??????? if(0 != stat){
??????????????? perror("mq_notify");
??????????????? exit(-1);
??????? }
??????? for( ; ; ){
??????????????? sigwait(&newmask, &signo);/* 等待產(chǎn)生在newmask中的未決的信號(hào) */
??????????????? if(signo == SIGUSR1){
??????????????????????? stat = mq_notify(mqd, &sigev);
??????????????????????? if(0 != stat){
??????????????????????????????? perror("mq_notify");
??????????????????????????????? exit(-1);
??????????????????????? }
??????????????????????? while((n = mq_receive(mqd, buff, attr.mq_msgsize, NULL)) >= 0){
??????????????????????????????? printf("read %ld bytes\n", (long) n);
??????????????????????? };
??????????????????????? if(errno != EAGAIN){
??????????????????????????????? perror("mq_receive error");
??????????????????????????????? exit(-1);
??????????????????????? }
??????????????? }
??????? }
??????? exit(0);
}
3.????? pipe(pipefd);
??????? /* establish signal handler, enable notification */
??????? signal(SIGUSR1, sig_usr1);
??????? sigev.sigev_notify = SIGEV_SIGNAL;
??????? sigev.sigev_signo = SIGUSR1;
??????? stat = mq_notify(mqd, &sigev);
??????? if(0 != stat){
??????????????? perror("mq_notify");
??????????????? exit(-1);
??????? }
??????? for( ; ; ){
??????????????? FD_SET(pipefd[0], &rset);
??????????????? /* 這種方法的優(yōu)點(diǎn)是,可以設(shè)置超時(shí)時(shí)間,超時(shí)后可以去執(zhí)行其他的操作,執(zhí)行完后繼續(xù)監(jiān)聽 */
??????????????? nfds? = select(pipefd[0] + 1, &rset, NULL, NULL, NULL);
??????????????? if(FD_ISSET(pipefd[0], &rset)){
??????????????????????? read(pipefd[0], &c, 1);
??????????????????????? stat = mq_notify(mqd, &sigev);
??????????????????????? if(0 != stat){
??????????????????????????????? perror("mq_notify");
??????????????????????????????? exit(-1);
??????????????????????? }
??????????????????????? while((n = mq_receive(mqd, buff, attr.mq_msgsize, NULL)) >= 0){
??????????????????????????????? printf("read %ld bytes\n", (long) n);
??????????????????????? };
??????????????????????? if(errno != EAGAIN){
??????????????????????????????? perror("mq_receive error");
??????????????????????????????? exit(-1);
??????????????????????? }
??????????????? }
??????? }
??????? exit(0);
}
static void sig_usr1(int signo)
{
??????? signal(SIGUSR1, sig_usr1);
??????? write(pipefd[1], "", 1);
??????? return;
}
Posix Realtime Signals(Posix 實(shí)時(shí)信號(hào))
實(shí)時(shí)信號(hào)是值在SIGRTMIN和SIGRTMAX之間的信號(hào),下面是調(diào)用sigaction時(shí)的幾種動(dòng)作情況
signal????????? 指定SA_SIGINFO標(biāo)識(shí)?????????? 未指定SA_SIGINFO標(biāo)識(shí)
實(shí)時(shí)信號(hào)???????? 確保實(shí)時(shí)信號(hào)動(dòng)作????????????? 不能確保實(shí)時(shí)信號(hào)動(dòng)作
一般信號(hào)???????? 不能確保實(shí)時(shí)信號(hào)動(dòng)作????????? 不能確保實(shí)時(shí)信號(hào)動(dòng)作
如果我們想要實(shí)現(xiàn)實(shí)時(shí)信號(hào)動(dòng)作,在用sigaction設(shè)置信號(hào)處理動(dòng)作時(shí)必須要用實(shí)時(shí)信號(hào),而且要指定SA_SIGINFO標(biāo)識(shí),到底什么是實(shí)時(shí)信號(hào)動(dòng)作呢?
實(shí)時(shí)信號(hào)動(dòng)作可以保證以下幾點(diǎn):
1.信號(hào)排隊(duì),比如,產(chǎn)生3次SIGRTMIN信號(hào),SIGRTMIN信號(hào)就被遞送3次,而且是先進(jìn)先出(FIFO)。
2.當(dāng)多個(gè)不阻塞的實(shí)時(shí)信號(hào)排隊(duì)時(shí),越小的優(yōu)先級(jí)越高,先被遞送,比如,排隊(duì)時(shí)SIGRTMIN就要排到SIGRTMAX的前面。
3.執(zhí)行普通信號(hào)的處理函數(shù)時(shí),處理函數(shù)只能得到一個(gè)參數(shù),即信號(hào)值,實(shí)時(shí)信號(hào)在執(zhí)行信號(hào)處理函數(shù)時(shí)則可以得到更多的信息
4.實(shí)時(shí)信號(hào)定義了很多新的函數(shù),例如sigqueue函數(shù)實(shí)現(xiàn)了kill函數(shù)的功能,并能夠攜帶一個(gè)sigval結(jié)構(gòu)體。
typedef struct {
??????? int???????????? si_signo;?????? /* same value as signo argument */
??????? int???????????? si_code;??????? /* SI(USER, QUEUE, TIMEER, ASYNCIO, MESGQ) */
??????? union sigval??? si_value;?????? /* integer or pointer value from sender */
} siginfo_t;
si_code的值是由系統(tǒng)設(shè)定的:
SI_ASYNCIO????? 異步IO請(qǐng)求完成時(shí),Posix的aio_XXX函數(shù)
SI_MESGQ??????? 消息被插入到空的mq時(shí)
SI_QUEUE??????? 用sigqueue發(fā)送消息時(shí)
SI_TIMER??????? timer_setting函數(shù)設(shè)置的時(shí)鐘過期時(shí)
SI_USER???????? 用kil發(fā)送信號(hào)時(shí)
si_value是由用戶設(shè)置的參數(shù),它僅在si_code為QUEUE, TIMEER, ASYNCIO, MESGQ這四個(gè)值時(shí)才有意義。
最后是一個(gè)實(shí)時(shí)信號(hào)的例子:
#include <stdio.h>
#include <stdlib.h>
#include <mqueue.h>
#include <signal.h>
#include <string.h>
#include <sys/stat.h>
#include <errno.h>
typedef void sigfunc_rt(int, siginfo_t *, void *);
static void sig_rt(int, siginfo_t *, void *);
sigfunc_rt * signal_rt(int signo, sigfunc_rt *func, sigset_t *mask);
#define MAX SIGRTMAX
//#define MAX 40
int main(int argc, char **argv)
{
??????? int i, j;
??????? pid_t pid;
??????? int stat = 0;
??????? sigset_t newset;
??????? union sigval val;
??????? printf("SIGRTMIN = %d, SIGRTMAX = %d\n", (int)SIGRTMIN, (int) SIGRTMAX);
??????? if((pid = fork()) == 0){
??????????????? /* child:block three realtime signals */
??????????????? sigemptyset(&newset);
??????????????? sigaddset(&newset, MAX);
??????????????? sigaddset(&newset, MAX - 1);
??????????????? sigaddset(&newset, MAX - 2);
??????????????? sigprocmask(SIG_BLOCK, &newset, NULL);
??????????????? signal_rt(MAX, sig_rt, &newset);
??????????????? signal_rt(MAX - 1, sig_rt, &newset);
??????????????? signal_rt(MAX - 2, sig_rt, &newset);
??????????????? /* let parentsend add the signals */
??????????????? sleep(6);
??????????????? sigprocmask(SIG_UNBLOCK, &newset, NULL);
??????????????? /* let add queued signals be delivered */
??????????????? sleep(3);
??????????????? exit(0);
??????? }
??????? sleep(3);
??????? for(i = MAX; i >= MAX -2; i--){
??????????????? for(j = 0; j <= 2; j++){
??????????????????????? val.sival_int = j;
??????????????????????? sigqueue(pid, i, val);
??????????????????????? printf("sent signal %d, val = %d\n", i, j);
??????????????? }
??????? }
??????? exit(0);
}
static void sig_rt(int signo, siginfo_t *info, void *contest)
{
??????? printf("received signal #%d, code = %d, ival = %d\n",
??????????????????????? signo, info->si_code, info->si_value.sival_int);
??????? return;
}
sigfunc_rt * signal_rt(int signo, sigfunc_rt *func, sigset_t *mask)
{
??????? struct sigaction act, oact;
??????? act.sa_sigaction = func;??????? /* must stort function addr here */
??????? act.sa_mask = *mask;??????????? /* signals to block */
??????? act.sa_flags = SA_SIGINFO;????? /* must specify this for realtime */
??????? if(signo == SIGALRM){
#ifdef SA_INTERRUPT
??????????????? act.sa_flags |= SA_INTERRUPT;?? /*SUNOS 4.x*/
#endif
??????? }else{
#ifdef SA_RESTART
??????????????? act.sa_flags |= SA_RESTART;???? /*SVR4, 4.4BSD*/
#endif
??????? }
??????? if(sigaction(signo, &act, &oact) < 0)
??????????????? return ((sigfunc_rt *)SIG_ERR);
??????? return (oact.sa_sigaction);
}
這個(gè)程序在SOL10上執(zhí)行的結(jié)果是:
SIGRTMIN = 41, SIGRTMAX = 48
sent signal 40, val = 0
sent signal 40, val = 1
sent signal 40, val = 2
sent signal 39, val = 0
sent signal 39, val = 1
sent signal 39, val = 2
sent signal 38, val = 0
sent signal 38, val = 1
sent signal 38, val = 2
/export/home/yangdk/test/unp/s5/rtsignal>received signal #38, code = -2, ival = 0
received signal #38, code = -2, ival = 1
received signal #38, code = -2, ival = 2
received signal #39, code = -2, ival = 0
received signal #39, code = -2, ival = 1
received signal #39, code = -2, ival = 2
received signal #40, code = -2, ival = 0
received signal #40, code = -2, ival = 1
received signal #40, code = -2, ival = 2
由此可見相同德實(shí)時(shí)信號(hào)以FIFO排序,小的實(shí)時(shí)信號(hào)比大的實(shí)時(shí)信號(hào)的優(yōu)先級(jí)要高。
轉(zhuǎn)載于:https://www.cnblogs.com/seaney/p/3237566.html
總結(jié)
以上是生活随笔為你收集整理的sigal mq_notify的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: RAC 安装完成后 节点间通信不依赖于S
- 下一篇: 算法导论——排序算法