【Linux】多进程管道通信,及select、poll函数在其中的应用

管道是常用的进程间的通信方法。通常的实验例程为——创建管道->创建进程->父进程通过管道发送数据->子进程通过管道接收数据并显示。如下代码所示,需要的东西都写在注释中。

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main()
{
    int     res;
    char    *buf = "this is data to trans"; //管道发送的数据
    char    readbuf[64] = {0};              //管道接受缓存
    int     fd[2];                          //0->读管道 1->写管道
    int     pid;

    //创建管道
    res = pipe(fd);                         
    if(res != 0)
    {
        printf("create pipe error\n");
        return 0;
    }

    //创建进程
    pid = fork();                           
    if(pid == -1)
    {
        printf("fork fail\n");
    }
    //子进程
    else if(pid == 0)                       
    {
        printf("this is child\n");
        close(fd[1]);
        //从读管道读取数据
        read(fd[0],readbuf,64);             
        printf("child rev :%s\n",readbuf);
        close(fd[0]);
    }
    //父进程
    else                                    
    {
        printf("this is parent\n");
        usleep(5000000);
        close(fd[0]);
        write(fd[1],buf,64);
        close(fd[1]);
        //等待子进程结束
        waitpid(pid,NULL,0);
    }

    return 0;
}

通过以上代码可以看出,在创建子进程后,父进程等待了5秒才通过管道向子进程发送了数据。而子进程一经创建,就通过read在试图获取读管道中的数据。实验结果如下

.wy@wy-VirtualBox:~/test/book/csdn$ ./a.out 
this is parent
this is child
child rev :this is data to trans
wy@wy-VirtualBox:~/test/book/csdn$

其中child rev :this is data to trans是在程序运行5秒后才打印出来的,这充分说明子进程中的read函数是默认阻塞的。通常,用read读字符终端设备、网络socket、管道时都是默认阻塞的。那么举一反三,某些情况下如果文件描述符中一直没有数据来,那我只能一直等着吗?select和poll这两个函数,就是加入了超时限制的触发函数。具体作用可以在linux下使用man手册查看,用在管道通信时,其大概功能描述就是,在超时时间内等待某一通道有数据可读,根据超时、可读等状态返回不同的返回值,然后我们就可以根据不同的返回值进行不同的操作。具体程序如下,select、poll的具体使用方法可自行man查看:
1.使用select的管道通信

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>

int main()
{
    int     res;
    char    *buf = "this is data to trans";
    char    readbuf[64] = {0};
    int     fd[2];
    int     pid;
    fd_set  fdread;                         //select文件句柄
    struct timeval timeout;                 //超时结构体

    timeout.tv_sec  = 6;
    timeout.tv_usec = 0;

    res = pipe(fd);
    if(res != 0)
    {
        printf("create pipe error\n");
        return 0;
    }
    //清select句柄
    FD_ZERO(&fdread);      
    //将读管道加入select句柄                 
    FD_SET(fd[0],&fdread);                  

    pid = fork();
    if(pid == -1)
    {
        printf("fork fail\n");
    }
    else if(pid == 0)
    {
        printf("this is child\n");
        close(fd[1]);

        //调用select,看规定timeout时间内,句柄集有无可读数据
        res = select(fd[0] + 1,&fdread,NULL,NULL,&timeout);
        if(res == -1)
        {
            printf("select error\n");
        }
        else if(res == 0)
        {
            printf("time out\n");
        }
        //有可读数据
        else
        {
            read(fd[0],readbuf,64);
            printf("child rev :%s\n",readbuf);
        }

        close(fd[0]);
    }
    else
    {
        printf("this is parent\n");
        usleep(5000000);
        close(fd[0]);
        write(fd[1],buf,64);
        close(fd[1]);
        //等待子进程结束
        waitpid(pid,NULL,0);
    }

    return 0;
}

2.使用poll的管道通信

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/poll.h>

int main()
{
    int     res;
    char    *buf = "this is data to trans";
    char    readbuf[64] = {0};
    int     fd[2];
    int     pid;
    int     timeout = 6000;                 //超时时间
    struct pollfd pfds;                     //poll结构体

    res = pipe(fd);
    if(res != 0)
    {
        printf("create pipe error\n");
        return 0;
    }

    //设置读管道为poll句柄
    pfds.fd     = fd[0];
    //设置poll的触发事件
    pfds.events = POLLIN | POLLPRI;

    pid = fork();
    if(pid == -1)
    {
        printf("fork fail\n");
    }
    else if(pid == 0) //child
    {
        printf("this is child\n");
        close(fd[1]);
        //规定时间内,监视pdfs这一个通道有无触发事件发生
        res = poll(&pfds,1,timeout);
        if(res == -1)
        {
            printf("poll error\n");
        }
        else if(res == 0)
        {
            printf("time out\n");
        }
        //有POLLIN或者POLLPRI事件,也意味着有数据可读
        else
        {
            read(fd[0],readbuf,64);
            printf("child rev :%s\n",readbuf);
        }

        close(fd[0]);
    }
    else
    {
        printf("this is parent\n");
        usleep(5000000);
        close(fd[0]);
        write(fd[1],buf,64);
        close(fd[1]);
        //等待子进程结束
        waitpid(pid,NULL,0);
    }

    return 0;
}

可以看出,在此程序中,select和poll在管道通信的应用中都是起到了“在超时时间内检查管道内有无可读数据”的作用。若超时时间内无数据可读,则提示超时,若有数据可读,则读出数据并打印。
父进程时隔5秒后向管道内发送数据,而程序中规定的超时时间为6秒,故不会超时,程序运行结果如下,也是5秒后子进程收到数据并打印,和一开始的程序结果相同:

wy@wy-VirtualBox:~/test/book/csdn$ ./a.out 
this is parent
this is child
child rev :this is data to trans
wy@wy-VirtualBox:~/test/book/csdn$

而当将超时时间设为5秒之内时,比如设置为2秒。那么2秒后提示超时,如下所示:

wy@wy-VirtualBox:~/test/book/csdn$ ./a.out 
this is parent
this is child
time out
wy@wy-VirtualBox:~/test/book/csdn$

猜你喜欢

转载自blog.csdn.net/spiremoon/article/details/106004076