9.1 信号
信号是一个32位整型数值,代表一个简单信息。每个信号都有一个以SIG开头的名字,实际就是系统定义的宏。
kill -l命令查看当前系统支持的所有信号。最小的信号值为1。
其中值为9的SIGKILL和值为19的SIGSTOP不可以被阻塞。
注意,只有具有root权限的进程才能向其他任一进程发送信号,非root权限的进程只能向属于同一个组或同一个用户的进程发送信号。
信号是可以被阻塞的。如果为进程产生一个信号,这个信号已经被进程设置为阻塞,并且对该信号的动作为系统默认动作或捕捉该信号,则该信号将一直处于未决(pending)状态。
当进程接收到信号后,信号会做什么?这要视情况而定,很多信号都会杀死进程,某时刻进程还在运行,下一秒就消亡了,从内存中被删除,相应的所有的文件描述符被关闭,并从进程表中被删除。但进程也有方法保护自己不被杀死。
9.1.1 signal系统调用
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
void sig_handler(int sig)
{
switch(sig){
case SIGINT:
printf("signal SIGINT is caught\n");
break;
default:
printf("other signal is caught\n");
}
}
int main()
{
int second = 0;
signal(SIGINT,SIG_DFL);
signal(SIGINT,sig_handler);//两个signal函数放在一起,以后面一个为准
return 0;
}
9.1.2 产生信号的两个函数:raise和write
9.1.3 设置定时器——alarm
9.1.4 挂起进程
进程调用pause会挂起,直到进程捕捉到一个信号。当执行完信号处理函数并返回时,pause才返回。pause始终返回1。
9.1.5 异常终止进程
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
void sig_handler(int sig)
{
switch(sig){
case SIGABRT:
printf("signal SIGABRT is caught\n");
break;
default:
printf("other signal is caught\n");
}
}
int main()
{
signal(SIGABRT,sig_handler);
abort();
printf("after abort()\n");
return 0;
}
在该程序中,abort使得内核向进程传递信号SIGABRT,因此,进程执行信号处理函数。由于abort使得程序异常退出,因此程序不会执行abort后面的语句。
9.1.6 添加延时:sleep
sleep的参数为无符号整型值,表示要挂起的秒数。进程调用sleep函数后挂起直到以下两种情况发生:
- 经过了seconds指定的秒数,此时sleep返回;
- 捕捉到一个信号并从信号处理函数返回,此时sleep返回剩余的秒数。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
void sig_handler(int sig)
{
switch(sig){
case SIGQUIT:
printf("SIGQUIT is caught\n");
break;
default:
printf("other signal is caught\n");
}
}
int main()
{
int rtn;
if(signal(SIGQUIT,sig_handler) != SIG_ERR){
printf("please enter Ctrl-\\\n");
rtn = sleep(1000);
printf("sleep returns and %d seconds remain\n",rtn);
rtn = sleep(3);
printf("sleep finishes and returns %d seconds\n",rtn);
}
return 0;
}
9.2 管道
管道是双向半双工的,即通过管道可以实现两个方向的数据流,但通信时只有一个方向的数据流。
使用管道进行通信的两个进程一定要有相同的祖先进程。(无名管道)
系统调用pipe接收一个具有两个元素的整型数组fds作为参数,pipe成功后返回0,并向fds返回两个文件描述符。fds[0]对应管道读出端,fds[1]对应管道写入端。每个管道都有一个管道缓冲区,大小为4096个字节。可在命令行输入“ulimit -p”查看系统当前管道缓冲区大小,以512字节为单位。(16个缓冲区)
int pipe(int fds[2]);
管道也是文件,是内存文件,没有inode节点,不会存放在磁盘中。
使用管道进行通信的父子进程:
- 父进程调用pipe创建管道,并返回两个文件描述符fds[0]和fds[1]
- 调用fork创建子进程,子进程继承fds[0]和fds[1]
如果子进程向父进程传输数据,则子进程关闭fds[0],父进程关闭fds[1];如果父进程向子进程传输数据,则子进程关闭fds[1],父进程关闭fds[0]
//pipe的典型用法1
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main()
{
pid_t pid;
char data[32] = "hello";
char buf[32] = {0};
int fds[2];
if(pipe(fds) == 0){
pid = fork();
if(pid == 0){
close(fds[0]);
write(fds[1],data,strlen(data));
exit(0);
}else if(pid > 0){
wait(NULL);
close(fds[1]);
read(fds[0],buf,sizeof(buf));
printf("%s\n",buf);
}
}
return 0;
}
//pipe的典型用法2。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
int fds[2];
int pid1,pid2;
int sibling,self;
if(pipe(fds) == 0)
{
if((pid1 = fork()) == 0)
{
close(fds[0]);
self = getpid();
write(fds[1],&self,sizeof(int));
exit(1);
}
if((pid2 = fork()) == 0)
{
close(fds[1]);
read(fds[0],&sibling,sizeof(int));
printf("sibling pid = %d\n",sibling);
exit(2);
}
}
return 0;
}
pipefork2中父进程创建了两个子进程,第一个子进程把自己的pid写入管道,第二个子进程从管道中读出第一个子进程的pid。<br />因为一个管道的缓冲区容量有限,所以,写进程向已满的管道写数据时,写进程将被阻塞,直至读进程把数据从管道中读出。与之类似,若读进程从空管道中读数据,则读进程也会被阻塞,直至写进程向管道中写数据。如果管道的读出端进程不存在,则写进程调用write时,内核向此进程发送信号SIGPIPE,系统默认动作为终止进程。<br />当fork创建子进程,子进程会继承父进程的文件描述符等。通过struct file就能找到文件缓冲区。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/1054933/1638105556402-d19b130b-ba54-46e8-b569-a1c042291eac.png#clientId=u3ca9c15c-7a40-4&from=paste&height=453&id=uad3cf893&margin=%5Bobject%20Object%5D&name=image.png&originHeight=604&originWidth=1215&originalType=binary&ratio=1&size=104062&status=done&style=none&taskId=uf1e94a07-ec42-4ebc-8b01-23ac999fbaa&width=911)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/1054933/1637487050441-4a8f9862-9e19-4895-84af-73b3883d10de.png#clientId=ubd9804ff-76d5-4&from=paste&height=208&id=u926f4e7e&margin=%5Bobject%20Object%5D&name=image.png&originHeight=416&originWidth=986&originalType=binary&ratio=1&size=66579&status=done&style=none&taskId=u6dfea755-e0fa-4223-891b-ad12daa1693&width=493)