中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

signal 信號

發布時間:2020-07-01 16:45:18 來源:網絡 閱讀:672 作者:張黑蛋2012 欄目:系統運維

信號

1.信號本質

 信號是在軟件層次上對中斷機制的一種模擬,在原理上,一個進程收到一個信號與處理器收到一個中斷請求可以說是一樣的。信號是異步的,一個進程不必通過任何操作來等待信號的到達,事實上,進程也不知道信號到底什么時候到達。

 信號是進程間通信機制中唯一的異步通信機制,可以看作是異步通知,通知接收信號的進程有哪些事情發生了。信號機制經過POSIX實時擴展后,功能更加強大,除了基本通知功能外,還可以傳遞附加信息。

2.信號來源

 信號事件的發生有兩個來源:

硬件來源(比如我們按下了鍵盤或者其它硬件故障);

軟件來源,最常用發送信號的系統函數是kill, raise, alarm和setitimer以及sigqueue函數,軟件來源還包括一些非法運算等操作。

3.信號的種類

 一:可靠信號與不可靠信號

     Linux信號機制基本上是從Unix系統中繼承過來的。早期Unix系統中的信號機制比較簡單和原始,后來在實踐中暴露出一些問題,因此,把那些建立在早期機制上的信號叫做"不可靠信號",信號值小于SIGRTMIN(Red hat 7.2中,SIGRTMIN=32,SIGRTMAX=63)的信號都是不可靠信號。這就是"不可靠信號"的來源。它的主要問題是:進程每次處理信號后,就將對信號的響應設置為默認動作。在某些情況下,將導致對信號的錯誤處理;因此,用戶如果不希望這樣的操作,那么就要在信號處理函數結尾再一次調用signal(),重新安裝該信號。

信號可能丟失, 因此,早期unix下的不可靠信號主要指的是進程可能對信號做出錯誤的反應以及信號可能丟失。

Linux支持不可靠信號,但是對不可靠信號機制做了改進:在調用完信號處理函數后,不必重新調用該信號的安裝函數(信號安裝函數是在可靠機制上的實現)。因此,Linux下的不可靠信號問題主要指的是信號可能丟失。

     隨著時間的發展,實踐證明了有必要對信號的原始機制加以改進和擴充。所以,后來出現的各種Unix版本分別在這方面進行了研究,力圖實現"可靠信號"。由于原來定義的信號已有許多應用,不好再做改動,最終只好又新增加了一些信號,并在一開始就把它們定義為可靠信號,這些信號支持排隊,不會丟失。同時,信號的發送和安裝也出現了新版本:信號發送函數sigqueue()及信號安裝函數sigaction()。POSIX.4對可靠信號機制做了標準化。但是,POSIX只對可靠信號機制應具有的功能以及信號機制的對外接口做了標準化,對信號機制的實現沒有作具體的規定。

信號值位于SIGRTMIN和SIGRTMAX之間的信號都是可靠信號,可靠信號克服了信號可能丟失的問題。Linux在支持新版本的信號安裝函數sigation()以及信號發送函數sigqueue()的同時,仍然支持早期的signal()信號安裝函數,支持信號發送函數kill()。

注:不要有這樣的誤解:由sigqueue()發送、sigaction安裝的信號就是可靠的。事實上,可靠信號是指后來添加的新信號(信號值位于SIGRTMIN及SIGRTMAX之間);不可靠信號是信號值小于SIGRTMIN的信號。信號的可靠與不可靠只與信號值有關,與信號的發送及安裝函數無關。目前linux中的signal()是通過sigation()函數實現的,因此,即使通過signal()安裝的信號,在信號處理函數的結尾也不必再調用一次信號安裝函數。同時,由signal()安裝的實時信號支持排隊,同樣不會丟失。

對于目前linux的兩個信號安裝函數:signal()及sigaction()來說,它們都不能把SIGRTMIN以前的信號變成可靠信號(都不支持排隊,仍有可能丟失,仍然是不可靠信號),而且對SIGRTMIN以后的信號都支持排隊。這兩個函數的最大區別在于,經過sigaction安裝的信號都能傳遞信息給信號處理函數(對所有信號這一點都成立),而經過signal安裝的信號卻不能向信號處理函數傳遞信息。對于信號發送函數來說也是一樣的。

 二:實時信號與非實時信號

