本文共 5902 字,大约阅读时间需要 19 分钟。
对于 Linux来说,信号实际是软中断,许多重要的程序都需要处理信号。
信号,为 Linux 提供了一种处理异步事件的方法。比如,终端用户输入了 ctrl+c 来中断程序,会通过信号机制停止一个程序。
每个信号都有一个名字和编号,这些名字都以“SIG”开头,例如“SIGIO ”、“SIGCHLD”等等。信号定义在signal.h
头文件中,信号名都定义为正整数。
kill -l
来查看信号的名字以及序号,信号是从1开始编号的,不存在0号信号。kill对于信号0又特殊的应用。 信号的处理有三种方法,分别是:忽略、捕捉和默认动作
SIGKILL
和SIGSTOP
)。因为他们向内核和超级用户提供了进程终止和停止的可靠方法,如果忽略了,那么这个进程就变成了没人能管理的的进程,显然是内核设计者不希望看到的场景man 7 signal
来查看系统的具体定义。在此,我就不详细展开了,需要查看的,可以自行查看。也可以参考 《UNIX 环境高级编程(第三部)》的 P251——P256中间对于每个信号有详细的说明。Linux常用的 kill 命令就是一个发送信号的工具,kill -9 PID
来杀死进程。
比如,我在后台运行了一个 top 工具,通过 ps 命令可以查看他的 PID,通过 kill 9 来发送了一个终止进程的信号(SIGKILL)来结束了 top 进程。
对于信号来说,最大的意义不是为了杀死信号,而是实现一些异步通讯的手段。
信号处理函数的注册不只一种方法,分为入门版和高级版
signal
sigaction
信号发送函数也不止一个,同样分为入门版和高级版
#includetypedef void (*sighandler_t)(int); //信号处理函数sighandler_t signal(int signum, sighandler_t handler); //注册函数
#include#include #include void handler(int signum){ if(signum == SIGIO) { printf("SIGIO signal: %d\n", signum); } else if(signum == SIGUSR1) { printf("SIGUSR1 signal: %d\n", signum); } else { //未注册,应用程序终止进程 printf("error \n"); }}int main(void){ signal(SIGIO, handler); signal(SIGUSR1, handler); printf("%d, %d\n", SIGIO, SIGUSR1); while(1) { sleep(1000); } return 0;}
注:信号的处理还有两种状态,分别是默认处理和忽略,这两种设置很简单,只需要将 handler 设置为 SIG_IGN(忽略信号)或 SIG_DFL(默认动作)即可。
在此还有两个问题需要说明一下:
#include#include int kill(pid_t pid, int sig);
#include#include #include #include #include int main(int argc, char *argv[]){ if( argc != 3) { printf("%s \n", argv[0]); return -1; } int pid = atoi(argv[1]); int sig = atoi(argv[2]); printf("pid = %d, sig = %d\n", pid, sig); if(pid > 0 && sig > 0) { kill(pid, sig); } else { printf("Target_PID or Signal_Number must > 0\n"); } return 0;}
关于 kill 函数,还有一点需要额外说明, kill 函数传入的 pid 可以是小于等于0的整数。
pid > 0:将发送个该 pid 的进程pid == 0:将会把信号发送给与发送进程属于同一进程组的所有进程,并且发送进程具有权限想这些进程发送信号。pid < 0:将信号发送给进程组ID 为 pid 的绝对值得,并且发送进程具有权限向其发送信号的所有进程pid == -1:将该信号发送给发送进程的有权限向他发送信号的所有进程。(不包括系统进程集中的进程)
#includeint sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);struct sigaction { void (*sa_handler)(int); //信号处理程序,不接受额外数据,SIG_IGN 为忽略,SIG_DFL 为默认动作 void (*sa_sigaction)(int, siginfo_t *, void *); //信号处理程序,能够接受额外数据和sigqueue配合使用 sigset_t sa_mask;//阻塞关键字的信号集,可以再调用捕捉函数之前,把信号添加到信号阻塞字,信号捕捉函数返回之前恢复为原先的值。 int sa_flags;//影响信号的行为SA_SIGINFO表示能够接受数据 };//回调函数句柄sa_handler、sa_sigaction只能任选其一 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 */}
在函数原型中,signum:
注册的信号的编号;act:
对该信号有新的配置;oldact:
对之前的信号配置进行备份,以方便之后进行恢复。
#include#include #include void handler(int signum, siginfo_t *info, void *context){ if(signum == SIGIO) { printf("SIGIO signal: %d\n", signum); } else if (signum == SIGUSR1) { printf("SIGUSR1 signal: %d\n", signum); } else { printf("error\n"); } if(context) { printf("content: %d\n", info->si_value.sival_int); }}int main(void){ struct sigaction act = {0}; act.sa_sigaction = handler; act.sa_flags = SA_SIGINFO; //表明信号处理程序带有附加信息 sigaction(SIGIO, &act, NULL); sigaction(SIGUSR1, &act, NULL); while(1) { sleep(10); } return 0;}
#includeint sigqueue(pid_t pid, int sig, const union sigval value);union sigval { int sival_int; void *sival_ptr; };
sigqueue 函数 只能把信号发送给单个进程,可以使用 value 参数向信号处理程序传递整数值或者指针值。
sigqueue 函数 不但可以发送额外的数据,还可以让信号进行排队(操作系统必须实现了 POSIX.1的实时扩展),对于设置了阻塞的信号,使用 sigqueue 发送多个同一信号,在解除阻塞时,接受者会接收到发送的信号队列中的信号,而不是直接收到一次。
但是,信号不能无限的排队,信号排队的最大值受到SIGQUEUE_MAX
的限制,达到最大限制后,sigqueue 会失败,errno 会被设置为 EAGAIN。
#include#include #include #include #include int main(int argc, char *argv[]) { if(4 != argc) { printf("%s \n", argv[0]); return -1; } union sigval val; int pid = atoi(argv[1]); int sig = atoi(argv[2]); val.sival_int = atoi(argv[3]); if(pid > 0 && sig > 0) { printf("send %d to pid_%d\n", val.sival_int, pid); sigqueue(pid, sig, val); } else { printf("Target_pid or Signal_number musb > 0\n"); }}
转载地址:http://kmldi.baihongyu.com/