1.进程间通信的概念
2.匿名管道
匿名管道的5个特点
管道是一个单向通信的通信信道;
匿名管道作用与具有血缘关系的进程,常用于父子进程;
管道是一个文件,生命周期随进程;
管道自带同步机制、原子性;
管道是面向字节流;
管道的4种结果
读端快且写端慢或者不写,读端要等待写端;
写端快且读端慢或者不读,写端要等待读端;
读端关闭,写端收到SIGPIPE信号直接终止
写端关闭,读端读完pipe内部的数据,然后再读会返回0,表明已经读到文件结尾;
pipe函数
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
int pipefd[2]={0};//创建匿名管道
if(pipe(pipefd)!=0)
{
perror("pipe error!\n");
exit(1);
}
if(fork()==0)//创建子进程
{
close(pipefd[0]);//子进程关闭读
const char* message="hello world\n";
while(1)
{
write(pipefd[1],message,strlen(message));
sleep(1);
}
exit(1);
}
close(pipefd[1]);//父进程关闭写
while(1)
{
char* buffer[64];
ssize_t s=read(pipefd[0],buffer,sizeof(buffer)-1);
if(s==0)
{
printf("子进程关闭写入\n");
break;
}
else if(s>0)
{
buffer[s]=0;
printf("child say to father:%s",buffer);
}
else//s==0说明写端关闭,且读端把内容读完
{
printf("读取失败\n");
break;
}
}
return 0;
}
执行结果
原理
3.命名管道
命名管道的情况和特点和匿名管道相同,但是命名可以运用到任意进程
mkfifo函数
server.c
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#define MYFIFO "./myfifo"
int main()
{
umask(0);//把umask设置0
if(mkfifo(MYFIFO,0666)<0)
{
perror("mkfifo fail\n");
exit(1);
}
int fd = open("myfifo",O_RDONLY);//服务器端以读方式打开文件
if(fd<0)
{
perror("open fail\n");
exit(2);
}
while(1)
{
char buffer[64];
ssize_t s=read(fd,buffer,63);//read返回值读取的字节数
if(s>0)
{
buffer[s]=0;
if(strcmp(buffer,"ls")==0)//输入ls就进程替换ls -l命令
{
execlp("ls","ls","-l",NULL);
return(1);
}
else
printf("client say to server:%s\n",buffer);
}
else if(s==0)
{
printf("client close");
break;
}
else
{
printf("read fail");
break;
}
}
return 0;
}
client.c
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<fcntl.h>
#define MYFIFO "./myfifo"
int main()
{
int fd=open(MYFIFO,O_WRONLY);//用户端以写的方式打开管道文件
if(fd<0)
{
perror("open\n");
return 1;
}
while(1)
{
char message[64];
printf("please enter:");
fflush(stdout);//刷新标准输出流
ssize_t s=read(0,message,63);//从键盘读
message[s]=0;
write(fd,message,strlen(message)-1);//写到管道文件的缓冲区
}
return 0;
}
进程使用管道通信,不会把内容写到磁盘中去,会在缓冲区中读写;
4.共享内存
共享内存的效率是最高的因为它是使用直接虚拟地址映射来写入和读写数据的,不像管道需要使用read和write来读写
4.1共享内存的创建
shmget和ftok接口
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<stdio.h>
#define PATHNAME "./"
#define PROJID 0x5555
int main()
{
key_t key=ftok(PATHNAME,PROJID);//获取一个唯一key值
int shmid=shmget(key,4096,IPC_CREAT);//创建共享内存
printf("key:%d,shmid:%d\n",key,shmid);
return 0;
}
创建共享内存,程序执行完毕不会被OS回收
4.2删除共享内存
命令行:
代码:
shmctl(shmid,IPC_RMID,NULL);//删除共享内存
4.3进程连接到共享内存(shmat),去关联(shmdt)
4.4简单使用共享内存通信
clien.c
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<stdio.h>
#include<unistd.h>
#define PATHNAME "./"
#define PROJID 0x5555
int main()
{
key_t key=ftok(PATHNAME,PROJID);
int shmid=shmget(key,4096,IPC_CREAT|0666);
char* mem=shmat(shmid,NULL,0);//关联
printf("client process attaches success\n");
while(1)//打印共享内存的内容
{
printf("%s\n",mem);
sleep(1);
}
shmdt(mem);//去关联
return 0;
}
server.c
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<stdio.h>
#include<unistd.h>
#define PATHNAME "./"
#define PROJID 0x5555
int main()
{
key_t key=ftok(PATHNAME,PROJID);
int shmid=shmget(key,4096,IPC_CREAT|0666);//创建一个共享内存并初始化权限为0666;
if(shmid<0)
{
perror("shmget\n");
return 1;
}
char* mem=(char*)shmat(shmid,NULL,0);//关联
printf("server process attaches success\n");
char i='a';
while(i<='z')//往共享内存写入数据
{
mem[i-'a']=i;
i++;
mem[i-'a']=0;
sleep(2);
}
shmdt(mem);//去关联
printf("server process detach success\n");
shmctl(shmid,IPC_RMID,NULL);//关闭共享内存
printf("shared memory close\n");
return 0;
}
执行结果: