前言
这次接着总结进程间通信——信号。^ ~ ^
之前总结了,匿名管道pipe,有名fifo,内存映射区mmap。这次总结信号。
信号
特点
- 简单
- 携带的信息量少
- 使用在某个特定的场景中
信号的状态
- 产生:
键盘,比如ctrl+c
命令:kill
系统函数:kill
软条件:定时器
硬件:段错误
通过 man 7 siganl可以查看man文档
- 概念:阻塞信号集,未决信号集
阻塞信号集:
(1)将某个信号放到阻塞信号集,这个信号就不会被进程处理
(2)阻塞解除之后,信号被处理
未决信号集:没有被处理的信号
常见信号
信号名字 | 信号编号 | 处理内容 |
---|---|---|
SIGINT | 2 | Ctrl+C时OS送给前台进程组中每个进程 |
SIGABRT | 6 | 调用abort函数,进程异常终止 |
SIGPOLL SIGIO | 8 | 指示一个异步IO事件,在高级IO中提及 |
SIGKILL | 9 | 杀死进程的终极办法 |
SIGSEGV | 11 | 无效存储访问时OS发出该信号 |
SIGPIPE | 13 | 涉及管道和socket |
SIGALARM | 14 | 涉及alarm函数的实现 |
SIGTERM | 15 | kill命令发送的OS默认终止信号 |
SIGCHLD | 17 | 子进程终止或停止时OS向其父进程发此信号 |
SIGUSR1 | 10 | 用户自定义信号,作用和意义由应用自己定义 |
SIGUSR2 | 12 | 用户自定义信号,作用和意义由应用自己定义 |
信号常用的函数
- kill ——发送信号给指定进程
函数原型:int kill(pid_t pid, int sig);
代码示例:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <signal.h>
int main(int argc, const char* argv[])
{
pid_t pid = fork();
if(pid == -1)
{
perror("fork error");
exit(1);
}
if(pid > 0)
{
while(1)
{
printf("parent process pid = %d\n", getpid());
sleep(1);
}
}
else if(pid == 0)
{
sleep(2);
// 弑父
kill(getppid(), SIGKILL);
}
return 0;
}
- raise函数—自己给自己发信号
函数原型:int raise(int sig);
代码例子:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
int main(int argc, const char* argv[])
{
pid_t pid = fork();
if(pid > 0)
{
// parent
int s;
wait(&s);
if(WIFSIGNALED(s))
{
printf("term by signal: %d\n", WTERMSIG(s));
}
}
else if(pid == 0)
{
raise(SIGQUIT);
}
return 0;
}
alarm函数
函数原型:unsigned int alarm(unsigned int seconds);
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
int main(int argc, const char* argv[])
{
int ret = alarm(5);
printf("ret = %d\n", ret);
sleep(2);
ret = alarm(2);
printf("ret = %d\n", ret);
while(1)
{
printf("hello\n");
sleep(1);
}
return 0;
}
alarm函数,设置了一个定时器,到时间产生信号,到时间后杀死进程。(每个进程只有一个定时器)
信号的捕捉
- siganl函数
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <signal.h>
void myfunc(int sig)
{
printf("cathc you signal: %d\n", sig);
}
int main(int argc, const char* argv[])
{
// 注册信号捕捉函数
signal(SIGINT, myfunc);
while(1)
{
printf("hello\n");
sleep(1);
}
return 0;
}
- sigaction函数
sigaction可以一次得到设置新捕获函数和获取旧的捕获函数
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <signal.h>
void myfunc(int sig)
{
printf("hello signal: %d\n", sig);
sleep(5);
printf("wake up .....\n");
}
int main(int argc, const char* argv[])
{
// 注册信号捕捉函数
struct sigaction act;
act.sa_flags = 0;
act.sa_handler = myfunc;
// 设置临时屏蔽的信号
sigemptyset(&act.sa_mask); // 0
// ctrl + 反斜杠
sigaddset(&act.sa_mask, SIGQUIT);
sigaction(SIGINT, &act, NULL);
while(1);
return 0;
}
守护进程
守护进程是什么呢?
守护进程是一种长期运行的状态(一般是开机运行直到关机时关闭)。
- daemon表示守护进程,简称d
- 长期运行(一般是开机运行直到关机时关闭)
- 与控制台脱离
进程组
进程的组长
组里的第一个进程
进程组的ID=进程组的组长的ID
会话
会话是多个进程组
创建会话的条件:
- 不能是进程组长
- 创建会话的进程会成为新的进程组的组长
- 创建出的新的会话会丢弃原有的控制终端
- 一般步骤:先fork,父亲死,儿子执行创建会话操作(setsid)
实现一个守护进程
创建守护进程的要素:
- 子进程等待父进程推出
- 子进程使用setsid创建新的会话期,脱离控制台
- 条用chdir将当前工作目录设置为/
- umask设置为0以取消任何文件权限屏蔽
- 关闭所有文件描述符
- 将0,1,2定位到/dev/null
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void create_daemon(void);
int main(void)
{
create_daemon();
while (1)
{
printf("I am running.\n");
sleep(1);
}
return 0;
}
// 函数作用就是把调用该函数的进程变成一个守护进程
void create_daemon(void)
{
pid_t pid = 0;
pid = fork();
if (pid < 0)
{
perror("fork");
exit(-1);
}
if (pid > 0)
{
exit(0); // 父进程直接退出
}
// 执行到这里就是子进程
// setsid将当前进程设置为一个新的会话期session,目的就是让当前进程
// 脱离控制台。
pid = setsid();
if (pid < 0)
{
perror("setsid");
exit(-1);
}
// 将当前进程工作目录设置为根目录
chdir("/");
// umask设置为0确保将来进程有最大的文件操作权限
umask(0);
// 关闭所有文件描述符
// 先要获取当前系统中所允许打开的最大文件描述符数目
int cnt = sysconf(_SC_OPEN_MAX);
int i = 0;
for (i=0; i<cnt; i++)
{
close(i);
}
open("/dev/null", O_RDWR);
open("/dev/null", O_RDWR);
open("/dev/null", O_RDWR);
}