linux进程间通信:命名管道FIFO

文章目录

        • FIFO 通信特点
        • 系统调用接口
        • 应用
          • 拥有亲缘关系之间的进程通信
          • 非亲缘关系进程之间的通信
        • 总结

FIFO 通信特点

  • FIFO文件有文件名
    • 可以像普通文件一样存储在文件系统之中
    • 可以像普通文件一样使用open/write读写
    • 和pipe文件一样属于流式文件,不能使用lseek系统调用重定位文件偏移地址
  • 具有写入原子性,支持多个进程同时对FIFO进行写操作,如日志系统/var/log
  • First in First out,先入先出。最先被写入的数据最先被读出来
    • 默认阻塞读,阻塞写的特性。可以在open的时候进行设置
    • 如果一个进程打开fifo的一端时,令一端没有打开,该进程会被阻塞。

fifo与pipe最大的区别就是pipe只能用于亲缘进程之间的通信(fork方式创建的父子进程),但是fifo可以支持任意两个进程之间的通信,并包括拥有亲缘关系的进程

系统调用接口

  • shell命令:mkfifo pathname,可以通过man mkfifo查看命令用法
  • 函数接口:int mkfifo(const char *pathname,mode_t mode)
  • 函数功能:创建fifo的有名管道
  • 函数参数:
    - pathname FIFO管道文件名
    - mode 读写权限

应用

拥有亲缘关系之间的进程通信

通信过程类似于pipe的fd[0]读出,fd[1]写入


#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define FIFO "testfifo"
int main()
{
        int a=mkfifo(FIFO,0644);
        if (0 != a)
                printf("mkfifo failed %d\n",a);
        int ret,fifo_fd;
        ret = fork();
        if (ret == -1) {
                perror("fork");
                _exit(-1);
        }
        //子进程负责写入管道
        else if (ret == 0) {
                fifo_fd = open("testfifo",O_RDWR);
                char buf[100];
                memset(buf,0,100);
                printf("child process(%d) input msg:",getpid());
                //scanf("%s",buf);
                fgets(buf,10,stdin);
                int write_len = write(fifo_fd,buf,strlen(buf));
                printf("%d bytes has been sent\n",write_len);
                _exit(1);
        }
        //父进程进行从管道进行读取
        else {
                sleep(5);
                fifo_fd = open("testfifo",O_RDWR);
                char buf[100];
                memset(buf,0,100);
                printf("father process befor read\n");
                int read_len = read(fifo_fd,buf,100);
                if (read_len == -1)
                        printf("read error %d\n",read_len);
                printf("father process(%d) read message from fifo :%s\n",getpid(),buf);
                _exit(1);
        }
        return 0;
}

非亲缘关系进程之间的通信

写端:


#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define FIFO_NAME "testfifo"

int main(int argc, char* argv[])
{
    int fd;
    char buf[100];
    mkfifo(FIFO_NAME, S_IFIFO|0666);
    //循环向testinfo中写入数据
    while (1)
	{
        printf("please input the string : \n");
	    fd = open(FIFO_NAME, O_WRONLY);
	    fgets(buf,100,stdin);
	    write(fd, buf, strlen(buf)+1);
	    close(fd);
	}

    unlink(FIFO_NAME); //删除命名管道文件
    sleep(1);
	return 0;
}

读端:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 



#define FIFO_NAME "testfifo"
#define BUF_SIZE 1024

int main(int argc, char* argv[])
{
    int fd;
    char buf[BUF_SIZE];
	
	//循环从testinfo中读取数据
    while (1) {
        fd = open(FIFO_NAME, O_RDONLY);
        //read系统调用会从打开的文件当前文件偏移地址开始读,即上次写入的数据
        read(fd, buf, BUF_SIZE);

        printf("%s\n", buf);

            close(fd);
    }
    return 0;
}

总结

为什么无名管道只能用于父子进程之间的通信呢?无名管道是一个文件,但是只能存在与内存之中,且该文件的读写方式是固定的,我们只能从一端写入,一端读出。使用无名管道通信,他得文件句柄需要在同一个进程内共享。即在同一个进程内只有fork的方式,能够将当前进程所有文件描述符以及进程状态信息拷贝给另一个进程;只有在这样的进程关系之下,无名管道的数据传输才能实现一端写入,一端读出。

命名管道fifo可以像普通文件一样存在与操作系统磁盘之中,同样支持read/write的数据读写,因此它能够被多个进程共同访问。虽然fifo相比于pipe使用范围扩大了,但是总体的阻塞式通信依然存在。

pipe的内核实现fs/pipe.cpipe_readpipe_write函数处理过程中都是通过加锁mutex来实现,并没有相关的异步操作,而fifo的fifo_open函数同样是阻塞式处理函数体。

你可能感兴趣的:(#,编程语言C,#,linux操作系统:进程管理,编程语言)