进程间通信为什么有那么多不同的方法?
资源的不同,所以通信的方式不同。想要获取管道资源,就需要用管道来通信,想要获取消息队列资源,就需要用消息队列来通信。
如上所示,一个进程就是一个PCB,PCB中的file_struct有三个默认文件描述符(0,1,2),下面就是file文件描述符,匿名管道中父子进程共享代码,所以,父子进程指向同一个文件描述符,我们把这种文件的通信方式称之为管道
管道的特点:
管道通信方式依赖的资源由文件系统资源提供
管道通信为单向传输。即只能一端读,一端写。
管道在通信的时候是基于字节流的。也是基于文件的。
管道的生命周期随进程。
管道是
自带同步机制的。//因为两个进程共享一份资源,需要保证互斥。
进程在访问临界资源时除了保证互斥,还要保证访问临界资源的原子性。
匿名管道
实现具有血缘关系进程间的通信。
C代码实现匿名管道通信。
#include<stdio.h>
#include<unistd.h>
#include<string.h>
int main(){
int fd[2]={0};
if(pipe(fd)<0){
perror("pipe");
return 0;
}
pid_t id=fork();//创建匿名管道
if(id<0){
perror("fork");
return 0;
}
if(id==0){
//child write
char* msg="你好";//子进程负责往管道写,需要关闭读文件描述符
int i=0;
for(i=0;i<5;i++){
write(fd[1],msg,strlen(msg));
sleep(1);
}
close(fd[0]);
}else{
//father read
close(fd[1]);//父进程负责读,需要关闭写文件描述符
char buf[1024];
while(1){
ssize_t s=read(fd[0],buf,sizeof(buf)-1);
if(s>0){
buf[s]=0;
printf("fathet get:%s\n",buf);
}else if(s==0){
printf("再见\n");
break;
}else{
perror("read");
break;
}
}
}
}
结果:
命名管道
命名管道满足不同血缘关系下进程间的通信
可以用命令生成命名管道:mkfifo filename
可以调用函数:
int mkfifo(const char *filename,mode_t mode);
用函数实现server和client进程之间通信
server.c
服务器负责读客户端发来的消息
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<unistd.h>
int main(){
if(mkfifo("./fifo",0644)<0) {//创建命名管道
perror("mkfifo");
return 0;
}
int fd=open("./fifo",O_RDONLY);
if(fd<0) {
perror("open");
return 1;
}
char buf[1024]={0};
while(1){
ssize_t s=read(fd,buf,sizeof(buf)-1);//读取客户端往管道写入的内容
if(s>0){
buf[s-1]=0;
printf("client# %s",buf);
}else if(s==0){
printf("client quit!\n");
break;
}else{
perror("write");
break;
}
}
close(fd);
return 0;
}
client.c
客户端负责往管道写内容,所以客户端代码不需要创建管道。
fcntl.h>
#include<unistd.h>
int main(){
int fd=open("./fifo",O_WRONLY);
if(fd<0) {
perror("open");
return 1;
}
char buf[1024]={0};
while(1){
printf("Please Enter:");
fgets(buf,sizeof(buf),stdin);//接收标准输入的内容
write(fd,buf,sizeof(buf)-1);//往管道写内容
}
close(fd);
return 0;
}
结果: