zoukankan      html  css  js  c++  java
  • Linux进程通信——有名管道

    管道(pipe)是无名管道,他是进程资源的一部分,随着进程的结束而消失。并且它只能在拥有公共祖先进程的进程内通信。而有名管道(FIFO)的出现则解决了这个问题。FIFO提供了一个路径名与它关联。这样可以通过访问该路径就能使得两个进程之间相互通信。此处的FIFO严格遵守“先进先出”原则。读总是从头开始的,写总是从尾部进行的。匿名管道和FIFO都不支持lseek函数对他们操作。Linux下建立有名管道的函数是mkfifo。

    函数原型: int mkfifo(const char * pathname,mode_t mode);

    函数功能:创建一个FIFO文件,用于进程之间的通信。pathname就是路径名,mode是该文件的权限。返回值表示是否成功,返回0表示成功,返回-1表示失败,失败原因在errno中。(建立FIFO的时候,要求不存在这样的FIFO)。

    例如执行下面代码来创建一个FIFO。

    #include<sys/types.h>
    #include<sys/stat.h>
    #include<stdio.h>
    
    int main()
    {
        int ret;
        ret = mkfifo("My_FIFO",0666);
        if(0 != ret)
        {
            perror("mkfifo");
        }
        return 0;
    }

     可以看到,它以P开头,表面它是一个FIFO文件。它是真实存在于磁盘上的,不仅仅在内存中。进程结束了这个文件仍然在。FIFO和匿名管道一样,默认下要考虑阻塞。

    1. 当使用O_NONBLOCK标志的时候,打开FIFO文件,读取操作会立即返回。但是如果没有进程读取FIFO文件,那么写入FIFO的操作会返回ENXIO错误代码。
    2. 不使用O_NONBLOCK标志时,打开FIFO的操作必须等待其他进程写入FIFO后才执行,当然写入FIFO的操作也必须等到其他进程来读取FIFO以后才能执行。

    当存在这个FIFO文件的时候,再次创建这个FIFO会显示File exists。首先,第一种情形的测试。

    #include<sys/types.h>
    #include<sys/stat.h>
    #include<stdio.h>
    #include<fcntl.h>
    #include<unistd.h>
    #include<stdlib.h>
    
    
    int main()
    {
        int ret;
        int fd;
        ret = mkfifo("My_FIFO",0666);
        if(0 != ret)
        {
            perror("mkfifo");
        }
        pid_t pid;
        pid = fork();
        if(0 < pid)
        {
            fd = open("My_FIFO",O_NONBLOCK|O_WRONLY);
            if(0 > fd)
            {
                perror("open FIFO");
            }
            else
            {
                write(fd,"Hello",5);
                printf("Write Over
    ");
            }
            exit(0);
        }
        if(0 == pid)
        {
            sleep(2);       //在设置O_NONBLOCK标志的情形下,让父进程先写入
        }
        if(-1 == pid)
        {
            perror("fork");
        }
        return 0;
    }

    运行结果如下:

    可以看到会发生错误,因为它没有阻塞。那么接着试一下直接读一个FIFO文件,看看会发生什么。

    #include<sys/types.h>
    #include<sys/stat.h>
    #include<stdio.h>
    #include<fcntl.h>
    #include<unistd.h>
    #include<stdlib.h>
    
    
    int main()
    {
        int ret;
        int fd;
        char str[30] = {0};
        ret = mkfifo("My_FIFO",0666);
        if(0 != ret)
        {
            perror("mkfifo");
        }
        pid_t pid;
        pid = fork();
        if(0 < pid)
        {
            fd = open("My_FIFO",O_NONBLOCK|O_RDONLY);   //注意这里和上面不一样,改成O_RDONLY
            if(0 > fd)
            {
                perror("open FIFO");
            }
            else
            {
                read(fd,str,sizeof(str));
                printf("Read Over
    ");
            }
            exit(0);
        }
        if(0 == pid)
        {
            sleep(2);       //在设置O_NONBLOCK标志的情形下,让父进程直接读取
        }
        if(-1 == pid)
        {
            perror("fork");
        }
        return 0;
    }

     看到的结果是对一个空的FIFO文件打开并执行read操作是没有问题的。

    先以只读方式打开,如果没有进程已经为写而打开一个 FIFO, 只读 open() 成功,并且 open() 不阻塞。

    下面,当不设置O_NONBLOCK标志的时候,FIFO和匿名管道的处理方式是一样的。管道这个名字是非常形象的,一个管道必须有两端(就是在一个进程中必须读,另一个进程必须写),只有这样,才能正常操作,否则进程将会阻塞。例如下面这样。

    #include<sys/types.h>
    #include<sys/stat.h>
    #include<stdio.h>
    #include<fcntl.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<wait.h>
    
    int main()
    {
        int ret;
        int fd;
        char str[30] = {0};
        ret = mkfifo("My_FIFO",0666);
        if(0 != ret)
        {
            perror("mkfifo");
        }
        pid_t pid;
        pid = fork();
        if(0 < pid)
        {
            fd = open("My_FIFO",O_WRONLY);
            if(0 > fd)
            {
                perror("Write FIFO");
            }
            else
            {
                write(fd,"Hello",5);
                printf("Write Over
    ");
            }
            wait(NULL);
            exit(0);
        }
        if(0 == pid)
        {
            sleep(2);
            // fd = open("My_FIFO",O_RDONLY);
            // read(fd,str,5);
            exit(0);
        }
        if(-1 == pid)
        {
            perror("fork");
        }
        return 0;
    }

    我们仅仅在父进程中进行了写(write),没有其他进程在读(read)。这样造成的结果是进程一直阻塞在这里,如下

    没有输出结果,阻塞在这里不动了。而当我们加上注释掉了那两句话以后,程序就会有输出,结果如下:

    或者说,这也体现了进程的并发行,管子有了一端以后,还必须有另一端,这才能构成管道。

    测试一下,FIFO用于两个无关进程直接的通信。首先建立我们有两个进程,一个是test1,另一个是test2.

    //test1的源代码
    //test1是写FIFO
    
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<stdio.h>
    #include<fcntl.h>
    #include<unistd.h>
    #include<stdlib.h>
    
    int main()
    {
        int fd,ret;
        char str[10] = {"Hello"};
        ret = mkfifo("fifo",0666);      //test1.c中创建FIFO文件
        fd = open("fifo",O_WRONLY);     //只写方式打开
        write(fd,str,5);
        close(fd);
    
        return 0;
    }
    
    //test2的源代码
    //test2是读FIFO
    
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<stdio.h>
    #include<fcntl.h>
    #include<unistd.h>
    #include<stdlib.h>
    int main()
    {
        int fd;
        char str[10] = {0};
        fd = open("fifo",O_RDONLY);
        read(fd,str,5);
        printf("%s
    ",str);
        close(fd);
        return 0;
    }

    我们把test1和test2的源代码生成可执行文件后,打开两个终端。如果我先运行test1,然后运行test2.那么test2将读取到FIFO中的数据。如下所示。

    我们没有设置O_NONBLOCK,先运行test1之后,会发现test1阻塞在这里。等我们把test2也运行了之后,test1不在阻塞,运行结束,然后test2也成功打印出了Hello。

    换个运行顺序,我们先运行test2,然后运行test1.这样会发现test2阻塞在这里。等我们把test1也运行了之后,test2不在阻塞,向屏幕打印Hello.

    如果我们不想让FIFO阻塞,那么打开文件的时候设置为可读可写即可。

    fd = open("fifo", O_RDWR);

    当然,如果FIFO是空的,那么即使设置了可读可写,read()操作仍旧会阻塞。这样的运行结果和上面所说的是一致的。自己运行一下才能深刻理解。这里不好用图片说明。

    调用 write() 函数向 FIFO 里写数据,当缓冲区已满时 write() 也会阻塞。

    通信过程中,读进程退出后,写进程向命名管道内写数据时,写进程也会退出。

  • 相关阅读:
    sqlserver2000及以上版本导出数据到mysql里的详细图解
    JDBC和ODBC的区别何在?什么是ODBC?
    mysql ODBC 在64位下提示找不到odbc驱动问题
    Microsoft sqlserver2000如何导入.mdf格式的数据库文件
    使用navicat 链接sql server 出现08001错误
    sql语句如何转成hql语句?
    显示所有SAP图标的ABAP代码
    SMW0上传EXCEL模板时报错无分配给对象***的MIME类型
    显示小闹钟的函数
    便携计算器
  • 原文地址:https://www.cnblogs.com/zy666/p/10504273.html
Copyright © 2011-2022 走看看