linux 信号是一种进程间异步通信机制。实际上是一种软中断机制。
linux 下信号的生命周期如下:
1) 在目的进程中安装该信号。即是设置捕获该信号时进程进程该执行的操作码。采用signal();sigaction()系统调用来实现。
2) 信号被某个进程产生,同时设置该信号的目的进程(使用pid),之后交给操作系统进行管理。采用kill()、arise()、alarm()等系统调用来实现。
3) 信号在目的进程被注册。信号被添加进进程的PCB(task_struct)中相关的数据结构里——未决信号的数据成员。信号在进程中注册就是把信号值加入到进程的未决信号集里。
并且,信号携带的其他信息被保留到未决信的队列的某个sigqueue结构中。
4) 信号在进程中注销。在执行信号处理函数前,要把信号在进程中注销。对于非实时信号(不可靠信号),其在信号未决信号信息链中最多只有一个sigqueue 结构,因此该结构被释放后,相应的信号要在未决信号集删除。而实时信号(可靠信号),如果有多个sigqueue,则不会把信号从进程的未决信 号集中删除。
5) 信号生命的终结。进程终止当前的工作,保护上下文,执行信号处理函数,之后回复。如果内核是可抢占的,那么还需要调度。
linux 信号的产生:
1) 用户在终端输入,如Ctrl+C则产生终止的信号。
2) 硬件产生的异常信号。
3) 软件异常产生的信号。
4) 进程终止的信号。例如某进程给另一个进程(包括自身)发送信号,如调用kill()函数可以发送信号给一进程或者进程组。
在终端下输入命令--->ps 显示如下:
之后可以输入指令:kill -SIGABRT XXXX 其中SIGABRT 为要发给指定进程的信号,这个的信号默认操作为终止进程,而XXXX很明显就是进程的PID啦。。。。。注意这里的kill不是指杀死的意思。是发送信号!
linux 信号的发送:
1) 进程发送信号给自己,用raise()函数。extern int raise(int __sig) 其中__sig为信号的值。
2) 进程发送信号给其他的进程,用 kill() 函数。 extern int kill(__pid_t __pid ,int __sig) 其中__pid 很明显就是某进程的pid,而__sig同上。我们会问怎么知道某个进程的pid啊。?哈哈这里有个函数--> getpid()。这样我们可以利用getpid() 与kill() 这两个函数完成raise() 的功能。kill( getpid(), __sig )。其实我们会注意到,如果当有两个C文件用GCC编译链接后为A , B 两个可执行文件,我们在编程时不知道其运行时的PID,即如果要A要给B发送信号,但我们在编程时A是不能知道B文件运行时的PID的。那样A在编程时,kill() 中的PID不能确定,不能给B发送信号啊!怎么办。。。? 哈哈,我这里有个思路!!在C程序文件中利用fork()函数产生一个子进程,在子进程中执行原来A程序文件的内容(又或者在子进程中调用execX系列的系统调用,执行一个新的程序,而这个新的程序就是B的可执行文件了。),而B程序文件则在父进程中执行,这样就可以知道B的PID啦,怎么知道?getpppid() 就得咯。
3) aralm() 定时器,在一定时间内产生SIGALRM信号。 extern unsigned int alarm( unsigned int __sec) 其中__sec是以秒为单位的时间。默认的情况下,在进程接收alarm() 的信号后会终止的。这里要注意的是,子进程并不继承父进程的alarm 信号。在调用exec()执行新的程序的时候,原来设置的alarm 仍然有效。
linux 信号的安装:
1) 安装信号函数,signal()。 extern _sighandler_t signal(int __sig, _sighandler_t __handler) 其中__sig为接收到的信号,__handler 为接收信号后处理代码的入口。__handler 可以为以下的3个宏————>SIG_DFL(默认的操作), SIG_ERR(返回错误), SIG_IGN(忽略信号).
例子: void my_sig_handler(ing __sig){
printf("i got a sig %d\n",__sig);
}
main(){
signal(SIGUSR1, my_sig_handler);
}
因为 signal() 只能安装简单的信号,在linux里其已近开始逐渐被淘汰了。取而代之的是强大的sigaction()
2) 安装信号函数,sigaction()。 extern int ssigaction(int __sig , struct sigaction * __N_act ,struct sigaction * __O_act)。
其中,__sig是要安装的信号,__N_act是描述信号要执行的操作与相关的信息。如果 __N_act 为NULL,那么信号的处理将不会改变。__O_act存储在执行这个信号之前这个信号安装的信息。
struct sigaction {
__sighandler_t _sa_handler; //对信号处理的函数
void (*_sa_sigaction)(int, struct siginfo *, void *); //信号捕获函数
} __u;
sigset_t sa_mask; //在执行信号捕获的期间要屏蔽的其他信号
unsigned long sa_flags; //这是影响信号行为的标记
void (*sa_restorer)(void); //这个是没有被使用的
};
#define sa_sigaction _u._sa_sigaction
这里对各个成员进行解释,sa_handler 与sa_sigaction两者取其一即可。使用前者则与signal()函数一样,而后者则有专门的用途!sa_mask为信号集,执行信号捕获函数时要添加到进程的屏蔽信号集的信号集。要注意的是,不会将SIGSTOP与SIGKILL 添加到进程的屏蔽信号集中, 这是系统强制执行用来给超级用户终止普通进程用的。sa_flags 是影响信号行为的标记。对于sa_flags有以下几种取值,可以为其中的一个也可以为其中的几个。
1:SA_NOCLDSTOP,在子进程退出的时候不会生成SIG_CHLD信号。
2:SA_ONSTACK ,设置了这个标志并且使用sigaltstack()或者sigstack() 声明备份信号堆栈,信号会传递给这个堆栈中的调用进程,否则,信号在当前堆栈中传递。
3:SA_RESETHAND ,如果设置了这个标志,sigcation()的行为如同设置了SA_NODEFER标志。而且处理后信号的处理方法将重置为SIG_DFL,并且进入信号处理程序后将清除SA_SIGINFO标志。
4:SA_RESTART ,影响可中断函数的行为,中断函数会失败。而且会设置errno全局变量。
5:SA_SIGINFO ,如果没有设置这个标志并且捕获了信号,信号的捕获函数会以以下的形式:void func (int __sig) ,这样必须使用sa_handler 成员来描述信号捕获函数,而且不许修改sa_sigaction的成员。
6:SA_NOCLDWAIT.如果设置这个标志,并且sig等于SIGCHLD,那么子进程在终止时不会转化为僵死进程。如果调用的进程之后等待其子进程,并且调用进程没有非等待的子进程(已经变为僵尸进程),那么调用的进程会阻塞直到所有的子进程都终止,并且wait()系列的函数都会失败。
7:SA_NODEFER ,设置这个标志并且捕获到信号,那么进程进入信号处理程序后,信号将不会添加到进程的屏蔽信号集中,除非,将其添加到sa_mask成员。否则,在进入信号处理程序后,sig始终会添加到进程屏蔽信号集中。
ps:。。。。。这么多。。不要记。。用时候拿出来查下就得啦。。。。对于这两个安装函数,由于signal()有系统漏洞,一般不建议使用。
linux 安装信号:
1) 先来区分一下信号的屏蔽与忽略。
信号的屏蔽:就是给进程发送信号,可是进程不捕获信号,而是让这个信号处于未决状态。当信号不再屏蔽这个信号的时候,才去捕获该信号。
信号的忽略:系统仍然传递这个信号给进程,但进程不对这个信号任何处理。
2) 信号集:为了方便管理进程的多个信号,linux 使用了信号集。
定义在不同的版本是不同的。以下是2.6.39的。linux-2.6.39.1/include/asm-generic/signal.h
typedef struct {
unsigned long sig[_NSIG_WORDS];
} sigset_t;
3) 设置屏蔽信号集:extern int sigprocmask(int __how, __const sigset_t *__restrict __set, sigset_t * __restrict __oset ) 调用成功返回0,否则为-1.
其中参数解释如下:
__how 指示如何修改屏蔽信号
__set是一个非空指针时,根据__how修改屏蔽信号
__oset是一个非空指针时,存放当前屏蔽信号集
若__set为NULL,不改变该进程的信号屏蔽字,__how也无意义,这是可以查看进程的屏蔽信号集!保存在__oset中。

