线程-》信号
信号,是unix和linux系统响应某些条件而产生的一个事件。接收到该信号的进程会相应地采取一些行动。
raise生成表示一个信号的产生
catch捕获表示接受到一个信号的产生;信号是由于某些错误条件而产生,内存段冲突,浮点处理器错误或者非法指令等。它们由shell和终端处理器生成
引起中断,可以作为进程见传递消息或修改行为的一种方式,明确地由一个进程发送给另一个进程。无论什么情况,它们的编程接口都是相同的,信号可以被
生成,捕获,响应,忽略。
signal.h======
sigabort *进程异常终止
sigalrm 超时警告
sigfpe *浮点运算错误
sighup 链接挂断
sigill *非法指令
sigint 终端中断
sigkill 终止进程(此信号不能被不或忽略)
sigpipe 向无读进程的管道写数据
sigquit 中断退出
sigsegv *无效内存段访问
sigterm 终止
siguser1 用户定义信号
siguser2 用户定义信号2
注意:*表示系统对信号的响应随具体实现而定
-----------》如果进程接受到这些信号中的一个,但事先没有安排不或他,进程将会立刻终止。通常系统会生产核心转储文件core,并将其放在当前
目录下。该文件是进程自爱内存中的印象,他对程序的调试很有帮助。
其他信号
sigchld 子进程已经停止或退出
sigcont 继续执行暂停进程
sigstop 停止执行(此信号不能被捕获或忽略)
sigstsp 终端挂起
sigttin 后台进程尝试读操作
sigttou 后台进程尝试写操作
如果shell和终端驱动程序是按通常情况配置的话,在键盘上敲入中断字符(Ctrl+C)就会向前台(即当前正在执行的程序)发生sigint信号,这将引起该
进程的终止,除非它事先安排了捕获这个信号。
如果想发送一个命令给进程,但该进程不是当前的前台进程,就需要使用kill命令,该命令需要
一个可选的信号代码或信号名称和
一个接受信号的目标进程的PID(pid一般需要ps命令查看)
kill -- 发送一个命令给一个进程,常用的信号包括HUP,INT,KILL,STOP,CONT,0.
程序可以使用signal库函数来处理信号,
#include <signal.h>
void (*signal(int sig,void (*func)(int)))(int);
signal是一个带有sig和func两个参数的函数;
准备捕获/忽略的信号有参数sig给出,接受到指定的信号后将要调用的函数由参数func给出,
信号处理函数必须有一个int类型的参数(即接收到的信号代码)并且返回类型为void,
signal函数本身也返回一个同类型的函数,即先前用来处理这个信号的函数(不是func函数);
返回的是先前对指定信号进行处理的信号处理函数的函数指针,如果未定义信号处理函数,则返回SIG_ERR,并设置errno为一个正整数值,
如果给出的是一个无效的信号,或者尝试处理的信号不可捕获/不可忽略(如SIGKILL),errno将被设置为EINVAL。
也可以使用特殊值(sig_ign,sig_dfl)来代替信号处理函数;
sig_ign 忽略信号
sig_dfl 恢复默认信号
-----------------------
信号处理例子:编写一个程序ctrlc.c,来响应用户敲入的Ctrl+c组合键,在屏幕上打印一条适当的消息而不是终止程序的运行;再次ctrl+c时,结束。
#include <signal.h> #include <stdio.h> #include <unistd.h> void ouch(int sig) { printf("ouch! - I got signal %d\n",sig); (void) signal(SIGINT,SIG_DFL); } int main(){ (void) signal(SIGINT,ouch); while(1){ printf("hello world\n"); sleep(1); } }
main()的作用是捕获ctrl+c产生的SIGINT信号,没有信号产生时,它会在一个无限循环中打印字符;
(void) signal(SIGINT,ouch),在捕获SIGINT信号后,会调用ouch函数进行处理;
(void) signal(SIGINT,SIG_DFL),再次捕获SIGINT信号,SIG_DFL来恢复信号的默认行为;
------------
发送信号
进程可以通过调用kill函数将包括它在内的其他进程发送一个信号,如果程序没有发送该信号的权限,对kill函数的调用就会失败;
失败的常见原因,是目标进程由另一个用户所拥有;
#include<sys/types.h>
#include<signal.h>
int kill(pid_t pid,int sig);
kill函数吧参数sig给定的信号发送给参数pid给出的进程号所指定的进程,成功时它返回0;
kill调用失败返回-1并设置errno变量,
失败原因:
给定的信号无效 errno = EINVAL
发送权限不够 errno = EPERM
目标进程不存在 errno = ESRCH
--
信号的闹钟功能,进程可以通过调用alarm()在经过预定时间后发送一个sigalrm信号,
#include<unistd.h>
unsigned int alarm(unsigned int seconds);
/************************************************************************* > File Name: alarm.c > Author: > Mail: > Created Time: 2016年03月26日 星期六 12时30分10秒 ************************************************************************/ #include<stdio.h> #include<sys/types.h> #include<signal.h> #include<unistd.h> #include<stdlib.h> static int alarm_fired = 0; void ding(int sig){ alarm_fired = 1; } int main(){ pid_t pid; printf("alarm application starting\n"); pid = fork(); switch(pid){ case -1: //failure perror("fork failed"); exit(1); case 0: //child printf("It is a child process, and it will sleep 5s\n"); printf("child process Pid = %d\n",getpid()); sleep(5); printf("5s has past, child process will generate a sigalrm\n"); kill(getppid(),SIGALRM); printf("child process has generated a sigalrm signal\n"); exit(0); } //if we get here ,we are the parent process printf("it is a parent process, it pId = %d\n",getpid()); printf("waiting for alarm to go off \n"); (void) signal(SIGALRM,ding); pause(); if(alarm_fired) printf("Ding!\n"); printf("done\n"); exit(0); }
执行结果:
lizhen@lizhen:~/basic$ ./a.out alarm application starting it is a parent process, it pId = 3908 waiting for alarm to go off It is a child process, and it will sleep 5s child process Pid = 3909 5s has past, child process will generate a sigalrm child process has generated a sigalrm signal Ding! done
---
getpid,getppid --get process identification
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void)
pid_t getppid(void)
getpid() return the process ID of the calling process(this is often used by routines
that generate unique temporay filenames.)
getppid() returns the process ID of the parent of the calling process.
----
pause -wait for signal
#include <unstd.h>
int pause(void)
pause() causes the calling process (or thread) to sleep until a signal
is delivered that either terminates the process or causes the invocation of a signal-catching
function.就是程序的执行挂起直到有一个信号出现为止,当程序接受到一个信号时,预设好的信号处理函数将开始运行,程序也恢复
正常的执行。当它被一个信号中断是,将返回-1(如果下一个接受到的信号没有导致程序终止的话)并把errno设为EINTR,
当需要等待信号时,一个更常见的做法就是使用sigsuspend函数;
===
使用信号并挂起程序的执行是linux程序设计的一个重要部分,意味着程序不需要总是在执行着;
程序不必在一个循环中无休止地检查某个事件是否已发生,他可以等待事件的发生。
这在只有一个cpu的多用户环境中很重要;
进程共享着一个cpu,繁忙的等待将会对系统的性能造成很大的影响。
一个特殊的问题,“如果信号出现在系统调用的执行过程中会发生什么情况?”
在编写程序中处理信号部分的代码时,应该非常小心,因为在使用信号的程序中会出现各种“竞态条件“,
例如,想调用pause等待一个信号,可信号却出现在调用pause之前,就会使程序无限期地等待一个不会发生的时间。
这些竞态条件都是一些对时间要求很苛刻的问题,所以在检查和信号相关的代码时总会非常小心;
---
一个健壮的信号接口
sigaction
#include<signal.h>
int sigaction(int sig,const struct sigaction *act,struct sigaction *oact);
sigaction结构定义在文件signal.h中,作用是定义接受到sig指定的信号后应该采取的动作;