标准流管道、命名管道_FIFO

一、标准流管道

当我们在调用popen()会创建一个管道,其实popen()会调用fork()函数产生一个子进程,执行shell以运行命令开启一个,并把执行结果写进管道中,然后返回一个文件指针。程序通过文件指针可读取管道中的内容。使用popen()创建的标准流管道需要pclose()进行关闭。

管道的创建与关闭:

例子:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#define BUFSIZE 1024
int main()
{
    FILE *fp;
    char *cmd = "printenv";
    char buf[BUFSIZE];
    buf[BUFSIZE-1] = '\0';
    if((fp=popen(cmd,"r"))==NULL)
        perror("popen");
    
    while((fgets(buf,BUFSIZE,fp))!=NULL)
        printf("%s",buf);
    
    pclose(fp);
    exit(0);
}

上述管道虽然实现了进程间通信,但是它具有一定的局限性:首先,这个管道只能是具有血缘关系的进程之间通信;第二,它只能实现一个进程写另一个进程读,而如果需要两者同时进行时,就得重新打开一个管道。 
为了使任意两个进程之间能够通信,就提出了命名管道(named pipe 或 FIFO)。

二、命名管道

命名管道创建完成后就可以使用,其使用方法与管道一样,区别在于:命名管道使用之前需要使用open()打开。这是因为:命名管道是设备文件,它是存储在硬盘上的,而管道是存在内存中的特殊文件。但是需要注意的是,命名管道调用open()打开有可能会阻塞,但是如果以读写方式(O_RDWR)打开则一定不会阻塞;以只读(O_RDONLY)方式打开时,调用open()的函数会被阻塞直到有数据可读;如果以只写方式(O_WRONLY)打开时同样也会被阻塞,知道有以读方式打开该管道。

FIFO:

O_NONBLOCK:

FIFO出错信息:

模拟FIFO读写:

写:

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

void sys_err(char *str)
{
    perror(str);
    exit(-1);
}

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

    if (argc < 2) {
        printf("Enter like this: ./a.out fifoname\n");
        return -1;
    }
    fd = open(argv[1], O_WRONLY);
    if (fd < 0) 
        sys_err("open");

    i = 0;
    while (1) {
        sprintf(buf, "hello itcast %d\n", i++);

        write(fd, buf, strlen(buf));
        sleep(1);
    }
    close(fd);

    return 0;
}

读:

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

void sys_err(char *str)
{
    perror(str);
    exit(1);
}

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

    if (argc < 2) {
        printf("./a.out fifoname\n");
        return -1;
    }
    fd = open(argv[1], O_RDONLY);
    if (fd < 0) 
        sys_err("open");
    while (1) {
        len = read(fd, buf, sizeof(buf));
        write(STDOUT_FILENO, buf, len);
        sleep(3);           //多個读端时应增加睡眠秒数,放大效果.
    }
    close(fd);

    return 0;
}

FIFO使用例子:

FIFO的通信方式类似于在进程中使用文件来传输数据,只不过FIFO类型文件同时具有管道的特性。在数据读出时,FIFO管道中同时清除数据,并且“先进先出”。下面的例子演示了使用 FIFO 进行 IPC 的过程:

write:

#include<stdio.h>
#include<stdlib.h>   // exit
#include<fcntl.h>    // O_WRONLY
#include<sys/stat.h>
#include<time.h>     // time
 
int main()
{
	int fd;
	int n, i;
	char buf[1024];
	time_t tp;
 
	printf("I am %d process.\n", getpid()); // 说明进程ID
	
	if((fd = open("fifo1", O_WRONLY)) < 0) // 以写打开一个FIFO 
	{
		perror("Open FIFO Failed");
		exit(1);
	}
 
	for(i=0; i<10; ++i)
	{
		time(&tp);  // 取系统当前时间
		n=sprintf(buf,"Process %d's time is %s",getpid(),ctime(&tp));
		printf("Send message: %s", buf); // 打印
		if(write(fd, buf, n+1) < 0)  // 写入到FIFO中
		{
			perror("Write FIFO Failed");
			close(fd);
			exit(1);
		}
		sleep(1);  // 休眠1秒
	}
 
	close(fd);  // 关闭FIFO文件
	return 0;
}

read

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<fcntl.h>
#include<sys/stat.h>
 
int main()
{
	int fd;
	int len;
	char buf[1024];
 
	if(mkfifo("fifo1", 0666) < 0 && errno!=EEXIST) // 创建FIFO管道
		perror("Create FIFO Failed");
 
	if((fd = open("fifo1", O_RDONLY)) < 0)  // 以读打开FIFO
	{
		perror("Open FIFO Failed");
		exit(1);
	}
	
	while((len = read(fd, buf, 1024)) > 0) // 读取FIFO管道
		printf("Read message: %s", buf);
 
	close(fd);  // 关闭FIFO文件
	return 0;
}

上述例子可以扩展成 客户进程—服务器进程 通信的实例,write_fifo的作用类似于客户端,可以打开多个客户端向一个服务器发送请求信息,read_fifo类似于服务器,它适时监控着FIFO的读端,当有数据时,读出并进行处理,但是有一个关键的问题是,每一个客户端必须预先知道服务器提供的FIFO接口,下图显示了这种安排:

发布了161 篇原创文章 · 获赞 15 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_42067873/article/details/104941503