目录
相关函数列表
信号概念
可靠信号术语和语义
一段有问题的代码,alarm()和pause()模拟sleep
sigaction 的sa_flags参数
signal函数例子
kill函数的例子
信号阻塞的例子
用sigaction函数实现signal功能
携带数据的信号
利用信号实现两个进程之间的互斥
sigsetjmp例子
参考
//系统信号机制最简单的接口是signal函数
#include
void (*signal(int signo, void (*func)(int))) (int);
//kill可以将信号发送给进程或进程组,raise允许进程向自身发送信号
#include
int kill(pid_t pid, int signo);
int raise(int signo); //raise(signo) 等于 kill(getpid(), signo)
//kill的pid参数有以下4种不同的情况
//1. pid>0 将该信号发送给进程ID为pid的进程
//2. pid==0 将该信号发送给与发送进程属于同一进程组的所有进程(这些进程的
// 进程组ID等于发送进程的进程组ID),而且发送进程具有权限向这些进程发送
// 信号。这里用的术语"所有进程"不包括实现定义的系统进程集。对于大多数
// UNIX系统,系统进程集包括内核进程和init
//3. pid<0 将该信号发送给其他进程组ID等于pid绝对值,而且发送进程具有
// 权限向其发送信号的所有进程。如前所述,所有进程并不包括系统进程集中
// 的进程
//4. pid==-1 将该信号发送给发送进程有权限向他们发送信号的所有进程。如前
// 所述,所有进程不包括系统进程集中的进程
//可以设置一个定时器(闹钟时间),在将来的某个时刻该定时器会超时。定时器
//超时时,产生SIGALRM信号
#include
unsigned int alarm(unsigned int seconds);
//此函数使调用进程挂起直至捕捉到一个信号
#include
int pause(void);
//一个能表示多个信号--信号集(signal set)的数据类型。以便告诉内核不允许发生该信号集合中的
//信号。不同的信号编号可能超过一个整型量锁包含的位数,所以一般而言,不能用整型量中的一位
//代表一种信号,也就是不能用一个整型表示信号集,下面定义5个信号集函数
#include
int sigemptyset(sigset_t *set); //初始化 set 中传入的信号集,清空其中所有信号
int sigfillset(sigset_t *set); //把信号集填1,让 set 包含所有的信号
int sigaddset(sigset_t *set, int signo); //把信号集对应位置为1
int sigdelset(sigset_t *set, int signo); //把信号集对应位置为0,删除
int sigismember(const sigset_t *set, int signo); //判断signal是否在信号集
//下面函数可以检测或更改,或同时进行检测和更改进程信号屏蔽字
//注意不能阻塞SIGKILL和SIGSTOP信号
#include
int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
//how的参数说明
//SIG_BLOCK 该进程新的信号屏蔽字是其当前信号屏蔽字和set指向的信号集的并集,set包含了
// 希望阻塞的附加信号
//SIG_UNBLOCK 该进程新的信号屏蔽字是其当前信号屏蔽字和set所指向信号集补集的交集。set包含
// 了希望解除阻塞的信号
//SIG_SETMASK 该进程新的信号屏蔽字是set指向的值
//下面函数返回一信号集,对于调用进程而言,其中的各信号是阻塞不能递送的,因而也一定是当前
//未决的。该信号通过set参数返回
#include
int sigpending(sigset_t *set);
//下面函数的功能是检查和修改(或检查并修改)与指定信号相关联的处理动作,此函数取代了UNIX早期
//版本使用的signal函数。
#include
int sigaction(int signo, const struct sigaction *restrict act, struct sigaction *restrict oact);
//setjmp和longjmp有一个问题,当捕捉到一个信号时。进入信号捕捉函数后,此时当前信号被自动的
//加入到进程的信号屏蔽字中。这阻止了后来产生的这种信号中断该信号处理程序。
//为了允许两种形式并存,POSIX.1定义了两个新函数
#include
int sigsetjmp(sigjmp_bug env, int savemask);
void siglongjmp(sigjmp_buf env, int val);
//
#include
int sigsuspend(const sigset_t *sigmask);
//使程序异常终止,使用方式如下
int main(void) {
printf("Calling abort()\n");
abort();
return 0; /* This is never reached */
}
#include
void abort(void);
//休眠函数
//此函数使调用进程被挂起直到满足下面两个条件之一
//1.已经过了seconds所指定的墙上时钟时间
//2.调用进程捕捉到一个信号并从信号处理程序返回
#include
unsigned int sleep(unsigned int seconds);
//纳秒级精度的休眠
//reqtp参数用秒和纳秒指定了需要休眠的时间长度
//如果某个信号中断了休眠间隔,并存并没有终止remtp参数指向的timespec结构就会被设置为未休眠
//完的时间长度。如果对未休眠完的时间并不感兴趣,可以把该参数置为NULL
//sleep()函数最终是由 nanosleep()函数实现的
//timespec结构体如下
struct timespec {
time_t tv_sec; /* 秒seconds */
long tv_nsec; /* 纳秒nanoseconds */
};
#include
int nanosleep(const struct timespec *reqtp, struct timespec *remtp);
//随着多个系统时钟的引入,需要使用相对于特定时钟的延迟时间来挂起调用线程
//比如是睡眠一段相对时间,还是绝对时间
#include
int clock_nanosleep(clockid_t clock_id, int flag, const struct timespec *reqtp,
struct timespec *remtp);
//在POSIX.1的实时扩展中有些系统开始增加对信号排队的支持,通常一个信号带有一个位信息:信号
//本身。除了对信号排队意外,这些扩展允许应用程序在递交信号时传递更多的信息,嵌入在siginfo
//结构中。使用排队信号必须做以下几个操作
//1.使sigaction函数安装信号处理程序时指定SA_SIGINFO标志,如果没有给出这个标准,信号会延迟,
// 但信号是否进入队列要取决于具体实现
//2.在sigaction结构的sa_sigaction成员中(而不是通常的sa_handler字段)提供信号处理程序。实现
// 可能允许用户使用sa_handler字段,但不能捕获sigqueue函数发送出来的额外信息
//3.使用sigqueue函数发送信号
#include
int sigqueue(pid_t pid, int signo, const union sigval value);
//如何在信号编号和信号名之间进行映射,某些系统提供数组
extern char *sys_siglist[];
//使用下面函数可以可移植打印与信号编号对应的字符串
#include
void psignal(int signo, const char *msg);
//如果sigaction信号处理程序中有siginfo结构,可以使用下面函数打印信号信息
#include
void psiginfo(const siginfo_t *info, const char *msg);
//如果只需要信号的字符描述不符,也不需要把它写入到标准错误文件中(如果可以写到日志文件中),
//可以使用strsignal函数,类似于strerror
#include
char *strsignal(int signo);
//Solaris提供一对函数,一个函数将信号编号映射为信号名,另一个则反之
#include
int sig2str(int signo, char *str);
int str2sig(const char *str, int *signop);
信号时软件终端,很多比较重要的应用程序都需要处理信号。信号提供了一种处理异步事件的方法
很多条件可以产生信号:
1.当用户按某些终端键时,引发终端产生的信号
2.硬件异常产生的信号,除数为0,无效内存引用等
3.进程调用kill函数可将任意信号发送给另一个进程或进程组
4.用户可用kill命令将信号发送给其他进程
5.当检测到某种软件条件发生,并将其统治有关进程时也产生信号。
信号时异步事件的经典实例。产生信号的事件对进程而言是随机出现的
在某个信号出现时,可以告诉内核按下列三种方式之一处理,我们称之为信号的处理或与信号相关的动作
1.忽略此信号。大多数信号都可使用这种方式进行处理,但有两种信号决不能被忽略,它们是SIGKILL和
SIGSTOP。原因是:它们向内核和超级用户提供了使进程终止或停止的可靠版本。如果忽略某些硬件异常
产生的信号,则进程运行的行为时未知的。
2.捕捉信号。为了做到这一点,要通知内核在某种信号发生时,调用一个用户函数。在用户函数中,可执行
用户希望对这种事件进行的处理
3.执行系统默认的动作。大多数信号的系统默认动作时终止该进程
UNIX系统信号(终止+core 表示在当前工作目录的core文件中复制了该进程的内存映像,文件名为core)
名字 | 说明 | 默认动作 | 详细说明 |
SIGABRT | 异常终止(abort) | 终止+core | 调用abort函数时产生此信号,进程异常终止 |
SIGALRM | 定时器超时(alarm) | 终止 | 当调用alarm函数设置的定时器超时时,产生此信号 settitimer函数设置的间隔已经超时时,也产生此信号 |
SIGBUS | 硬件故障 | 终止+core | 指示一个实现定义的硬件故障,当出现某些类型的内存故障时, 产生此信号 |
SIGCANCEL | 线程库内部使用 | 忽略 | 这是Solaris线程库内部使用的信号,它不适用于一般应用 |
SIGCHLD | 子进程状态改变 | 忽略 | 在一个进程终止时,SIGCHLD信号被发送给父进程。系统默认 将忽略此信号。如果父进程希望被告知子进程的这种状态改变, 则应捕捉此信号,使用wait函数取得子进程ID和终止状态 早起System V版本有一个SIGCLD(无H)的类似信号 |
SIGCONT | 使暂停进程继续 | 继续/忽略 | 此作业控制信号发送给需要继续运行,但当前处于停滞状态的进程, 如果接受到此信号的进程处于停止状态,则系统默认动作时 使该进程继续运行;否则默认动 作时忽略此信号 |
SIGEMT | 硬件故障 | 终止+core | 指示一个实现定义的硬件故障 |
SIGFPE | 算术异常 | 终止+core | 此信号表示一个算术运算异常,如除0,浮点溢出等 |
SIGFREEZE | 检查点冻结 | 忽略 | 此信号仅由Solaris定义。它用于通知进程再冻结系统状态之前需要采取特定 动作,如系统进入休眠或挂起状态时可能需要做这种处理 |
SIGHUP | 连接断开 | 终止 | 如果终端接口检测到一个连接断开,则将此信号发送给与该终 端相关的控制 进程(会话首进程)。注意,接收到此信号的会话首进程可能 在后台,这区别于 由终端正常产生的几个信号(中断,退出和挂起)这些信号总是传递给前 台进程组 |
SIGILL | 非法硬件指令 | 终止+core | 此信号表示进程已执行一条非法硬件指令 |
SIGINFO | 键盘状态请求 | 忽略 | 这是一种BSD信号,当用户按状态键(一般是ctrl+T)时,终端驱动程序产生此 信号并发送至前台进程组中的每一个进程,此信号通常造成在终端上显示前 台进程组中各进程状态的状态信息 |
SIGINET | 终端终端符 | 终止 | 当用户按中断键(一般是Delete或ctrl+C)时,终端驱动产生此信号并发送至 前台进程组中的每一个进程。当一个进程在运行时失控,特别是 它正在屏幕上产生大量不需要的输出时,常用此信号终止它 |
SIGIO | 异步IO | 终止/忽略 | 此信号指示一个异步I/O事件 |
SIGIOT | 硬件故障 | 终止+core | 这指示一个实现定义的硬件故障 |
SIGJVM1 | java虚拟机内部使用 | 忽略 | Solaris上为java虚拟机预留的一个信号 |
SIGJVM2 | java虚拟机内部使用 | 忽略 | Solaris上为java虚拟机预留的另一个信号 |
SIGKILL | 终止 | 终止 | 这是两个不能被捕捉或忽略信号中的一个。它向系统管理员 提供了一种可以杀死任意进程的可靠方法 |
SIGLOST | 资源丢失 | 终止 | 运行在Solaris NFSv4客户端系统中的进程,恢复阶段不能重新 获得锁,此时将由这个信号通知该进程 |
SIGLWP | 线程库内部使用 | 终止/忽略 | 此信号Solaris线程库内部使用欧冠,并不做一般使用。在 FreeBSD系统中SIGLWP是SIGTHR的别名 |
SIGPIPE | 写至无读进程的管道 | 终止 | 如果在管道的读进程已经终止写管道,则产生此信号。 当类型为SOCK_STREAM的套接字已不再连接时,进程写该 套接字也产生此信号 |
SIGPOLL | 可轮询事件(poll) | 终止 | 这个信号在SUSv4中被标记为弃用,将来的标准可能会将此 信号移除,当在一个可轮询的设备上发生一个特定事件时产生 此信号 |
SIGPROF | 梗概事件超时 (setitimer) |
终止 | 这个信号在SUSv4中被标记为弃用,将来的标准可能会将此 信号移除,当setitimer函数设置的梗概统计间隔定时器(profiling interval timer)已经超时时产生此信号 |
SIGPWR | 电源失效/重启动 | 终止/忽略 | 这是一种依赖于系统的信号,它主要用于具有不间断电源UPS 的系统,如果电源失效,则UPS起作用。并且通常软件会接到 通知。在这种情况下系统依靠蓄电池继续运行,所以无须作 任何处理。但是如果蓄电池也将不能支持工作,则软件通常会 再次接到通知。此时系统必须使其各部分都停止运行,这时 应当发送SIGPRW信号。大多数系统中接到蓄电池电压过低 信号的进程将信号SIGPRW发送给init进程,init处理停机操作 |
SIGQUIT | 终端退出符 | 终止+core | 当用户在终端上按退出键(ct+\)时,终端驱动程序产生此信号, 并发送给前台进程组中的所有进程,此信号不仅终止前台进程 组,同时产生一个core文件 |
SIGSEGV | 无效内存引用 | 终止+core | 指示进程进行了一次无效的内存引用(通常说明程序有错,比如 访问了一个未经初始化的指针) SEGV表示段违例(segmentation violation) |
SIGSTKFLT | 协处理器栈故障 | 终止 | 此信号仅由Linux定义。出现在早起的linux版本,企图用于数学 协处理器的栈故障,该信号并非由内核产生,但仍保留以向后 兼容 |
SIGSTOP | 停止 | 停止进程 | 这是一个作业控制信号,它停止一个进程,它类似于交互停止 信号(SIGTSTP)但是SIGSTOP不能捕捉或忽略 |
SIGSYS | 无效系统调用 | 终止+core | 该信号指示一个无效的系统调用,由于某种未知原因,进程执行 了一条机器执行,内核认为这是一条系统调用。但该指令指示 系统调用类型的参数却是无效的。如用户编写了一道用新系统 调用的程序然后运行该程序二进制代码,而所用的操作系统却 是不支持该系统调用的早起版本于是出现上述情况 |
SGTERM | 终止 | 终止 | 这是由kill命令发送的系统默认终止信号。由于该信号是由应用 程序捕获的,使用SIGTERM也让程序有机会在退出之前做好 清理工作,从而优雅终止(相对于SIGKILL而言,SIGKILL不 能被捕获或忽略) |
SIGTHAW | 检查点解冻 | 忽略 | 此信号仅由Solaris定义,在被挂起的系统恢复时,该信号用于 通知相关进程,他们需要采取特定的动作。 |
SIGTHR | 线程库内部使用 | 忽略 | FreeBSD线程库预留的信号,它的之定义或与SIGLWP相同 |
SIGTRAP | 硬件故障 | 终止+core | 指示一个实现定义的硬件故障 |
SIGTSTP | 终端停止符 | 停止进程 | 交互停止信号,当用户在终端上按挂起键(ctrl+Z)时,终端驱动 程序产生此信号,该信号发送至前台进程组中的所有进程 |
SIGTTIN | 后台读控制tty | 停止进程 | 当一个后台进程组试图读其控制终端时,终端驱动程序产生此 信号,下列情况不产生此信号 a.读进程忽略或阻塞此信号 b.读进程所属的进程组是孤儿进程组,此时读操作返回出错 |
SIGTTOU | 后台写向控制tty | 停止进程 | 当一个后台进程组进程试图写控制终端时,终端驱动程序产生 此信号,与上所述的SIGTTIN信号不同,一个进程可以选择 允许后台进程写控制终端。如果不允许后台进程写控制终端则 与SIGTTIN相似,也有两种特殊情况 a.写进程忽略或阻塞此信号 b.写进程所属进程组是孤儿进程组,此时不产生信号返回出错 |
SIGURG | 紧急情况(socket | 忽略 | 此信号通知进程已经发送了一个紧急情况,在网络连接上接到 带外的数据时,可选择的产生此信号 |
SIGUSR1 | 用户定义信号 | 终止 | 这是一个用户定义的信号,可用于应用程序 |
SIGUSR2 | 用户定义信号 | 终止 | 这是另一个用户定义的信号,同样也可以用于应用程序 |
SIGVTALRM | 虚拟时间闹钟 (setitimer) |
终止 | 当一个由setitimer函数设置的虚拟间隔时间已经超时时, 产生此信号 |
SIGWAITING | 线程库内部使用 | 忽略 | 此信号由Solaris线程库内部使,不做他用 |
SIGWINCH | 终端窗口大小改变 | 忽略 | 内核维持与每个终端或伪终端相关联窗口的大小,进程可以 用ioctl函数得到或设置窗口大小,如果用 ioctl的设置更改了 窗口大小,则内核将SIGWINCH信号发送至前台进程组 |
SIGXCPU | 超过CPU限制 (setrlimit) |
终止或 终止+core |
Single UNIX Specification的XSI扩展支持资源限制的概念, 如果进程超过了其软CPU时间限制,则产生此信号 |
SIGXFSZ | 超过文件长度限制(setrlimit) |
终止或 终止+core |
如果进程超过了其软文件长度限制,则产生此信号 |
SIGXRES | 超过资源控制 | 忽略 | 此信号仅由Solaris定义。可选择的使用此信号以通知进程 超过了预配置的资源值。Solaris资源限制机制是一个通用 设施,用于控制在独立应用集之间共享资源的使用 |
kill命令
用过kill 命令可以给以进程发送信号
kill -l 可以查看能发送的所有信号
可重入函数
Single UNIX Specification说明了在信号处理程序中保证调用安全的函数,这些函数是可重入的并被称为是
异步信号安全的(async-signal safe)。除了可重入以外,在信号处理操作期间,它会阻塞任何会引起不一致
的信号发送
当对进程发送一个信号时,在信号产生(generation)和递送(delivery)之间的事件间隔内信号时未决的(pending)
如果有多个信号要递送给一个进程,POSIX.1没有规定这些信号的递送顺序,但POXIS.1基础部分建议:
在其他信号之前递送与进程当前状态有关的信号,如SIGSEGV
每个进程都有一个信号屏蔽字(signal mask),对规定了当前要阻塞递送到该进程的信号集,对于每种可能的
信号,该屏蔽字中都有一位与其对应。进程可以调用sigprocmask来检测和更改其当前信号屏蔽字
static void sig_alrm(int signo) {
//...
}
unsigned int sleep1(unsigned int seconds) {
if(signal(SIGALRM, sig_alrm) == SIG_ERR) {
return (seconds);
}
alarm(seconds);
pause();
return (alarm(0));
}
1)在调用sleep1函数之前可能已经设置了alarm函数,那么再一次执行这个函数可能会将之前设置的参数给覆盖
2)程序中修改了对SIGALRM的配置,如果编写了一个函数供其他函数调用,则在该函数被调用时要先保存原先
配置,在该函数返回前再恢复原配置。更正这一点的方法是:保存signal函数的返回值,在返回前重置原配置
3)在调用alarm()和pause()之间有一个竞争条件,假设调用alarm(1),即设置1秒的闹钟,然后系统中断,但是
超过了一秒,这样会调用sig_alrm()函数,然后又继续执行后面的pause(),如果之后再不发生信号,那么
程序将会永远暂停
sigpending用法
if(sigpending(&pendmask) < 0) {
err_sys("sigpending error");
}
参数 | 说明 |
SA_INTERRUPT | 由此信号中断的系统调用不自动重启动 |
SA_NOCLDSTOP | 若signo是SIGCHLD,当子进程停止(作业控制),不产生此信号。若已设置此标志,则 当停止的进程继续运行时,作为XSI扩展,不产生SIGCHLD信号 |
SA_NOCLDWAIT | 若signo是SIGCHLD,则当调用进程的子进程终止时,不创建僵死进程。若调用进程 随后调用wait,则阻塞到它所有子进程都终止,此时返回-1.errno设置为ECHILD |
SA_NODEFER | 当捕捉到此信号时,在执行其信号捕捉函数时,系统部自动阻塞此信号(除非sa_mask 包含了此信号)。注意此种类型的操作对应于早期的不可靠信号 |
SA_NOSTACK | 若用sigaltstack已声明了一个替换栈,则此信号递送给替换栈上的进程 |
SA_RESETHAND | 在此信号捕捉函数的入口处,将此信号的处理方式重置为SIG_DFL,并清除 SIG_SIGINFO标志。注意此种类型的信号对应于早期不可靠信号,但是不能自动重置 SIGILL和SIGTRAP这两个信号的配置。设置此标志使sigaction的行为如同设置了 SA_NODEFER标志 |
SA_RESTART | 由此信号中断的系统调用自动重启动 |
SA_SIGINFO | 此选项对信号处理程序提供了附加信息:一个指向siginfo结构的指针以及一个执行 进程上下文标识符的指针 |
sigsuspend,一段有问题的代码,对一个信号解除阻塞,然后pause等待以前被阻塞的信号发生
sigset_t newmask, oldmask;
sigemptyset(&netmask);
sigaddset(&newmask, SIGINT);
//lbock SIGINT
if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) {
err_sys("SIG_BLOCK error");
}
//critical region of code
if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {
err_sys("SIG_SETMASK error");
}
//windows is open
pause();
如果在解除阻塞和pause()之间发生了信号,此时窗口中发生的信号会丢失,这样使得进程永远阻塞
应当使用 sigsuspend()函数,它是原子的执行上面两部(解除阻塞并等待)
函数system
POSIX.1要求system忽略SIGINT和SIGQUIT,因为键入的中断字符可以发送给前台进程组中的所有进程
而父进程(也就是shell)不需要捕获这个信号。
比如 a.out --> /bin/sh --> /bin/ed
这里a.out和/bin/ed需要捕获信号,所以应该讲/bin/sh的信号屏蔽
作业控制信号
POSIX.1认为以下六个与作业控制有关
参数 | 说明 |
SIGCHLD | 子进程已停止或终止 |
SIGCONT | 如果进程已停止,则使其继续运行 |
SIGSTOP | 停止信号(不能被捕捉或忽略) |
SIGTSTP | 交互式停止信号 |
SIGTTIN | 后台进程组成员读控制终端 |
SIGTTOU | 后台进程组成员写控制终端 |
#include
#include
#include
#include
void handler(int signum) {
if(signum==SIGINT) {
printf("SIGINT signal %d\n",signum);
}
else if(signum==SIGUSR1) {
printf("SIGUSR1 signal %d\n",signum);
}
else {
printf("error\n");
}
}
int main(int argc, char *argv[]) {
signal(SIGINT,handler);
signal(SIGUSR1,handler);
signal(SIGRTMAX,handler);
signal(SIGRTMIN,handler);
while(1) {
sleep(100);
}
return 0;
}
//发送命令
kill -2 24215
kill -10 24215
kill -SIGINT 24215
kill -SIGUSR1 24215
kill 24215
//执行结果
SIGINT signal 2
SIGUSR1 signal 10
SIGINT signal 2
SIGUSR1 signal 10
Terminated
//strace分析程序
。。。
nanosleep({100, 0},
//执行 kill -2 [PID] 后
{79, 496147652}) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
--- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=24182, si_uid=0} ---
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 6), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f46423be000
write(1, "SIGINT signal 2\n", 16SIGINT signal 2
) = 16
rt_sigreturn({mask=[]}) = -1 EINTR (Interrupted system call)
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
nanosleep({100, 0},
//执行 kill -10 [pid] 之后
{71, 642794812}) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
--- SIGUSR1 {si_signo=SIGUSR1, si_code=SI_USER, si_pid=24182, si_uid=0} ---
write(1, "SIGUSR1 signal 10\n", 18SIGUSR1 signal 10
) = 18
rt_sigreturn({mask=[]}) = -1 EINTR (Interrupted system call)
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
nanosleep({100, 0},
//执行kill [pid] 之后
{24, 911829147}) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
--- SIGTERM {si_signo=SIGTERM, si_code=SI_USER, si_pid=24182, si_uid=0} ---
+++ killed by SIGTERM +++
#include
#include
#include
#include
int main(int argc, char *argv[]) {
if(argc < 3) {
printf("input %s \n",argv[0]);
exit(1);
}
int pid = atoi(argv[1]);
int sig = atoi(argv[2]);
if(pid>0 && sig>0) {
kill(pid,sig);
}
else {
printf("pid or sig must bigger zero\n");
}
return 0;
}
//先启动一个进程,再执行
./s2 27051 2
./s2 27051 29
//另一个进程显示结果
SIGINT signal 2
Profiling timer expired
//strace s2 27051 2 结果
。。。
munmap(0x7f97df44b000, 30479) = 0
kill(27076, SIGINT) = 0
exit_group(0) = ?
//strace s2 27051 27 结果
。。。
munmap(0x7fdd56c1f000, 30479) = 0
kill(27076, SIGPROF) = 0
exit_group(0) = ?
#include
#include
#include
#include
#include
sigset_t o_set,set,p_set;
void handler(int sig_num) {
printf("[handler]->signal %d,%d\n",sig_num,pthread_self());
sigpending(&set);
if(sigismember(&set,SIGINT) == 1) {
printf("[handler]->catch SIGINT %d\n",pthread_self());
}
if(sigismember(&set,SIGQUIT) == 1) {
printf("[handler]->catch SIGQUIT,%d\n",pthread_self);
}
if(sigismember(&set,SIGRTMIN+4) == 1) {
printf("[handler]->catch SIGRTMIN+4,%d\n",pthread_self);
}
printf("[handler]->sig pending is %8.8ld.%d\n",p_set,pthread_self());
}
void handler_pending(int sig_num) {
//在信号处理函数中,解除阻塞似乎不管用,得放到main函数中取做才行
//sigprocmask(SIG_SETMASK, &o_set, NULL);
if(sigismember(&set,SIGINT) == 1) {
printf("[handler_pending]->catch SIGINT,%d\n",pthread_self());
}
if(sigismember(&set,SIGQUIT) == 1) {
printf("[handler_pending]->catch SIGQUIT,%d\n",pthread_self());
}
if(sigismember(&set,SIGRTMIN+4) == 1) {
printf("[handler_pending]->catch SIGRTMIN+4,%d\n",pthread_self);
}
printf("[handler_pending]->... ok,%d\n",pthread_self());
}
void handler_int(int sig_num) {
printf("[handler_int]->catch SIGINT...,%d\n",pthread_self());
}
void handler_quit(int sig_num) {
printf("[handler_quit]->catch SIGQUIT...\n");
}
void handler_sig_kill(int sig_num) {
printf("[handler_sig_kill]->catch SIGKILL,%d\n",pthread_self());
}
void handler_SIGRTMIN_4(int sig_num) {
printf("[handler_SIGRTMIN_4]->catch SIGRTMIN+4\n");
}
int main(int argc, char *argv[]) {
int count = 0;
sigemptyset(&set);
sigemptyset(&o_set);
sigemptyset(&p_set);
sigaddset(&set,SIGINT);
sigaddset(&set,SIGQUIT);
sigaddset(&set,SIGURG);
sigdelset(&set,SIGURG);
sigaddset(&set,SIGRTMIN+4);
//sigaddset(&set,SIGRTMIN);
sigprocmask(SIG_BLOCK, &set, &o_set);
signal(SIGRTMIN,handler);
signal(SIGUSR2,handler_pending);
signal(SIGINT,handler_int);
signal(SIGQUIT,handler_quit);
signal(SIGRTMIN+4,handler_SIGRTMIN_4);
while(1) {
pause();
count++;
if(3 == count) {
printf("[main]->count==3\n");
sigprocmask(SIG_SETMASK, &o_set, NULL);
}
}
return 0;
}
//执行kill -SIGINT没结果显示
kill -SIGINT 28635
kill -SIGINT 28635
kill -SIGINT 28635
//执行kill -SIGQUIT没结果显示
kill -SIGQUIT 28635
kill -SIGQUIT 28635
kill -SIGQUIT 28635
//执行kill -SIGRTMIN+4没结果显示
kill -SIGRTMIN+4 28635
kill -SIGRTMIN+4 28635
kill -SIGRTMIN+4 28635
kill -SIGRTMIN 28635
//显示结果
[handler]->signal 34,0
[handler]->catch SIGINT 0
[handler]->catch SIGQUIT,4196160
[handler]->catch SIGRTMIN+4,4196160
[handler]->sig pending is 00000000.0
kill -SIGUSR2 28635
//显示结果
[handler_pending]->catch SIGINT,0
[handler_pending]->catch SIGQUIT,0
[handler_pending]->catch SIGRTMIN+4,4196160
[handler_pending]->... ok,0
kill -SIGUSR2 28635
//显示结果,注意handler_SIGRTMIN_4被打印了三次,所以这个信号是支持排队的
//SIGINT和SIGQUIT在阻塞前调用了三次,但只打印了一次,所以不支持排队
//信号编号超过31的就支持排队,是可靠的信号
[handler_pending]->catch SIGINT,0
[handler_pending]->catch SIGQUIT,0
[handler_pending]->catch SIGRTMIN+4,4196160
[handler_pending]->... ok,0
[main]->count==3
[handler_SIGRTMIN_4]->catch SIGRTMIN+4
[handler_SIGRTMIN_4]->catch SIGRTMIN+4
[handler_SIGRTMIN_4]->catch SIGRTMIN+4
[handler_quit]->catch SIGQUIT...
[handler_int]->catch SIGINT...,0
#include
#include
#include
#include
void handler(int sig_num) {
printf("catch signal -> %d\n",sig_num);
}
int main(int argc, char *argv[]) {
struct sigaction act;
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT,&act,NULL);
sigaction(SIGQUIT,&act,NULL);
while(1) {
pause();
}
return 0;
}
//用gdb 反编译代码,函数的起始地址是 40061d
(gdb) disas handler
Dump of assembler code for function handler:
0x000000000040061d <+0>: push %rbp
0x000000000040061e <+1>: mov %rsp,%rbp
0x0000000000400621 <+4>: sub $0x10,%rsp
0x0000000000400625 <+8>: mov %edi,-0x4(%rbp)
0x0000000000400628 <+11>: mov -0x4(%rbp),%eax
0x000000000040062b <+14>: mov %eax,%esi
0x000000000040062d <+16>: mov $0x400750,%edi
0x0000000000400632 <+21>: mov $0x0,%eax
0x0000000000400637 <+26>: callq 0x4004e0
0x000000000040063c <+31>: leaveq
0x000000000040063d <+32>: retq
End of assembler dump.
//strace后向程序发送kill -SIGINT [pid]命令
//这里的0x40061d就是 handler函数的地址
rt_sigaction(SIGINT, {0x40061d, [], SA_RESTORER, 0x7fe7d3f86270}, NULL, 8) = 0
rt_sigaction(SIGQUIT, {0x40061d, [], SA_RESTORER, 0x7fe7d3f86270}, NULL, 8) = 0
pause(
) = ? ERESTARTNOHAND (To be restarted if no handler)
--- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=29347, si_uid=0} ---
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 4), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe7d4533000
write(1, "catch signal -> 2\n", 18catch signal -> 2
) = 18
rt_sigreturn({mask=[]}) = -1 EINTR (Interrupted system call)
pause(
sigaction函数,sigqueue函数,siginfo_t结构体,sigval 联合体
#include
int 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 */
}
int sigqueue(pid_t pid, int sig, const union sigval value);
union sigval {
int sival_int;
void *sival_ptr;
};
接收端
#include
#include
#include
#include
// (*sa_sigaction)(int, siginfo_t *, void *);
void handler(int sig_num, siginfo_t *info, void *msg) {
printf("sig num->%d\n",sig_num);
printf("si_signo->%d\n",info->si_signo);
printf("si_code->%d\n",info->si_code);
printf("si_pid->%d\n",info->si_pid);
char *arg = (char*)info->si_value.sival_ptr;
printf("msg -> %s\n",arg);
}
int main(int argc, char *argv[]) {
struct sigaction act;
act.sa_sigaction = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
sigaction(SIGINT, &act, NULL);
sigaction(SIGQUIT, &act, NULL);
while(1) {
pause();
}
return 0;
}
发送端
#include
#include
#include
#include
int main(int argc, char *argv[]) {
if(argc < 4) {
printf("input \n");
exit(0);
}
int pid = atoi(argv[1]);
int sig = atoi(argv[2]);
union sigval val;
val.sival_ptr = argv[3];
//int sigqueue(pid_t pid, int sig, const union sigval value);
sigqueue(pid, sig, val);
return 0;
}
上面的接收端和发送端 实际是有问题的,通过strace分析两个程序
strace 发送端,可以看到最终调用的是rt_sigqueueinfo这个系统函数
。。。
getpid() = 30580
getuid() = 0
rt_sigqueueinfo(30575, SIGINT, {si_signo=SIGINT, si_code=SI_QUEUE, si_pid=30580, si_uid=0, si_value={int=-818055144, ptr=0x7ffccf3d7818}}) = 0
exit_group(0) = ?
+++ exited with 0 +++
strace接收端,报错了
pause(
) = ? ERESTARTNOHAND (To be restarted if no handler)
--- SIGINT {si_signo=SIGINT, si_code=SI_QUEUE, si_pid=30580, si_uid=0, si_value={int=-818055144, ptr=0x7ffccf3d7818}} ---
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc4068eb000
write(1, "sig num->2\n", 11sig num->2
) = 11
write(1, "si_signo->2\n", 12si_signo->2
) = 12
write(1, "si_code->-1\n", 12si_code->-1
) = 12
write(1, "si_pid->30580\n", 14si_pid->30580
) = 14
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x7ffccf3d7818} ---
+++ killed by SIGSEGV +++
Segmentation fault
SIGINT 接受到的数据,其中ptr=0x7ffccf3d7818,这个是一个地址
A进程发送的数据ptr内容指向的只是一个地址,所以B进程拿到这个地址读写就报错了
SIGSEGV 意思是读取非法的地址
如果想实现任意两个进程之间的,带字符串这种的信号发送,需要加上共享内存的方式
任意两个进程之间,只能传送整数
//接收端
#include
#include
#include
#include
// (*sa_sigaction)(int, siginfo_t *, void *);
void handler(int sig_num, siginfo_t *info, void *msg) {
printf("sig num->%d\n",sig_num);
printf("si_signo->%d\n",info->si_signo);
printf("si_code->%d\n",info->si_code);
printf("si_pid->%d\n",info->si_pid);
int arg = info->si_value.sival_int;
printf("msg -> %d\n",arg);
}
int main(int argc, char *argv[]) {
struct sigaction act;
act.sa_sigaction = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
sigaction(SIGINT, &act, NULL);
sigaction(SIGQUIT, &act, NULL);
while(1) {
pause();
}
return 0;
}
//发送端
#include
#include
#include
#include
int main(int argc, char *argv[]) {
if(argc < 4) {
printf("input \n");
exit(0);
}
int pid = atoi(argv[1]);
int sig = atoi(argv[2]);
union sigval val;
val.sival_int = atoi(argv[3]);
//int sigqueue(pid_t pid, int sig, const union sigval value);
sigqueue(pid, sig, val);
return 0;
}
//发送端执行
a3 30592 2 12345
//接收端
sig num->2
si_signo->2
si_code->-1
si_pid->30601
msg -> 12345
在父子进程模式中,发送带字符串的信号
注意,sigqueue()函数只能放到子进程中,放到父进程中似乎子进程就不执行了
#include
#include
#include
#include
//void (*sa_sigaction)(int, siginfo_t *, void *);
void handler(int sig_num, siginfo_t *info, void *arg) {
printf("catch signal->%d\n",sig_num);
//printf("catch signal name ->%s\n",strsignal(sig_num));
printf("cat msg->%s\n",info->si_value.sival_ptr);
}
int main(int argc, char *argv[]) {
if(argc < 2) {
printf("input \n");
exit(0);
}
char *msg = argv[1];
struct sigaction act;
sigval_t val;
val.sival_ptr = msg;
act.sa_sigaction = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
sigaction(SIGINT,&act,NULL);
sigaction(SIGQUIT,&act,NULL);
pid_t pid = fork();
int ppid;
printf("current pid=%d\n",getpid());
if(pid == 0) { //child
printf("child install sigaction...\n");
ppid = getppid();
printf("child send val->%s\n",(char*)val.sival_ptr);
sigqueue(ppid, SIGINT, val);
sigqueue(ppid, SIGQUIT, val);
}
else if(pid > 0) { //parent
printf("parent send sig,pid=%d\n",pid);
printf("parent val->%s\n",(char*)val.sival_ptr);
while(1) {
pause();
}
}
else {
printf("fork error\n");
exit(1);
}
return 0;
}
//执行程序
./a4 aaaaaaa
current pid=30606
parent send sig,pid=30607
parent val->aaaaaaa
current pid=30607
child install sigaction...
child send val->aaaaaaa
catch signal->3
cat msg->aaaaaaa
catch signal->2
cat msg->aaaaaaa
#include
#include
#include
#include
int parent_sig_num = 10;
int child_sig_num = 12;
int num = 100;
pid_t pid;
//void (*sa_sigaction)(int, siginfo_t *, void *);
void handler_parent(int sig_num, siginfo_t *info, void *msg) {
printf("handler parent --> child\n");
}
void handler_child(int sig_num, siginfo_t *info, void *msg) {
printf("handler child --> parent\n");
}
void parent() {
num++;
pause();
kill(pid,SIGUSR1);// parent signal child
pause();
num++;
}
void child() {
sleep(1);
kill(getppid(),SIGUSR2);// child signal parent
pause();
kill(getppid(),SIGUSR2);// child signal parent
}
int main(int argc, char *argv[]) {
struct sigaction act_parent;
act_parent.sa_sigaction = handler_parent;
sigemptyset(&act_parent.sa_mask);
act_parent.sa_flags = SA_SIGINFO;
struct sigaction act_child;
act_child.sa_sigaction = handler_child;
sigemptyset(&act_child.sa_mask);
act_child.sa_flags = SA_SIGINFO;
sigaction(SIGUSR1,&act_parent,NULL);
sigaction(SIGUSR2,&act_child,NULL);
pid = fork();
if(pid > 0) { //parent
parent();
printf("num->%d\n",num);
}
else if(pid == 0) { //child
child();
}
else {
printf("fork error\n");
exit(1);
}
return 0;
}
//执行结果
handler child --> parent
handler parent --> child
handler child --> parent
num->102
//strace分析程序
pause(
) = ? ERESTARTNOHAND (To be restarted if no handler)
--- SIGUSR2 {si_signo=SIGUSR2, si_code=SI_USER, si_pid=30839, si_uid=0} ---
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 6), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7eff6ddf1000
write(1, "handler child --> parent\n", 25handler child --> parent
) = 25
rt_sigreturn({mask=[]}) = -1 EINTR (Interrupted system call)
kill(30839, SIGUSR1) = 0
pause(handler parent --> child
) = ? ERESTARTNOHAND (To be restarted if no handler)
--- SIGUSR2 {si_signo=SIGUSR2, si_code=SI_USER, si_pid=30839, si_uid=0} ---
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=30839, si_uid=0, si_status=0, si_utime=0, si_stime=0} ---
write(1, "handler child --> parent\n", 25handler child --> parent
) = 25
rt_sigreturn({mask=[]}) = -1 EINTR (Interrupted system call)
write(1, "num->102\n", 9num->102
) = 9
exit_group(0) = ?
+++ exited with 0 +++
#include
#include
#include
#include
#include
#include
static void sig_usr1(int), sig_alrm(int);
static sigjmp_buf jmpbuf;
static volatile int canjump;
int main(void) {
if (signal(SIGUSR1, sig_usr1) == SIG_ERR) {
printf("signal(SIGUSR1) error\n");
exit(1);
}
if (signal(SIGALRM, sig_alrm) == SIG_ERR) {
printf("signal(SIGALRM) error\n");
exit(1);
}
printf("starting main: \n");
if (sigsetjmp(jmpbuf, 1)) {
printf("ending main: \n");
exit(0);
}
canjump = 1; /* now sigsetjmp() is OK */
for(;;) {
pause();
}
}
static void sig_usr1(int signo) {
time_t starttime;
if (canjump == 0)
return; /* unexpected signal, ignore */
printf("[sig_usr1] starting sig_usr1: \n");
alarm(3); /* SIGALRM in 3 seconds */
starttime = time(NULL);
for(;;) /* busy wait for 5 seconds */
if (time(NULL) > starttime + 5)
break;
printf("[sig_usr1] finishing sig_usr1: \n");
canjump = 0;
siglongjmp(jmpbuf, 1); /* jump back to main, don't return */
}
static void sig_alrm(int signo) {
printf("[sig_alrm] in sig_alrm: \n");
}
//执行命令
kill -SIGALRM 943
//显示
starting main:
[sig_alrm] in sig_alrm:
//执行命令
kill -SIGUSR1 943
//显示
[sig_usr1] starting sig_usr1:
[sig_alrm] in sig_alrm:
[sig_usr1] finishing sig_usr1:
ending main:
Linux 信号signal处理机制
Linux信号详解
Linux 信号(signal)
使用sigqueue函数发送信号
sigqueue函数 进程间通信 信号的发送携带数据
信号之sigsetjmp和siglongjmp函数
10.19 sleep,nanosleep以及clock_nanosleep函数