Linux进程---有名管道与无名管道(详细篇)

进程的语言

进程间的通信 (IPC) 方式,总归起来主要有如下这些:

1,无名管道 (PIPE) 和有名管道 (FIFO) 。

2,信号 (signal) 。

3system V-IPC 之共享内存。

4system V-IPC 之消息队列。

5system V-IPC 之信号量。

6,套接字

这些通信方式各有各的特,无名管道是最简单的常用于一对一的亲缘进程间通信的方

式,有名管道存在于文件系统之中,提供写入原子性特征,信号是唯一一种异步通信方式, 共享内存的效率最高但是要结合信号量等同步互斥机制一起使用,消息队列提供一种带简 单消息标识的通信方,套接字是一种更为宽泛意义上的进程间通信方式——它允许进程间 网络。

 无名管道

常说的管道通常指无名管道 (PIPE) 或者有名管道 (FIFO) ,但实际上套接字也都 道。这里先把 PIPE FIFO 的相关接口摆出:

建无名管道:PIPE

文件

#include <unistd.h>

int pipe(int pipefd[2]);

参数

pipefd

一个少具有 2 int 型数据的数组,用来存放 PIPE 的读写端描述符

回值

成功

0

- 1

创建有名管道:FIFO

文件

#include <sys/types.h>

#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);

参数

pathname

FIFO 文件名

mode

权限

回值

成功

0

- 1

 创建无名管道和有名管道的函数接口规范

先来看看 PIPE 。既然叫管道,那么可以想象他就像一根水管,连接两个进程,一个进 程要给另一个进程数,就好像将水灌进管道一样,另一方就可以读取出来了,反过来也一 样。这是我们对 PIPE 最简单的感官认识。

先来罗列 PIPE 的特征:

1有名字,因此无法使用 open( )

2,只能用于亲缘进程间 (比如父子进程、兄弟进程、祖孙进程……) 通信。

 3,半双工工作方式:读写端分开,一个只能用来读,一个只能用来写。

4,写入操作不具有原子性,因此只能用于一对一的简单通信情形。

5,不使用 lseek( )来定位:因为他们的数据不能像普通文件那样按块的方式存储在硬盘,而是像一个看不见源头的水龙,无法定位。

6、管道都不可以在共享文件中创建。

PIPE的缺点是:

 他对写操作不做任何保护!即:假如有多个进程或线程同时PIPE 进行写操作,那么这些数据很有可能会相互践踏。因此,PIPE 只能用于一对一的亲缘进程通信。

无名管道示例代码如下:

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <string.h>

int main()
{
	int fd[2];
	
	int ret = pipe(fd);
	if(-1 == ret)
	{
		perror("open pipe failed");
		return -1;
	}
	char buf[128];
	
	char msg[128];
	
	pid_t pid = fork();
	if(pid == 0)
	{
		read(fd[0],msg,sizeof(msg)-1);//阻塞等待
		printf("[from child]%s\n",msg);
	}
	if(pid > 0)
	{
		fgets(buf,sizeof(buf),stdin);
		write(fd[1],buf,strlen(buf));
		
	}
	
	
}
上述代码中,创建一个无名管道父进程负责管道的发送端,子进程负责管道的接受端。

有名管道 

 任何事物的优缺点都是相对的,PIPE 很简单,同时也适用场景比较单一,性能比较弱, 限制条比较多,如果要在任意进程间通信,并且保证写入有原子性,那么你可以使用 FIFO --- 一种更加强大的管道。

管道 FIFO 的特征:

1,有名字,存储于普通文件系统之中。

2,任何具有相应权限进程都可以使用 open( )来获取 FIFO 的文件描述符。 

3,跟普通文件一样:使用统一的 read( )/write( )来读写。

4,跟普通文件不同:不能使用 lseek( )来定位,原因同 PIPE

5,具有写入原子性,支持多写者同时进行写操作而数据不会互相践踏。

 6First In First Out,最先被写入 FIFO 的数据,最先被读出来。

代码示例:写端

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>  //ioctl()
#include <unistd.h>

#define PATH "/home/xwq/ls"  //一个宏定义内容是管道存放路径

