• Linux进程间通信(一)


    管道(pipe

    普通的Linux shell都允许重定向,而重定向使用的就是管道。

    例如:ps | grep vsftpd .管道是单向的、先进先出的、无结构的、固定大小的字节流,它把一个进程的标准输出和另一个进程的标准输入连接在一起。写进程在管道的尾端写入数据,读进程在管道的头端读出数据。数据读出后将从管道中移走,其它读进程都不能再读到这些数据。管道提供了简单的流控制机制。管道主要用于不同进程间通信。

    可以通过打开两个管道来创建一个双向的管道。但需要在子进程中正确地设置文件描述符。必须在系统调用fork()前调用pipe(),否则子进程将不会继承文件描述符。当使用半双工管道时,任何关联的进程都必须共享一个相关的祖先进程。因为管道存在于系统内核之中,所以任何不在创建管道的进程的祖先进程之中的进程都将无法寻址它。而在命名管道中却不是这样。

    clip_image002

    相关函数:

    //打开一个管道,2int的数组fildes分别存储读端和写端的FD

    Int pipe(int fildes[2]);

    //管道读

    ssize_t read(int fd, void* buf, size_t count);

    //管道写

    ssize_t write(int fd, const void* buf, size_t count);

     

    代码说明:子进程写,父进程读

    #include<unistd.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include <string.h>
    
    #define READFD 0
    #define WRITEFD 1
    
    int main(int argc, char *argv[])
    {
        int pipe_fd[2];
        pid_t pid;
        
        if (pipe(pipe_fd) < 0) 
        {
            printf("pipe create error
    ");
            exit(1);
        }
        
        if ((pid = fork()) < 0 )
        {
            printf("fork error
    ");
            exit(1);
        }
    
        // parent
        if (pid > 0) 
        {
            char buf_r[100];
            memset(buf_r, 0, sizeof(buf_r));
            
            close(pipe_fd[WRITEFD]);
            sleep(2);
            while (read(pipe_fd[0], buf_r, 100)) 
            {
                printf("print from parent ==> %s
    ", buf_r);
            }
            close(pipe_fd[READFD]);
            exit(0);
        }
        else if (pid == 0) 
        {
            close(pipe_fd[READFD]);
            write(pipe_fd[WRITEFD], "Hello", 5);
            write(pipe_fd[WRITEFD], " Pipe", 5);
            close(pipe_fd[WRITEFD]);
            exit(0);
        }
        
        exit(0);
    }

    结果说明:

    [root@rocket ipc]# g++ -g -o ipc_pipe ipc_pipe.cpp

    [root@rocket ipc]# ./ipc_pipe

    print from parent ==> Hello Pipe

    命名管道(FIFO

    命名管道也被称为FIFO文件,它是一种特殊类型的文件,它在文件系统中以文件名的形式存在,但是它的行为却和之前所讲的匿名管道(pipe)类似。

    由于Linux中所有的事物都可被视为文件,所以对命名管道的使用也就变得与文件操作非常的统一,也使它的使用非常方便,同时我们也可以像平常的文件名一样在命令中使用。

    相关函数:

    //创建命名管道

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

    观察命名管道:

    [root@rocket tmp]# file my_fifo

    my_fifo: fifo (named pipe)

    可以看出,命名管道是一种特殊的文件,可以按照文件的读写方式去操作。

    访问命名管道

    打开FIFO文件

    与打开其他文件一样,FIFO文件也可以使用open调用来打开。注意,mkfifo函数只是创建一个FIFO文件,要使用命名管道还是要调用open将其打开。

     

    有两点要注意:

    1、就是程序不能以O_RDWR模式打开FIFO文件进行读写操作,而其行为也未明确定义,因为如一个管道以读/写方式打开,进程就会读回自己的输出,我们通常使用FIFO只是为了单向的数据传递。

    2、就是传递给open调用的是FIFO的路径名,而不是正常的文件。

     

    打开FIFO文件通常有四种方式,

    open(const char *path, O_RDONLY);

    open(const char *path, O_RDONLY | O_NONBLOCK);

    open(const char *path, O_WRONLY);

    open(const char *path, O_WRONLY | O_NONBLOCK);

    open函数的调用的第二个参数中,你看到选项O_NONBLOCK,选项O_NONBLOCK表示非阻塞,加上这个选项后,表示open调用是非阻塞的,如果没有这个选项,则表示open调用是阻塞的。

     

    open调用的阻塞是什么一回事呢?很简单,对于以只读方式(O_RDONLY)打开的FIFO文件,如果open调用是阻塞的(即第二个参数为O_RDONLY),除非有一个进程以写方式打开同一个FIFO,否则它不会返回;如果open调用是非阻塞的(即第二个参数为O_RDONLY | O_NONBLOCK),则即使没有其他进程以写方式打开同一个FIFO文件,open调用将成功并立即返回。

    对于以只写方式(O_WRONLY)打开的FIFO文件,如果open调用是阻塞的(即第二个参数为O_WRONLY),open调用将被阻塞,直到有一个进程以只读方式打开同一个FIFO文件为止;如果open调用是非阻塞的(即第二个参数为O_WRONLY | O_NONBLOCK),open总会立即返回,但如果没有其他进程以只读方式打开同一个FIFO文件,open调用将返回-1,并且FIFO也不会被打开。

    代码说明:fifo读写进程

    写进程

    #include <unistd.h>  
    #include <stdlib.h>  
    #include <fcntl.h>  
    #include <limits.h>  
    #include <sys/types.h>  
    #include <sys/stat.h>  
    #include <stdio.h>  
    #include <string.h>  
      
    int main(int argc, char** argv)
    {  
        const char *fifo_name = "/tmp/my_fifo";  
        int pipe_fd = -1;    
        int res = 0;  
        const int open_mode = O_WRONLY;  
         
        if(access(fifo_name, F_OK) == -1)  
        {  
            //管道文件不存在  
            //创建命名管道  
            res = mkfifo(fifo_name, 0777);  
            if(res != 0)  
            {  
                fprintf(stderr, "Could not create fifo %s
    ", fifo_name);  
                exit(EXIT_FAILURE);  
            }  
        }  
      
        printf("Process %d opening FIFO O_WRONLY
    ", getpid());  
        //以只写阻塞方式打开FIFO文件,以只读方式打开数据文件  
        pipe_fd = open(fifo_name, open_mode);  
        printf("Process %d result %d
    ", getpid(), pipe_fd);  
      
        if(pipe_fd != -1)  
        {  
            write(pipe_fd, "hello", 5);
            write(pipe_fd, " fifo", 5);
            printf("Process write finished
    ", getpid());  
            close(pipe_fd);
        }  
        else
        {
            exit(EXIT_FAILURE);  
        }
        printf("Process %d finished
    ", getpid());  
        exit(EXIT_SUCCESS);  
    }

    读进程

    #include <unistd.h>  
    #include <stdlib.h>  
    #include <stdio.h>  
    #include <fcntl.h>  
    #include <sys/types.h>  
    #include <sys/stat.h>  
    #include <limits.h>  
    #include <string.h>  
      
    int main(int argc, char** argv)  
    {  
        const char *fifo_name = "/tmp/my_fifo";  
        int pipe_fd = -1;  
        int res = 0;  
        int open_mode = O_RDONLY;  
        char buffer[PIPE_BUF + 1];  
        int bytes_read = 0;  
        
        //清空缓冲数组  
        memset(buffer, '', sizeof(buffer));  
        
         if(access(fifo_name, F_OK) == -1)  
        {  
            //管道文件不存在  
            //创建命名管道  
            res = mkfifo(fifo_name, 0777);  
            if(res != 0)  
            {  
                fprintf(stderr, "Could not create fifo %s
    ", fifo_name);  
                exit(EXIT_FAILURE);  
            }  
        }  
      
        printf("Process %d opening FIFO O_RDONLY
    ", getpid());  
        //以只读阻塞方式打开管道文件,注意与fifowrite.c文件中的FIFO同名  
        pipe_fd = open(fifo_name, open_mode);  
        printf("Process %d result %d
    ",getpid(), pipe_fd);  
        sleep(3); //这里sleep一下,先等写进程写数据
        if (pipe_fd != -1)  
        {
            //读取FIFO中的数据  
            res = read(pipe_fd, buffer, PIPE_BUF);
            bytes_read += res;
            printf("get data %s 
    ", buffer);  
            
            close(pipe_fd);
        }  
        else  
        {
            exit(EXIT_FAILURE);  
        }
      
        printf("Process %d finished, %d bytes read
    ", getpid(), bytes_read);  
        exit(EXIT_SUCCESS);  
    }

    结果说明:

    [root@rocket ipc]# g++ -g -o ipc_fifo_reader ipc_fifo_reader.cpp

    [root@rocket ipc]# g++ -g -o ipc_fifo_writer ipc_fifo_writer.cpp

    [root@rocket ipc]# ./ipc_fifo_writer

    Process 74640 opening FIFO O_WRONLY

    // 这里会阻塞住,直到ipc_fifo_reader调用open打开同一个fifo

    Process 74640 result 3

    Process write finished

    Process 74640 finished

     

    [root@rocket ipc]# ./ipc_fifo_reader

    Process 74639 opening FIFO O_RDONLY

    Process 74639 result 3

    get data hello fifo

    Process 74639 finished, 10 bytes read

     

    两个程序都使用阻塞模式的FIFO,为了让大家更清楚地看清楚阻塞究竟是怎么一回事,首先我们运行./ipc_fifo_writer,发现其阻塞在open调用,直到./ipc_fifo_reader调用open,而./ipc_fifo_readeropen调用虽然也是阻塞模式,但是./ipc_fifo_writer早已运行,即早有另一个进程以写方式打开同一个FIFO,所以open调用立即返回。

     

  • 相关阅读:
    DICOM:DICOM3.0网络通信协议
    Maven使用—拷贝Maven依赖jar包到指定目录
    Spring Boot使用JavaMailSender发送邮件
    SpringBoot配置Email发送功能
    MariaDB 安装与启动 过程记录
    ESXi与Linux主机配置syslog日志上传远程服务器
    Linux--忘记MySQL密码的解决方法和输入mysqld_safe --skip-grant-tables &后无法进入MySQL的解决方法
    centos killall安装
    centos安装lspci工具
    oracle创建job和删除job
  • 原文地址:https://www.cnblogs.com/linuxbug/p/4863724.html
Copyright © 2020-2023  润新知