进程间通信(四)有名管道

有名管道解决了无名管道进程只能是具有情缘关系的问题。

一、有名管道

无名管道是临时的,通信完成后自行消失。有名管道或者叫做命名管道(named pipe),可以实现没有亲缘关系的进程间通信,方法是使用FIFO文件。

1.1 shell中使用有名管道

一般而言,Linux中的fifo文件存储在/tmp/my_fifo, shell中可以直接创建fifo:

sudo mkfifo abc

然后进行读取和写入:

cat < /tmp/abc &  #让FIFO立刻打印收到的内容
echo "Hello world" > /tmp/abc #需要root权限,注意不是sudo权限

cat < filename将文件的内容打印至标准输出,如果此时另一进程将内容写至fifo文件可以直接在终端观察到管道的内容。

1.2 mkfifo创建有名管道

除了在shell中直接创建外,在C/C++程序中使用mkfifo接口创建:

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char * filename, mode_t mode);

输入参数

const char * filename 要打开的fifo文件

mode_t mode 打开模式

mode含义
O_RDONLY只读
O_WRONLY只写
O_NONBLOCK非阻塞

如以只读非阻塞方式打开文件,O_RDONLY|O_NONBLOCK

在没有设置O_NONBLOCK时,读写都可能出现阻塞:

  • read当文件为空时阻塞都关闭时,才会立刻返回0。

read是阻塞还是非阻塞读取取决于设备的设置,默认情况都是阻塞读取。

  • write当文件为满阻塞

FIFO文件大小一般为4096byte(4kb)在系统头文件limits.h PIPE_BUF宏定义。

输出参数
返回一个实际读取的字节数n(n<PIPE_BUF)。

unlink - call the unlink function to remove the specified file

1.3 读写有名管道

有名管道需要两个进程分别完成读写打开操作,否则系统将阻塞其中一个进程直至另一个相异的进程加入。

有名管道形成回路后,读操作:

  • 管道没有数据,读操作默认阻塞
  • 管道有数据,但小于规定量,读操作读取所有数据立刻返回
  • 管道有数据,但大于规定量,读操作读取期望数据立刻返回

有名管道形成回路后,写操作:

  • 管道没有空间,写操作默认阻塞
  • 管道有空间,但小于规定量,写满后阻塞
  • 管道有空间,但大于规定量,写入规定量后返回

有名管道形成回路后,中间有一个退出:

  • 未退出端为写操作,返回SIGPIPE信号
  • 未退出端为阻塞读操作,返回0后立即返回

二、例子

对于例子1,使用time命令证明管道命令在不到0.1s的时间却读取了10MB数据,可以说速度相当快了。

2.1 C/S 的例子1

客户端:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
const char* FIFO_NAME="/tmp/my_fifo";
#define BUFFER_SIZE PIPE_BUF
#define TEN_MEG (1024*1024*10)

int main()
{
    int pipe_fd;
    int res;
    int open_mode=O_WRONLY;
    int byte_sent=0;
    char buffer[BUFFER_SIZE+1];
    /************************创建FIFO********************************/
    if(access(FIFO_NAME,F_OK)==-1)//linux API,判断用户对于某个文件的权限
    {
        res=mkfifo(FIFO_NAME,0777);
        if(res!=0)
        {
            fprintf(stderr,"Could not create fifo %s\n",FIFO_NAME);
            exit(EXIT_FAILURE);
        }
    }

    /************************打开FIFO********************************/
    printf("Process %d opening FIFO O_WRONLY\n",getpid());
    pipe_fd=open(FIFO_NAME,open_mode);
    printf("Process %d result %d\n",getpid(),pipe_fd);

    /************************读写FIFO********************************/
    if(pipe_fd!=-1)
    {
        while(byte_sent<TEN_MEG)//没有写够足够的字节
        {
            res=write(pipe_fd,buffer,BUFFER_SIZE);//描述符 起始地址 大小,返回实际写的字节数
            if(res==-1)
            {
                fprintf(stderr,"Write error on pipe\n");
                exit(EXIT_FAILURE);
            }
            byte_sent+=res;//更新总的发送字节数
        }
        (void)close(pipe_fd);
    }
    else
    {
        exit(EXIT_FAILURE);
    }
    return 0;
}

客户端:

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

const char * FIFO_NAME="/tmp/my_fifo";
#define BUFFER_SIZE PIPE_BUF