int main()
{
    //unlink(PATH);  // 如果fifo文件已存在,则先删除

    // 判断文件是否存在,如果不存在,则创建fifo文件
    if (access(PATH, F_OK) != 0)
    {
        int ret = mkfifo(PATH, 0777);
        if (ret == -1)
        {
            perror("creat myfifo failed");
            return -1;
        }
    }

    // 打开fifo文件,返回文件描述符
    int fifo_fd = open(PATH, O_RDWR);  //权限
    if (-1 == fifo_fd)
    {
        perror("open myfifo failed");
        return -1;
    }

    pid_t pid = fork();  // 创建子进程

    char buf[128];
    while (1)
    {
        memset(buf, 0, sizeof(buf));  //清零

        // 从标准输入获取用户输入
        fgets(buf, sizeof(buf), stdin);

        // 将用户输入写入fifo文件
        write(fifo_fd, buf, strlen(buf));

        if (strcmp(buf, "exit\n") == 0)
        {
            break;
        }
    }

    // 关闭文件描述符
    close(fifo_fd);

    return 0;
}

读端

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>  //ioctl()
#include <unistd.h>


#define PATH "/home/xwq/myfifo"

int main()
{
    if(access(PATH,F_OK) != 0)//判断文件是否存在
    {
        int ret = mkfifo(PATH, 0777); // 创建命名管道
        if(ret == -1)
        {
            perror("creat myfifo failed");
            return -1;
        }
    }
    
    int fifo_fd = open(PATH,O_RDWR); // 打开命名管道
    if(-1 == fifo_fd)
    {
        perror("open myfifo failed");
        return -1;
    }
    
    char buf[128];
    while(1)
    {
        memset(buf, 0, sizeof(buf)); // 清空buf数组
        
        read(fifo_fd, buf, sizeof(buf)-1); // 读取命名管道中的数据
        
        printf("[from jack] %s",buf);    //输出接收到的信息
        
        if(strcmp(buf,"exit\n") == 0) // 如果接收到了退出信号,则退出循环  strcmp 字符串的等式判断
        {
            break;
        }
        
    }
    
    close(fifo_fd); // 关闭命名管道
    
    return 0;
    
}

上述代码中

所谓写者:持有文件可写权限的描述符的进程。

所谓读者:持有文件可读权限的描述符的进程

access函数通常用于检查文件或目录是否存在以及是否具有特定权限。它接受文件路径和权限参数,并返回一个整数值,表示访问权限的状态。常见的返回值包括0(表示有访问权限)、-1(表示访问被拒绝)和其他错误代码。

FIFO PIPE 区别的还有一个最大的不同点在于:FIFO 具有一种所谓写入原子性的特

这种特征使得我们可以同时对 FIFO 进行写操作而不怕数据遭受破坏,一个典型应用是 Linux 志系统。

日志

日志示例代码如下:

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>//ioctl()
#include <unistd.h>
#include <time.h>

#define PATH "/home/gec/myfifo"

int main()
{
	if(access(PATH,F_OK) != 0)
	{
		int ret = mkfifo(PATH,0777);
		if(-1 == ret)
		{
			perror("creat\n");
			return -1;
		}
	}
	
	int fifo_fd = open(PATH, O_WRONLY);
	if(-1 == fifo_fd)
	{
		perror("open\n");
		return -1;
	}
	
	char buf[128];
	
	
	while(1)
	{
		memset(buf,0,sizeof(buf));
		
		time_t curTime;
		
		sleep(1);
		
		curTime++;
		
		time(&curTime);//把时间转换为当前的时间
		
		char *curDate = ctime(&curTime);//把时间转换为char *字符串
		
		snprintf(buf,sizeof(buf),"[%d]%s",getpid(),curDate);
		
		printf("%s\n",buf);
		
		write(fifo_fd,buf,strlen(buf));
		
	}
	
	close(fifo_fd);
	
}

写端

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>//ioctl()
#include <unistd.h>


#define PATH "/home/gec/myfifo"

int main()
{
	if(access(PATH,F_OK) != 0)//判断文件是否存在
	{
		int ret = mkfifo(PATH, 0777);
		if(ret == -1)
		{
			perror("creat myfifo failed");
			return -1;
		}
	}
	
	int fifo_fd = open(PATH,O_RDWR);
	if(-1 == fifo_fd)
	{
		perror("open myfifo failed");
		return -1;
	}
	
	int fd = open("./rizhi.txt", O_RDWR | O_CREAT ,0777);
	if(-1 == fd)
	{
		perror("open fd failed");
		return -1;
	}
	
	char buf[128];
	while(1)
	{
		memset(buf, 0, sizeof(buf));
		
		read(fifo_fd, buf, sizeof(buf)-1);
		
		write(fd,buf,strlen(buf));
		
		//printf("[from jack] %s",buf);
		
	}
	
	close(fifo_fd);
	close(fd);
	
	return 0;
	
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值