close

記上一篇的pipe和FIFO,這篇繼續來看看另外一種IPC的方式

就是signal,大部分是參考這裡 http://myblog-maurice.blogspot.com/2011/12/linux-signal.html 和 https://www.thegeekstuff.com/2012/03/catch-signals-sample-c-code/ 

 

其實說實在signal只能算是傳遞一個訊息給process,比如說中斷(使用者按Ctrl+c)或是殺死process(執行kill)之類的

並不是用來傳遞資料的

signal的種類有很多,包括錯誤、中斷、計時、使用者自訂等等

這個有興趣就去看看吧~ 網路上很多資料~

 

對於不同的signal呢,process可以選擇幾種處理方式

第一種是指定處理函式去處理,第二種是不處理,第三種是依照系統的預訂方式處理

那麼來看看第一種怎麼做吧

 

首先我們來看一個function

void (*signal(int signo, void (*func )(int)))(int);

嗯... 我基本功不太好,這是啥意思啊= ="

有興趣可以看看這邊的解釋https://blog.csdn.net/sever2012/article/details/8281271  

拆開來看看就知道了,void (*func)(int)看得出來是一個有int參數的函式的指標

signal(int singo, void (*func)(int))是一個帶有int和(*func)(int)兩個參數的函式

(*signal(int singo, void (*func)(int)))就是一個函式指標

那麼... void (*signal(int singo, void (*func)(int)))(int),就是一個(*signal(int singo, void (*func)(int)))指向的函式,這個函式帶有一個int參數,回傳是void

 

...應該可以理解吧

 

不過直接這樣看太複雜了,於是簡化成這樣:

typedef void sigfunc(int)
sigfunc *signal(int, sigfunc*);

稍微看一下就ok了,這種東西我覺得知道怎麼用比較重要...

#include <signal.h>
#include <unistd.h>
#include <stdio.h>

void sigroutine(int dunno) { /* 信號處理常式,其中dunno將會得到信號的值 */
    switch (dunno) {
        case 1:
            printf("Get a signal -- SIGHUP ");
            break;
        case 2:
            printf("Get a signal -- SIGINT ");
            break;
        case 3:
            printf("Get a signal -- SIGQUIT ");
            break;
    }
    return;
}
    
int main() {
    printf("process id is %d ",getpid());
    signal(SIGHUP, sigroutine); //* 下面設置三個信號的處理方法
    signal(SIGINT, sigroutine);
    signal(SIGQUIT, sigroutine);
    for (;;) ;
}

應該也不難懂吧,就是設定收到不同signal要有不同的處理而已

不過也有一些signal是沒被法被指定處理的,比如kill和stop的signal

如果要忽略或是恢復預設值,在signal的第二個參數可以代這兩個值:

SIG_IGN:忽略參數signum所指的信號。
SIG_DFL:恢復參數signum所指信號的處理方法為預設值。

 

系統中也有提供一些函式可以讓使用者發出signal

主要有kill()、raise()、 sigqueue()、alarm()、setitimer()以及abort()等函式 

 

1. kill()

長這樣:int kill(pid_t pid, int sig),可以向process發出不同的signal

pid表示process的id

如果pid為0,表示要發給和現在的process所屬進程組裡的所有process

如果pid為-1,表示發給除了本身之外系統裡的所有process

如果pid<-1,表示將發送給屬於進程組-pid的所有process

sig表示要發出的signal的編號,signal的編號都是在signal.h中定義的

另外如果發送0是不會發signal的,但是會有回傳值,所以可以用來確認process是否存在

 

2. raise()

int raise(int sig)

向process本身發送signal,調用成功返回 0;否則返回 -1。

 

3.  sigqueue()

int sigqueue(pid_t pid, int sig, const union sigval val) 

調用成功返回 0;否則,返回 -1。

sigqueue的第一個參數是指定接收信號的進程ID,第二個參數確定即將發送的信號,第三個參數是一個聯合資料結構union sigval,指定了信號傳遞的參數

基本上是屬於另一種發signal方式,和kill()不同的地方在於他只能傳給一個process,但是相對可以帶更多的資訊(就是union sigval)

 

然後可以搭配sigaction()使用,這個sigaction()呢長這樣:

int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));

其實sigaction也是設定收到signal時的行為用的,第一個參數是sibnal的編號,第二個參數是指向結構sigaction的一個實例的指標,在結構sigaction的實例中,指定了對特定信號的處理,可以為NULL,進程會以預設方式對信號處理;第三個參數oldact指向的物件用來保存原來對相應信號的處理,可指定oldact為NULL。如果把第二、第三個參數都設為NULL,那麼該函數可用於檢查信號的有效性。

struct sigaction定義是這樣:

struct sigaction {
       union{
           __sighandler_t _sa_handler;
           void (*_sa_sigaction)(int,struct siginfo *, void *);
       }_u

