前言
这次对进程间通信进行总结。^ ~ ^
上一篇文章以及介绍完了进程的创建,进程的性质,进程的5种状态,改变进程中程序的办法(exec族函数),对进程回收的方法(wait函数)
但是我们可以发现,在进程与进程间的交流,也就是通信还没有了解。这篇文章,将详细的讲述进程间通信的各种方式。
进程间通信相关概念
- 什么是IPC
IPC就是进程间通信的英文缩写(InterProcess Communication) - 进程间通信常用的4种方式
(1) 管道-简单
(2) 信号-系统开销小
(3)共享映射区-(有无血缘关系的进程间通信都可以)
(4)本地套接字-稳定
管道
管道的概念
本质:
内核缓冲区
伪文件-不占磁盘空间
特点:
两部分:
读端,写端,对应两个文件描述符
数据写端流入,读端流出
操作管道的进程被销毁之后,管道自动被释放
管道默认是阻塞的。
管道的原理
内部实现方式:队列
环形队列
特点:先进先出
缓冲区大小:
默认4k
大小会根据实际情况做适当调整
管道的局限性
队列:
数据只能读取一次,不能重复读取
半双工:
数据的流向是单向的
匿名管道:
适用于有血缘关系的进程
创建匿名管道
int pipe(int fd[2]);
fd-传出元素
fd[0]-读端
fd[1]-写端
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
int main(int argc, const char* argv[])
{
int fd[2];
int ret = pipe(fd);
if(ret == -1)
{
perror("pipe error");
exit(1);
}
printf("fd[0] = %d\n", fd[0]);
printf("fd[1] = %d\n", fd[1]);
return 0;
}
再看一个例子:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/wait.h>
int main(int argc, const char* argv[])
{
int fd[2];
int ret = pipe(fd);
if(ret == -1)
{
perror("pipe error");
exit(1);
}
printf("fd[0] = %d\n", fd[0]);
printf("fd[1] = %d\n", fd[1]);
pid_t pid = fork();
if(pid == -1)
{
perror("fork error");
exit(1);
}
// 父进程 写,关闭读操作
if(pid > 0)
{
sleep(1);
close(fd[0]);
char* p = "hell, world\n";
write(fd[1], p, strlen(p)+1);
close(fd[1]);
wait(NULL);
}
else if(pid == 0)
{
close(fd[1]);
char buf[1024];
read(fd[0], buf, sizeof(buf));
printf("buf = %s\n", buf);
close(fd[0]);
}
return 0;
}
fifo(有名管道)
特点
有名管道
在磁盘上有这样一个文件ls -l -> p
伪文件,在磁盘的大小永远为0
在内核中有一个对应的缓冲区
半双工的通信方式
使用场景
没有血缘关系的进程间通信
创建方式
命令:mkfifo管道名
直接看代码例子:
wirte_fifo
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
int main(int argc, const char* argv[])
{
if(argc < 2)
{
printf("./a.out fifoname\n");
exit(1);
}
// 判断文件是否存在
int ret = access(argv[1], F_OK);
if(ret == -1)
{
int r = mkfifo(argv[1], 0664);
if(r == -1)
{
perror("mkfifo error");
exit(1);
}
printf("有名管道%s创建成功\n", argv[1]);
}
int fd = open(argv[1], O_WRONLY);
if(fd == -1)
{
perror("open error");
exit(1);
}
char *p = "hello, world";
while(1)
{
sleep(1);
int len = write(fd, p, strlen(p)+1);
}
close(fd);
return 0;
}
read_fifo
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
int main(int argc, const char* argv[])
{
if(argc < 2)
{
printf("./a.out fifoname\n");
exit(1);
}
// 判断文件是否存在
int ret = access(argv[1], F_OK);
if(ret == -1)
{
int r = mkfifo(argv[1], 0664);
if(r == -1)
{
perror("mkfifo error");
exit(1);
}
printf("有名管道%s创建成功\n", argv[1]);
}
int fd = open(argv[1], O_RDONLY);
if(fd == -1)
{
perror("open error");
exit(1);
}
char buf[512];
while(1)
{
int len = read(fd, buf, sizeof(buf));
buf[len] = 0;
printf("buf = %s\n, len = %d", buf, len);
}
close(fd);
return 0;
}
内存映射区
函数原型:
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/mman.h>
#include <fcntl.h>
int main(int argc, const char* argv[])
{
int fd = open("english.txt", O_RDWR);
if(fd == -1)
{
perror("open error");
exit(1);
}
// get file length
// len > 0
int len = lseek(fd, 0, SEEK_END);
void * ptr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if(ptr == MAP_FAILED)
{
perror("mmap error");
exit(1);
}
close(fd);
char buf[4096];
// 从内存中读数据
printf("buf = %s\n", (char*)ptr);
//释放内存映射区
int ret = munmap(ptr, len);
if(ret == -1)
{
perror("munmap error");
exit(1);
}
return 0;
}