一、无名管道
1.1 无名管道的概述
管道(pipe)又称无名管道。 无名管道是一种特殊类型的文件,在应用层体现为两个打开的文件描述符。
任何一个进程在创建的时候,系统都会 给他分配4G的虚拟内存,分为3G的用户空间和1G 的内核空间,内核空间是所有进程公有的,无名管道就是创建在内核空间的,多个进程知道 同一个无名管道的空间,就可以利用他来进行通信。
无名管道虽然是在内核空间创建的,但是会给当前用户进程两个文件描述符,一个负责执行 读操作,一个负责执行写操作。
管道是最古老的UNIX IPC方式,其特点是:
1、半双工,数据在同一时刻只能在一个方向上流动。
2、数据只能从管道的一端写入,从另一端读出。
3、写入管道中的数据遵循先入先出的规则。
4、管道所传送的数据是无格式的,这要求管道的读出方与写入方必须事先约定好数据的格 式,如多少字节算一个消息等。
5、管道不是普通的文件,不属于某个文件系统,其只存在于内存中。
6、管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。
7、从管道读数据是一次性操作,数据一旦被读走,它就从管道中被抛弃,释放空间以便写 更多的数据。
8、管道没有名字,只能在具有公共祖先的进程之间使用
1.2 无名管道的创建 ---pipe函数
#include <unistd.h>
int pipe(int pipefd[2]);
功能:创建一个有名管道,返回两个文件描述符负责对管道进行读写操作
参数:
pipefd:int型数组的首地址,里面有两个元素
pipefd[0] 负责对管道执行读操作
pipefd[1] 负责对管道执行写操作
返回值: 成功:0
失败:‐1
基础案例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main()
{
// 使用pipe创建一个无名管道
int fd_pipe[2];
if (pipe(fd_pipe) == -1)
{
perror("fail to pipe");
exit(1);
}
printf("fd_pipe[0]=%d\n",fd_pipe[0]);
printf("fd_pipe[1]=%d\n",fd_pipe[1]);
//描述符就可以操作无名管道,所以通过文件IO中的read和write函数对无名管道进行操作
//通过write函数向无名管道中写入数据
//fd_pipe[1]负责执行写操作
if(write(fd_pipe[1],"hello world",11) == -1){
perror("fail to write");
exit(1);
}
printf("测试一下sizeof(hello) =%ld\n",sizeof("hello"));
//写完之后,再写
//如果管道中有数据,再次写入的数据会放在之前数据的后面,不会把之前的数据替换(注意,前面的数据并没有字符串结束符\0)
write(fd_pipe[1],"write agin",sizeof("write agin")); //11,strlen 不会将\0记入长度
char buf[32]="kkk";
ssize_t bytes;
//fd_pipe[0]负责执行读操作
if(bytes = read(fd_pipe[0],buf,20)==-1){
perror("fail to read");
exit(1);
}
printf("buf = [%s]\n", buf);
printf("bytes = %ld\n", bytes);
//读完后再读,如果管道中没有数据了 发生阻塞
read(fd_pipe[0],buf,sizeof(buf));
printf("buf = [%s]\n", buf);
printf("bytes = %ld\n", bytes);
read(fd_pipe[0],buf,sizeof(buf));
printf("buf = [%s]\n", buf);
printf("bytes = %ld\n", bytes);
}
补充案例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(){
int pip_fd[2];
pipe(pip_fd);
// write(pip_fd[1],"hello world\0",12); //如果使用结束符,发现就没的数据不能连续的写入,
//只能读到 hello world
//write(pip_fd[1],"hello world",12); 效果同上,多出的一位,自动补全了结束符
write(pip_fd[1],"hello world",11);//如果前面写的字符串没有结束符,才会在管道中继续写
write(pip_fd[1],"write agin!",12);
char buf[32]="";
read(pip_fd[0],buf,32);
printf("buf =[%s]\n",buf);
return 1;
}
1.3 无名管道实现进程间通信
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char const *argv[])
{
int fd_set[2];
int ret;
ret = pipe(fd_set);
if (ret == -1)
{
perror("fail to create pipe");
exit(1);
}
int pid = fork();
if (pid > 0)
{ // 父进程代码 :从标准输入IO获取字符串,写入管道
char buf[128] = "";
while (1)
{
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf) - 1] = '\0'; // 封尾
// 写入管道
if (write(fd_set[1], buf, sizeof(buf)) == -1)
{
perror("fail to write");
exit(1);
}
}
}
else if (pid == 0)
{
// 子进程代码:去管道里读
char buf[128] = "";
while (1)
{
if (read(fd_set[0], buf, sizeof(buf)) == -1)
{
perror("fail to read");
exit(1);
}
printf("buf=[%s]\n", buf);
}
}
else if (pid == -1)
{
perror("创建进程失败");
exit(1);
}
return 0;
}
注意:
利用无名管道实现进程间的通信,都是父进程创建无名管道,然后再创建子进程,子进 程继承父进程的无名管道的文件描述符,然后父子进程通过读写无名管道实现通信
1.4 无名管道的读写规律
1.4.1 读写端都存在,只读不写
读写端都存在,只读不写,
如果管道中有数据,会正常读取数据
如果管道中没有数据,则读操作会阻塞等待,直到有数据为止
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(void)
{
int pip_fd[2];
if(pipe(pip_fd)==-1){
perror("fail to pipe");
exit(-1);
}
//读写端都存在,只读不写,
//如果管道中有数据,会正常读取数据
//如果管道中没有数据,则读操作会阻塞等待,直到有数据为止
write(pip_fd[1],"hello_world",11);
char buf[128]="";
if(read(pip_fd[0],buf,sizeof(buf))==-1){
perror("fail to read");
exit(-1);
}
printf("buf = %s\n", buf);
printf("before fork pid %d\n",getpid());
int pid;
pid = fork();
if(pid<0){
perror("fail to fork");
exit(-1);
}
if(pid>0){
//父进程
printf("father pid %d\n",getpid());
//父进程继续读
strcpy(buf,""); //清空buf
if(read(pip_fd[0],buf,sizeof(buf))==-1){
perror("fail to read");
exit(-1);
}
printf("buf = %s\n", buf);
}else{
printf("son pid %d ,子进程5秒后写入\n",getpid());
sleep(5);
//测试让子进程写入
write(pip_fd[1],"I am son\0",9);
printf("son pid %d\n",getpid());
}
return 0;
}
1.4.2 读写端都存在,只写不读
如果一直执行写操作,则无名管道对应的缓冲区会被写满,写满之后,write函数也 会阻塞等待,
管道缓冲默认为64k;
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
//读写段都存在,只写不读
int main(){
int pipe_fd[2];
pipe(pipe_fd);
int count=0;
for(;;){
write(pipe_fd[1],"6666",1024);
count++;
printf("count = %d\n",count);
}
return 1;
}
1.4.3 只有读端,没有写端
关闭写文件描述符,只有读端
//如果原本管道中有数据,则读操作正常读取数据
//如果管道中没有数据,则read函数会返回0
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> //只有读端 int main(){ int pipe_fd[2]; pipe(pipe_fd); //管道中原本有数据 write(pipe_fd[1],"hello world",12); //关闭写端 close(pipe_fd[1]); ssize_t bytes; char buf[32]=""; bytes =read(pipe_fd[0],buf,32); printf("the bytes is %d\n",bytes); printf("buf=[%s]\n",buf); strcpy(buf,""); bytes =read(pipe_fd[0],buf,32); printf("the bytes is %d\n",bytes); printf("buf=[%s]\n",buf); return 1; }
1.4.4 只有写端,没有读端
管道会破裂
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
void handler(int sig)
{
printf("SIGPIPE信号产生了,管道破裂\n");
}
int main(int argc, char const *argv[])
{
signal(SIGPIPE, handler);
int pipe_fd[2];
if (pipe(pipe_fd) == -1)
{
perror("fail to pipe");
exit(1);
}
// 关闭读文件描述符
close(pipe_fd[0]);
// 发出破裂信号
write(pipe_fd[1], "6666", 10);
return 0;
}
关于信号 :SIGPIPE
1.5 通过fcntl 函数设置文件阻塞特性
fcntl - manipulate file descriptor
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{
int pip_fd[2];
char buf[] = "hello world";
pid_t pid;
if (pipe(pip_fd) == -1)
{
perror("fail to pipe");
exit(-1);
}
pid = fork();
if (pid < 0)
{
perror("fail to fork");
exit(-1);
}
if (pid == 0)
{
// 子进程
while (1)
{
sleep(5);
write(pip_fd[1], buf, sizeof(buf));
}
}
else
{
// 将pip_fd[0] 设置为阻塞
// fcntl(pip_fd[0],F_SETFL,0);
// 非阻塞
fcntl(pip_fd[0], F_SETFL, O_NONBLOCK);
while (1)
{
memset(buf, 0, sizeof(buf)); // memset - fill memory with a constant byte
read(pip_fd[0], buf, sizeof(buf));
printf("buf=[%s]\n", buf);
}
}
return 0;
}
二、文件描述符
2.1 文件描述符的概述
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
#if 1
// 在进程中打开其他文件时,
// 系统会返回文件描述符表中最小可用的文件描述符,
// 并将此文件描述符记录在进程的文件描述符表中。
// 注意:新创建的文件描述符的值不一定是最大的
int fd1, fd2, fd3;
fd1 = open("file.txt", O_RDONLY | O_CREAT, 0664); // 3
fd2 = open("file.txt", O_RDONLY | O_CREAT, 0664); // 4
fd3 = open("file.txt", O_RDONLY | O_CREAT, 0664); // 5
printf("fd=[%d]\n", fd1);
printf("fd=[%d]\n", fd2);
printf("fd=[%d]\n", fd3);
int fd;
close(fd1);
fd = open("file.txt", O_RDONLY | O_CREAT, 0664);
printf("fd=[%d]\n", fd);
#endif
return 0;
}
注意:Linux中一个进程最多只能打开NR_OPEN_DEFAULT(即1024)个文件,故当文件不再使用时应及时调用close函数
//实验2
int count;
while(1){
int fd;
fd = open("file.txt", O_RDONLY | O_CREAT, 0664);
if(fd==-1){
perror("fail to open");
printf("the count is %d\n",count);
exit(1);
}
count++;
}
注:还有 stdin,stdout, stderr,分别是 0,1,2
2.2 文件描述符的复制
1、dup函数
# include <unistd.h>int dup ( int oldfd );功能:复制 oldfd 文件描述符,并分配一个新的文件描述符,新的文件描述符是调用进程文件描述符表中最小可用的文件描述符。参数:要复制的文件描述符 oldfd 。返回值:成功:新文件描述符。失败:返回- 1 ,错误代码存于 errno 中。
案例1:使用dup函数复制文件描述符
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char const *argv[])
{
// 通过dup函数复制一个文件描述符
int fd;
//dup执行后给返回值文件描述符分配的值是文件描述符表中最小可用的文件描述符
fd = dup(1); // 3
printf("fd =%d\n", fd);
//由于通过dup函数将1这个文件描述符复制了一份为fd,所以fd现在就相当于1,
//所以写数据就是想终端写入数据
write(fd, "测试dup函数!!\n", strlen("测试dup函数!!\n")+1);
return 0;
}
dup 复制旧的fd 到当前进程最小可用 文件描述符(一般为3)
如果,把标准输出的文件描述符关闭,则当前进程最小可用的描述符就变成了1,此时旧fd就被重定向到标准输出
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{
int fd_file;
fd_file =open("file.txt",O_RDWR|O_CREAT|O_APPEND,0664);
//关闭标准输出
close(1);
int fd = dup(fd_file); //dup之后 一定是1
printf("hello world\n");
printf("nihao\n");
printf("fd = %d\n", fd);
return 0;
}
看一下文件内容:
2、dup2
# include <unistd.h>int dup2 ( int oldfd , int newfd )功能:复制一份打开的文件描述符 oldfd ,并分配新的文件描述符 newfd , newfd 也标识 oldfd 所标识的文件。注意:newfd 是小于文件描述符最大允许值的非负整数,如果 newfd 是一个已经打开的文件描述符,则首先关闭该文件,然后再复制。参数:oldfd :要复制的文件描述符newfd :分配的新的文件描述符返回值:成功:返回 newfd失败:返回 ‐ 1 ,错误代码存于 errno 中
三、有名管道
3.1 有名管道概述
3.2 有名管道的创建
方法 1 :用过 shell 命令 mkfifo 创建有名管道mkfifo 文件名
方法 2 :使用函数 mkfifo 库函数 man 3 mkfifo#include <sys/types.h>
#include <sys/stat.h>int mkfifo(const char *pathname, mode_t mode);
功能:创建一个有名管道,产生一个本地文件系统可见的文件 pathname参数:pathname :有名管道创建后生成的文件,可以带路径mode :管道文件的权限,一般通过八进制数设置即可,例如 0664返回值:成功: 0失败: ‐ 1
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
int main(int argc, char const *argv[])
{
if(mkfifo("fifo_file",0664)==-1){
printf("errno =%d\n",errno);
//如果管道文件已经存在,不需要报错退出,直接使用即可,所以需要在错误输出之前把//因为文件存在的错误排除
if(errno!=EEXIST){
perror("fail to mkfifo");
exit(1);
}
}
return 0;
}
/usr/include/asm-generic/errno-base.h文件中
3.3 有名管道的基本读写操作
注意:有名管道创建的本地的文件只是起到标识作用,真正有名管道实现进程间通信还是在内核空间开辟内存,所以本地产生的文件只是一个标识,没有其他作用,对本地管道文件的操作实质就是对内核空间的操作
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#define FIFONAME "fifo_file"
int main(int argc, char const *argv[])
{
if((mkfifo(FIFONAME,0664))==-1){
//17
if(errno!=EEXIST){
perror("fail to create");
exit(-1);
}
}
//对有名管道进行操作
//管道后写入的数据会保存在之前写入数据的后面,不会替换
//如果管道中没有数据了,读操作会阻塞
//通过open函数打开管道文件并得到文件描述符
int fd;
fd =open(FIFONAME,O_RDWR); //可读可写
if(fd==-1){
perror("fail to open"); //打开失败
exit(-2);
}
//通过write函数向管道中写入数据
if(write(fd,"nihao testFIFO",strlen("nihao testFIFO"))==-1){
perror("fail to write"); //打开失败
exit(-3);
}
write(fd, "nihao beijing", strlen("nihao beijing"));
//通过read函数读取管道中的数据
char buf[32]="";
if(read(fd,buf,sizeof(buf))==-1){
perror("fail to read");
exit(1);
}
printf("buf = [%s]\n", buf);
//上面已经读完,此处被阻塞
if(read(fd,buf,sizeof(buf))==-1){
perror("fail to read");
exit(1);
}
printf("buf = [%s]\n", buf);
close(fd);
return 0;
}
这里关于write 和 read 补充一句:
这两个系统调用,不会因为写入,读到 \0,\n而被截断。
真正被 \0截断的,其实是 char buf[] ,参考下面这个代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#define FIFONAME "fifo_file"
int main(int argc, char const *argv[])
{
if((mkfifo(FIFONAME,0664))==-1){
//17
if(errno!=EEXIST){
perror("fail to create");
exit(-1);
}
}
//对有名管道进行操作
//管道后写入的数据会保存在之前写入数据的后面,不会替换
//如果管道中没有数据了,读操作会阻塞
//通过open函数打开管道文件并得到文件描述符
int fd;
fd =open(FIFONAME,O_RDWR); //可读可写
if(fd==-1){
perror("fail to open"); //打开失败
exit(-2);
}
//通过write函数向管道中写入数据
if(write(fd,"hell\0o\nworld",sizeof("hell\0o\nworld"))==-1){
perror("fail to write"); //打开失败
exit(-3);
}
write(fd, "beijing", strlen("beijing"));
//通过read函数读取管道中的数据
char buf[32]="";
int len;
if((len=read(fd,buf,sizeof(buf)))==-1){
perror("fail to read");
exit(1);
}
printf("len =%d\n",len);
printf("buf = [%s]\n", buf); //\0是字符串结束符。打印buf输出时候就被截断了,write写入,read读 遇到\0是没有任何影响的
//可以通过打印buf 查看 完整字符内容 包含\0
int i;
for(i=0;i<len;i++){
printf("buf = [%c]\n", (char)buf[i]);
}
off_t pos;
pos = lseek(fd,-3,SEEK_CUR);
//注意 管道读写是一次性的
printf("当前文件读写位置为 %ld\n",pos);
//上面已经读完,此处被阻塞
if(read(fd,buf,sizeof(buf))==-1){
perror("fail to read");
exit(1);
}
printf("buf = [%s]\n", buf);
close(fd);
return 0;
}
思考一下,为什么调用Iseek 返回结果仍然是-1
3.4 有名管道实现进程间通信
// 两个管道
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO1 "fifo_1"
#define FIFO2 "fifo_2"
int main(int argc, char const *argv[])
{
printf("This is sender \n");
// 创建两个fifo文件
int fd_r, fd_w;
if ((mkfifo(FIFO1, 0664)) == -1)
{
if (errno != EEXIST)
{
perror("fail to create!");
exit(1);
}
printf("errno = %d\n", errno);
}
if ((mkfifo(FIFO2, 0664)) == -1)
{
if (errno != EEXIST)
{
perror("fail to create!");
exit(1);
}
printf("errno = %d\n", errno);
}
// 以只读的方式打开 fifo2
// 以只写的方式打开fifo1
fd_w = open(FIFO1, O_WRONLY);
printf("打开fd_w =%d\n", fd_w);
if ((fd_w) == -1)
{
perror("fail to open");
exit(1);
}
fd_r = open(FIFO2, O_RDONLY);
printf("打开fd_r =%d\n", fd_r);
if (fd_r == -1)
{
perror("fail to open");
exit(1);
}
char buf[128] = "";
ssize_t bytes;
while (1)
{
// 从标准输入中获取,遇到换行结束,验证,fgets 会不会自动加上\0
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf) - 1] = '\0'; // 封尾
// 发送操作
if ((bytes = write(fd_w, buf, sizeof(buf))) == -1)
{
perror("fail to write");
exit(-1);
}
else
{
printf("写入 --%s\n", buf);
}
// 读操作
if ((bytes = read(fd_r, buf, sizeof(buf))) == -1)
{
perror("fail to write");
exit(-1);
}
printf("from recv: %s\n", buf);
}
return 0;
}
recv.c
// 两个管道
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO1 "fifo_1"
#define FIFO2 "fifo_2"
int main(int argc, char const *argv[])
{
printf("This is recv \n");
// 创建两个fifo文件
int fd_r, fd_w;
if ((mkfifo(FIFO1, 0664)) == -1)
{
if (errno != EEXIST)
{
perror("fail to create!");
exit(-1);
}
}
if ((mkfifo(FIFO2, 0664)) == -1)
{
if (errno != EEXIST)
{
perror("fail to create!");
exit(-1);
}
}
//以只读打开 fifo1
if ((fd_r = open(FIFO1, O_RDONLY)) == -1)
{
perror("fail to open");
exit(-2);
}
//只写方式,打开 fifo2
if ((fd_w = open(FIFO2, O_WRONLY)) == -1)
{
perror("fail to open");
exit(-2);
}
char buf[128] = "";
ssize_t bytes;
for (;;)
{
// 读操作
if ((bytes = read(fd_r, buf, sizeof(buf))) == -1)
{
perror("fail to write");
exit(-1);
}
printf("from send: %s\n",buf);
// 从标准输入中获取,遇到换行结束,验证,fgets 会不会自动加上\0
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf) - 1] = '\0'; // 封尾
// 发送操作
if ((bytes = write(fd_w, buf, sizeof(buf))) == -1)
{
perror("fail to write");
exit(-1);
}
}
return 0;
}
3.5 有名管道的读写规律(阻塞)
1 读写端都存在,只读不写
读写端都存在,只读不写如果原本管道中有数据,则正常读取如果管道中没有数据,则 read 函数会阻塞等待
//读写都存在,只读不写
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO "fifo_file"
int main(int argc, char const *argv[])
{
//1、创建管道文件
if( mkfifo(FIFO,0664)==-1){
if(errno!=EEXIST){
perror("fail to create");
exit(-1);
}
}
//2、以可读可写方式打开(让读写端都存在)
int fd;
if((fd=open(FIFO,O_RDWR))==-1){
perror("fail to open");
exit(-1);
}
write(fd,"测试",sizeof("测试"));
//读
char buf[32]="";
int len;
len = read(fd,buf,sizeof(buf));
printf("buf=[%s] ,len=%d\n",buf,len);
len = read(fd,buf,sizeof(buf));
printf("buf=[%s] ,len=%d\n",buf,len);
return 0;
}
2 读写端都存在,只写不读
// 读写都存在,只写不读
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO "fifo_file"
int main(int argc, char const *argv[])
{
// 1、创建管道文件
if (mkfifo(FIFO, 0664) == -1)
{
if (errno != EEXIST)
{
perror("fail to create");
exit(-1);
}
}
// 2、以可读可写方式打开(让读写端都存在)
int fd;
if ((fd = open(FIFO, O_RDWR)) == -1)
{
perror("fail to open");
exit(-1);
}
int num=0;
while (1)
{
//写满阻塞,默认64k
write(fd, "666", 1024);
num++;
printf("num = %d\n",num);
}
return 0;
}
3 在一个进程中,只有读端,没有写端
// 一个进程 只有读端
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO "fifo_file"
int main(int argc, char const *argv[])
{
// 1、创建管道文件
if (mkfifo(FIFO, 0664) == -1)
{
if (errno != EEXIST)
{
perror("fail to create");
exit(-1);
}
}
// 2、以只读方式打开(让读写端都存在),阻塞在open处
int fd;
if ((fd = open(FIFO, O_RDONLY)) == -1)
{
perror("fail to open");
exit(-1);
}
//不执行
printf("*************************\n");
return 0;
}
4 在一个进程中,只有写端,没有读端
// 一个进程 只有读端
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO "fifo_file"
int main(int argc, char const *argv[])
{
// 1、创建管道文件
if (mkfifo(FIFO, 0664) == -1)
{
if (errno != EEXIST)
{
perror("fail to create");
exit(-1);
}
}
// 2、以只写方式打开(让读写端都存在),阻塞在open处
int fd;
if ((fd = open(FIFO, O_WRONLY)) == -1)
{
perror("fail to open");
exit(-1);
}
//不执行
printf("*************************\n");
return 0;
}
5.一个进程读,一个进程写
reader.c
// 一个进程 只有读端
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#define FIFO "fifo_file"
int main(int argc, char const *argv[])
{
// 1、创建管道文件
if (mkfifo(FIFO, 0664) == -1)
{
if (errno != EEXIST)
{
perror("fail to create");
exit(-1);
}
}
// 2、以只写方式打开(让读写端都存在),阻塞在open处
int fd;
if ((fd = open(FIFO, O_RDONLY)) == -1)
{
perror("fail to open");
exit(-1);
}
printf("*************************\n");
char buf[32]="";
int len;
while (1)
{
len = read(fd,buf,sizeof(buf));
printf("buf=[%s],len=%d\n ",buf,len);
}
return 0;
}
writer.c
// 一个进程 只有xie端
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#define FIFO "fifo_file"
void handler(){
printf("触发了 SIGPIPE\n");
}
int main(int argc, char const *argv[])
{
signal(SIGPIPE,handler);
// 1、创建管道文件
if (mkfifo(FIFO, 0664) == -1)
{
if (errno != EEXIST)
{
perror("fail to create");
exit(-1);
}
}
// 2、以只写方式打开
int fd;
if ((fd = open(FIFO, O_WRONLY)) == -1)
{
perror("fail to open");
exit(-1);
}
printf("*************************\n");
while (1)
{
write(fd,"hello",sizeof("hello"));
sleep(3);
printf("666\n");
}
return 0;
}
如果一个进程只读,一个进程只写,都运行后,如果关闭写端,读端read会返回0
(read直接返回0,不阻塞)
如果一个进程只读,一个进程只写,都运行后,如果关闭读端,写端会立即产生SIGPIPE信号,默认的处理方式是退出进程