Linux进程10-有名管道概述、创建、读写操作、两个管道进程间通信、读写规律(只读、只写、读写区别)、设置阻塞/非阻塞

news2025/5/14 18:50:14

目录

1.有名管道

1.1概述

1.2与无名管道的差异

2.有名管道的创建 

2.1 直接用shell命令创建有名管道

2.2使用mkfifo函数创建有名管道

 3.有名管道读写操作

3.1单次读写

3.2多次读写

4.有名管道进程间通信

4.1回合制通信

4.2父子进程通信

5.有名管道读写规律(默认阻塞)

5.1读写端都存在,只读不写

5.2读写端都存在,只写不读

5.3 在一个进程中,只有读端,没有写端

5.4 在一个进程中,只有写端,没有读端

5.5 一个进程只读,一个进程只写

5.5.1两个进程,一个只读一个只写

5.5.2两个进程,一个只读一个只写,关闭写端

5.5.3两个进程,一个只读一个只写,关闭读端

5.6读写端都存在,默认阻塞

6.有名管道的读写规律(设置:非阻塞)

6.1只读方式打开(设置非阻塞)

6.2只写方式打开(设置非阻塞)

6.3读写方式打开(设置非阻塞)


1.有名管道

1.1概述

命名管道(FIFO)和管道(pipe)基本相同,但也有一些显著的不同,
其特点是:
1、半双工,数据在同一时刻只能在一个方向上流动。
2、写入FIFO中的数据遵循先入先出的规则。
3、FIFO所传送的数据是无格式的,这要求FIFO的读出方与写入方必须事先约定好数据的格
式,如多少字节算一个消息等。
4、FIFO在文件系统中作为一个特殊的文件而存在并且在文件系统中可见,所以有名管道可
以实现不相关进程间通信,但FIFO中的内容却存放在内存中。
5、管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。
6、从FIFO读数据是一次性操作,数据一旦被读,它就从FIFO中被抛弃,释放空间以便写更
多的数据。
7、当使用FIFO的进程退出后,FIFO文件将继续保存在文件系统中以便以后使用。
8、FIFO有名字,不相关的进程可以通过打开命名管道进行通信。

1.2与无名管道的差异

特性有名管道无名管道
进程关系允许无亲缘关系的进程通信仅限有亲缘关系的进程
存在形式文件系统中的 FIFO 文件内存中的临时缓冲区
持久性文件删除前一直存在进程终止后自动销毁
创建方式mkfifo()/mkfifoat()pipe()

2.有名管道的创建 

2.1 直接用shell命令创建有名管道

在终端直接用shell命令mkfifo创建有名管道

使用ls -l命令查看 myfifo1文件的类型为p,表示该文件是一个管道文件。

2.2使用mkfifo函数创建有名管道

mkfifo函数原型:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
功能:创建一个有名管道,产生一个本地文件系统可见的文件pathname

参数:
pathname:有名管道创建后生成的文件,可以带路径
mode:管道文件的权限,一般通过八进制数设置即可,例如0664

返回值:
 成功:0
 失败:‐1

(1)管道文件不存在

程序:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

int main(int argc, char const *argv[])
{
    //通过mkfifo函数创建有名管道
    if(mkfifo("fifo_file", 0664) == -1)
    {
        perror("fail to mkfifo");
        exit(1);
    }

    return 0;
}

运行结果:管道文件不存在,创建了fifo_file管道文件

(2)管道文件存在

再次运行程序,报错:文件已存在

程序修改:如果错误码不是 EEXIST,继续运行

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