       sigset_t sa_mask;
       unsigned long sa_flags;
       void (*sa_restorer)(void);
}

第一個union是處理函式,sa_mask是用來過濾哪些signal要被block住的,sa_flag則是決定一些... 行為吧,預設情況下當前信號本身被block,除非指定SA_NODEFER或者SA_NOMASK標誌位元

當SA_NODEFER設置時在信號處理函數執行期間不會屏蔽當前信號;當SA_SIGINFO設置時與sa_sigaction 搭配出現,sa_sigaction函數的第一個參數與sa_handler一樣表示當前信號的編號,第二個參數是一個siginfo_t 結搆體,第三個參數一般不用。當使用sa_handler時sa_flags設置為0即可。

 

...講了一堆亂複雜一把的,如果沒有要特別用到的話,sigqueue()就是發signal的,sigaction()就是決定收到signal要做啥的

兩個可以互相搭配,當sigqueue()發送對應signal時,會把sugqueue()裡的sigval參數的值copy到sigaction裡的sigaction裡的handler裡的siginfo裡面,然後接收端就可以存取到這個數值,達到傳輸的目的

下面這個圖我覺得滿清楚的,參考下吧:

這邊其實滿複雜滿多細節的,詳細可以參考這邊 http://www.cnblogs.com/mickole/p/3191804.html

我就簡易的紀錄了

 

然後一如往常...,我會去做幾個小實驗,再補充code和結果上來紀錄吧

 

這邊列幾個我有參考的網頁,之後如果有要用到可以參考參考:

http://www.cnblogs.com/mickole/p/3191804.html

https://blog.csdn.net/jnu_simba/article/details/8947652

http://myblog-maurice.blogspot.com/2011/12/linux_20.html

 

4. pause()

int pause(void)

就是讓process進入sleep狀態,停在這裡等待一個signal的意思

 

5. alarm()和setitimer()

就是當作計時器用的

unsigned int alarm(unsigned int seconds)

設定系統在seconds秒後發出一個SIGALRM的signal,就是個有點陽春的功能

比較進階的方式是用底下這兩個:

int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue);

int getitimer(int which, struct itimerval *value);

簡單說就是一個設定timer,一個可以查看timer的狀態

系統提供了process三個計時器,它們各自有其獨有的計時域,當其中任何一個到達,就發送一個相應的信號給進程,並使得計時器重新開始。三個計時器由參數which指定,分別是:

        TIMER_REAL:按實際時間計時,計時到達將給進程發送SIGALRM信號。
        ITIMER_VIRTUAL:僅當進程執行時才進行計時。計時到達將發送SIGVTALRM信號給進程。
        ITIMER_PROF:當進程執行時和系統為該進程執行動作時都計時。與ITIMER_VIRTUAL是一對,該計時器經常用來統計進程在使用者態和內核態花費的時間。計時到達將發送SIGPROF信號給進程。

參數value表示timer的時間,結構如下:

struct itimerval {
    struct timeval it_interval; /* 時間的間隔,就是到達設定值後每隔多久再發一次signal */
    struct timeval it_value; /* 本次的設定值 */
};

timeval的結構如下:

struct timeval {
    long tv_sec; /* 秒 */
    long tv_usec; /* 微秒,1秒 = 1000000 微秒*/
}

在setitimer 調用中,參數ovalue如果不為空,則其中保留的是上次調用設定的值。計時器將it_value遞減到0時,產生一個信號,並將it_value的值設 定為it_interval的值,然後重新開始計時,如此往復。當it_value設定為0時,計時器停止,或者當它計時到期,而it_interval為0時停止。調用成功時,返回0;錯誤時,返回-1,並設置相應的error code

這篇我覺得寫的滿清楚的,參考下:https://www.cnblogs.com/mickole/p/3191977.html

 

一樣我會做幾個小實驗,有結果之後再補充

 

6. abort()

翻成中文是放棄的意思,真的是直接放棄的意思

代表異常的中止這個process,不會去做釋放記憶體之類的清除動作

有興趣可以去看看這篇,裡面有一些assert()、exit()等中止函式的比較:

http://q1696.pixnet.net/blog/post/64617849-linux%E7%B7%A8%E7%A8%8B%E2%80%94%E5%87%BA%E9%8C%AF%E8%99%95%E7%90%86%E4%B9%8Bassert%2Cabort%2Cexit%2Catexit%2Cstrerr

 

其實沒接觸過學起來還滿累的...,至少先有點概念吧XD

搞不好之後也用不到啊XDDD

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 頁頁頁六滴 的頭像
    頁頁頁六滴

    人森很精彩,所以要把所有事情都記起來=ˇ=

    頁頁頁六滴 發表在 痞客邦 留言(0) 人氣()