信号
信号的共性:
- 简单
- 不能携带大量的信息
- 要满足条件了才可以发送
信号的特质:
-
信号是软件层面的“中断” 一旦信号产生,无论程序执行到了什么位置,必须立刻停止运行,来处理信号,之后再继续执行指令 -
所有的信号的产生和处理均是由内核产生和处理的
信号相关的事件和状态
信号的产生:
- 按键产生
Ctrl + c - 系统调用产生
kill, raise - 软件条件产生
alarm - 硬件异常产生
段错误 - 命令产生
kill
递达: 传递且到达进程
未决: 产生和递达直接的状态, 由于阻塞所导致
信号的处理的方式:
- 执行默认动作
- 忽略
- 捕捉(调用用户处理函数)
阻塞信号集:
- 将集合中的信号进行屏蔽, 若收到了该信号, 则会将其处理推后
未决信号集:
- 信号产生时, 该信号集中的信号设置为1, 当信号处理后,设置为0
- 若该信号被屏蔽, 则为解除前, 该信号一直为未决状态
常见信号:
9). SIGKILL 无条件终止进程,无法被忽略,处理和阻塞
19). SIGSTOP 停止进程, 无法被忽略,处理和阻塞
10). SIGUSR1 用户自定义信号, 默认为终止进程
12). SIGUSR2 用户自定义信号, 默认为终止进程
17). SIGCHLD 子进程的状态发生变化的时候,用于通知父进程, 默认为忽略
kill函数
int kill(pid_t pid, int sig);
参数:
pid
>0 : 把信号发送给指定的进程
=0 : 把信号发送给与调用kill函数相同进程组的所有的进程
<0 : 取|pid|发给对应的进程组(进程组的id和父进程的id是一样的)
== -1 : 发送给进程有权限发送的系统中的所有的进程
返回值:
成功: 0
失败: -1, 设置errno
raise函数
int raise(int sig);
等价于
kill(getpid(), sig);
abort函数
void abort(void);
用于终止进程
alarm函数
作用: 定时发送SIGALRM信号给进程(自然计时法 --- 无论进程处在什么状态, 始终计时)
unsigned int alarm(unsigned int seconds);
参数:
seconds: 定时的时间
返回值:
上次剩余的时间,
无错误现象
time命令: 查看程序执行的时间
实际时间 = 用户时间 + 内核时间 + 等待时间 -> 优化瓶颈在IO
setitimer/getitimer函数
int getitimer(int which, struct itimerval *curr_value);
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
参数:
which: 指定定时方式
1. 自然定时: ITIMER_REAL -> SIGALRM
2. 虚拟空间计时(用户空间): ITIMER_VIRTUAL -> SIGVTALRM
3. 运行时计时(用户 + 内核): ITIMERR_PROF -> SIGPROF
new_value: 定时秒数
old_value: 传出参数, 上次定时剩余时间
返回值:
成功: 0
失败: -1, 设置errno
struct itimerval {
struct timeval it_interval; 周期定时的秒数
struct timeval it_value; 第一次计时的秒数
};
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
信号集操作函数
总共有两个信号集, 一个是阻塞信号集, 一个是未决信号集, 可以操作阻塞信号集从而来间接影响未决信号集
sigset_t set : 自定义信号集
int sigemptyset(sigset_t *set); 清空信号集
int sigfillset(sigset_t *set); 全部阻塞
int sigaddset(sigset_t *set, int signum); 在一个信号集中阻塞一个信号
int sigdelset(sigset_t *set, int signum); 在一个信号集中不阻塞一个信号
int sigismember(const sigset_t *set, int signum); 判断一个集合是否被阻塞
返回值:
被阻塞了: 1
没有被阻塞: 0
失败: -1
设置信号屏蔽字:
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
参数:
how:
SIG_BLOCK: 设置阻塞
SIG_UNBLOCK: 取消阻塞
SIG_SETMASK: 用自定义的set替换mask
set:自定义的set
lodset:旧的mask, 传出参数
返回值:
成功: 0
失败: -1
查看未决信号集
int sigpending(sigset_t *set);
参数:
set: 传出参数, 未决信号集
信号的捕捉
signal函数
函数指针
函数返回值类型 (* 指针变量名) (函数参数列表);
函数指针类型
typedef 函数返回值类型 (* 指针变量类型名) (函数参数列表);
作用: 用于 注册 一个信号捕捉函数(执行依然是内核)
sighandler_t signal(int signum, sighandler_t handler);
参数:
signum: 信号的数字
handler: 一个函数指针, 指向的是一个形参为int, 返回值为void的函数
返回值:
handler
typedef void (*sighandler_t)(int);
sigaction函数
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
参数:
signum: 信号的编号
act: 新的动作
oldact: 旧的动作
struct sigaction {
void (*sa_handler)(int);// 设置回调函数
1. 捕捉函数名
2. SIG_IGN表示忽略
3. SIG_DFL表示执行默认动作
void (*sa_sigaction)(int, siginfo_t *, void *); // 传递的参数, 要传参数的时候
sigset_t sa_mask; // 设置sa_handler函数工作的时候mask的值
int sa_flags; // 设置属性值
0: 默认动作
SA_RESTART: 被中断的时候重启
SA_SIGINFO: 使用sa_sigaction来指定捕捉函数
SA_DEFER: 不自动屏蔽信号
SA_INTERRUPT: 系统调用被信号中断后,不重启
void (*sa_restorer)(void);
};
信号捕捉的特性
- 当一个捕捉函数捕捉到了一个信号以后, 当调用该函数的时候, 信号屏蔽字由sa_mask决定, 不由原来的信号屏蔽字决定, 调用完了以后, 则恢复成原来的信号屏蔽字
- XXX 信号捕捉函数期间, XXX信号会自动被屏蔽
- 阻塞的常规信号不支持排队, 产生多次, 只执行一次
sigchld 信号
sigchld的产生的条件
- 子进程终止时
- 子进程接收到SIGSTOP信号停止时
- 子进程处在停止态, 接受到SIGCONT后唤醒时
编程技巧
如何防止子进程在捕捉函数注册前就已经死掉?
如何防止僵尸进程的出现?
- 使用循环来回收子进程, 一次捕捉, 多次回收
while ((wpid = waitpid(-1, &status, 0)) != -1);
慢速系统调用
- 可能造成进程永久堵塞的系统调用叫慢速系统调用, 如
read, wait, waitpid
被中断的行为
- 中断系统调用的信号不能被屏蔽, 忽略, 必须被捕捉
- 中断后返回-1, 设置
errno 为EINTR - sa_flags
- SA_RESTART 重启
- SA_INTERRUPT 不重启
|