1. 程式人生 > >Linux系統編程之進程間通信之淺談信號

Linux系統編程之進程間通信之淺談信號

編程 不能 status 系統編程 編寫 sim 發送信號 存在 就會

我們接著談Linux學習過程中一個重要的話題--信號。


一、信號的概念:
信號是一種軟件中斷,它提供了一種處理異步事件的方法,也是進程間唯一的異步通信方式。
二、信號的來源:
1、硬件方式:
當用戶按下終端上某些鍵時,將產生信號。
硬件異常產生信號:除0操作、訪問非法空間……
2、軟件方式
用戶在終端下調用kill命令向進程發送任意信號
進程調用kill或者sigqueue函數發送信號。
當檢測到某種軟件條件發生時,如alarm或者setimer

三、信號處理
1、信號的捕捉和處理


1、signal函數
a、函數作用:
signal函數用來設置進程在接收到信號時的動作。
b、函數原型:

              #include <signal.h>
         typedef void (*sighandler_t)(int);
         sighandler_t signal(int signum, sighandler_t handler);

c、參數解析與函數說明:

signal函數會根據參數signal指定的信號編號來設置該信號的處理函數。當指定的信號到達時就會

跳到參數hander指定的函數執行。如果hander不是函數指針,則必須時常數SIG_IGN忽略該信號)或者S

IG_DFL(對該信號執行默認操作)。hander是 一個函數指針,它所指向的函數的類型時sighandel_t,即

它 所指向的函數有一個int型參數,且返回值的類型為void。
d、樣這裏給出signal處理信號的例子:


/**********************************************************
*    > File Name: signal-1.c
*    > Author:xiao_k
*    > Mail:[email protected] 
*    > Created Time: Fri 23 Feb 2018 12:05:46 AM CST
**********************************************************/
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include<signal.h>
//信號處理函數
void do_sig(int num)
{
   printf("I am do_sig\n");
}
int main()
{
      //安裝處理信號
     signal(SIGINT,do_sig);
     while(1)
      {
        printf("********\n");
        sleep(1);
      }
       return 0;
 }

2、sigaction函數
a、函數作用:
sigction函數可以用來檢查和設置進程在接收信號時的動作。
b、函數原型
#include <signal.h>
int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);
c、參數解析與函數說明:
sigzction函數會根據參數signum指定的信號編號來設置該信號的處理函數。參數signum可以是除了

SIGKILLh和SIGSTOP以外的任何信號。
如果參數act 不是空指針,則為signum設置新的信號處理函數;
如果oldzct不是空指針,則舊的信號處理函數將被存儲在oldact中,struct sigaction的定義如下:
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
sa_handler可以是常數SIG_DFL或者SIG_IGN,或者是一個信號處理函數名。
sa_sigaction也是用來指定信號的signum的處理函數。
sa_mask成員聲明了一個信號集,在調用信號捕捉函數之前,該信號會增加到進程的信號屏蔽碼中,

新的信號屏蔽碼會自動包括正在處理的信號(sa_flags未指定SA_NODEFER或者SA_NOMASK)。當從信號捕

捉函數返回時,進程的信號屏蔽碼會恢復為原來的值。因此,當處理一個給定的 信號時,如果這種

信號再次發生,那麽它會阻塞直到本次信號處理結束為止。若這種信號發生了多次,則對於不可靠信號,

即本次信號處理結束以後只會再次處理一次(相當於丟失了信號),對於可靠信號(實時信號),則會

被阻塞多次,即信號不會丟失,信號發生了多少次就會調用信號處理函數多少次。
sa_flags成員用來說明信號處理的其他相關操作。
當使用三參數的sa_sigaction來指定信號處理函數時,它的第 二個參數可以用來傳遞數據,其定義

如下:
siginfo_t {
int si_signo; /* Signal number */
int si_errno; /* An errno value */
int si_code; /* Signal code */
int si_trapno; /* Trap number that caused
hardware-generated signal (unused on most architectures) */
pid_t si_pid; /* Sending process ID */
uid_t si_uid; /* Real user ID of sending process */
int si_status; /* Exit value or signal */
clock_t si_utime; /* User time consumed */
clock_t si_stime; /* System time consumed */
sigval_t si_value; /* Signal value */
int si_int; /* POSIX.1b signal */
void *si_ptr; /* POSIX.1b signal */
int si_overrun; /* Timer overrun count;POSIX.1b timers */
int si_timerid; /* Timer ID; POSIX.1b timers */
void *si_addr; /* Memory location which caused fault */
int si_band; /* Band event */
int si_fd; /* File descriptor */
}