早期Unix系統只定義了32種信號,Ret hat7.2支持64種信號,編號0-63(SIGRTMIN=31,SIGRTMAX=63),將來可能進一步增加,這需要得到內核的支持。前32種信號已經有了預定義值,每個信號有了確定的用途及含義,并且每種信號都有各自的缺省動作。如按鍵盤的CTRL ^C時,會產生SIGINT信號,對該信號的默認反應就是進程終止。后32個信號表示實時信號,等同于前面闡述的可靠信號。這保證了發送的多個實時信號都被接收。實時信號是POSIX標準的一部分,可用于應用進程。

非實時信號都不支持排隊,都是不可靠信號;實時信號都支持排隊,都是可靠信號。

4.進程對信號的響應

 進程可以通過三種方式來響應一個信號:

(1)忽略信號,即對信號不做任何處理,其中,有兩個信號不能忽略:SIGKILL及SIGSTOP;

(2)捕捉信號。定義信號處理函數,當信號發生時,執行相應的處理函數;

(3)執行缺省操作,Linux對每種信號都規定了默認操作。

 注意,進程對實時信號的缺省反應是進程終止。

 Linux究竟采用上述三種方式的哪一個來響應信號,取決于傳遞給相應API函數的參數。

5.信號的發送

 一:int kill(pid_t pid,int signo)  調用成功返回0,否則返回-1。

pid>0進程ID為pid的進程

pid=0同一個進程組的進程

pid<0 pid!=-1進程組ID為 -pid的所有進程

pid=-1除發送進程自身外,所有進程ID大于1的進程

    Sinno是信號值,當為0時(即空信號),實際不發送任何信號,但照常進行錯誤檢查,因此,可用于檢查目標進程是否存在,以及當前進程是否具有向目標發送信號的權限(root權限的進程可以向任何進程發送信號,非root權限的進程只能向屬于同一個session或者同一個用戶的進程發送信號)

 二:int raise(int signo) 調用成功返回0,否則返回-1。

     向進程本身發送信號,參數為即將發送的信號值。

 三:int sigqueue(pid_t pid, int sig, const union sigval val) 調用成功返回0,否則返回-1。

     sigqueue的第一個參數是指定接收信號的進程ID,第二個參數確定即將發送的信號,第三個參數是一個聯合數據結構union sigval,指定了信號傳遞的參數,即通常所說的4字節值。

     sigqueue()比kill()傳遞了更多的附加信息,但sigqueue()只能向一個進程發送信號,而不能發送信號給一個進程組。如果signo=0,將會執行錯誤檢查,但實際上不發送任何信號,0值信號可用于檢查pid的有效性以及當前進程是否有權限向目標進程發送信號。

在調用sigqueue時,sigval_t指定的信息會拷貝到3參數信號處理函數(3參數信號處理函數指的是信號處理函數由sigaction安裝,并設定了sa_sigaction指針,稍后將闡述)的siginfo_t結構中,這樣信號處理函數就可以處理這些信息了。由于sigqueue系統調用支持發送帶參數信號,所以比kill()系統調用的功能要靈活和強大得多。

注:sigqueue()發送非實時信號時,第三個參數包含的信息仍然能夠傳遞給信號處理函數;sigqueue()發送非實時信號時,仍然不支持排隊,即在信號處理函數執行過程中到來的所有相同信號,都被合并為一個信號。

 四:unsigned int alarm(unsigned int seconds) 調用成功返回0,否則返回-1。

     專門為SIGALRM信號而設,在指定的時間seconds秒后,將向進程本身發送SIGALRM信號,又稱為鬧鐘時間。進程調用alarm后,任何以前的alarm()調用都將無效。如果參數seconds為零,那么進程內將不再包含任何鬧鐘時間。

返回值,如果調用alarm()前,進程中已經設置了鬧鐘時間,則返回上一個鬧鐘時間的剩余時間,否則返回0。

 五:int setitimer(int which, const struct itimerval *value, struct itimerval*ovalue)); 調用成功返回0,否則返回-1。

     第一個參數which指定定時器類型(

ITIMER_REAL: 設定絕對時間;經過指定的時間后,內核將發送SIGALRM信號給本進程;

ITIMER_VIRTUAL 設定程序執行時間;經過指定的時間后,內核將發送SIGVTALRM信號給本進程;

ITIMER_PROF 設定進程執行以及內核因本進程而消耗的時間和,經過指定的時間后,內核將發送ITIMER_VIRTUAL信號給本進程;);

     第二個參數是結構itimerval的一個實例,結構itimerval形式見附錄1。

     第三個參數可不做處理。

 六:void abort(void);

     向進程發送SIGABORT信號,默認情況下進程會異常退出,當然可定義自己的信號處理函數。即使SIGABORT被進程設置為阻塞信號,調用abort()后,SIGABORT仍然能被進程接收。該函數無返回值。