int main()
{
    int pipe_fd;
    int res;
    int open_mode=O_RDONLY;
    char buffer[BUFFER_SIZE+1];
    int byte_read=0;

    memset(buffer,'\0',sizeof(buffer));
    printf("Process %d opening FIFO O_RDONLY\n",getpid());
    pipe_fd=open(FIFO_NAME,open_mode);
    printf("Process %d result %d\n",getpid(),pipe_fd);

    if(pipe_fd!=-1)
    {
        do{
            res=read(pipe_fd,buffer,BUFFER_SIZE);
            byte_read+=res;
        }while(res>0);
        (void)close(pipe_fd);
    }
    else{
        exit(EXIT_FAILURE);
    }
    printf("Process %d finished, %d byte read\n",getpid(),byte_read);
}

运行结果:
在这里插入图片描述

2.2 C/S 的例子2

客户端:

#include "client.h"
#include <ctype.h>

int main()
{
    int server_fifo_fd,client_fifo_fd;
    struct data_to_pass_st my_data;
    int time_to_send;
    char client_fifo[256];

    server_fifo_fd=open(SERVER_FIFO_NAME,O_WRONLY);
    if(server_fifo_fd==-1){
        fprintf(stderr,"Sorry, no server\n");
        exit(EXIT_FAILURE);
    }
    my_data.client_pid=getpid();
    sprintf(client_fifo,CLIENT_FIFO_NAME,my_data.client_pid);
    if(mkfifo(client_fifo,0777)==-1){
        fprintf(stderr,"Sorry, can't make %s\n",client_fifo);
        exit(EXIT_FAILURE);
    }
    for(time_to_send=0;time_to_send<5;time_to_send++){
        sprintf(my_data.some_data,"Hello from %d",my_data.client_pid);
        printf("%d sent %s, ",my_data.client_pid,my_data.some_data);
        write(server_fifo_fd,&my_data,sizeof(my_data));
        client_fifo_fd=open(client_fifo,O_RDONLY);
        if(client_fifo_fd!=-1){
            if(read(client_fifo_fd,&my_data,sizeof(my_data))>0){
                printf("received: %s\n",my_data.some_data);
            }
            close(server_fifo_fd);
        }
        

    }
    close(server_fifo_fd);
    unlink(client_fifo);
    exit(EXIT_SUCCESS);

}

服务端:

#include "client.h"
#include <ctype.h>
#include <iostream>

using namespace std;

int main()
{
    int server_fifo_fd,client_fifo_fd;
    struct data_to_pass_st my_data;
    int read_res;
    char client_fifo[256];
    char * tmp_char_ptr;

    mkfifo(SERVER_FIFO_NAME,0777);
    server_fifo_fd=open(SERVER_FIFO_NAME,O_RDONLY);
    if(server_fifo_fd==-1){
        fprintf(stderr,"Server fifo failure\n");
        exit(EXIT_FAILURE);
    }
    sleep(10);

    do{
        read_res=read(server_fifo_fd,&my_data,sizeof(my_data));
        // if(read_res>0){
            tmp_char_ptr=my_data.some_data;
            while(*tmp_char_ptr){
                *tmp_char_ptr=toupper(*tmp_char_ptr);
                tmp_char_ptr++;
            // }
            sprintf(client_fifo,CLIENT_FIFO_NAME,my_data.client_pid);
            client_fifo_fd=open(client_fifo,O_WRONLY);
            if(client_fifo_fd!=-1){
                int ttt=write(client_fifo_fd,&my_data,sizeof(my_data));
                close(client_fifo_fd);
            }
        }
    }while(read_res>0);
    close(server_fifo_fd);
    unlink(SERVER_FIFO_NAME);
    exit(EXIT_SUCCESS);
}
//头文件
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>

#define SERVER_FIFO_NAME "/tmp/server_fifo"
#define CLIENT_FIFO_NAME "/tmp/cli_%d_fifo"

#define BUFFER_SIZE 20

struct data_to_pass_st{
    pid_t client_pid;
    char some_data[BUFFER_SIZE-1];
};

三、小结

管道是一种抽象的概念,它充当了进程间信息交互的桥梁。常见的两种管道如下:

  • 有名管道
  • 无名管道

其中有名管道拥有更加丰富的访问控制规则,适用于没有亲缘关系的管道。无名管道则需要进程间有亲缘关系。


【1】《Linux程序设计(第五版)》
【2】《Linux高级程序设计(第三版)》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值