Linux信號

信號概念

每個信號用一個整形常量宏表示,以SIG開頭
定義在頭文件<signal.h>
man 7 signal查看信號的默認行為

2號信號 Ctrl+c SIGINT
3號信號 Ctrl+\ SIGQUIT
信號都是異步的

忽略2號信號(命令行效果)

不是真正的忽略,而是把行為換成了\n

    1 #include <func.h>
?   2 void sigfunc(int signum)
    3 {
    4     //printf("%d is coming\n",signum);
    5     printf("\n");
    6 }
    7                                                                                                                               
?   8 int main(int argc,char*argv[])
    9 {
   10     if(signal(SIGINT,sigfunc)==SIG_ERR)
   11     {
   12         perror("signal\n");
   13     }   
   14     while(1);
   15     
   16     return 0;
   17 } 
 wenrou@uDesktop  ~/workplace/LinuxDay10  ./signal 
^C
^C
^C

真正的忽略2號信號

設(shè)置宏SIG_IGN

    1 #include <func.h>
    2 void sigfunc(int signum)
    3 {
    4     printf("%d is coming\n",signum);
    5 }
    6 
?   7 int main(int argc,char*argv[])
    8 {
    9     if(signal(SIGINT,SIG_IGN)==SIG_ERR)//設(shè)置宏SIG_IGN
   10     {
   11         perror("signal\n");
   12     }
   13     while(1);
   14                                                                                                                               
   15     return 0;       
   16 }
 wenrou@uDesktop  ~/workplace/LinuxDay10  ./signal 
^C^C^C^C^C^C

多個信號的執(zhí)行行為

  1 #include <func.h>
    2 //模擬sigfunc執(zhí)行很長時間,所以使用sleep
    3 void sigfunc(int signum)
    4 {
    5     printf("befor sleep %d is coming\n",signum);
    6     sleep(3);
    7     printf("after sleep %d is coming\n",signum);             
    8 }
    9 
?  10 int main(int argc,char*argv[])
   11 {
   12     //注冊2號信號
   13     if(signal(SIGINT,sigfunc)==SIG_ERR)
   14     {
   15         perror("signal\n");
   16     }
   17     //注冊3號信號
   18     if(signal(SIGQUIT,sigfunc)==SIG_ERR)
   19     {
   20         perror("signal\n");
   21     }
   22     while(1);
   23 
   24     return 0;
   25 }

不同信號到來時,可以打斷其他信號的信號處理流程
當(dāng)前信號到來時,如果正處于當(dāng)前信號的信號處理流程,不會打斷
最多再執(zhí)行一次
任何信號都具有喚醒功能

//先按Ctrl+c 很快再按Ctrl+\
//效果: 會打斷2號信號,進入3號信號的處理,相當(dāng)于遞歸的進入另一個信號處理函數(shù)
 ? wenrou@uDesktop  ~/workplace/LinuxDay10  ./signal    
^Cbefor sleep 2 is coming//不同信號到來
^\befor sleep 3 is coming
after sleep 3 is coming
after sleep 2 is coming
^Cbefor sleep 2 is coming//同一信號到來
^C^C^C^C^C^C^C^Cafter sleep 2 is coming
befor sleep 2 is coming
after sleep 2 is coming

原理 : 實際相當(dāng)于一個數(shù)組,每個信號占據(jù)一個元素,當(dāng)收到2號信號時,將對應(yīng)元素位置置為1,處理該信號時,將該位置為0,
信號 2 3 2

^Cbefor sleep 2 is coming
^\befor sleep 3 is coming
^Cafter sleep 3 is coming
after sleep 2 is coming
befor sleep 2 is coming
after sleep 2 is coming

