linux下进程通信------FIFO

13 篇文章 0 订阅
5 篇文章 0 订阅

1. 命名管道(FIFO)

  管道应用的一个重大缺陷就是没有名字,因此只能用于亲缘进程之间的通信。后来从管道的基础上提出命名管道(named pipe, FIFO)的概念,该限制得到了克服。FIFO 不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件的形式存在于文件系统中。这样即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能彼此通过FIFO通信(能够访问该路径的进程以及FIFO的创建进程之间),因此,通过FIFO不相关的进程也能交换数据。值得注意的是,FIFO严格遵循先进先出(first in first out),对管道及FIFO的读总是从开始处返回数据,对它们的写则是把数据添加到末尾。它们不支持诸如lsek() 等文件定位操作。

2. 命名管道的创建

#include <sys/types.h>
#include <sys/stat.h> 
int mkfifo(const char *pathname,    //文件的路径
       mode_t mode          //和open函数mode一样
      );

如果pathname路径下的文件已经存在,则mkfifo返回-1,errono将会返回EEXIST。

3. 命名管道的操作

  FIFIO在文件系统中表现为一个文件,大部分的系统文件调用都可以在FIFO上面,比如read, open, write, close, unlink, stat等函数,但是seek函数不能对FIFO调用。
可以调用open打开FIFO,请注意以下方面:

A 当以阻塞(未指定O_NONBLOCK)方式只读打开FIFO的时候,则将会被阻塞,直到有其他进程以写方式打开该FIFO。
B 类似的,当以阻塞(未指定O_NONBLOCK)方式只写打开FIFO的时候,则将会被阻塞,直到有其他进程以读方式打开该FIFO
C 当以非阻塞方式(指定O_NONBLOCK)方式只读打开FIFO的时候,则立即返回。当只写open时,如果没有进程为读打开FIFO,则返回-1,其errno是ENXIO
如果写入管道的数据量小于等于PIPE_BUF,则系统保证write为原子操作,多个进程同时写管道,将不会出现穿插;如果写入数据量大于PIPE_BUF,则系统将不保证write为原子操作,多个进程同时写管道,则将有可能会穿插写入。这个规则在命名管道中继续适用。

4. 应用实例

  本例为一个client-server模式,服务器端将会创建一个fifo文件(本例中为“/tmp/server”),并读取该fifo文件。客户端则打开该fifo文件,并把其请求写入该FIFO文件。服务器端读取该命令并执行之。

服务器端的代码为:

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

#define SERVER_FIFO_NAME "/tmp/serv_fifo"
#define CLIENT_FIFO_NAME "/tmp/cli_%d_fifo"
#define BUFFER_SIZE 50


//传递数据的结构体
struct data_to_pass_st {
    pid_t client_pid;
    char some_data[BUFFER_SIZE - 1];
};

int main() 
{
    int server_fifo_fd, client_fifo_fd;
    struct data_to_pass_st mydata;
    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, &mydata, sizeof(mydata));
        if (read_res > 0) 
        {
            tmp_char_ptr = mydata.some_data;
            while (*tmp_char_ptr > 0) 
            {
                //把some_data中的所有字符全部转换为大写
                *tmp_char_ptr = toupper(*tmp_char_ptr);
                tmp_char_ptr++;
            }
            sprintf(client_fifo, CLIENT_FIFO_NAME, mydata.client_pid);
            client_fifo_fd = open(client_fifo, O_WRONLY);

            if (client_fifo_fd != -1) 
            {
                write(client_fifo_fd, &mydata, sizeof(mydata));
                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 <unistd.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>

#define SERVER_FIFO_NAME "/tmp/serv_fifo"
#define CLIENT_FIFO_NAME "/tmp/cli_%d_fifo"
#define BUFFER_SIZE 50

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

int main() 
{
    int server_fifo_fd, client_fifo_fd;
    struct data_to_pass_st my_data;
    int times_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 (times_to_send = 0; times_to_send < 5; times_to_send++) 
    {
        sprintf(my_data.some_data, "Hello from %d", my_data.client_pid);
        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(client_fifo_fd);
        }
    }
    close(server_fifo_fd);
    unlink(client_fifo);
    exit(EXIT_SUCCESS);

}

5. 总结

  与管道相比,FIFO最大的特点就是其在文件系统中有fifo文件存在,这样就可以做到进程间通信。

6. FIFO的缺点

  当然FIFO也有它的局限性。客户端可以发请求到服务器,但前提是要知道一个公共的FIFO通道,对于实现服务器回传应答到客户端的问题,可以通过为每一个客户端创建一个专用的FIFO,来实现回传应答。但也有不足,服务器会同时应答成千上万个客户端,创建如此多的FIFO是否会使系统负载过大,相应的如何判断客户端是否因意外而崩溃成为难题,或者客户端不读取应答直接退出,所以服务器必须处理SIGPIPE信号,并做相应处理。(当没有进程为读打开FIFO文件时,write函数进行写操作会产生SIGPIPE信号)。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值