d、同樣給出 sigaction的測試用例

/**********************************************************
*    > File Name: sigaction-1.c
*    > Author:xiao_k
*    > Mail:[email protected] 
*    > Created Time: Fri 23 Feb 2018 01:47:43 AM CST
**********************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<signal.h>
int temp = 0;
//信號處理函數
void do_sigact(int num)
{
    printf("recv SIGINT\n");
    sleep(5);
    temp +=1;
    printf("the value of temp is :%d\n",temp);
    printf("in do_sigact,after sleep\n");
}
int main()
  {
     struct sigaction act;
      //結構體賦值
      act.sa_handler = do_sigact;
      act.sa_flags = SA_NOMASK;
      //安裝信號處理函數
      sigaction(SIGINT,&act,NULL);
     while(1)
      {
        printf("********************\n");
        sleep(1);
      }
     return 0;
   }

編寫信號處理函數時,要註意不要使用不可重入的函數,使用了不重入的函數將將產生不可預料的結果。

上邊就是sigaction函數的基本用法,復雜的用法後邊使用。
補充:滿足下列條件的函數是不可重入的:
a、使用了靜態的數據結構,如getgrpid(),全局變量等。
b、函數實現時,調用了malloc()或者free()函數。
c、函數實現時,使用了標準I/O函數。


3、pause()函數
a、函數作用:
pause函數使調用進程掛起直到捕捉到一個信號。pause函數會令目前的進程暫停(進入睡眠狀態),

直到被信號(signal)所中斷,該函數只返回-1,並將errno設置為EINTR。
b、函數原型:
#include <unistd.h>
int pause(void);
四、信號的發送:
信號的發送主要由函數kill、raise、sigqueue、alarm、setitimer以及 abort來完成。
1、kill函數
a、函數作用:
kill函數用來發送信號給指定的進程。
b、函數原型

       #include <sys/types.h>
       #include <signal.h>
       int kill(pid_t pid, int sig);

c、參數解析與函數說明:
該函數的行為與第二個參數pid的取值有關,第二個參數sig表示信號編號:
如果pid時正數,則發送信號sig給進程號為pid的進程;
果pid為0,則發送信號sig給當前進程所屬進程組裏的所有進程;
如果pid為-1,則把信號sig廣播給系統內的除1號進程和自身以外的所有進程;
如果pid為比-1還小的負數,則發送信號sig給屬於進程組-pid的所有進程;
如果參數sig是0,則kill(),仍然執行正常的錯誤檢查,但不發送信號。可以利用這一點來確定某

進程是否有權向另外一個進程發送信號。如果向一個並不存在的進程發送空信號,則kill()返回-1,

errno則被設置為ESRCH。
註意:只有具有root權限的進程才能向其他任意一個進程發送信號,非root權限的進程只能向屬於同一

個組成或者同一個用戶創建的進程發送信號。
2、raise函數
a、函數作用:
raise函數是ANSI C 而非POSIX標準定義的。用來給調用它的進程發送信號。
b、函數原型

       #include <signal.h>
        int raise(int sig);

3、sigqueue函數
a、函數作用
sigqueue函數是一個比較新的發送信號的函數,它支持信號帶有參數,從而可以與函數sigaction配

合使用。
b、函數原型
#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval value);
c、參數解析與函數說明:
sigqueue用來發送給信號sig給進程pid。與kill系統調用不同的是,sigqueue在發送信號的同時

還支持信號攜帶參數;另外一個不同點是sigqueue 不能給一組進程發送信號,參數value是一個共用
體,其定義如下:
union sigval {
int sival_int;
void *sival_ptr;
};

4、alarm函數
a、函數作用
alarm函數可以用來設置定時器,定時器超時將產生SIGALRM信號給調用進程。
b、函數原型

         #include <unistd.h>
      unsigned int alarm(unsigned int seconds);

c、參數解析與函數說明:
參數seconds表示設定的秒數,經過seconds後,內核將給調用該函數的進程發送SIGALRM信號。如果seconds為0,

則不再發送SIGALRM信號,最新一次調用alarm函數將取消之前一次的設定。
5、abrot函數
a、函數作用:
abrot函數用來向進程發送SIGABRT信號。
b、函數原型

      #include <stdlib.h>
     void abort(void)

上邊我只對信號的基本用法做了總結,後邊將對信號的高級用法做單獨總結。



Linux系統編程之進程間通信之淺談信號