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