目录
信号发送
int kill(pid_t pid, int signum) //kill 命令产生信号
参数:pid:
> 0:发送信号给指定进程
= 0:发送信号给跟调用kill函数的那个进程处于同一进程组的进程。
< -1: 取绝对值,发送信号给该绝对值所对应的进程组的所有组员。如kill -9 -10698 杀死进程组id为10698的所有进程 kill -9 -组id
= -1:发送信号给,有权限发送的所有进程。
signum:待发送的信号
返回值:
成功: 0
失败: -1 errno
软件条件产生信号
alarm 函数:使用自然计时法。
定时发送SIGALRM给当前进程。
unsigned int alarm(unsigned int seconds); //seconds:定时秒数 返回值:上次定时剩余时间。
alarm(0); 取消闹钟。
time 命令 : 查看程序执行时间。 实际时间 = 用户时间 + 内核时间 + 等待时间。 --> 优化瓶颈 IO
setitimer函数:
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
参数:
which: ITIMER_REAL: 采用自然计时。 ——> SIGALRM
ITIMER_VIRTUAL: 采用用户空间计时 ---> SIGVTALRM
ITIMER_PROF: 采用内核+用户空间计时 ---> SIGPROF
new_value:定时秒数
类型:struct itimerval {
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
}it_interval;---> 周期定时秒数
struct timeval {
time_t tv_sec;
suseconds_t tv_usec;
}it_value; ---> 第一次定时秒数
};
old_value:传出参数,上次定时剩余时间。
e.g.
struct itimerval new_t;
struct itimerval old_t;
new_t.it_interval.tv_sec = 0;
new_t.it_interval.tv_usec = 0;
new_t.it_value.tv_sec = 1;
new_t.it_value.tv_usec = 0;
int ret = setitimer(&new_t, &old_t); 定时1秒
返回值:
成功: 0
失败: -1 errno
其他几个发信号函数:
int raise(int sig);
void abort(void);
信号集操作函数
sigset_t set; // typedef unsigned long sigset_t;
int sigemptyset(sigset_t *set); //将某个信号集清 0 成功:0;失败:-1
int sigfillset(sigset_t *set); //将某个信号集置 1 成功:0;失败:-1
int sigaddset(sigset_t *set, int signum); //将某个信号加入信号集 成功:0;失败:-1
int sigdelset(sigset_t *set, int signum); //将某个信号清出信号集 成功:0;失败:-1
int sigismember(const sigset_t *set, int signum); //判断某个信号是否在信号集中 返回值:在集合:1;不在:0;出错:-1
int sigpending(sigset_t *set); //读取当前进程的未决信号集 set 传出参数。 返回值:成功:0;失败:-1,设置 errno
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); //成功:0;失败:-1,设置 errno
用来屏蔽信号、解除屏蔽也使用该函数。其本质,读取或修改进程的信号屏蔽字(PCB 中)
严格注意,屏蔽信号:只是将信号处理延后执行(延至解除屏蔽);而忽略表示将信号丢处理。
参数:
set:传入参数,是一个位图,set 中哪位置 1,就表示当前进程屏蔽哪个信号。
oldset:传出参数,保存旧的信号屏蔽集。
how 参数取值: 假设当前的信号屏蔽字为 mask
1. SIG_BLOCK: 当 how 设置为此值,set 表示需要屏蔽的信号。相当于 mask = mask|set
2. SIG_UNBLOCK: 当 how 设置为此,set 表示需要解除屏蔽的信号。相当于 mask = mask & ~set
3. SIG_SETMASK: 当 how 设置为此,set 表示用于替代原始屏蔽及的新屏蔽集。相当于 mask = set
若,调用 sigprocmask 解除了对当前若干个信号的阻塞,则在 sigprocmask 返回前,至少将其中一
个信号递达。
sigset_t 类型的本质是位图。但不应该直接使用位操作,而应该使用上述函数,保证跨系统操作有效。
使用
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
void sys_err(const char *str)
{
perror(str);
exit(1);
}
void print_set(sigset_t *set) //判断信号集内容
{
int i;
for (i = 1; i<32; i++) {
if (sigismember(set, i))
putchar('1');
else
putchar('0');
}
printf("\n");
}
int main(int argc, char *argv[])
{
sigset_t set, oldset, pedset;
int ret = 0;
sigemptyset(&set);
sigaddset(&set, SIGINT);
sigaddset(&set, SIGQUIT);
sigaddset(&set, SIGBUS);
sigaddset(&set, SIGKILL);
ret = sigprocmask(SIG_BLOCK, &set, &oldset); //屏蔽信号
if (ret == -1)
sys_err("sigprocmask error");
while (1) {
ret = sigpending(&pedset);
print_set(&pedset);
sleep(1);
}
return 0;
}
信号捕捉函数signal
typedef void (*sighandler_t)(int); //处理动作 回调函数
sighandler_t signal(int signum, sighandler_t handler); //捕捉函数siganl
使用
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
void sys_err(const char *str)
{
perror(str);
exit(1);
}
void sig_catch(int signo)
{
printf("catch you!! %d", signo);
return ;
}
int main(int argc, char *argv[])
{
signal(SIGINT, sig_catch);
while (1);
return 0;
}
信号捕捉函数sigaction
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
成功:0;失败:-1,设置 errno
参数:
act:传入参数,新的处理方式。
oldact:传出参数,旧的处理方式。
struct 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);
};
sa_restorer:该元素是过时的,不应该使用,POSIX.1 标准将不指定该元素。(弃用)
sa_sigaction:当 sa_flags 被指定为 SA_SIGINFO 标志时,使用该信号处理程序。(很少使用)
重点掌握:
① sa_handler:指定信号捕捉后的处理函数名(即注册函数)。也可赋值为 SIG_IGN 表忽略 或 SIG_DFL 表执行默认动作
② sa_mask: 调用信号处理函数时,所要屏蔽的信号集合(信号屏蔽字)。注意:仅在处理函数被调用期间屏蔽生效,是临时性设置。
③ sa_flags:通常设置为 0,表使用默认属性。(设置被信号中断后系统调用是否重启。SA_INTERRURT 不重启。 SA_RESTART 重启)
sigaction使用
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
void sys_err(const char *str)
{
perror(str);
exit(1);
}
void sig_catch(int signo) // 回调函数
{
if (signo == SIGINT) {
printf("catch you!! %d\n", signo);
sleep(10); //模拟信号处理函数执行很长时间
}
/*
else if (signo == SIGQUIT)
printf("-----------catch you!! %d\n", signo);
*/
return ;
}
int main(int argc, char *argv[])
{
struct sigaction act, oldact;
act.sa_handler = sig_catch; //设置回调函数
sigemptyset(&(act.sa_mask)); //清空sa_mask屏蔽字, 只在sig_catch工作时有效
sigaddset(&act.sa_mask, SIGQUIT); //将SIGQUIT加入信号屏蔽集,这就导致,在调用信号处理函数期间
/*不仅不响应SIGINT信号本身,还不响应SIGQUIT*/
act.sa_flags = 0; //默认值
int ret = sigaction(SIGINT, &act, &oldact); //注册信号捕捉函数
if (ret == -1)
sys_err("sigaction error");
// ret = sigaction(SIGQUIT, &act, &oldact); //注册信号捕捉函数 可以捕捉不同信号使用同一个动作,只需动作里识别信号
while (1);
return 0;
}
信号捕捉特性
- 进程正常运行时,默认 PCB中有一个信号屏蔽字,假定为☆,它决定了进程自动屏蔽哪些信号。当注册了某个信号捕捉函数,捕捉到该信号以后,要调用该函数。而该函数有可能执行很长时间,在这期间所屏蔽的信号不由☆来指定。而是用sa_mask 来指定。调用完信号处理函数,再恢复为☆。
- XXX 信号捕捉函数执行期间,XXX 信号自动被屏蔽。
- 阻塞的常规信号不支持排队,产生多次只记录一次。(后 32 个实时信号支持排队)
使用SIGCHLD 信号完成子进程回收
SIGCHLD 的产生条件
- 子进程终止时
- 子进程接收到 SIGSTOP 信号停止时
- 子进程处在停止态,接受到 SIGCONT 后唤醒时
SIGCHLD 信号注意问题
- 子进程继承父进程的信号屏蔽字和信号处理动作,但子进程没有继承未决信号集 spending。
- 注意注册信号捕捉函数的位置。
- 应该在 fork 之前,阻塞 SIGCHLD 信号。注册完捕捉函数后解除阻塞。
SIGCHLD 信号使用
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <errno.h>
#include <pthread.h>
void sys_err(const char *str)
{
perror(str);
exit(1);
}
void catch_child(int signo)
{
pid_t wpid;
int status;
while((wpid = waitpid(-1, &status, 0)) != -1) { // 循环回收,防止僵尸进程出现.
if (WIFEXITED(status))
printf("catch child id %d, ret=%d\n", wpid, WEXITSTATUS(status));
}
return ;
}
int main(int argc, char *argv[])
{
pid_t pid;
sigset_t set, oldset, pedset; //fork之前,阻塞SIGCHLD信号
int ret = 0;
sigemptyset(&set);
sigaddset(&set, SIGCHLD);
ret = sigprocmask(SIG_BLOCK, &set, &oldset);
if (ret == -1)
sys_err("sigprocmask BLOCK error");
int i;
for (i = 0; i < 15; i++)
if ((pid = fork()) == 0) // 创建多个子进程
break;
if (15 == i) { //父进程
struct sigaction act;
act.sa_handler = catch_child; // 设置回调函数
sigemptyset(&act.sa_mask); // 设置捕捉函数执行期间屏蔽字
act.sa_flags = 0; // 设置默认属性, 本信号自动屏蔽
sigaction(SIGCHLD, &act, NULL); // 注册信号捕捉函数
ret = sigprocmask(SIG_UNBLOCK, &set, &oldset); //开始解除SIGCHLD信号阻塞
if (ret == -1)
sys_err("sigprocmask UNBLOCK error");
printf("I'm parent, pid = %d\n", getpid());
while (1);
} else {
printf("I'm child pid = %d\n", getpid());
return i;
}
return 0;
}