一:进程通信概述
1.什么是进程间通信?
什么是线程间通信?
进程通信:在用户空间实现进程通信是不可能的,通过Linux内核通信
线程间通信:可以在用户空间就可以实现,可以通过全局变量通信。
2.有哪几种通信方式?
管道通信:无名管道、有名管道(文件系统中有名)
信号通信:信号(通知)通信包括:信号的发送、信号的接收和信号的处理。
IPC(Inter-Process Communication)通信:共享内存、消息队列和信号灯。
以上是单机模式下的进程通信(只有一个Linux内核)
Socket通信(网络编程的内容):存在于一个网络中两个进程之间的通信(两个Linux内核)。
3.进程通信课程的学习思路:每一种通信方式都是基于文件IO的思想。
open:
功能:创建或打开进程通信对象。函数形式不一样,有的是有多个函数完成。
(内核空间的通信对象需要open创建)
write:
功能:向进程通信对象中写入内容。函数形式可能不一样。
read:
功能:从进程通信对象中读取内容。函数形式可能不一样。
close:功能:关闭或删除进程通信对象。形式可能不一样。
二:管道通信
1. 无名管道:文件系统中无文件名
(1)通信原理:![通信原理](https://i-blog.csdnimg.cn/blog_migrate/1e962dc9b76f913ae8ec4717b2faad2a.png)
管道文件是一个特殊的文件,是由队列来实现的。(入队 出队)
在文件IO中创建一个文件或打开一个文件是由open函数来实现的,open只能创建普通文件,它不能创建管道文件。只能用pipe函数来创建管道。
pipe
函数形式:int pipe(int fd[2])
功能:创建管道,为系统调用:unistd.h
参数:返回文件描述符,即文件描述符返回在函数参数里面。可见有两个文件描述符:f[0]和fd[1]。 fd[0]指向管道的读端,fd[1]指向管道的写端。fd[1]的输出是fd[0]的输入。
返回值:成功返回0,出错返回-1。
eg:
//对pipe创建的管道进行读写
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
int main()
{
int fd[2];
int ret;
//int i=0;
char writebuf[]="hello linux";
char readbuf [128]={0};
ret=pipe(fd);
if(ret <0)
{
printf("creat pipe failure\n");
return -1;
}
printf("creat pipe sucess fd[0]=%d,fd[1]=dn",fd[0],fd[1]);
write(fd[1],writebuf,sizeof(writebuf));
//start read from pipe
read(fd[0],readbuf,128);//管道中的东西,读完了就删除了;队列
printf("readbuf:%s\n",readbuf);
/*清空readbuf之后再读一次
memset(readbuf,0,128);
read(fd[0],readbuf,128);
printf("read readbuf again");//1.执行时发现此句不会输出,验证了如果管道中没有东西可读,则会阻塞。2.也验证了管道内容读完之后会自动删除
*/
//把writebuf的内容循环写入管道,看何时产生阻塞
/*
while(i<5500)
{
write(fd[1],writebuf,sizeof(writebuf));
}
printf("write pipe end");
*/
close(fd[1]);
close(fd[0]);
return 0;
}
管道特点总结:
- 管道是创建在内存中的,进程结束,空间释放,管道就不存在了;
- 如果管道中没有东西可读则会阻塞;
- 管道中的东西,读完了就删除了。(队列)
- write的内容超过内核开辟(pipe)的管道大小,会产生写阻塞
(2)无名管道实现进程间通信
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
int main()
{
int fd[2];
int ret;
pdi_t pid;
char process_inter=0;
char writebuf[]="hello linux";
char readbuf [128]={0};
ret=pipe(fd);//pipe在内核创建一个管道文件 没有名称
if(ret <0)
{
printf("creat pipe failure\n");
return -1;
}
printf("creat pipe sucess fd[0]=%d,fd[1]=dn",fd[0],fd[1]);
pid =fork();
if(pid == 0)
{
int i=0;
read(fd[0],&process_inter,1);//子进程等待父进程 if pipe empty sleep
while(process_inter==0);
for(i=0;1<5;1++)
{
printf("this is child process i=o",i);
usleep(100);
}
}
if(pid >0)//parent process code first
{
int i=0;
for(i=0;i<5;i++)
{
printf("this is child process i=o",i);
usleep(100);
}
process_inter=1;
write(fd[1],&process_inter,1);//父进程写入管道
}
while(1);
return 0;
}
无名管道的缺点:只能实现父子关系之间、有亲缘关系的进程之间通信,无法实现非父子进程之间的通信。
正由于这无名管道的缺点,对无名管道进行改进:有名管道。
2. 有名管道
(1)基本概念
通道通行过程中,需要保证两个进程对同一个管道进行操作,在无名管道过程中,是通过pipe()放在fork()之前保证两个对同一个管道通信。
有名:所谓的有名,即文件系统中存在这个一样文件节点,每一个文件节点都有一个inode号,文件节点类型是管道类型。
//
有名管道:文件系统中存在一个文件节点。这个文件节点就是管道文件。
1.创建这个文件节点,不可以通过open函数,open函数只能创建普通文件,不能创建特殊文件(管道-mkdifo,套接字-socket,字符设备文件-mknod,块设备文件-nknod,符号链接文件-ln-s,目录文件mkdir)
2.管道文件只有inode号,不占磁盘块空间,和套接字、字符设备文件、块设备文件一样。普通文件和符号链接文件及目录文件,不仅有inode号,还占磁盘块空间
//
(2)有名管道实现进程通信
**如何创建管道文件mkfifo?**
函数形式:int mkfifo(const char*filename,mode t mode):
功能:创建管道文件
参数:管道文件文件名,mode权限,创建的文件权限仍然和umask有关系。
返回值:创建成功返回0,创建失败返回-1.
用户层调用mkfifo(mkfifo是一个系统调用函数),mkfifo进入内核层,然后在用户层创建文件节点(管道文件)
例1:mkfifo的用法。
//以下只在用户空间生成了一个myfifo文件名 没有在内核空间生成管道
//什么时候在内核空间生成管道?:当使用open函数打开myfifo文件节点时,会在内核//空间生成管道
#include "stdio.h"
#include "unistd.h"
#include "stdlib.h"
int main()
{
int ret;
ret=mkfifo("./myfifo",0777);// 创建成功返回0,失败返回-1
if(ret <0)
{
printf("creat myfifo failed \n");
return -1;
}
printf("creat myfifo sucess \n");
return 0;
}
例子:通过管道实现无亲缘关系进程间通信。
第一个进程:
//first.c
#include "stdio.h"
#include "unistd.h"
#include "stdlib.h"
#include "fcntl.h"
int main()
{
int fd;
char process_inter=0;
//myfifo是mkfifo提前创建好的管道文件
fd=open("./myfifo",o_WRONLY);//打开管道文件即就在内核空间创建了一个管道文件
if(fd<0)
{
printf("open myfifo failed\n");
return -1;
}
printf("open myfifo success\n");
int i;
for( i=0;i<5;i++)
{
printf("this is first process i=%d\n",i);
usleep(100);
}
process_inter=1;
write(fd,&process_inter,1);//向有名管道写入process_inter
sleep(5);
while(1);
return 0;
}
第2个进程:
//second.c
#include "stdio.h"
#include "unistd.h"
#include "stdlib.h"
#include "fcntl.h"
int main()
{
int fd;
int i;
char peocess_inter=0;
fd=open("./myfifo",O_RDONLY);//只读方式打开管道
if(fd<0)
{
printf("open myfifo failed\n");
return -1;
}
printf("open myfifo success\n");
read(fd,&peocess_inter,1);
while(peocess_inter == 0);
for( i=0;i<5;i++)
{
printf("this is first process i=%d\n",i);
usleep(100);
}
while(1);
return 0;
}
注释:分别编译两个.c文件生成first
和second
,先运行第一个进程first
,就会在内核空间创建管道文件,但是first
里面只有对myfifo
只写的权限,读端不存在,所以要等待读端打开,即second
运行first
才会输出。验证了第1个进程进行完,第2个进程才会进行。
备注:到此学习到的文件类型:普通文件、目录文件、链接文件(软链接)、管道文件
LInux系统下一共分为 7种文件类型:
普通文件 open
目录文件 mkdir
字符设备文件和块文件
符号链接文件 ln -s
套接字文件
管道文件 mkfifo