int main(int argc, char const *argv[])
{
    //通过mkfifo函数创建有名管道
    if(mkfifo("fifo_file", 0664) == -1)
    {
        printf("errno = %d\n", errno);
        
        //如果管道文件已经存在,不需要报错退出,直接使用即可,
        //所以需要在错误输出之前把因为文件存在的错误排除
        if(errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }

    return 0;
}

运行结果:

终端输入: sudo find /usr/include -name "errno-base.h" ,查找错误码文件

 3.有名管道读写操作

由于有名管道在本地创建了一个管道文件,所以系统调用的IO函数基本都可以对有名管道

进行操作, 但是不能使用lseek修改管道文件的偏移量.

注意:有名管道创建的本地的文件只是起到标识作用,真正有名管道实现进程间通信还是在

内核空间开辟内存,所以本地产生的文件只是一个标识,没有其他作用,对本地管道文件的

操作实质就是对内核空间的操作。

3.1单次读写

程序:

#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[])
{
    //通过mkfifo函数创建有名管道
    if(mkfifo(FIFONAME, 0664) == -1)
    {
        if(errno != EEXIST)//如果不存在报错
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }

    //对有名管道进行读写操作

    //通过open函数打开管道文件并得到文件描述符
    int fd;
    fd = open(FIFONAME, O_RDWR);//权限:可读可写
    if(fd == -1)
    {
        perror("fail to open");
        exit(1);
    }

    //通过write函数向管道中写入数据
    if(write(fd, "hello world", strlen("hello world")) == -1)
    {
        perror("fail to write");
        exit(1);
    }

    //通过read函数读取管道中的数据
    char buf[32] = "";
    if(read(fd, buf, sizeof(buf)) == -1)
    {
        perror("fail to read");
        exit(1);
    }
    printf("buf = [%s]\n", buf);

    //使用close函数关闭文件描述符
    close(fd);

    return 0;
}

 运行结果:

3.2多次读写

程序:

#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[])
{
    //通过mkfifo函数创建有名管道
    if(mkfifo(FIFONAME, 0664) == -1)
    {
        if(errno != EEXIST)//如果不存在报错
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }

    //对有名管道进行操作
    //管道后写入的数据会保存在之前写入数据的后面,不会替换
    //如果管道中没有数据了,读操作会阻塞

    //通过open函数打开管道文件并得到文件描述符
    int fd;
    fd = open(FIFONAME, O_RDWR);
    if(fd == -1)
    {
        perror("fail to open");
        exit(1);
    }

    //第一次 通过write函数向管道中写入数据
    if(write(fd, "hello world", strlen("hello world")) == -1)
    {
        perror("fail to write");
        exit(1);
    }

    //第2次 通过write函数向管道中写入数据
    if(write(fd, "nihao", strlen("nihao")) == -1)
    {
        perror("fail to write");
        exit(1);
    }

    //第1次 通过read函数读取管道中的数据,已将管道中的所有数据读取
    char buf[32] = "";
    if(read(fd, buf, sizeof(buf)) == -1)
    {
        perror("fail to read");
        exit(1);
    }
    printf("第1次读取 buf = [%s]\n", buf);

	//第2次 通过read函数读取管道中的数据,管道中已无数据
    if(read(fd, buf, sizeof(buf)) == -1)
    {
        perror("fail to read");
        exit(1);
    }
    printf("第2次读取 buf = [%s]\n", buf);

    //使用close函数关闭文件描述符
    close(fd);

    return 0;
}

运行结果:第1次 通过read函数读取管道中的数据,已将管道中的所有数据读取;第2次 通过read函数读取管道中的数据,管道中已无数据,read函数执行阻塞;

4.有名管道进程间通信

有名管道在本地创建了一个管道文件,所以不相关的进程间也可以实现通信。

4.1回合制通信

发送端程序:
#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>

int main(int argc, char const *argv[])
{
    //如果没有创建有名管道,则创建有名管道
    //为了实现两个进程都可以收发数据,所以需要创建两个有名管道
    if(mkfifo("myfifo1", 0664) == -1)
    {
        if(errno != EEXIST)//如果不存在报错
        {
            perror("fail to mkfifo");
            exit(1);//退出
        }
    }

    if(mkfifo("myfifo2", 0664) == -1)
    {
        if(errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }

    //打开两个有名管道并得到文件描述符
    //fd_w:向管道myfifo1写入数据,fd_r:从管道myfifo2读取数据,
    //另一个接收方正好相反
    int fd_w, fd_r;
    if((fd_w = open("myfifo1", O_WRONLY)) == -1)
    {
        perror("fail to open");
        exit(1);
    }

    if((fd_r = open("myfifo2", O_RDONLY)) == -1)
    {
        perror("fail to open");
        exit(1);
    }

    char buf[128] = "";
    char buf1[128] = "";
    ssize_t bytes;
    while(1)
    {
    	   printf("发送端运行中:请输入向接收端要发送的内容\n");
        fgets(buf, sizeof(buf), stdin);
        buf[strlen(buf) - 1] = '\0';//将fgets最后输入的\n替换为\0,终端就没有空行

        //send进程负责将数据写入myfifo1,接着从myfifo2中读取数据
        //发送端将终端输入的数据写入myfifo1,接收端:myfifo2会读取写入的数据
        if((bytes = write(fd_w, buf, sizeof(buf))) == -1)
        {
            perror("fail to write");
            exit(1);
        }
		//发送端从myfifo2读取数据
        if((bytes = read(fd_r, buf1, sizeof(buf1))) == -1)
        {
            perror("fail to read");
            exit(1);
        }

        printf("发送端:读取到接收端发送的数据: %s\n", buf1);
        
        memset(buf, 0, sizeof(buf));//buf清0
        memset(buf1, 0, sizeof(buf1));
    }

    return 0;
}

接收端程序:

#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>

int main(int argc, char const *argv[])
{
    if(mkfifo("myfifo1", 0664) == -1)
    {
        if(errno != EEXIST)//如果不存在报错
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }

    if(mkfifo("myfifo2", 0664) == -1)
    {
        if(errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }

	//fd_w:向管道myfifo2写入数据,fd_r:从管道myfifo1读取数据,
    int fd_w, fd_r;
    
    if((fd_r = open("myfifo1", O_RDONLY)) == -1)
    {
        perror("fail to open");
        exit(1);
    }
    
    if((fd_w = open("myfifo2", O_WRONLY)) == -1)
    {
        perror("fail to open");
        exit(1);
    }

    char buf[128] = "";
    char buf1[128] = "";
    ssize_t bytes;
    while(1)
    {
    		
    	   //recv进程负责从myfifo1中读取数据,接着将终端输入数据写入myfifo2
    	   
       //接收端:读取myfifo1写入数据
        if((bytes = read(fd_r, buf, sizeof(buf))) == -1)
        {
            perror("fail to read");
            exit(1);
        }

        printf("接收端:读取到发送端发送的数据: %s\n", buf);
	   printf("接收端运行中,清输入要发送的内容: \n");
        fgets(buf1, sizeof(buf1), stdin);
        buf1[strlen(buf1) - 1] = '\0';
		//接收端:向myfifo2写入数据,发送端:myfifo1会读取写入的数据
         if((bytes = write(fd_w, buf1, sizeof(buf1))) == -1)
        {
            perror("fail to write");
            exit(1);
        }

        memset(buf, 0, sizeof(buf));//buf清0
        memset(buf1, 0, sizeof(buf1));
    }

    return 0;
}

运行结果:打开两个终端,分别运行发送端和接收端程序。发送和接收端程序运行时,发送端:先给接收端发送数据,保存在管道1;接着等待从管道2读取接收端发送的数据。接收端:从管道1读取发送端发送的数据;接着向发送端发送数据,保存在管道2。

程序缺点:只能一端输出后,等待另一端回复;回合制发送和接收

优化:选择父子进程。一个用于发送,一个用于接收。

(1)发送端

(2)接收端

4.2父子进程通信

(1)发送端程序:

#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>

int main(int argc, char const *argv[])
{
    //如果没有创建有名管道,则创建有名管道
    //为了实现两个进程都可以收发数据,所以需要创建两个有名管道
    if(mkfifo("myfifo1", 0664) == -1)
    {
        if(errno != EEXIST)//如果不存在报错
        {
            perror("fail to mkfifo");
            exit(1);//退出
        }
    }

    if(mkfifo("myfifo2", 0664) == -1)
    {
        if(errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }

    //打开两个有名管道并得到文件描述符
    //fd_w:向管道myfifo1写入数据,fd_r:从管道myfifo2读取数据,
    //另一个接收方正好相反
    int fd_w, fd_r;
    if((fd_w = open("myfifo1", O_WRONLY)) == -1)
    {
        perror("fail to open");
        exit(1);
    }

    if((fd_r = open("myfifo2", O_RDONLY)) == -1)
    {
        perror("fail to open");
        exit(1);
    }

    char buf[128] = "";
    char buf1[128] = "";
    ssize_t bytes;

	//使用fork函数创建子进程
    pid_t pid;
    if((pid = fork()) < 0)
    {
        perror("fail to fork");
        exit(1);
    }
    else if(pid > 0) // 父进程
    {
	    	while(1)
	    {
	    	   printf("发送端运行中:请输入向接收端要发送的内容\n");
	        fgets(buf, sizeof(buf), stdin);
	        buf[strlen(buf) - 1] = '\0';//将fgets最后输入的\n替换为\0,终端就没有空行
	
	        //send进程负责将数据写入myfifo1,接着从myfifo2中读取数据
	        //发送端将终端输入的数据写入myfifo1,接收端:myfifo2会读取写入的数据
	        if((bytes = write(fd_w, buf, sizeof(buf))) == -1)
	        {
	            perror("fail to write");
	            exit(1);
	        }
			        
	        memset(buf, 0, sizeof(buf));//buf清0
	        sleep(1);
	    }
    	}
    	else // 子进程
    	{
    		while(1)
	    {
			//发送端从myfifo2读取数据
	        if((bytes = read(fd_r, buf1, sizeof(buf1))) == -1)
	        {
	            perror("fail to read");
	            exit(1);
	        }
	
	        printf("发送端:读取到接收端发送的数据: %s\n", buf1);	
	        if(strcmp("end_end",buf1) == 0)
		   {
		   	printf("发送端:读取到接收端发送的: 终止信号,退出\n");
		   	exit(1);
		   }
		           
	        memset(buf1, 0, sizeof(buf1));
		   sleep(1);
		   
	    }

    	}
        
    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>

int main(int argc, char const *argv[])
{
    //如果没有创建有名管道,则创建有名管道
    //为了实现两个进程都可以收发数据,所以需要创建两个有名管道
    if(mkfifo("myfifo1", 0664) == -1)
    {
        if(errno != EEXIST)//如果不存在报错
        {
            perror("fail to mkfifo");
            exit(1);//退出
        }
    }

    if(mkfifo("myfifo2", 0664) == -1)
    {
        if(errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }

    //打开两个有名管道并得到文件描述符
    //fd_w:向管道myfifo1写入数据,fd_r:从管道myfifo2读取数据,
    //另一个接收方正好相反
    int fd_w, fd_r;
    if((fd_w = open("myfifo1", O_WRONLY)) == -1)
    {
        perror("fail to open");
        exit(1);
    }

    if((fd_r = open("myfifo2", O_RDONLY)) == -1)
    {
        perror("fail to open");
        exit(1);
    }

    char buf[128] = "";
    char buf1[128] = "";
    ssize_t bytes;

	//使用fork函数创建子进程
    pid_t pid;
    if((pid = fork()) < 0)
    {
        perror("fail to fork");
        exit(1);
    }
    else if(pid > 0) // 父进程
    {
	    	while(1)
	    {
	    	   printf("发送端运行中:请输入向接收端要发送的内容\n");
	        fgets(buf, sizeof(buf), stdin);
	        buf[strlen(buf) - 1] = '\0';//将fgets最后输入的\n替换为\0,终端就没有空行
	
	        //send进程负责将数据写入myfifo1,接着从myfifo2中读取数据
	        //发送端将终端输入的数据写入myfifo1,接收端:myfifo2会读取写入的数据
	        if((bytes = write(fd_w, buf, sizeof(buf))) == -1)
	        {
	            perror("fail to write");
	            exit(1);
	        }
		        
	        memset(buf, 0, sizeof(buf));//buf清0
	        sleep(1);
	    }
    	}
    	else // 子进程
    	{
    		while(1)
	    {
			//发送端从myfifo2读取数据
	        if((bytes = read(fd_r, buf1, sizeof(buf1))) == -1)
	        {
	            perror("fail to read");
	            exit(1);
	        }
	
	        printf("发送端:读取到接收端发送的数据: %s\n", buf1);	
	        if(strcmp("end_end",buf1) == 0)
		   {
		   	printf("发送端:读取到接收端发送的: 终止信号,退出\n");
		   	exit(1);
		   }
		           
	        memset(buf1, 0, sizeof(buf1));
		   sleep(1);
		   
	    }

    	}
    
    
    return 0;
}

运行结果:打开两个终端,分别运行发送端和接收端程序。发送和接收端程序运行时,发送端既可以接收多次数据,也可以发送多次数据;接收端也支持同时发送和接收多次数据。发送特殊字符end_end退出运行。

(1)发送端

(2)接收端

5.有名管道读写规律(默认阻塞)

5.1读写端都存在,只读不写

程序:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    if(mkfifo("myfifo", 0664) == -1)
    {
    	   //如果管道文件已经存在,不需要报错退出,直接使用即可,
        //所以需要在错误输出之前把因为文件存在的错误排除
        if(errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }

    //读写端都存在,只读不写
    //如果原本管道中有数据,则正常读取
    //如果管道中没有数据,则read函数会阻塞等待
    
    int fd;
    if((fd = open("myfifo", O_RDWR)) == -1)
    {
        perror("fail to open");
        exit(1);
    }

	//先写入数据验证,管道中有数据,则正常读取
    write(fd, "hello world", 11);

    char buf[128] = "";
    read(fd, buf, sizeof(buf));
    printf("buf = %s\n", buf);

	//管道中已无数据,读取会阻塞。
    read(fd, buf, sizeof(buf));
    printf("buf = %s\n", buf);

    return 0;
}

运行结果:读写端都存在,只读不写。

第一次读取,管道中有数据,正常读取;第二次读取,管道中已经无数据,在read处阻塞。

5.2读写端都存在,只写不读

程序:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
	//如果管道文件已经存在,不需要报错退出,直接使用即可,
     //所以需要在错误输出之前把因为文件存在的错误排除
    if(mkfifo("myfifo", 0664) == -1)
    {
        if(errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }

    //读写端都存在,只写不读
    //当有名管道的缓冲区写满后,write函数会发生阻塞
    //默认有名管道的缓冲区为64K字节

    int fd;
    if((fd = open("myfifo", O_RDWR)) == -1)
    {
        perror("fail to open");
        exit(1);
    }

    int num = 0;
    while(1)
    {
        write(fd, "", 1024);
        num++;
        printf("num = %d\n", num);
    }

    return 0;
}

运行结果:读写端都存在,只写不读 ;当有名管道的缓冲区写满后,write函数会发生阻塞;默认有名管道的缓冲区为64K字节。

5.3 在一个进程中,只有读端,没有写端

程序:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    if(mkfifo("myfifo", 0664) == -1)
    {
        if(errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }

    //在一个进程中,只有读端,没有写端
    //会在open函数的位置阻塞

    printf("***********************\n");

    int fd;
    if((fd = open("myfifo", O_RDONLY)) == -1)
    {
        perror("fail to open");
        exit(1);
    }

    printf("------------------------\n");

    char buf[128] = "";
    ssize_t bytes;

      if((bytes = read(fd, buf, sizeof(buf))) == -1)
      {
         perror("fail to read");
         exit(1);
      }

     printf("bytes = %ld\n", bytes);
     printf("buf = %s\n", buf);
    

    return 0;
}

运行结果:在一个进程中,只有读端,没有写端;会在open函数的位置阻塞(printf("------------------------\n"); 未执行)。

5.4 在一个进程中,只有写端,没有读端

程序:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
	//如果管道文件已经存在,不需要报错退出,直接使用即可,
     //所以需要在错误输出之前把因为文件存在的错误排除
    if(mkfifo("myfifo", 0664) == -1)
    {
        if(errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }

    //在一个进程中,只有写端,没有读端
    //会在open函数的位置阻塞

    printf("*****************************\n");

    int fd;
    if((fd = open("myfifo", O_WRONLY)) == -1)
    {
        perror("fail to open");
        exit(1);
    }

    printf("-----------------------------\n");

        write(fd, "hello world", 11);
        printf("666\n");

        sleep(1);
    
    return 0;
}

运行结果:在一个进程中,只有写端,没有读端;会在open函数的位置阻塞

5.5 一个进程只读,一个进程只写

5.5.1两个进程,一个只读一个只写

只要保证有名管道的 读写端都存在 ,不管是几个进程,都 不会再open这阻塞 了。
(1)只读程序
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    if(mkfifo("myfifo", 0664) == -1)
    {
        if(errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }

    //在一个进程中,只有读端,没有写端
    //会在open函数的位置阻塞

    printf("***********************\n");

    int fd;
    if((fd = open("myfifo", O_RDONLY)) == -1)
    {
        perror("fail to open");
        exit(1);
    }

    printf("------------------------\n");

    char buf[128] = "";
    ssize_t bytes;
    
        if((bytes = read(fd, buf, sizeof(buf))) == -1)
        {
            perror("fail to read");
            exit(1);
        }

        printf("bytes = %ld\n", bytes);
        printf("buf = %s\n", buf);
    
    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>

int main(int argc, char const *argv[])
{
	//如果管道文件已经存在,不需要报错退出,直接使用即可,
     //所以需要在错误输出之前把因为文件存在的错误排除
    if(mkfifo("myfifo", 0664) == -1)
    {
        if(errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }

    //在一个进程中,只有写端,没有读端
    //会在open函数的位置阻塞

    printf("*****************************\n");

    int fd;
    if((fd = open("myfifo", O_WRONLY)) == -1)
    {
        perror("fail to open");
        exit(1);
    }

    printf("-----------------------------\n");

        write(fd, "hello world", 11);
        printf("666\n");

        sleep(1);
    
    return 0;
}

运行结果:打开2个终端,对同一个管道进行读写操作,读写端同时存在,程序没有阻塞在open。

(1)只写端,程序没有阻塞在open,(printf("------------------------\n"); 已执行)

(2)只读端,程序没有阻塞在open,(printf("------------------------\n"); 已执行)

5.5.2两个进程,一个只读一个只写,关闭写端

如果一个进程只读,一个进程只写,都运行后,如果关闭写端,读端read会返回0。
(1)只写端程序
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    if(mkfifo("myfifo", 0664) == -1)
    {
        if(errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }

    //在一个进程中,只有写端,没有读端
    //会在open函数的位置阻塞

    printf("*****************************\n");

    int fd;
    if((fd = open("myfifo", O_WRONLY)) == -1)
    {
        perror("fail to open");
        exit(1);
    }

    printf("-----------------------------\n");

    while(1)
    {
        write(fd, "hello world", 11);
        printf("666\n");

        sleep(1);
    }

    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>

int main(int argc, char const *argv[])
{
    if(mkfifo("myfifo", 0664) == -1)
    {
        if(errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }

    //在一个进程中,只有读端,没有写端
    //会在open函数的位置阻塞

    printf("***********************\n");

    int fd;
    if((fd = open("myfifo", O_RDONLY)) == -1)
    {
        perror("fail to open");
        exit(1);
    }

    printf("------------------------\n");

    char buf[128] = "";
    ssize_t bytes;
    while(1)
    {
        if((bytes = read(fd, buf, sizeof(buf))) == -1)
        {
            perror("fail to read");
            exit(1);
        }

        printf("bytes = %ld\n", bytes);
        printf("buf = %s\n", buf);
        sleep(1);
    }

    return 0;
}

运行结果:打开两个终端运行。一个进程只读,一个进程只写,都运行后,如果关闭写端,读端read会返回0。

(1)写端,运行几次后,关闭写端,终止运行。

(2)读端,关闭写端,读端read会返回0。

5.5.3两个进程,一个只读一个只写,关闭读端

如果一个进程只读,一个进程只写,都运行后, 如果关闭读端,写端会立即产生 SIGPIPE信号(管道破裂),默认的处理方式是退出进程
(1)只写端程序
与5.5.2两个进程,一个只读一个只写,关闭写端;程序相同。
(2)只读端程序
与5.5.2两个进程,一个只读一个只写,关闭写端;程序相同。
运行结果:打开两个终端运行
(1)读端,关闭后,写端产 生SIGPIPE信号(管道破裂),默认的处理方式是退出进程。
(2)写端

5.6读写端都存在,默认阻塞

有名管道默认读写状态为阻塞,读写端同时存在,不会再open函数处阻塞

程序:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
	int fd;

	if(mkfifo("myfifo", 0664) == -1)
    {
        if(errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }
    
	//如果open设置为可读可写,默认:阻塞效果
    char send[100] = "nihao world";
	
	fd = open("myfifo", O_RDWR);//读写打开
	if(fd < 0)
	{
		perror("fail open fifo");
		exit(1);
	}
	write(fd, send, strlen(send));
	
	char recv[100] = "";
    read(fd, recv, sizeof(recv));
	printf("read from my_fifo buf=[%s]\n",recv);

	return 0;
}

运行结果:有名管道默认读写状态为阻塞,读写端同时存在,不会再open函数处阻塞。

6.有名管道的读写规律(设置:非阻塞)

指定 O_NONBLOCK( open 位或 O_NONBLOCK)
1 、先以只读方式打开:如果没有进程已经为写而打开一个 FIFO, 只读 open 成功,并且 open 不阻塞。
2 、先以只写方式打开:如果没有进程已经为读而打开一个 FIFO ,只写 open 将出错返回 -1
3 read write 读写命名管道中读数据时不阻塞。
4 、通信过程中,读进程退出后,写进程向命名管道内写数据时,写进程也会(收到 SIGPIPE 信号)退出。

6.1只读方式打开(设置非阻塞)

程序:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
	int fd;

	if(mkfifo("myfifo", 0664) == -1)
    {
        if(errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }

	//如果open标志位设置为非阻塞,并且以只读的方式打开管道文件
	//open函数和read函数都不会阻塞
    fd = open("myfifo", O_RDONLY | O_NONBLOCK);
	if(fd < 0)
	{
		perror("fail open fifo");
		exit(1);
	}

	while(1)
	{
		char recv[100] = "";
		
		bzero(recv, sizeof(recv));
		read(fd, recv, sizeof(recv));
		printf("read from my_fifo buf=[%s]\n",recv);
		sleep(1);
	}

	return 0;
}

运行结果:管道只读 非阻塞打开,在 open函数 不阻塞,程序继续运行。

6.2只写方式打开(设置非阻塞)

程序:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
	int fd;

	if(mkfifo("myfifo", 0664) == -1)
    {
        if(errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }


	//如果open标志位设置为非阻塞,并且以只写的方式打开管道文件
	//open函数会直接报错
	//如果open设置为可读可写,那么跟阻塞是一样的效果
    char send[100] = "Hello world";
	
	fd = open("myfifo", O_WRONLY | O_NONBLOCK);
	//fd = open("myfifo", O_RDWR | O_NONBLOCK);
	if(fd < 0)
	{
		perror("fail open fifo");
		exit(1);
	}
	write(fd, send, strlen(send));
	
	char recv[100] = "";
    read(fd, recv, sizeof(recv));
	printf("read from my_fifo buf=[%s]\n",recv);


	return 0;
}

运行结果:管道只写 非阻塞打开管道文件,open函数会直接报错。(不加非阻塞设置,在open函数处,阻塞。见5.4章节)

6.3读写方式打开(设置非阻塞)

程序:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
	int fd;

	if(mkfifo("myfifo", 0664) == -1)
    {
        if(errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }
    

	//如果open设置为可读可写,那么跟阻塞是一样的效果
    char send[100] = "Hello world";
	
	//fd = open("myfifo", O_WRONLY | O_NONBLOCK);
	fd = open("myfifo", O_RDWR | O_NONBLOCK);//读写打开
	if(fd < 0)
	{
		perror("fail open fifo");
		exit(1);
	}
	write(fd, send, strlen(send));
	
	char recv[100] = "";
    read(fd, recv, sizeof(recv));
	printf("read from my_fifo buf=[%s]\n",recv);

	return 0;
}

运行结果:读写端同时存在,设置为非阻塞,不会再open函数处阻塞。效果与读写端同时存在,设置为阻塞,效果相同。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2374580.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

精品可编辑PPT | 全面风险管理信息系统项目建设风控一体化标准方案

这份文档是一份全面风险管理信息系统项目建设风控一体化标准方案&#xff0c;涵盖了业务架构、功能方案、系统技术架构设计、项目实施及服务等多个方面的详细内容。方案旨在通过信息化手段提升企业全面风险管理工作水平&#xff0c;促进风险管理落地和内部控制规范化&#xff0…

YOLOv8网络结构

YOLOv8的网络结构由输入端(Input)、骨干网络(Backbone)、颈部网络(Neck)和检测头(Head)四部分组成。 YOLOv8的网络结构如下图所示&#xff1a; 在整个系统架构中&#xff0c;图像首先进入输入处理模块&#xff0c;该模块承担着图像预处理与数据增强的双重任务。接着&#xff0c…

笔记本电脑升级实战手册【扩展篇1】:flash id查询硬盘颗粒

文章目录 前言&#xff1a;一、硬盘颗粒介绍1、MLC&#xff08;Multi-Level Cell&#xff09;2、TLC&#xff08;Triple-Level Cell&#xff09;3、QLC&#xff08;Quad-Level Cell&#xff09; 二、硬盘与主控1、主控介绍2、主流主控厂家 三 、硬盘颗粒查询使用flash id工具查…

AutoDL租用服务器教程

在跑ai模型的时候&#xff0c;容易遇到算力不够的情况。此时便需要租用服务器。autodl是个较为便宜的服务器租用平台&#xff0c;h20仅需七点几元每小时。下面是简单的介绍。 打开网站AutoDL算力云 | 弹性、好用、省钱。租GPU就上AutoDL&#xff0c;并登录账号 登录后&#xff…

goner/otel 在Gone框架接入OpenTelemetry

文章目录 背景与意义快速上手&#xff1a;五步集成 OpenTelemetry运行效果展示代码详解与实践目录结构说明组件加载&#xff08;module.load.go&#xff09;业务组件示例&#xff08;your_component.go&#xff09;程序入口&#xff08;main.go&#xff09; 进阶用法与最佳实践…

杨校老师项目之基于SSM与JSP的鲜花销售系统-【成品设计含文档】

基于SSMJSP鲜花商城系统 随着电子商务的快速发展&#xff0c;鲜花在线销售已成为一种重要的消费模式。本文设计并实现了一个基于JSP技术的鲜花销售管理系统&#xff0c;采用B/S架构&#xff0c;使用SSM框架进行开发&#xff0c;并结合Maven进行项目依赖管理。系统分为前台用户模…

springboot集成langchain4j实现票务助手实战

前言 看此篇的前置知识为langchain4j整合springboot&#xff0c;以及springboot集成langchain4j记忆对话。 Function-Calls介绍 langchain4j 中的 Function Calls&#xff08;函数调用&#xff09;是一种让大语言模型&#xff08;LLM&#xff09;与外部工具&#xff08;如 A…

Feed流推送之订阅推送

分类 feed流分为TimeLine和智能排序&#xff0c;前者不对内容进行过滤&#xff0c;一般根据发布的时间来进行排序&#xff0c;一般用于好友动态或者推送关注的人的消息&#xff0c;而后者一般有着复杂的算法&#xff0c;可以根据算法智能地向目标用户推送内容&#xff0c;例如…

wordpress自学笔记 第四节 商城菜单的添加和修改美化

wordpress自学笔记 摘自 超详细WordPress搭建独立站商城教程-第四节 商城菜单的添加和修改美化&#xff0c;2025 WordPress搭建独立站商城#WordPress建站教程https://www.bilibili.com/video/BV1UwwgeuEkK?spm_id_from333.788.videopod.sections&vd_sourcea0af3bbc6b6d…

GPU L2 Cache一致性协议对科学计算的影响研究

点击 “AladdinEdu&#xff0c;同学们用得起的【H卡】算力平台”&#xff0c;H卡级别算力&#xff0c;按量计费&#xff0c;灵活弹性&#xff0c;顶级配置&#xff0c;学生专属优惠。 一、GPU缓存层级革命&#xff1a;从Volta到Hopper的演进图谱 1.1 架构级缓存策略对比 ‌Vo…

【速写】KV-cache与解码的再探讨(以束搜索实现为例)

文章目录 1 Beam Search 解码算法实现2 实现带KV Cache的Beam Search解码3 关于在带kv-cache的情况下的use_cache参数 1 Beam Search 解码算法实现 下面是一个使用PyTorch实现的beam search解码算法&#xff1a; 几个小细节&#xff1a; 束搜索可以加入length_penalty&#…

(网络)应用层协议-HTTPS

1.HTTPS是什么&#xff1f; HTTPS是应用层的一种协议&#xff0c;是在HTTP的基础上进行了加密层的处理。 HTTP协议的内容都是按照文本的形式进行传输的&#xff0c;所以呢就很容易被别人知道传输的是什么。 我们在了解了TCP/IP之后是知道我们的数据在传输的过程中是通过路由器进…

vue3: pdf.js 3.4.120 using javascript

npm install pdfjs-dist3.4.120 项目结构&#xff1a; pdfjsViewer.vue <template><div><div v-if"loading" class"flex justify-center items-center py-8"><div class"animate-spin rounded-full h-12 w-12 border-b-2 borde…

想实现一个基于MCP的pptx生成系统架构图【初版实现】

技术栈:Python + MCP协议 + python-pptx + FastMCP 核心创新点:通过MCP协议实现PPTX元素的动态化生成与标准化模板管理 当前还是个半成品,后续持续更新。 主要先介绍一下思路。 一、MCP协议与系统设计原理 1.1 为什么选择MCP? 标准化工具调用:通过MCP将PPTX元素生成逻辑封…

PyTorch Lightning实战 - 训练 MNIST 数据集

MNIST with PyTorch Lightning 利用 PyTorch Lightning 训练 MNIST 数据。验证梯度范数、学习率、优化器对训练的影响。 pip show lightning Version: 2.5.1.post0Fast dev run DATASET_DIR"/repos/datasets" python mnist_pl.py --output_grad_norm --fast_dev_run…

力扣2094题解

记录&#xff1a; 2025.5.12 题目&#xff1a; 思路&#xff1a; 暴力遍历。 解题步骤&#xff1a; 1.统计数字出现次数&#xff1a;使用数组cnt来记录输入数组中每个数字的出现次数。 2.生成三位偶数&#xff1a;通过循环从100开始&#xff0c;每次递增2&#xff0c;生成…

DHCP自动分配IP

DHCP自动分配IP 练习1 路由器 Router>en Router#conf t Router(config)#ip dhcp pool ip10 //创建DHCP地址池 Router(dhcp-config)#network 192.168.20.0 255.255.255.0 // 配置网络地址和子网掩码 Router(dhcp-config)#default-router 192.168.20.254 //配置默认网关 Rou…

【CF】Day57——Codeforces Round 955 (Div. 2, with prizes from NEAR!) BCD

B. Collatz Conjecture 题目&#xff1a; 思路&#xff1a; 简单模拟 很简单的模拟&#xff0c;我们只需要快速的找到下一个离 x 最近的 y 的倍数即可&#xff08;要大于 x&#xff09; 这里我们可以这样写 add y - (x % y)&#xff0c;这样就知道如果 x 要变成 y 的倍数还要…

(done) 补充:xv6 的一个用户程序 init 是怎么启动的 ?它如何启动第一个 bash ?

先看 main.c 从函数名来看&#xff0c;比较相关的就 userinit() 和 scheduler() #include "types.h" #include "param.h" #include "memlayout.h" #include "riscv.h" #include "defs.h"volatile static int started 0;//…

超详细讲解C语言转义字符\a \b \r \t \? \n等等

转义字符 C语言有一组字符很特殊&#xff0c;叫做转义字符&#xff0c;顾名思义&#xff0c;改变原来的意思的字符。 1 \? ??)是一个三字母词&#xff0c;在以前的编译器它会被编译为] (??会被编译为[ 因此在以前输入(are you ok ??)就会被编译为are you ok ] 解决这个…