__how的取值:
SIG_BLOCK
该进程新的信号屏蔽字是其当前信号屏蔽字和__set指向信号集的并集。__set包含了我们希望阻塞的附加信号
SIG_UNBLOCK
该进程新的信号屏蔽字是其当前信号屏蔽字和__set所指向信号集的交集。__set包含了我们希望解除阻塞的信号
SIG_SETMASK
该进程新的信号屏蔽是set指向的值
4) 获取未决信号量:extern int sigpending(sigset_t *__set); 调用成功返回0;否则为-1。
5) 信号量的操作:
1 : 清空信号集。extern int sigemptyset(sigset_t * __set); 其中,__set 为要操作的信号集。
2 : 将所有信号加入set集合 。extern int sigfillset(sigset_t * __set);
3 : 添加信号到指定的信号集中。extern int sigaddset(sigset_t * __set);
4 : 把信号从信号集中删除。extern int sigdelset(sigset_t * __set,int __sig); 其中__sig 为要添加的信号。
5 : 检测信号集是否为空。extern int sigisemptyset(__const sigset_t * __set);
6 : 检查信号是否在某一信号集中。extern int sigismember(__const sigset_t * __set,int __sig);
7 : 按逻辑与的方式把两个信号集合并。extern int sigandset(sigset_t * __set,__const sigset_t * __set_l,__const sigset_t * __set_r); 其中__set_l与__set_r是要合并的两个信号集,逻辑与后结果合并到__set中。
8 : 按逻辑或的方式把两个信号集合并。extern int sigorset(sigset_t * __set,__const sigset_t * __set_l,__const sigset_t * __set_r); 其中__set_l与__set_r是要合并的两个信号集,逻辑或后结果合并到__set中。
以上函数如果调用成功返回0,否则为-1,而且7、8两个函数调用错误时会设置errno。
linux等待信号
1) pause()函数;extern int pause(void); pause() 使当前的进程处于等待的状态,直到进程屏蔽信号外的任意一个信号出现去“拯救”他。
2) sigsuspend(__sigset_t * __set); sigsuspend()函数将当前进程的屏蔽信号集替换为__set信号集。直到收到非__set信号集中的信号后继续执行。当进程恢复时会自动回复到原来的信号集。
linux中常用的信号:
* +--------------------+------------------+
* | POSIX signal | default action |
* +--------------------+------------------+
* | SIGHUP | terminate |
* | SIGINT | terminate |
* | SIGQUIT | coredump |
* | SIGILL | coredump |
* | SIGTRAP | coredump |
* | SIGABRT/SIGIOT | coredump |
* | SIGBUS | coredump |
* | SIGFPE | coredump |
* | SIGKILL | terminate(+) |
* | SIGUSR1 | terminate |
* | SIGSEGV | coredump |
* | SIGUSR2 | terminate |
* | SIGPIPE | terminate |
* | SIGALRM | terminate |
* | SIGTERM | terminate |
* | SIGCHLD | ignore |
* | SIGCONT | ignore(*) |
* | SIGSTOP | stop(*)(+) |
* | SIGTSTP | stop(*) |
* | SIGTTIN | stop(*) |
* | SIGTTOU | stop(*) |
* | SIGURG | ignore |
* | SIGXCPU | coredump |
* | SIGXFSZ | coredump |
* | SIGVTALRM | terminate |
* | SIGPROF | terminate |
* | SIGPOLL/SIGIO | terminate |
* | SIGSYS/SIGUNUSED | coredump |
* | SIGSTKFLT | terminate |
* | SIGWINCH | ignore |
* | SIGPWR | terminate |
* | SIGRTMIN-SIGRTMAX | terminate |
* +--------------------+------------------+
* | non-POSIX signal | default action |
* +--------------------+------------------+
* | SIGEMT | coredump |
* +--------------------+------------------+
PS:终于写完了。很长!!!!!!
注:主要参考资料《linux高级程序设计第三版》、linux内核、网上资料。