6.信號的安裝(設置信號關聯動作)

 如果進程要處理某一信號,那么就要在進程中安裝該信號。安裝信號主要用來確定信號值及進程針對該信號值的動作之間的映射關系,即進程將要處理哪個信號;該信號被傳遞給進程時,將執行何種操作。

 linux主要有兩個函數實現信號的安裝:signal()、sigaction()。其中signal()在可靠信號系統調用的基礎上實現, 是庫函數。它只有兩個參數,不支持信號傳遞信息,主要是用于前32種非實時信號的安裝;而sigaction()是較新的函數(由兩個系統調用實現:sys_signal以及sys_rt_sigaction),有三個參數,支持信號傳遞信息,主要用來與 sigqueue() 系統調用配合使用,當然,sigaction()同樣支持非實時信號的安裝。sigaction()優于signal()主要體現在支持信號帶有參數。

 一:void (*signal(int signum, void (*handler))(int)))(int);

    第一個參數指定信號的值,第二個參數指定針對前面信號值的處理,可以忽略該信號(參數設為SIG_IGN);可以采用系統默認方式處理信號(參數設為SIG_DFL);也可以自己實現處理方式(參數指定一個函數地址)。

如果signal()調用成功,返回最后一次為安裝信號signum而調用signal()時的handler值;失敗則返回SIG_ERR

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

     sigaction函數用于改變進程接收到特定信號后的行為。該函數的第一個參數為信號的值,可以為除SIGKILL及SIGSTOP外的任何一個特定有效的信號(為這兩個信號定義自己的處理函數,將導致信號安裝錯誤)。第二個參數是指向結構sigaction的一個實例的指針,在結構sigaction的實例中,指定了對特定信號的處理,可以為空,進程會以缺省方式對信號處理;第三個參數oldact指向的對象用來保存原來對相應信號的處理,可指定oldact為NULL。如果把第二、第三個參數都設為NULL,那么該函數可用于檢查信號的有效性。

7.一些結構體

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);

              }

ex:sa_restorer,已過時,POSIX不支持它,不應再被使用。

1、聯合數據結構中的兩個元素_sa_handler以及*_sa_sigaction指定信號關聯函數,即用戶指定的信號處理函數。除了可以是用戶自定義的處理函數外,還可以為SIG_DFL(采用缺省的處理方式),也可以為SIG_IGN(忽略信號)。

2、由_sa_handler指定的處理函數只有一個參數,即信號值,所以信號不能傳遞除信號值之外的任何信息;由_sa_sigaction是指定的信號處理函數帶有三個參數,是為實時信號而設的(當然同樣支持非實時信號),它指定一個3參數信號處理函數。第一個參數為信號值,第三個參數沒有使用(posix沒有規范使用該參數的標準),第二個參數是指向siginfo_t結構的指針,結構中包含信號攜帶的數據值,參數所指向的結構如下:


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 */

          }



3、sa_mask指定在信號處理程序執行過程中,哪些信號應當被阻塞。缺省情況下當前信號本身被阻塞,防止信號的嵌套發送,除非指定SA_NODEFER或者SA_NOMASK標志位。

注:請注意sa_mask指定的信號阻塞的前提條件,是在由sigaction()安裝信號的處理函數執行過程中由sa_mask指定的信號才被阻塞。

4、sa_flags中包含了許多標志位,包括剛剛提到的SA_NODEFER及SA_NOMASK標志位。另一個比較重要的標志位是SA_SIGINFO,當設定了該標志位時,表示信號附帶的參數可以被傳遞到信號處理函數中,因此,應該為sigaction結構中的sa_sigaction指定處理函數,而不應該為sa_handler指定信號處理函數,否則,設置該標志變得毫無意義。即使為sa_sigaction指定了信號處理函數,如果不設置SA_SIGINFO,信號處理函數同樣不能得到信號傳遞過來的數據,在信號處理函數中對這些信息的訪問都將導致段錯誤(Segmentation fault)。

注:很多文獻在闡述該標志位時都認為,如果設置了該標志位,就必須定義三參數信號處理函數。實際不是這樣的,驗證方法很簡單:自己實現一個單一參數信號處理函數,并在程序中設置該標志位,可以察看程序的運行結果。實際上,可以把該標志位看成信號是否傳遞參數的開關,如果設置該位,則傳遞參數;否則,不傳遞參數。

8.信號生命周期

 對于一個完整的信號生命周期(從信號發送到相應的處理函數執行完畢)來說,可以分為三個重要的階段,這三個階段由四個重要事件來刻畫:信號誕生;信號在進程中注冊完畢;信號在進程中的注銷完畢;信號處理函數執行完畢。相鄰兩個事件的時間間隔構成信號生命周期的一個階段。

 struct sigpending pending:

struct sigpending{

      struct sigqueue *head, **tail;

      sigset_t signal;

};

第三個成員是進程中所有未決信號集,第一、第二個成員分別指向一個sigqueue類型的結構鏈(稱之為"未決信號信息鏈")的首尾,信息鏈中的每個sigqueue結構刻畫一個特定信號所攜帶的信息,并指向下一個sigqueue結構:

struct sigqueue{

      struct sigqueue *next;

      siginfo_t info;

}


信號在進程中注冊指的就是信號值加入到進程的未決信號集中(sigpending結構的第二個成員sigset_t signal),并且信號所攜帶的信息被保留到未決信號信息鏈的某個sigqueue結構中。 只要信號在進程的未決信號集中,表明進程已經知道這些信號的存在,但還沒來得及處理,或者該信號被進程阻塞。

注:

當一個實時信號發送給一個進程時,不管該信號是否已經在進程中注冊,都會被再注冊一次,因此,信號不會丟失,因此,實時信號又叫做"可靠信號"。這意味著同一個實時信號可以在同一個進程的未決信號信息鏈中占有多個sigqueue結構(進程每收到一個實時信號,都會為它分配一個結構來登記該信號信息,并把該結構添加在未決信號鏈尾,即所有誕生的實時信號都會在目標進程中注冊);

當一個非實時信號發送給一個進程時,如果該信號已經在進程中注冊,則該信號將被丟棄,造成信號丟失。因此,非實時信號又叫做"不可靠信號"。這意味著同一個非實時信號在進程的未決信號信息鏈中,至多占有一個sigqueue結構(一個非實時信號誕生后,(1)、如果發現相同的信號已經在目標結構中注冊,則不再注冊,對于進程來說,相當于不知道本次信號發生,信號丟失;(2)、如果進程的未決信號中沒有相同信號,則在進程中注冊自己)。

信號在進程中的注銷。在目標進程執行過程中,會檢測是否有信號等待處理(每次從系統空間返回到用戶空間時都做這樣的檢查)。如果存在未決信號等待處理且該信號沒有被進程阻塞,則在運行相應的信號處理函數前,進程會把信號在未決信號鏈中占有的結構卸掉。是否將信號從進程未決信號集中刪除對于實時與非實時信號是不同的。對于非實時信號來說,由于在未決信號信息鏈中最多只占用一個sigqueue結構,因此該結構被釋放后,應該把信號在進程未決信號集中刪除(信號注銷完畢);而對于實時信號來說,可能在未決信號信息鏈中占用多個sigqueue結構,因此應該針對占用sigqueue結構的數目區別對待:如果只占用一個sigqueue結構(進程只收到該信號一次),則應該把信號在進程的未決信號集中刪除(信號注銷完畢)。否則,不應該在進程的未決信號集中刪除該信號(信號注銷完畢)。

進程在執行信號相應處理函數之前,首先要把信號在進程中注銷。

信號生命終止。進程注銷信號后,立即執行相應的信號處理函數,執行完畢后,信號的本次發送對進程的影響徹底結束。


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

金华市| 北票市| 华宁县| 安庆市| 天峨县| 石河子市| 巴彦县| 同心县| 延长县| 桑植县| 京山县| 延边| 鄄城县| 皋兰县| 石屏县| 定兴县| 长沙市| 延安市| 响水县| 于都县| 丰顺县| 吉木萨尔县| 山西省| 汝州市| 日喀则市| 泾川县| 昌都县| 西安市| 息烽县| 东源县| 射阳县| 富川| 南岸区| 乐陵市| 西盟| 永川市| 扎赉特旗| 页游| 河源市| 娱乐| 韶山市|