3號信號會將2號信號打斷,再按2號信號,此時整體是在2號信號里面,所以后面的2號信號不會將3號信號打斷
原理
2號信號到來,將1置為0,轉(zhuǎn)去執(zhí)行處理
3號信號來,將1置為0,轉(zhuǎn)去執(zhí)行處理,打斷2號處理函數(shù)
2號信號再來,置為1,此時,整體再2號信號處理中,不會將3號信號打斷
前兩個信號 2 3 處理完,再次檢測數(shù)組,2號仍為1,則再置為0,轉(zhuǎn)去執(zhí)行2號信號處理函數(shù)
信號 2 3 3

^Cbefor sleep 2 is coming//2號

^\befor sleep 3 is coming
^\after sleep 3 is coming//同一信號再來,也在2號信號執(zhí)行內(nèi)部
befor sleep 3 is coming
after sleep 3 is coming

after sleep 2 is coming//2號

恢復(fù)信號默認處理

signal(SIGINT,SIG_DFL)

在read阻塞等待讀取的狀態(tài)下,也是可以接收信號的


sigaction信號處理機制

sigaction信號處理注冊

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
                //signum 是要捕捉的信號
                //act是一個結(jié)構(gòu)體
                //oldact直接用NULL
//sigaction結(jié)構(gòu)體
struct sigaction {
  void     (*sa_handler)(int);
  void     (*sa_sigaction)(int, siginfo_t *, void *);
  sigset_t   sa_mask;
  int        sa_flags;
  void     (*sa_restorer)(void);
};

使用sigaction實現(xiàn)signal的效果

    1 #include <func.h>
    2 
?   3 void sigfunc(int signum,siginfo_t *p,void *p1)
    4 {
    5     printf("%d is coming\n",signum);
    6 }
    7                                                        
?   8 int main(int argc,char*argv[])
    9 {
   10     struct sigaction act;
   11     //將act結(jié)構(gòu)體清空
   12     bzero(&act,sizeof(act));
   13     act.sa_flags=SA_SIGINFO;
   14     act.sa_sigaction=sigfunc;
   15     int ret;
   16     ret=sigaction(SIGINT,&act,NULL);
   17     ERROR_CHECK(ret,-1,"sigaction");
   18     while(1);
   19 
   20     return 0;
   21 }
 ? wenrou@uDesktop  ~/workplace/LinuxDay10  ./sigaction
^C2 is coming
^C2 is coming
^C2 is coming
^C2 is coming

使用sigaction捕捉多個信號執(zhí)行

    1 #include <func.h>
    2                
?   3 void sigfunc(int signum,siginfo_t *p,void *p1)
    4 {              
    5     printf("before sleep %d is coming\n",signum);
    6     sleep(3);  
    7     printf("after sleep %d is coming\n",signum);
    8 }              
    9                
?  10 int main(int argc,char*argv[])
   11 {              
   12     struct sigaction act;
   13     //將act結(jié)構(gòu)體清空  
   14     bzero(&act,sizeof(act));
   15     act.sa_flags=SA_SIGINFO;
   16     act.sa_sigaction=sigfunc;
   17     int ret;           
   18     //注冊2號信號      
   19     ret=sigaction(SIGINT,&act,NULL);
   20     ERROR_CHECK(ret,-1,"sigaction");
   21     //注冊3號辛哈      
   22     ret=sigaction(SIGQUIT,&act,NULL);
   23     ERROR_CHECK(ret,-1,"sigaction");                         
   24     while(1);
   25     
   26     return 0;
   27 }  

信號 2 3 2

//與signal效果一樣
 wenrou@uDesktop  ~/workplace/LinuxDay10  ./sigaction 
^Cbefore sleep 2 is coming
^\before sleep 3 is coming
^Cafter sleep 3 is coming
after sleep 2 is coming
before sleep 2 is coming
after sleep 2 is coming

sa_flags

設(shè)置為 SA_RESETHAND

