1.kill:
kill -l 查看所有信号
man 7 signal 查看man手册信号章节
前32个信号是Unix系统经典信号,与软件,操作系统相关, 后32个是实时信号, 驱动编程时使用
#include <sys/types.h>
#include <signal.h>
int raise(int sig); 向自己发送信号
void abort(void); 向自己发送SIGABRT信号
int kill(pid_t pid, int sig);
pid > 0
sig发送给ID为pid的进程
pid == 0
sig发送给与发送进程同组的所有进程
pid < 0
sig发送给组ID为|-pid|的进程, 并且发送进程具有向其发送信号的权限
pid == -1
sig发送给发送进程有权限向他们发送信号的系统上的所有进程
sig为0时, 用于检测, 特定为pid进程是否存在, 若不存在, 返回-1
若成功,至少一个信号发送成功,返回0, 否则返回-1, 并设置errno
2.alarm:
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
alarm函数用来计时, 计时seconds秒, 返回值是未计时的秒数,比如计时60秒, 已经计时了10秒,
由于某种原因停止计时, 那么就返回50,当计时完毕, alarm会向调用它的进程发送SIGALRM信号
信号产生种类
ctl + c SIGINT 从键盘的输入中断
ctl + z SIGTSTP 终端暂停信号
CTL + \ SIGQUIT 从键盘输入退出
进程处理信号的行为
默认动作(Action):
A 缺省动作是结束进程.
B 缺省动作是忽略这个信号.
C 缺省动作是结束进程, 并且核心转储.
D 缺省动作是停止进程.
E 信号不能被捕获.
F 信号不能被忽略.
忽略:
捕捉:
SIGKILL, SIGSTOP这两个信号不能被捕捉, 阻塞, 忽略
信号作用的过程:
产生的信号先记录在PEND未决信号集中,再查看Block阻塞信号集(信号屏蔽字)的对应位,
若对应位为1,则阻塞该信号,否则进程执行该信号默认的Action
未决态 : 信号产生未被响应
递达态 : 信号产生且被响应
注意: 1. 未决信号集由内核自动设置, 用户只能读未决信号集, 但是不能设置
2. 用户可以设置阻塞信号集
3.信号集处理函数:
sigset_t为信号集, 可用sizeof(sigset_t)查看大小
#include <signal.h>
int sigemptyset(sigset_t *set); 信号集全部位置为0
int sigfillset(sigset_t *set); 信号集全部位置为1
int sigaddset(sigset_t *set, int signum); 信号集指定信号位置为1
int sigdelset(sigset_t *set, int signum); 信号集指定信号位置为0
int sigismember(const sigset_t *set, int signum); 判断指定信号位是否置1
函数不直接操作信号集, 而是构造一个sigset_t信号集, 然后通过"注册"的方式, 将构造的
信号集变成Block阻塞信号集
信号集注册函数:
调用函数可以读取或更改进程的Block阻塞信号集(信号屏蔽字)
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
若成功则返回0, 否则返回-1
how的取值:
SIG_BLOCK set包含了我们希望添加到当前信号屏蔽字的信号, 相当于mask=mask | set
SIG_UNBLOCK set包含了我们希望从当前信号屏蔽字中解除阻塞的信号, 相当于mask=mask &~set
SIG_SETMASK 设置当前信号屏蔽字为set所指向的值, 相当于mask=set
如果调用sigprocmask解除了对当前若干个未决信号的阻塞, 则在sigprocmask返回前, 至少将其中一个信号递达
4.sigaction
收到指定信号后, 执行用户定义的操作
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
sigaction结构体:
struct sigaction {
void (*sa_handler)(int); //可以被设置SIG_DFL执行默认动作,SIG_IGN忽略该信号
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask; //阻塞信号集
int sa_flags;
void (*sa_restorer)(void);
};
sa_handler是早期的捕捉函数, sa_sigaction是新加入的捕捉函数,二者不可同时调用
通过设置sa_flags, 置0调用第一个, 置1调用第二个
注意: 1.在执行sa_handler时, 操作系统会自动把捕捉的信号的信号屏蔽字置1,再次到来的被捕捉信号记录在未决信号集中
sa_handler执行完毕后, 被捕捉信号的信号屏蔽字又被置为0, 未决信号集中的信号立即响应再次执行sa_handler
2.用户设置的sa_mask是临时的, 只有在执行sa_handler时会生效, 在sa_handler执行时, 产生的被临时阻塞的信号
会记录在未决信号集中, sa_handler执行完毕后, 被临时阻塞的信号会立即生效
5.SIGUSR1/SIGUSR2
利用操作系统提供给用户来自定义动作的信号, 可以实现父子进程同步
6.signal(C标准库提供的函数)
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
signal更适合跨平台项目, 功能不如sigaction强大
7.system(C标准库提供的函数)
#include <stdlib.h>
int system(const char *command);
集fork, exec, wait 于一体
8.可重入函数/不可重入函数
所谓可重入函数是指一个可以被多个任何调用的过程,任务在调用时不必担心数据是否出错。因为进程在收到信号后,就将跳转到信号处理函数
去接着执行,如果信号处理函数中使用了不可重入函数,那么信号处理函数可能会修改原来进程中不应该被修改的数据,这样进程从信号处理函
数返回接着执行时,可能会出现不可预料的后果,不可重入函数在信号处理函数中被视为不安全函数
信号处理函数禁止使用不可重入函数
不含全局变量和静态变量是可重入函数的一个要素
strtok_r
#include <string.h>
char *strtok(char *str, const char *delim);
tok 是一个不可重入函数
char *strtok_r(char *str, const char *delim, char **saveptr);
strtok_r 是一个可重入函数
9. 信号引起的时序竟态和异步IO问题
pause 引发的问题: 时序竟态
#include <unistd.h>
int pause(void);
使调用进程挂起, 直到有信号递达, 如果递达信号是忽略, 则继续挂起
#include <signal.h>
int sigsuspend(const sigset_t *mask);
以通过指定mask来临时解除对某个信号的屏蔽, 然后挂起等待, 当sigsuspend返回,mask恢复为原来的值
避免异步IO的类型
sig_atomic_t
平台下的原子类型, 平台有多少位, 该类型就占多少位
volatile
防止编译器开启优化选项时, 优化对内存对齐的读写
10.SIGCHLD信号处理
SIGCHLD产生条件:
子进程终止时
子进程接收到SIGSTOP信号停止时
子进程处在停止态, 接收到SIGCONT后唤醒时
子进程在以上情况时都会发送SIGCHLD信号, 父进程想知道是那种情况, 需要用到status
status处理方式:
pid_t waitpid(pid_t pid, int *status, int options);
status:
WIFEXITED(status) 如果子进程正常终止,返回真
WEXITSTATUS(status) 返回子进程的正常退出值
WIFSIGNALED(status) 子进程被信号终止, 返回真
WTERMSIG(status) 返回终止子进程的信号值
WIFSTOPPED(status) 子进程被停止, 返回真
WSTOPSIG(status) 返回停止子进程的信号值
WIFCONTINUED(status) 子进程由停止态转为就绪态, 返回真