管道的基本概述及其特性
管道是Unix中最古老的进程间通信的形式。 我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”
特性:半双工通信---可以选择方向的单向通信。
本质:在内核中开辟的一段缓冲区(内核空间中的一块内存)
原理:多个进程通过访问同一块内核中的缓冲区实现通信
分类:
匿名管道:缓冲区没有标识符,只能用于具有亲缘关系的进程间通信
命名管道:缓冲区具有标识符(在文件系统中会显示一个管道文件),可用于同一主机上任意的进程间通信。
匿名管道
管道的创建
函数原型
#include <unistd.h>
int pipe(int pipefd[2]);
pipefd[0]:用于读
pipefd[1]:用于写
返回值:成功返回0 失败返回-1;。
父子进程间实现匿名管道通信
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
int main()
{
int pipefd[2];
int ret = pipe(pipefd);
if (ret < 0)
{
perror("pipe");
return -1;
}
ret = fork();
if (ret < 0)
{
perror("fork");
return -1;
}
else if (ret == 0)
{
//child read
close(pipefd[1]);
char buf[1024] = {0};
ret = read(pipefd[0], buf, 1023);
if (ret < 0)
{
perror("read");
return -1;
}
printf("child read:%s\n", buf);
close(pipefd[0]);
}
//父进程 write
close(pipefd[0]);
printf("father write:");
char buf[128] = {0};
fgets(buf, 127, stdin);
write(pipefd[1], buf, strlen(buf));
close(pipefd[1]);
wait(NULL);
return 0;
}
命名管道
命名管道的标识符就是一个可见于文件系统的管道类型文件。 多个进程通过打开同一个管道文件,访问同一块内核中的缓冲区实现通信,当mkfifo一个管道文件后 内核不会立刻开辟内存 而是等到进行读写操作的时候会进行内存开辟。
命令操作:mkfifo filename 创建一个命名管道文件
函数操作:int mkfifo(const char *pathname, mode_t mode);
mode 创建权限
返回值 成功返回0 失败返回-1;
创建一个命名管道进行进程间通信
//write.c
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
int main()
{
int ret = mkfifo("./test.fifo", 0664);
if (ret < 0 && errno != EEXIST)
{
perror("mkfifo");
return -1;
}
int fd = open("./test.fifo", O_WRONLY);
if (fd < 0)
{
perror("open");
return -1;
}
printf("creat fifo!!\n");
char buf[1024] = {0};
while (1)
{
//sprintf(buf,"%s",stdin);
//scanf("%s",buf);
fgets(buf,1023,stdin);
ret = write(fd, buf, strlen(buf));
if(ret<0)
{
perror("write");
return -1;
}
}
close(fd);
return 0;
}
//read.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <memory.h>
int main()
{
int ret = mkfifo("./test.fifo", 0664);
if (ret < 0 && errno != EEXIST)
{
perror("mkfifo");
return -1;
}
int fd = open("./test.fifo", O_RDONLY);
if (fd < 0)
{
perror("open");
return -1;
}
printf("creat fifo!!\n");
char buf[1024] = {0};
while (1)
{
ret = read(fd, buf, 1023);
if (ret < 0)
{
perror("read");
return -1;
}
printf("%s\n", buf);
memset(buf, 0, 1024);
}
close(fd);
return 0;
}
管道的读写特性
匿名管道
特性:只能用于具有亲缘关系的进程间通信 因为缓冲区没有标识符,无法被其它进程找到,只能通过子进程父进程的方式获取到操作句柄实现通信。
读写特性:若管道中没有数据则read会阻塞,若管道中数据满了,则write会阻塞。
所有的管道读端被关闭,则继续write则会触发异常导致进程崩溃退出 哪个进程去写数据哪个进程就会崩溃(原因会给进程发送SIGPIPE信号)。
所有的管道写端被关闭,则继续read则会读完数据后返回0,不再阻塞。
管道提供字节流传输服务:有序的,可靠的,基于连接的流式传输。
命名管道
打开特性:若是以只读方式打开管道文件则会阻塞,直到管道被其它进程以写的方式打开 则会继续,反之亦是如此。
特性:半双工通信 可以选择方向的单向通信
管道提供字节流传输服务 ---- 有序的 可靠的 基于连接的一种流式传输
基于连接:所有read关闭则write异常,所有write关闭则read返回
互斥的体现:对管道进行写入操作大小不超过PIPE_BUF-4096大小,则保证操作的原子性
同步的体现:若管道没有数据则read阻塞 若管道数据满了 则write阻塞。
生命周期随进程---不人为干预,所有打开管道的进程退出了后则管道缓冲区被释放。
同步与互斥
互斥:同一时间进程对临界资源的唯一访问实现访问操作安全
同步:通过一些条件判断让进程对临界资源的访问更加合理有序
临界资源:公共资源,大家都能访问到的资源
临界区:对临界资源访问的这段代码区域