處理完畢要捕捉的信號之后,自動撤銷信號處理函數(shù)的注冊
信號處理行為只有一次生效

 12     struct sigaction act;
   13     //將act結(jié)構(gòu)體清空
   14     bzero(&act,sizeof(act));
   15     act.sa_flags=SA_SIGINFO|SA_RESETHAND;
   16     act.sa_sigaction=sigfunc;                          

設(shè)置為 SA_NODEFER

信號不會再丟失,信號不斷的打斷,會打斷自己

 12     struct sigaction act;
   13     //將act結(jié)構(gòu)體清空
   14     bzero(&act,sizeof(act));
   15     act.sa_flags=SA_SIGINFO|SA_NODEFER;                      
   16     act.sa_sigaction=sigfunc;

真 · 遞歸調(diào)用
信號不會丟失

 wenrou@uDesktop  ~/workplace/LinuxDay10  ./sigaction 
^Cbefore sleep 2 is coming
^Cbefore sleep 2 is coming
^Cbefore sleep 2 is coming
^Cbefore sleep 2 is coming
^Cbefore sleep 2 is coming
^Cbefore sleep 2 is coming
after sleep 2 is coming
after sleep 2 is coming
after sleep 2 is coming
after sleep 2 is coming
after sleep 2 is coming
after sleep 2 is coming

設(shè)置為 SA_RESTART

當(dāng)發(fā)生信號時,正阻塞在某系統(tǒng)調(diào)用,設(shè)置此參數(shù),立即進入信號的處理,處理完畢之后,繼續(xù)處理該阻塞

    1 #include <func.h>
    2           
?   3 void sigfunc(int signum,siginfo_t *p,void *p1)
    4 {         
    5     printf("%d is coming\n",signum);
    6 }         
    7           
?   8 int main(int argc,char*argv[])
    9 {         
   10     struct sigaction act;
   11     //將act結(jié)構(gòu)體清空
   12     bzero(&act,sizeof(act));
   13     act.sa_flags=SA_SIGINFO|SA_RESTART;
   14     act.sa_sigaction=sigfunc;
   15     int ret;
   16     //注冊2號信號
   17     ret=sigaction(SIGINT,&act,NULL);
   18     ERROR_CHECK(ret,-1,"sigaction");
   19     //卡在讀取標準輸入                                                                                                        
   20     char buf[128];
   21     read(STDIN_FILENO,buf,sizeof(buf));
   22     printf("buf is %s\n",buf);
   23     return 0;              
   24 } 
? wenrou@uDesktop  ~/workplace/LinuxDay10  ./sigaction
^C2 is coming
^C2 is coming
^C2 is coming
^C2 is coming
nihao
buf is nihao

sigaction結(jié)構(gòu)體成員 sigset_t sa_mask 將要被阻塞的信號集合

//系統(tǒng)提供對sigset_t結(jié)構(gòu)體的接口
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);
   1 #include <func.h>
    2 
?   3 void sigfunc(int signum,siginfo_t *p,void *p1)
    4 {
    5     printf("before sleep %d is coming\n",signum);
    6     sleep(3);
    7     printf("after sleep %d is coming\n",signum);
    8 }
    9 
?  10 int main(int argc,char*argv[])
   11 {
   12     struct sigaction act;
   13     //將act結(jié)構(gòu)體清空
   14     bzero(&act,sizeof(act));
   15     act.sa_flags=SA_SIGINFO;
   16     act.sa_sigaction=sigfunc;
   17     //我們將3號信號填入sa_mask
   18     //1.清空sa_mask集合
   19     sigemptyset(&act.sa_mask);
   20     //2.把3號信號填入阻塞集合
   21     sigaddset(&act.sa_mask,SIGQUIT);
   22     int ret;
   23     ret=sigaction(SIGINT,&act,NULL);
   24     ERROR_CHECK(ret,-1,"sigaction");
   25     ret=sigaction(SIGQUIT,&act,NULL);
   26     ERROR_CHECK(ret,-1,"sigaction");
   27     while(1);                                                                                                                 
   28     return 0;
   29 }


