管道
管道是UNIX系统IPC的最古老形式,并且所有UNIX系统都提供此种通信机制,我们把一个进程连接到另一个进程的数据流称为管道,在管道中一个进程写,一个进程读。
管道的本质也就是操作系统内核提供的一段内存。
管道在创建时获得固定字节数的大小,当一个进程往里面写时,如果有空间,写请求立即执行,否则该进程被阻塞。类似的,如果一个进程试图读取多于当前管道的字节数时,也会阻塞。
管道有两类:匿名管道和命名管道
匿名管道
匿名管道的操作必须是有两个亲缘关系的进程
管道是调用pipe函数创建的:
int pipe(int fd[2])
fd是一个文件描述符数组,fd[0]表示读端,fd[1]表示写端
成功返回0,失败返回小于0的错误代码
下面一段匿名管道创建的代码:
#include<stdio.h>
#include<string.h>
#include<unistd.h>
void Test1()
{
int fd[2];
int ret = pipe(fd);
if(ret < 0)
{
perror("pipe");
return;
}
//通过fork创建子进程,父进程从管道一端写,子进程从管道读
int pid = fork();
const char* str = "hello\n";
if(pid > 0)
{
//father write
close(fd[0]);//父进程只写,所以关闭读端的文件描述符
write(fd[1],str,strlen(str));
close(fd[1]);
printf("father:%d\n",getpid());
}
else if(pid == 0)
{
//child read
close(fd[1]);//子进程只读,所以关闭写端的文件描述符
char buf[1024] = {0};
read(fd[0],buf,sizeof(buf)-1);//-1操作是为了给'\0'预留位置
close(fd[0]);
printf("child:%d ,%s\n",getpid(),buf);
}
else
perror("fork");
sleep(1);
}
用frok共享管道原理:
注意:当数据被读走后,数据就会被管道释放,以便继续接收更多的数据。
管道结构
在底层实现总,管道的实现是两个进程的f_inode指向同一个临时的inode节点,这个节点指向一个物理页面
因此,对管道的操作也就是对文件的操作,通过不同的文件描述符来标识不同的文件
命名管道
命名管道不需要进程之间有任何关系便能完成通信。
创建一个命名管道:
mkfifo filename
也可以通过函数创建
int mkfifo(const char*filename,mode_t mode);
代码实现:
client.c
#include<stdlib.h>
#include<string.h>
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>
int main()
{
int ret = open("./mypipe",O_WRONLY);
if(ret < 0)
{
perror("open");
exit(1);
}
char buf[1024] = {0};
while(1)
{
printf(">:");
fflush(stdout);
ssize_t read_size = read(0,buf,sizeof(buf)-1);
if(read_size > 0)
{
buf[read_size] = '\0';
write(ret,buf,strlen(buf));
}
else
{
perror("read");
continue;
}
}
return 0;
}
server.c
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<stdio.h>
int main()
{
int ret = open("./mypipe",O_RDONLY);
if(ret < 0)
{
perror("open");
exit(1);
}
char buf[1024] = {0};
while(1)
{
ssize_t read_size = read(ret,buf,sizeof(buf)-1);
if(read_size < 0)
{
perror("read");
continue;
}
else if(read_size == 0)
{
printf("一个客户端退出\n");
break;
}
else
{
buf[read_size] = '\0';
printf("[client]:");
fflush(stdout);
write(1,buf,strlen(buf));
}
}
close(ret);
return 0;
}
mkfifo创建一个命名管道:
用命名管道实现服务器/客户端之间的通信,满足:客户端从标准输入读取信息,通过命名管道发送给服务器端,服务器端将其输出到屏幕上。
管道的特点
- 单向通信
- 面向字节流
- 缓冲区大小有限制
- 生命周期根随进程
- 管道有容量限制
- 管道内部具有同步机制(同时只能由一个进程读写)