本节主要学习信号和与信号相关的处理函数,兴许还会更新。
一 信号 信号是UNIX和Linux系统响应某些条件而产生的一个事件。接收到该信号的进程会对应地採取一些行动。通常信号是由一个错误产生的。但它们还能够作为进程间通信或改动行为的一种方式。明白地由一个进程发送给还有一个进程。一个信号的产生叫生成。接收到一个信号叫捕获。 二 信号的种类 Signal Description SIGABRT 由调用abort函数产生。进程非正常退出 SIGALRM 用alarm函数设置的timer超时或setitimer函数设置的interval timer超时 SIGBUS 某种特定的硬件异常,通常由内存訪问引起 SIGCANCEL 由Solaris Thread Library内部使用。通常不会使用 SIGCHLD 进程Terminate或Stop的时候,SIGCHLD会发送给它的父进程。缺省情况下该Signal会被忽略 SIGCONT 当被stop的进程恢复执行的时候,自己主动发送 SIGEMT 和实现相关的硬件异常 SIGFPE 数学相关的异常。如被0除,浮点溢出。等等 SIGFREEZE Solaris专用。Hiberate或者Suspended时候发送 SIGHUP 发送给具有Terminal的Controlling Process,当terminal被disconnect时候发送 SIGILL 非法指令异常 SIGINFO BSD signal。由Status Key产生,一般是CTRL+T。发送给全部Foreground Group的进程 SIGINT 由Interrupt Key产生,一般是CTRL+C或者DELETE。发送给全部ForeGround Group的进程 SIGIO 异步IO事件 SIGIOT 实现相关的硬件异常,一般相应SIGABRT SIGKILL 无法处理和忽略。中止某个进程 SIGLWP 由Solaris Thread Libray内部使用 SIGPIPE 在reader中止之后写Pipe的时候发送 SIGPOLL 当某个事件发送给Pollable Device的时候发送 SIGPROF Setitimer指定的Profiling Interval Timer所产生 SIGPWR 和系统相关。和UPS相关。 SIGQUIT 输入Quit Key的时候(CTRL+/)发送给全部Foreground Group的进程 SIGSEGV 非法内存訪问 SIGSTKFLT Linux专用,数学协处理器的栈异常 SIGSTOP 中止进程。
无法处理和忽略。
SIGSYS 非法系统调用 SIGTERM 请求中止进程,kill命令缺省发送 SIGTHAW Solaris专用,从Suspend恢复时候发送 SIGTRAP 实现相关的硬件异常。通常是调试异常 SIGTSTP Suspend Key。通常是Ctrl+Z。发送给全部Foreground Group的进程 SIGTTIN 当Background Group的进程尝试读取Terminal的时候发送 SIGTTOU 当Background Group的进程尝试写Terminal的时候发送 SIGURG 当out-of-band data接收的时候可能发送 SIGUSR1 用户自己定义signal 1 SIGUSR2 用户自己定义signal 2 SIGVTALRM setitimer函数设置的Virtual Interval Timer超时的时候 SIGWAITING Solaris Thread Library内部实现专用 SIGWINCH 当Terminal的窗体大小改变的时候。发送给Foreground Group的全部进程 SIGXCPU 当CPU时间限制超时的时候 SIGXFSZ 进程超过文件限制大小 SIGXRES Solaris专用,进程超过资源限制的时候发送 三 信号相关的函数 1 signal函数 void (*signal(int sig, void (*func)(int)))(int); signal是一个带有sig和func两个參数的函数,func是一个类型为void (*)(int)的函数指针。该函数返回一个与func同样类型的指针。指向先前指定信号处理函数的函数指针。准备捕获的信号的參数由sig给出,接收到的指定信号后要调用的函数由參数func给出。事实上这个函数的使用是相当简单的,通过以下的样例就能够知道。注意信号处理函数的原型必须为void func(int),或者是以下的特殊值: SIG_IGN:忽略信号 SIG_DFL:恢复信号的默认行为 这个函数经常能够用以下要将的sigaction函数替代。 2 sigaction int sigaction(int sig, const struct sigaction *act, struct sigaction *oact); 该函数与signal函数一样,用于设置与信号sig关联的动作,而oact假设不是空指针的话。就用它来保存原先对该信号的动作的位置,act则用于设置指定信号的动作。 參数结构体sigaction定义例如以下: struct sigaction{ void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t*, void*); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void); }; 信号集sigset_t结构体: typedef struct { unsigned long sig[_NSIG_WORDS]; }sigset_t; 信号处理函数能够採用void (*sa_handler)(int)或void (*sa_sigaction)(int, siginfo_t *, void *)。究竟採用哪个要看sa_flags中是否设置了SA_SIGINFO位。假设设置了就採用void (*sa_sigaction)(int, siginfo_t *, void *)。此时能够向处理函数发送附加信息;默认情况下採用void (*sa_handler)(int),此时仅仅能向处理函数发送信号的数值。 sa_handler 此參数和signal()的參数handler同样。代表新的信号处理函数,其它意义请參考signal()。 sa_mask 用来设置在处理该信号时临时将sa_mask指定的信号集搁置。 sa_restorer 此參数没有使用。 sa_flags 用来设置信号处理的其它相关操作,下列的数值可用: sa_flags 还能够设置其它标志: SA_RESETHAND 当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL SA_RESTART 假设信号中断了进程的某个系统调用,则系统自己主动启动该系统调用 SA_NODEFER 普通情况下, 当信号处理函数执行时。内核将堵塞该给定信号。可是假设设置了 SA_NODEFER标记, 那么在该信号处理函数执行时,内核将不会堵塞该信号。 返回值 运行成功则返回0,假设有错误则返回-1。 错误代码 EINVAL 參数signum 不合法, 或是企图拦截SIGKILL/SIGSTOPSIGKILL信号 EFAULT 參数act,oldact指针地址无法存取。 EINTR 此调用被中断 以下一个简单的样例,程序里面的sigemptyset、kill函数会在后面讲 action.c实例代码:
#include3 sigaddset sigdelset int sigaddset(sigset_t *set, int signum); sigaddset()用来将參数signum 代表的信号增加至參数set 信号集里。 返回值 运行成功则返回0,假设有错误则返回-1。 错误代码 EFAULT 參数set指针地址无法存取 EINVAL 參数signum非合法的信号编号 int sigdelset(sigset_t *set, int signum) sigdelset()用来将參数signum代表的信号从參数set信号集里删除。 返回值 运行成功则返回0 假设有错误则返回-1。 错误代码 EFAULT 參数set指针地址无法存取 EINVAL 參数signum非合法的信号编号 4 sigemptyset sigfillset int sigemptyset(sigset_t *set); sigemptyset()用来将參数set信号集初始化并清空。 返回值 运行成功返回0; 失败返回-1。 错误代码 EFAULT 參数set指针地址无法存取 int sigfillset(sigset_t * set); sigfillset()用来将參数set信号集初始化。然后把全部的信号增加到此信号集里。 返回值 运行成功返回0。 失败返回-1。 错误代码 EFAULT 參数set指针地址无法存取 5 sigismember int sigismember(const sigset_t *set,int signum); sigismember()用来測试參数signum 代表的信号是否已增加至參数set信号集里。假设信号集里已有该信号则返回1,否则返回0。 返回值 信号集已有该信号则返回1。 没有则返回0; 假设有错误则返回-1。 错误代码 EFAULT 參数set指针地址无法存取 EINVAL 參数signum 非合法的信号编号 6 sigpending int sigpending(sigset_t *set); sigpending()会将被搁置的信号集合由參数set指针返回 返回值执 行成功则返回0。 假设有错误则返回-1。 错误代码 EFAULT 參数set指针地址无法存取 EINTR 此调用被中断。 7 sigprocmask int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); 一个进程的信号屏蔽字规定了当前堵塞而不能传送给该进程的信号集。sigprocmask()能够用来检測或改变眼下的信号屏蔽字,其操作參数how来决定,假设參数oldset不是NULL指针,那么当前的信号屏蔽字会由此指针返回。假设set是一个非空指针,则參数how指示怎样改动当前信号屏蔽字。每一个进程都有一个用来描写叙述哪些信号递送到进程时将被堵塞的信号集。该信号集中的全部信号在传送到进程后都将被堵塞。 參数how: SIG_BLOCK:该进程新的信号屏蔽字是其当前信号屏蔽字和set指向信号集的并集,set包括了我们希望堵塞的附件信号。 SIG_UNBLOCK:该进程新的信号集屏蔽字是其当前信号屏蔽字和set所指向信号集的补集的交集。#include #include #include #include void sig_handler(int sig){ switch(sig) { case 23: printf("child : the signo is 23, hehe\n"); return; case 22: printf("father : hello wordl 22!\n"); return; }}int main(){ struct sigaction act, oact; int status; sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = sig_handler; sigaction(23, &act, &oact); sigaction(22, &act, &oact); pid_t pid, ppid; if (!(pid = fork())) { printf("child begin\n"); kill(getppid(), 22); sleep(3); printf("child over\n"); } else { printf("father begin\n"); kill(getpid(), 23); wait(&status); if (WIFSIGNALED(status)) { printf("child process receive siganl %d\n", WTERMSIG(status)); } printf("father over\n"); } return 0;}
set包括了我们希望解除堵塞的信号。
SIG_SETMASK:该进程新的信号屏蔽是set指向的值。 注意: 1)除SIG_SETMASK外,假设set是个空指针。则不改变该进程的信号屏蔽字,这时how的值也没有意义。 2)SIG_SETMASK与set空指针结合使用。即清空全部屏蔽的信号。 返回值: 运行成功返回0; 失败返回-1。 错误码 EFAULT:參数set、oldsset指针地址无法获取 EINTR:此调用被中断 以下是一个測试样例,測试被屏蔽的信号: sigprocmask.c的实例代码:
//#include#include #include void sig_handler(int signum){ switch(signum) { case SIGINT: printf("the signo is SIGINT %d received, hehe\n", SIGINT); return; case SIGCHLD: printf("the signo is SIGCHLD %d received, hihi!\n", SIGCHLD); return; case SIGUSR1: printf("the signo is SIGUSR1 %d received, hihi!\n", SIGUSR1); return; case SIGIO: printf("the signo is SIGIO %d received, hihi!\n", SIGIO); return; case SIGALRM: printf("the signo is SIGALRM %d received, hihi!\n", SIGALRM); return; default: printf("the signo %d is not processed\n", signum); return; }}int main(){ int i; int status; pid_t pid; sigset_t set; sigset_t oldset; struct sigaction act; struct sigaction oldact; act.sa_flags = 0; act.sa_handler = sig_handler; sigemptyset(&act.sa_mask); sigaction(SIGINT, &act, &oldact); sigaction(SIGCHLD, &act, &oldact); sigaction(SIGUSR1, &act, &oldact); sigaction(SIGALRM, &act, &oldact); sigaction(SIGIO, &act, &oldact); sigemptyset(&set); sigaddset(&set, SIGINT); sigaddset(&set, SIGUSR1); if ((pid = fork()) < 0) { fprintf(stdout, "fork error\n"); exit(-1); } else if (pid > 0) { if (sigprocmask(SIG_BLOCK, &set, &oldset) < 0)//屏蔽信SINGINT、SIGUSR1 { fprintf(stdout, "sigprocmask error\n"); kill(pid, SIGKILL); exit(-1); }/* waitpid(pid, &status, 0); if (WIFSIGNALED(status)) { printf("child process receive siganl %d\n", WTERMSIG(status)); }*/ pause();//接收SIGIO? pause();//接收SIGALRM? pause();//接收SIGCHLD?
//pause(); //pause(); } else { sleep(1); kill(getppid(), SIGINT);//信号被屏蔽 sleep(1); kill(getppid(), SIGIO); sleep(1); kill(getppid(), SIGUSR1);//信号被屏蔽 sleep(1); kill(getppid(), SIGALRM); sleep(2); //子进程退出会发送一个SIGCHLD信号 } return 0; }
8 sigsuspend int sigsuspend(const sigset_t *mask); 函数sigsuspend将进程的信号屏蔽码设置为mask,然后与pause()函数一样等待信号的发生并运行完信号处理函数。信号处理函数运行完后再把进程的信号屏蔽码设置为原来的屏蔽字,然后sigsuspend函数才返回。 返回值 sigsuspend总是返回-1 以下是一个sigsuspend的样例: sigsuspend的实例代码:
#include#include #include void sig_handler(int sig){ if (sig == SIGINT) printf("SIGINT sig\n"); else if (sig == SIGQUIT) printf("SIGQUIT sig\n"); else if (sig == SIGUSR1) printf("SIGUSR1 sig\n"); else if (sig == SIGUSR2) printf("SIGUSR2 sig\n"); else printf("SIGCHLD sig\n");}int main(){ int i; sigset_t new; sigset_t old; sigset_t wait; struct sigaction act; act.sa_handler = sig_handler; act.sa_flags = 0; sigemptyset(&act.sa_mask); sigaction(SIGINT, &act, 0); sigaction(SIGQUIT, &act, 0); sigaction(SIGUSR1, &act, 0); sigaction(SIGUSR2, &act, 0); sigaction(SIGCHLD, &act, 0); sigemptyset(&new); sigaddset(&new, SIGINT); sigemptyset(&wait); sigaddset(&wait, SIGUSR1); sigaddset(&wait, SIGUSR2); sigprocmask(SIG_BLOCK, &new, &old); for (i=0; i < 90; ++i) { printf("%d\n", i); sleep(1); } sigsuspend(&wait); printf("After sigsuspend\n"); printf("yyyy\n"); if (-1 == sigprocmask(SIG_SETMASK, &old, NULL)) { perror("sigprocmask"); exit(-1); } for (i=0; i < 30; ++i) { printf("%d\n", i); sleep(1); } printf("xxxxx\n"); return 0;}