3號信號不會將2號信號打斷

 wenrou@uDesktop  ~/workplace/LinuxDay10  ./sigaction 
^Cbefore sleep 2 is coming
^\^\^\after sleep 2 is coming
before sleep 3 is coming
after sleep 3 is coming

取出當(dāng)前內(nèi)核中掛起的信號

必須寫在當(dāng)前正在執(zhí)行的信號處理函數(shù)內(nèi),否則,當(dāng)前信號處理完畢,阻塞信號得到響應(yīng)邊查不到

?   3 void sigfunc(int signum,siginfo_t *p,void *p1)
    4 {
    5     printf("before sleep %d is coming\n",signum);
    6     sleep(3);
    7     sigset_t pend;//存儲掛起的信號集合
    8     sigpending(&pend);//從內(nèi)核中把掛起的信號取出
    9     if(sigismember(&pend,SIGQUIT))
   10     {
   11         printf("SIGQUIT is pending\n");
   12            
          }                                                                                                         
   13     printf("after sleep %d is coming\n",signum);
   14 } 
 wenrou@uDesktop  ~/workplace/LinuxDay10  ./sigaction 
^Cbefore sleep 2 is coming
after sleep 2 is coming

^Cbefore sleep 2 is coming //2號處理過程中發(fā)送3號信號 3號信號掛起
^\SIGQUIT is pending
after sleep 2 is coming
before sleep 3 is coming
after sleep 3 is coming

關(guān)鍵代碼若不希望被打斷,實現(xiàn)保護代碼,借助 sigprocmask信號阻塞(全程阻塞)

sigprocmask信號阻塞

    1 #include <func.h>
    2 
?   3 int main(int argc,char*argv[])
    4 {
    5     sigset_t block;
    6     sigemptyset(&block);
    7     sigaddset(&block,SIGINT);
    8     sigprocmask(SIG_BLOCK,&block,NULL);
    9     sleep(10);//重要代碼                                                              
   10     return 0; 
   11 }     
wenrou@uDesktop  ~/workplace/LinuxDay10  ./sigprocmask 
^C^C^C^C^C%                                                     

接觸阻塞sigprocmask(SIG_UNBLOCK,&block,NULL)

kill信號發(fā)送函數(shù)

  5     //kill(pid,sig)
  6     //pid 為要接收信號的進程pid 可以通過getpid獲取
  7     //sig 為要發(fā)送的信號
  8     ARGS_CHECK(argc,2);
  9     pid_t pid=atoi(argv[1]);
 10     //SIGINT 即為Ctrl+c                                                                
 11     int ret=kill(pid,SIGINT);
 12     ERROR_CHECK(ret,-1,"kill");
    //發(fā)送SIGQUIT信號把自己殺死
    kill(getpid(),SIGQUIT);

計時器與信號

睡眠函數(shù)

sleep();//以秒為單位
usleep();//以微秒為單位

sleep函數(shù)內(nèi)部是用信號機制處理的
alarm(seconds);
seconds秒后自動產(chǎn)生一個SIGALRM信號

計時統(tǒng)計

    1 #include <func.h>
    2 
    3 int main(int argc,char*argv[])
    4 {
    5     struct timeval start,end;                                                                                                 
    6     gettimeofday(&start,NULL);
    7     for(int i=0;i<100000000;i++);
    8     gettimeofday(&end,NULL);
    9     printf("use time=%ld\n",(end.tv_sec-start.tv_sec)*1000000+end.tv_usec-start.tv_usec);
   10                                                                                
   11     return 0;                                                                  
   12 }  

時鐘處理

真實計時器 : 直觀感受到的時間,包括read阻塞
虛擬計時器 :
實用計時器 : 實際運行在內(nèi)核態(tài)和用戶態(tài),不包括read阻塞

使用真實計時器

    1 #include <func.h>
    2 
