进程间通讯机制:
unix继承: 管道、信号
system V IPC对象:共享内存、消息队列、信号灯集
1.管道
管道分为无名管道和有名管道--区别在于创建的管道是否在文件系统中不可见,无名不可见,有名可见。
(1)无名管道
特点:
1.在创建之后再文件系统中不可见
2.以半双工的方式进行通信,两根管道
3.拥有固定的读段和写段
4.只能用于具有亲缘关系的进程间通讯
(2)无名管道的功能函数
1.无名管道的创建--pipe()
#include <unistd.h>
int pipe(int pipefd[2]);
参数:
pipefd:存放无名管道读段和写段的数组首地址
pipefd[0]---读段
pipefd[1]---写段
返回值:
成功返回0,失败返回-1
练习:在一个进程中,创建一个子进程,子进程从键盘获取数据,父进程打印出数据
/*===============================================
* 文件名称:pipe.c
* 创 建 者:
* 创建日期:2022年08月10日
* 描 述:
================================================*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <semaphore.h>
#include <sys/types.h>
#include <wait.h>
int main(int argc, char *argv[])
{
int pipefd[2]={0}; //文件描述符的空间
char buf[64]={0};
int ret=pipe(pipefd); //创建无名管道
if(ret<0)
{
perror("pipe");
exit(-1);
}
pid_t pid=fork(); //创建子进程
if(pid<0)
{
perror("fork");
}
if(pid==0) //子进程
{
printf("请输入一串数据\n");
scanf("%s",buf);
write(pipefd[1],buf,strlen(buf));//写入无名管道
memset(buf,0,64); //清空缓冲区
}
else //父进程
{
sleep(1); //等待子进程输完
read(pipefd[0],buf,64); //从无名管道读取数据
printf("父:%s\n",buf); //打印数据
wait(NULL); //回收子进程
}
return 0;
}
2.无名管道的读写特性
读特性:
写端存在:
管道有数据:返回读到得到字节数
管道无数据:read阻塞
写段不存在:
管道有数据:返回读到的字节数
管道无数据:返回0
写特性:
读段存在:
管道有空间:返回写入的字节数
管道无空间:write阻塞,直到有空间为止,有一个空间写一个字符
读段不存在:
无论管道是否有空间,管道破裂,系统会发送SIGPIPE给当前进程,进程会死亡
练习:1.计算无名管道空间大小
2.验证管道破裂
/*===============================================
* 文件名称:length_pipe.c
* 创 建 者:
* 创建日期:2022年08月10日
* 描 述:
================================================*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int pipefd[2];
pipe(pipefd);
close(pipefd[0]);//关闭无名管道的读端
pid_t pid=fork();
if(pid<0)
{
perror("");
}
if(pid==0)
{
write(pipefd[1],"hello",5);
sleep(3);//测试是否会正常退出
/*int count=1,num=0;
char buf[1024]={0};
while(1)
{
count=write(pipefd[1],buf,1024);
num+=count;
printf("%d\n",num); //测试管道大小
}
*/
}
else
{
int status;
wait(&status);//捕获信息
printf("%d %d %d %d\n",WIFEXITED(status),WEXITSTATUS(status),WIFSIGNALED(status),WTERMSIG(status));
}
return 0;
}
(3)有名管道
有名管道创建之后,会在文件系统中以管道文件的形式存在。
有名管道可以用于任意两个进程间通讯,没有固定的读端,写端
目录
1.管道
(1)无名管道
(2)无名管道的功能函数
1.无名管道的创建--pipe()
2.无名管道的读写特性
(3)有名管道
(4)有名管道的功能函数
2.信号
1.常用信号
2.信号相关指令
3.信号相关接口函数
4.定时器
5.捕获信号--signal
(4)有名管道的功能函数
1.有名管道的创建 -mkfifo()
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
参数:
pathname:创建管道文件的文件名
mode:表示创建的管道文件的权限,0664
返回值:
成功返回0,失败返回1
练习:创建一个有名管道,一个进程向管道输入数据,另外一个向管道输出程序
输入程序:
/*===============================================
* 文件名称:mkfifo.c
* 创 建 者:
* 创建日期:2022年08月10日
* 描 述:
================================================*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
/*
int ret1=mkfifo("fifo",0664);//创建管道文件,已存在会保存
if(ret1<0)
{
perror("mkfifo");
exit(-1);
}
*/
int fp=open("fifo",O_RDWR);
printf("fifo open\n");
char buf[64]={0};
int n=5;
while(n--)
{
scanf("%s",buf);
write(fp,buf,64);
}
close(fp);
return 0;
}
输出程序:
/*===============================================
* 文件名称:getfifo.c
* 创 建 者:
* 创建日期:2022年08月10日
* 描 述:
================================================*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
int fd=open("fifo",O_RDWR);
if(fd<0)
{
perror("open");
exit(-1);
}
printf("fifo open \n");
char buf[64]={0};
int n=5;
while(n--)
{
read(fd,buf,64);
puts(buf);
memset(buf,0,64);
}
close(fd);
return 0;
}
2.信号
信号:是终端在软件层次上的一种模拟
kill -l 常用信号 前32种是从unix继承而来。
型号的处理方式:
默认处理:
忽略处理:
捕获信号:
1.常用信号
SIGHUP: (1) 终端的控制进程结束时
SIGINT: (2) (ctrl+c)结束前台进程
SIGQUIT: (ctrl +\ )
SIGLL: 进程企图执行一条非法指令时(可执行文件本身出错,堆栈溢出)段错误
SIGFPE:致命的算术运算错误,除数为0
SIGKILL:用来结束程序的运行,不能被阻塞、处理、忽略
SIGALRM:(14)定时器到时发出
SIGSTOP:用来暂停一个进程,不能被阻塞、处理、忽略
SIGTSTP:暂停交互过程(ctrl+z)
SIGCHLD:子进程改变状态时,父进程收到此信号
SIGABORT:用于结束进程
2.信号相关指令
kill -l:查看看单签系统中的所有信号
kill -信号编号 进程号 :向指定进程发送对应边好的信号
eg: kill -9 1234 向进程号为1234的进程发送信号9
kill -9 -1234 (-负号)向进程组1234发送信号9
kill -9 -1 向除了init以外的所有进程发送信号9
kill -9 -0 信号被发送到所有和当前进程在同一进程组的进程
3.信号相关接口函数
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
参数:
pid:指定的进程号
sig:指定的信号,编号也可以
返回值:
成功返回0,失败返回-1;
#include <signal.h>
int raise(int sig);
向当前进程发送信号
参数:
sig:指定信号
返回值:
成功返回0值,失败返回非0值
4.定时器
定时时间到,当前进程会接收到编号为14的SIGARLM信号,默认终止当前进程、
一个进程中,最多只能存在一个定时器。
定时器不改变原本函数的执行过程,只是计时,当程序运行时间超过定时器的时间,进程接收到SIGAELM信号
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
参数:
seconds:定时的秒数
返回值:
成功,如果之前没有定时器,返回0。如果之前有定时器,返回其删一个定时器剩余时间
eg:
alarm(10); 返回值为0
sleep(3);
alarm(5); 返回值为10-3=7
总定时时间为3+5=8,第二次调用ararm,刷新为5.
#include <unistd.h>
int pause(void);
功能:阻塞函数,等待定时结束
/*===============================================
* 文件名称:alarm.c
* 创 建 者:
* 创建日期:2022年08月10日
* 描 述:
================================================*/
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int ret = alarm(5);
printf("ret=%d\n",ret);
sleep(3);
ret=alarm(3);
printf("ret=%d\n",ret); //打印上次定时器的剩余时间
sleep(6);
return 0;
}
5.捕获信号--signal
#include <signal.h>
typedef void (*sighandler_t)(int);
//等同于typedef void (*sighandler_t)(int) sighandler;
sighandler_t signal(int signum, sighandler_t handler);
捕捉到信号就调用函数,而不是采用默认方式
参数:
signum:指定信号,可填编号
handler:信号处理函数或者SIG_IGN(选择以忽略方式处理指定信号)、SIG_DFL(选择以默认方式指定信号)
信号处理函数不传参,参数为捕获到的型号的编号
返回值:
eg:
signal(14,time_out)
void time_out(int num)
{
printf("time_out %d",num);//num默认为捕获到的信号编号
}
执行完函数之后,继续向下执行程序
练习:
创建一个子进程,子进程结束时,父进程提示子进程退出信息
SIGCHLD:子进程改变状态时,父进程收到此信号
/*===============================================
* 文件名称:signal.c
* 创 建 者:
* 创建日期:2022年08月10日
* 描 述:
================================================*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
void func(int num);
int main(int argc, char *argv[])
{
pid_t pid = fork();//创建子进程
if(pid<0)
{
perror("fork");
}
if(pid==0)//子进程
{
alarm(2);//定时2秒
while(1)
{
signal(14,func);//接收到信号,则执行func
}
//exit(0);
}
else
{
//kill(pid,9);
int wstatus;
signal(SIGINT,func);
signal(17,func);
wait(&wstatus); //回收子进程
printf("%d %d %d %d\n",WIFEXITED(wstatus),WEXITSTATUS(wstatus),WIFSIGNALED(wstatus),WTERMSIG(wstatus));
}
return 0;
}
void func(int num)
{
printf("signal = %d\nchile is dead \n",num);
}