sigal mq_notify
sigal & mq_notify?- [學習筆記]
Tag:signal?mq_notify版權聲明:轉載時請以超鏈接形式標明文章原始出處和作者信息及本聲明
http://yangdk.blogbus.com/logs/65056733.html
信號是軟件中斷,它提供了一種處理異步事件的方法
當信號產生時有3種方法可以處理這個信號:
1.忽略,如signal(SIGINT, SIG_IGN)
2.捕捉,如signal(SIGINT, sigint_handler) sigint_handler是處理函數
3.系統默認動作,如signal(SIGINT, SIG_DFL)
void (*signal(int signo, void (*func) (int))) (int);
?returns: the older hander if OK, SIG_ERR if error
signo是要捕捉的信號。
func則是signal handler(信號處理函數),
#define SIG_ERR?(void (*) ()) -1
#define SIG_DFL?(void (*) ()) 0
#define SIG_IGN?(void (*) ()) 1
這是<signal.h>頭文件中的定義,也說明了為什么SIG_DFL和SIG_IGN可以作為signal函數的第二個參數,SIG_ERR可以是signal函數的返回值。
fork之后,子進程集成父進程的信號處理方式,而調用exec創建新進程之后,新進程并沒有繼承父進程設置的信號處理方式。
在早期的UNIX版本中,信號是不可靠的(不可靠是指有可能丟失),下面兩個經典實例將會說明這個問題:
實例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 ... */
}
從信號產生到信號處理函數重新設置信號處理函數這段時間內有一個時間窗口,如果在這段時間內再次收到信號,程序會按照SIGINT的默認處理,也就是終止進程
實例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 */
}
程序的本意是等信號發生后,將標志設為1,并把把程序從pause中喚醒,喚醒后程序判斷標志已經被改變,于是退出while循環,繼續執行下面的操作,可是一旦消息在判斷標志是否為0與pause()之間產生,該進程將被終止或者是永遠pause下去(假設不在產生信號),這個信號也就丟失了。
中斷的系統調用
早期UNIX的一個特性是,如果進程在執行一個低速系統調用而阻塞期間捕捉到一個信號,則該系統調用就被中斷不再繼續執行。
如若read系統調用已接受并傳送數據至應用程序緩沖區,但尚未接收到應用程序請求的全部數據,此時被中斷,操作系統可以認為系統調用失敗,并將errno設置為EINTR,另一種處理方法是允許該系統調用成功返回,返回已接收到的部分數據量,write同理。
對某些系統可以通過判斷函數返回值和錯誤碼來判斷是否低速系統調用被中斷,代碼如下:
again:
?if((n = read(fd, buf, BUFFSIZE)) < 0){
??if(errno == EINTR)
???goto again;?/* just an interrupted system call */
??/* handle other errors */
?}
為了幫助應用程序不必處理被中斷的系統調用,4.2BSD引入了某些被中斷系統調用的自動重啟動的功能。自動重啟動的系統調用包括ioctl,read,readv,write,writev,wait和waitpid。
系統V的默認工作方式是從不重啟系統調用。而BSD則重啟動被信號中斷的系統調用。FreeBSD5.2.1, linux 2.4.22和Mac OS X 10.3的默認方式是重啟被中斷的系統調用,而Solaris 9的默認方式是出錯返回,并將errno設置為EINTR,這樣看只有在Solaris上才需要上面的代碼。
信號集
int sigemptyset(sigset_t *set);???/* 清空所有信號 */
int sigfillset(sigset_t *set);???/* 包括所有信號 */
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返回當前的信號屏蔽字
如果set非空,則how的取值決定了該函數的動作
SIG_BLOCK? 使該進程的信號屏蔽字為當前屏蔽字與set的并集? set包含了我們希望阻塞的附加信號
SIG_UNBLOCK? 使該進程的信號屏蔽字為當前屏蔽字與set的補集的交集? set包含了我們希望解除阻塞的信號
SIG_SETMASK? 將set設置為當前信號屏蔽字
如果set為空,how的值也就沒有意義
int sigpending(sigset_t *set);
?returns: 0 if OK, -1 if error
該函數通過set返回當前未決的信號,即阻塞而沒有遞送的信號。
int sigaction(int signo, const struct sigaction *restrict act, struct sigaction *restrict oact);
?returns: 0 if OK, -1 if error
如果act非空,則要修改其動作
如果oact非空,則返回該信號的上一動作
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為信號捕捉函數的地址
sa_mask 在調用信號捕捉函數之前,這個信號集要加入到進程的信號屏蔽字中,從信號捕捉函數返回后再將進程的信號屏蔽字復位為原先值。
sa_flags指定對信號處理的各個選項
sa_sigaction 當sa_flags為SA_SIGINFO時,sa_sigaction是替代sa_handler成為信號處理函數
int sigsuspend(const sigset_t *sigmask);
?return: always be -1, set errno to EINTR
這個函數作用是:先將當前進程的信號屏蔽字設為sigmask,然后掛起等待一個信號,捕捉到信號后從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 */
/* 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 */
如果信號在sigprocmask(SIG_SETMASK, &oldmask, NULL)與pause之間deliver,那么該信號就會丟失,pause函數有可能永遠等待下去,sigsuspend就相當于把這兩個函數合并,成為一個原子操作。
這個函數一般的應用場景:
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 */
下面是關于mq_notify和signal的介紹
我們在實現mq的接收模塊時,可能會采用msgrcv函數,但是在沒有消息的時候就得阻塞在msgrcv函數上,如果設置了nonblock標識,我們就得不停地調用msgrcv查看是否有消息,這種輪詢非常地消耗CPU。
Posix message queue提供了這樣一個功能,當有消息被插入到一個空的mq時,我們可以選擇向一個我們注冊的進程發送一個信號或者創建一個線程,這樣就提供了一種異步處理消息的機制,在沒有消息的時候,進程可以執行別的操作,有消息后,mq會給注冊進程發送信號,注冊進程調用信號處理函數或接收消息或者新創建的線程接收消息。
int mq_notify(mqd_t, const struct sigevent *notification);
?returns: 0 if OK, -1 on error
以下是幾種典型的利用mq_notify實現異步事件的例子:
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信號,保護代碼臨界區 */
??????????????? sigprocmask(SIG_BLOCK, &newmask, &oldmask);
??????????????? while(mqflag == 0){
??????????????????????? sigsuspend(&zeromask);/* 解除對SIGUSR1的阻塞并等待信號,返回時對SIGUSR1繼續阻塞 */
??????????????? }
??????????????? mqflag = 0;???????????? /*reset flag*/
??????????????? 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);
??????????????? }
??????????????? sigprocmask(SIG_UNBLOCK, &newmask, NULL);?????? /* unblock SIGUSR1 */
??????? }
??????? exit(0);
}
static void sig_usr1(int signo)?/* 信號處理函數內必須用可重入的函數,因此不能在這里面調用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);/* 等待產生在newmask中的未決的信號 */
??????????????? 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);
??????????????? /* 這種方法的優點是,可以設置超時時間,超時后可以去執行其他的操作,執行完后繼續監聽 */
??????????????? 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 實時信號)
實時信號是值在SIGRTMIN和SIGRTMAX之間的信號,下面是調用sigaction時的幾種動作情況
signal????????? 指定SA_SIGINFO標識?????????? 未指定SA_SIGINFO標識
實時信號???????? 確保實時信號動作????????????? 不能確保實時信號動作
一般信號???????? 不能確保實時信號動作????????? 不能確保實時信號動作
如果我們想要實現實時信號動作,在用sigaction設置信號處理動作時必須要用實時信號,而且要指定SA_SIGINFO標識,到底什么是實時信號動作呢?
實時信號動作可以保證以下幾點:
1.信號排隊,比如,產生3次SIGRTMIN信號,SIGRTMIN信號就被遞送3次,而且是先進先出(FIFO)。
2.當多個不阻塞的實時信號排隊時,越小的優先級越高,先被遞送,比如,排隊時SIGRTMIN就要排到SIGRTMAX的前面。
3.執行普通信號的處理函數時,處理函數只能得到一個參數,即信號值,實時信號在執行信號處理函數時則可以得到更多的信息
4.實時信號定義了很多新的函數,例如sigqueue函數實現了kill函數的功能,并能夠攜帶一個sigval結構體。
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的值是由系統設定的:
SI_ASYNCIO????? 異步IO請求完成時,Posix的aio_XXX函數
SI_MESGQ??????? 消息被插入到空的mq時
SI_QUEUE??????? 用sigqueue發送消息時
SI_TIMER??????? timer_setting函數設置的時鐘過期時
SI_USER???????? 用kil發送信號時
si_value是由用戶設置的參數,它僅在si_code為QUEUE, TIMEER, ASYNCIO, MESGQ這四個值時才有意義。
最后是一個實時信號的例子:
#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);
}
這個程序在SOL10上執行的結果是:
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
由此可見相同德實時信號以FIFO排序,小的實時信號比大的實時信號的優先級要高。
轉載于:https://www.cnblogs.com/seaney/p/3237566.html
總結
以上是生活随笔為你收集整理的sigal mq_notify的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: RAC 安装完成后 节点间通信不依赖于S
- 下一篇: 算法导论——排序算法