IPC 进程间通信 inter process communicate
1、古老的通信方式
无名管道 :只能给有亲缘关系进程通信(pipe)
有名管道 :可以给任意单机进程通信(fifo)
管道的特性:
1、管道是 半双工的工作模式
2、所有的管道都是特殊的文件不支持定位操作。
lseek->> fd fseek ->>FILE*
3、管道是特殊文件,读写使用文件IO。
open,read,write,close;;
单工 同一时间只能一端读或者写
半双工 同一时间内,一端读(写),l另一端只能写(读)
全双工 同一时间内双方可以既读又写
2、IPC对象通信 system v
共享内存*
消息队列
信号量集
3、socket通信
网络通信
1.无名管道
1.使用框架:
创建管道 == 》 读写管道 ==》关闭管道
流程:
创建并打开管道: pipe函数
#include <unistd.h>
int pipefd[2] = {0};
int pipe(int *pipefd);
功能:创建并打开一个无名管道
参数:pipefd[0] ==>无名管道的固定读端
pipefd[1] ==>无名管道的固定写端
返回值:成功 0
失败 -1;
注意事项:
1、无名管道的读写:===》文件IO的读写方式。
pipefd 等价于fd
读: read()
写: write()
关闭管道: close();
2、无名管道的打开应该在fork之前进行。
3.不用重复去关闭管道(可能会把两个端口的读写都关了,导致堵塞破裂等),所以不要把关闭放在循环里。
4.管道类似队列,读了就没了。
2.管道的特殊情概况
1.写阻塞:
管道中至少有一个读端:
写:
管道中缓存区没有存满则直接写入
管道中缓存区写满则阻塞等待直到有数据读出才能继续写入
2.读阻塞:
管道中至少有一个写端:
读:
有数据时直接读出
没有数据时阻塞等待直到有数据写入才能读出
3.管道破裂:
管道中没有读端,写入数据:
写:
会产生管道破裂信号SIGPIPE,进程异常结束
4.读到0返回:
管道中没有写端:
读:
有数据时直接读出
没有数据时不会阻塞等待直接返回,读到0
3.问题
1、父子进程是否都有fd[0] fd[1],
如果在单一进程中写fd[1]能否直接从fd[0]中读到。
可以,写fd[1]可以从fd[0]读
2、管道的数据存储方式是什么样的
数据是否一直保留?
队列形式存储 读数据会剪切取走数据不会保留
先进先出
3、管道的数据容量是多少,有没有上限值。
操作系统的建议值: 512* 8 = 4k
代码测试实际值: 65536byte= 64k
4、管道的同步效果如何验证?读写同步验证。
读端关闭能不能写? 不可以 ===>SIGPIPE 异常终止
写端关闭能不能读? 可以,取决于pipe有没有内容,===>read返回值为0 不阻塞
结论:读写端必须同时存在,才能进行
管道的读写。
5、固定的读写端是否就不能互换?
能否写fd[0] 能否读fd[1]? 不可以,是固定读写端。
4.示例 copy 一张图片(文件)
#include <stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/wait.h>
#include<string.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc ,char *argv[])
{
int pipefd[2] = {0};
int ret = pipe(pipefd);
if(ret < 0)
{
perror("fail pipe create\n");
return -1;
}
pid_t pid = fork();
char buff[1024] = {0};
if(pid > 0)
{
close(pipefd[0]);
int fd1 = open (argv[1],O_RDWR );
size_t ret = read(fd1,buff,sizeof(buff));
while(ret != 0)
{
write(pipefd[1],buff,ret);
ret = read(fd1,buff,sizeof(buff));
}
close(fd1);
close(pipefd[1]);
wait(NULL);
}
else if(pid == 0)
{
close(pipefd[1]);
size_t ret= read(pipefd[1],buff,sizeof(buff));
int fd2 = open(argv[2],O_RDWR | O_CREAT,0664);
while(0 != ret)
{
write(fd2,buff,ret);
ret= read(pipefd[0],buff,sizeof(buff));
}
close(fd2);
}
else
{
perror("fail fork");
}
close(pipefd[0]);
close(pipefd[1]);
return 0;
}
2.有名管道 (FIFO)
1.框架
创建有名管道 ==》打开有名管道 》读写管道》关闭管道 ==》卸载有名管道
1、创建:mkfifo
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
功能:在指定的pathname路径+名称下创建一个权限为
mode的有名管道文件。
参数:pathname要创建的有名管道路径+名称
mode 8进制文件权限(一般给0664)。
返回值:成功 0
失败 -1;
2、打开有名管道 open
以O_RDONLR打开时 ,管道在open处阻塞
以O_WRONLY打开时 ,管道在open处阻塞
当两端同时打开时,才解除阻塞。
3、管道的读写: 文件IO
读: read(fd-read,buff,sizeof(buff));
写: write(fd-write,buff,sizeof(buff));
4、关闭管道:
close(fd);
5、卸载管道:remove();
int remove(const char *pathname);
功能:将指定的pathname管道文件卸载,同时
从文件系统中删除。
参数: ptahtname 要卸载的有名管道
返回值:成功 0
失败 -1;
示例:将一个图片通过有名管道发送到另一个进程
**读进程**
#include <stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/wait.h>
#include<string.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
int main(int argc ,char *argv[])
{
int ret = mkfifo("./myfifo3",0664);
if (ret < 0 && errno != EEXIST)
{
perror("fail mkfifo");
return -1;
}
int fd1 = open (argv[1],O_RDWR );
if(fd1<0)
{
perror("fail open");
}
int fd = open("./myfifo3", O_WRONLY);
if (fd < 0)
{
perror("fail open fifo");
return -1;
}
char buff[1024] = {0};
size_t ret1= read(fd1,buff,sizeof(buff));
while(ret1 != 0)
{
write(fd,buff,ret1);
ret1= read(fd1,buff,sizeof(buff));
}
remove("./myfifo3");
close(fd1);
return 0;
}
**写进程**
#include <stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/wait.h>
#include<string.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
int main(int argc ,char *argv[])
{
int ret = mkfifo("./myfifo3",0664);
if (ret < 0 && errno != EEXIST)
{
perror("fail mkfifo");
return -1;
}
int fd1 = open (argv[1],O_RDWR );
if(fd1<0)
{
perror("fail open");
}
int fd = open("./myfifo3", O_WRONLY);
if (fd < 0)
{
perror("fail open fifo");
return -1;
}
char buff[1024] = {0};
size_t ret1= read(fd1,buff,sizeof(buff));
while(ret1 != 0)
{
write(fd,buff,ret1);
ret1= read(fd1,buff,sizeof(buff));
}
remove("./myfifo3");
close(fd1);
return 0;
}
示例:两个终端聊天一人任意句
搞两个不同的管道 一个进程读写 一个进程写读
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include<string.h>
int main(int argc, const char *argv[])
{
pid_t pid = fork();
if(pid> 0)
{
int ret = mkfifo("./myfifo", 0664);
if (ret < 0 && errno != EEXIST)
{
perror("fail mkfifo");
return -1;
}
int fd = open("./myfifo", O_WRONLY);
if (fd < 0)
{
perror("fail open fifo");
return -1;
}
char buff[1024]= {0};
while(1)
{
read(0,buff,sizeof(buff));
write(fd, buff, sizeof(buff));
}
close(fd);
}
else if(pid == 0)
{
int ret1 = mkfifo("./myfifo1", 0664);
if (ret1 < 0 && errno != EEXIST)
{
perror("fail mkfifo");
return -1;
}
int fd1 = open("./myfifo1", O_RDONLY);
if (fd1 < 0)
{
perror("fail open fifo");
return -1;
}
char buff1[1024]= {0};
while(1)
{
ssize_t size = read(fd1, buff1, sizeof(buff1));
printf("size= %ld, buff1 = %s\n", strlen(buff1), buff1);
}
close(fd1);
}
else
{
perror("fail fork");
return -1;
}
return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main(int argc, const char *argv[])
{
pid_t pid = fork();
if(pid > 0)
{
int ret = mkfifo("./myfifo", 0664);
if (ret < 0 && errno != EEXIST)
{
perror("fail mkfifo");
return -1;
}
int fd = open("./myfifo", O_RDONLY);
if (fd < 0)
{
perror("fail open fifo");
return -1;
}
char buff[1024]= {0};
while(1)
{
ssize_t size = read(fd, buff, sizeof(buff));
printf("size= %ld, buff = %s\n", strlen(buff), buff);
}
close(fd);
}
else if(pid == 0)
{
int ret1 = mkfifo("./myfifo1", 0664);
if (ret1 < 0 && errno != EEXIST)
{
perror("fail mkfifo");
return -1;
}
int fd1 = open("./myfifo1", O_WRONLY);
if (fd1 < 0)
{
perror("fail open fifo");
return -1;
}
char buff1[1024] = {0};
while(1)
{
read(0,buff1,sizeof(buff1));
write(fd1, buff1, sizeof(buff1));
}
close(fd1);
}
else
{
perror("fail fork");
return -1;
}
remove("./myfifo1");
remove("./myfifo");
return 0;
}