天天看點

linux信号實作淺析1--信号及其資料結構

一linux 信号常識

1linux信号是用來程序間或程序與核心間通信的一種機制,使用者可以注冊對應的信号處理函數來處理相應的信号。

2linux信号分為實時信号和普通信号,兩者之間的差別在于同種類型信号在排隊時,實時信号允許有多個同種類型實時信号同時排隊,而普通信号隻允許同種類型信号隻有一個在排隊。

3使用者可以對程序設定相應的信号阻塞掩碼,處于該掩碼中的信号,當程序接收到此信号時,不會進行處理,直到解除了對該信号的阻塞。

4對信号的預設處理操作通常有三種:terminal(殺死程序),dump(殺死程序,并産生core檔案),ignore(忽略).

5執行信号處理通常發生在系統從核心态傳回到使用者态的時候。

6對于多線程的應用,線程組中的多個線程會共享同一個信号描述符和信号處理函數描述符,是以在一個線程中使用signal()來注冊信号處理函數的話,對整個線程組的影響都是同樣的。但是每個線程可以有自己的信号掩碼和阻塞信号掩碼,在共享的信号描述符中,存放着共享的信号隊列,通常信号隊列中的信号由線程組中一個不會阻塞該信号的程序來執行。另外如果向線程組發出一個緻命的信号(執行動作為terminal或者dump),則會殺死整個線程組而不僅僅是執行該信号處理函數的線程。

7 一個fork的子程序繼承父程序的信号掩碼;信号掩碼可以跨越execve。一個通過fork建立的子程序繼承父程序的信号處理方式。當調用execve時,處理方式為handle的将被重置為default,處理方式為ignore的将繼續保持ignore。

8一個信号可以生成(未決)并傳遞給整個線程組(如使用kill(2))或傳遞給指定線程(如,某些信号,像SIGSEGV和SIGFPE,作為某個線程特定機器指令的執行結果而生成是特定于線程的,如需指定線程則使用pthread_kill(3))。一個針對程序的信号也許被遞送到任意一個沒有阻塞目前信号的線程。如果沒有阻塞該信号的線程多于一個,則核心随意挑選一個。一個線程可以用sigpending擷取一個未決信号集。此集合将取程序和調用線程未決信号集的并集。一個fork初始化的子程序未決信号集合為空集。未決信号集可以跨越execve。

二信号的相關資料結構

linux信号實作淺析1--信号及其資料結構

程序描述符中存放着與信号相關的字段,pending指向一個struct sigpending的結構,該結構用來存放該程序私有的挂起的信号隊列,signal指向一個信号描述符,該信号描述符中主要存放着線程組公用的挂起信号隊列。sighand字段指向信号處理函數描述符,記錄了對于各個信号的對應的處理方式。在多線程應用中,各個線程共享signal字段的信号描述符以及sighand字段的信号處理函數描述符。blocked字段指向的是被阻塞的信号的掩碼。

1signal_struct資料結構

struct signal_struct {

atomic_t count;//信号描述符的計數器

atomic_t live;//線程組中活動線程的數目

task_t *curr_target;//接收信号的線程組中的最後一個程序

struct sigpendingshared_pending;//存放公共挂起信号的資料結構

int group_exit;

int group_exit_code;//線程組的終止代碼

struct task_struct*group_exit_task;//殺死整個線程組時使用

int notify_count; //殺死整個線程組時使用

int group_stop_count; //停止整個線程組時使用

  int stop_state;

pid_t pgrp;//存放組領頭程序

pid_t tty_old_pgrp;

pid_t session;//存放會話領頭程序

int leader;

struct rlimit rlim[RLIM_NLIMITS];//程序的資源限制數組

};

2sigpending,sigqueue,siginfo_t

struct sigpending {

struct list_head list;//挂起信号的連結清單頭

sigset_t signal;//指定了挂起信号的位掩碼

};

struct sigqueue {

struct list_head list;//連結到挂起信号連結清單上去

spinlock_t *lock;//指向與挂起信号相對應的信号處理程式描述符中的siglock字段

int flags;//sigqueue資料結構的标志

siginfo_t info;//描述産生信号的操作

struct user_struct *user;

};

typedef struct siginfo {

int si_signo;//信号編号

int si_errno;//觸發該信号時的錯誤碼

int si_code;//發送信号者的代碼,比如SI_USER(kill或raise發出),SI_KERNEL(核心函數發出),SI_TKILL(tkill或者tgkill發出)等

//一個與信号類型相關的聯合體,sig_kill情況下存放發送者的pid和uid,段錯誤情況下,存放引發段錯誤的位址

union {

int _pad[SI_PAD_SIZE];

struct {

pid_t _pid;

__ARCH_SI_UID_T _uid;

} _kill;

struct {

timer_t _tid;

int _overrun;

char _pad[sizeof( __ARCH_SI_UID_T) - sizeof(int)];

sigval_t _sigval;

int _sys_private;      

} _timer;

struct {

pid_t _pid;

__ARCH_SI_UID_T _uid;

sigval_t _sigval;

} _rt;

struct {

pid_t _pid;

__ARCH_SI_UID_T _uid;

int _status;

clock_t _utime;

clock_t _stime;

} _sigchld;

struct {

void __user *_addr;

#ifdef __ARCH_SI_TRAPNO

int _trapno;

#endif

} _sigfault;

struct {

__ARCH_SI_BAND_T _band;

int _fd;

} _sigpoll;

} _sifields;

} siginfo_t;

3sighand_struct 及k_sigaction

struct sighand_struct {

atomic_t count;//信号處理描述符的引用計數

struct k_sigactionaction[_NSIG];//對應的信号處理函數

spinlock_t siglock;//保護描述符的自旋鎖

};

struct k_sigaction {

struct sigaction sa;

};

struct sigaction {

union {//指定要執行操作的類型,可以指向信号處理函數,也可以使SIG_DEF,SIG_IGN

 __sighandler_t _sa_handler;

 void (*_sa_sigaction)(int, struct siginfo *, void *);

} _u;

sigset_t sa_mask;//指定運作信号處理函數時要屏蔽的信号

unsigned long sa_flags;//标志,指定如何處理信号

void (*sa_restorer)(void);

};

繼續閱讀