?   3 void sigfunc(int signum)
    4 {
    5     //獲取當(dāng)前時間并打印
    6     time_t t;
    7     time(&t);
    8     printf("%s\n",ctime(&t));
    9 }
   10 
?  11 int main(int argc,char*argv[])
   12 {
   13     signal(SIGALRM,sigfunc);
   14     struct itimerval t;
   15     bzero(&t,sizeof(t));
   16     t.it_value.tv_sec=3;//初始間隔
   17     t.it_interval.tv_sec=2;//重復(fù)間隔
   18     sigfunc(0);
   19     int ret=setitimer(ITIMER_REAL,&t,NULL);
   20     ERROR_CHECK(ret,-1,"setitimer");
   21     while(1);
   22     return 0;                                             
   23 }

真實計時器,程序執(zhí)行是直觀感受到的時間,阻塞也計算在內(nèi)

使用實用計時器

    1 #include <func.h>
    2     
?   3 void sigfunc(int signum)
    4 {   
    5     //獲取當(dāng)前時間并打印
    6     time_t t;
    7     time(&t);
    8     printf("%s\n",ctime(&t));
    9 }   
   10     
?  11 int main(int argc,char*argv[])
   12 {   
   13     signal(SIGPROF,sigfunc);
   14     struct itimerval t;
   15     bzero(&t,sizeof(t));
   16     t.it_value.tv_sec=3;//初始間隔
   17     t.it_interval.tv_sec=2;//重復(fù)間隔
   18     sigfunc(0);
   19     int ret=setitimer(ITIMER_PROF,&t,NULL);
   20     ERROR_CHECK(ret,-1,"setitimer");
   21     char buf[128]={0};
   22     read(STDIN_FILENO,buf,sizeof(buf));
   23     printf("%s\n",buf);
   24     while(1);                                             
   25     return 0;
   26 }
 ? wenrou@uDesktop  ~/workplace/LinuxDay10  ./setitimer_real
Wed Jan 27 21:33:30 2021
//實用計時器,不統(tǒng)計read阻塞
hello world
hello world
//直到不阻塞才繼續(xù)統(tǒng)計
Wed Jan 27 21:33:39 2021

Wed Jan 27 21:33:41 2021

Wed Jan 27 21:33:43 2021

^C

使用虛擬計時器

   13     signal(SIGVTALRM,sigfunc);
   14     struct itimerval t;
   15     bzero(&t,sizeof(t));
   16     t.it_value.tv_sec=3;//初始間隔
   17     t.it_interval.tv_sec=2;//重復(fù)間隔
   18     sigfunc(0);
   19     int ret=setitimer(ITIMER_VIRTUAL,&t,NULL);
   20     ERROR_CHECK(ret,-1,"setitimer");
   21     sleep(5);
   22     while(1);                                             
   23     return 0;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 對于 Linux來說,實際信號是軟中斷,許多重要的程序都需要處理信號。信號,為 Linux 提供了一種處理異步事件...
    故事狗閱讀 86,271評論 2 63
  • 信號的機制 A給B發(fā)送信號,B收到信號之前執(zhí)行自己的代碼,收到信號后,不管執(zhí)行到程序的什么位置,都要暫停運行,去處...
    Sharkchilli閱讀 497評論 0 0
  • 本文將從以下幾個方面來闡述信號: (1)信號的基本知識 (2)信號生命周期與處理過程分析 (3) 基本的信號處理函...
    linux大本營閱讀 624評論 0 0
  • 信號(signal)是一種軟件中斷,它提供了一種處理異步事件的方法,也是進程間惟一的異步通信方式。在Linux系統(tǒng)...
    夏大王2019閱讀 1,073評論 0 1
  • 久違的晴天,家長會。 家長大會開好到教室時,離放學(xué)已經(jīng)沒多少時間了。班主任說已經(jīng)安排了三個家長分享經(jīng)驗。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,849評論 16 22

友情鏈接更多精彩內(nèi)容