Linux中管道的使用(父或子进程中能否都保留读写端)

cumirror 2011-10-31 10:25:58
最近在看管道相关的操作,管道可用于父子进程间的IPC。
通常情况下,数据流是单向的(半双工),如对于父->子的管道,在父进中要关闭读端(fd[0]),子进程中关闭写端(fd[1])。对于子->父的管道,在父进中要关闭写端(fd[1]),子进程中关闭读端(fd[0])。

我的问题是,能否在父子进程中对于读写端都不关闭,从而可以实现父子的全双工通讯。

我写了简单的测试代码,输出为:
C
P
kkkkkkkkkkkk

用sleep进行延时,让子进程先执行,由输出看确实先输出了"C",但为何这时,在父进程中的write(fd[1], "tttttt", 6);无效呢?

code:

#include <stdio.h>
#include <unistd.h>

int main(int argc, char **argv)
{
int fd[2];
pid_t pid;
char get[13];
if(pipe(fd) < -1){
printf("Pipe Error!\n");
return -1;
}
if((pid = fork()) < 0){
printf("Fork Error!\n");
return -1;
}else if(pid > 0){
/* parent */
sleep(1);
printf("P\n");
write(fd[1], "tttttt", 6);
read(fd[0], get, 12);
get[12] = '\0';
printf("read: %s\n", get);
}else{
/* child */
printf("C\n");
write(fd[1], "kkkkkkkkkkkk", 12);
}

}



...全文
528 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
qq120848369 2011-11-01
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 cumirror 的回复:]

引用 3 楼 qq120848369 的回复:

C/C++ code
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>

void alr……
[/Quote]

呵呵,孩子退出信号是发给父进程的,那个信号处理函数当然是写给父进程用的.

你没看到我在父进程里注册了那个信号么,子进程是没有注册的。

再其次,waitpid设置NOHANG的情况下如果当前没有孩子退出,那么返回0,相当于我们说的EAGAIN/EWOULDBLOCK。 如果返回-1,那么可能是EINTR,也就是被信号中断了,那么应该继续调用waitpid。
如果返回-1,也有可能是ECHILD,这个错误的意思是父进程已经没有孩子在运行了,所以不需要再waitpid了,
cumirror 2011-11-01
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 qq120848369 的回复:]

C/C++ code
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>

void alrmFunc(int signo)
{
printf("……
[/Quote]

关于管道的问题基本理解了。

不过对chldFunc()函数不是很清楚,子进程结束时,是由子进程还是父进响应这个函数啊?还是它们都会响应?

我目前感觉:子进程结束后,由父进程响应chldFunc函数,父进程最终是通过alarm函数中的exit退出。我改写了chldFunc中的打印信息,发现在while循环中循环了两次,第一次printf了子进程的pid,第二次waitpid返回<0的数值,然后跳出了while循环。
查看waitpid,如果有错误发生则返回-1,不知道这里的错误如何理解?

能再给一些意见吗,谢谢~
cumirror 2011-11-01
  • 打赏
  • 举报
回复
哈哈,谢谢LS。
Linux-Torvalds 2011-10-31
  • 打赏
  • 举报
回复
[Quote=引用楼主 cumirror 的回复:]
最近在看管道相关的操作,管道可用于父子进程间的IPC。
通常情况下,数据流是单向的(半双工),如对于父->子的管道,在父进中要关闭读端(fd[0]),子进程中关闭写端(fd[1])。对于子->父的管道,在父进中要关闭写端(fd[1]),子进程中关闭读端(fd[0])。

我的问题是,能否在父子进程中对于读写端都不关闭,从而可以实现父子的全双工通讯。

我写了简单的测试代码,输出为:
C……
[/Quote]子进程完了后,应该通知父进程吧?是不是应该加以个通知?
cumirror 2011-10-31
  • 打赏
  • 举报
回复
谢谢LS各位的意见,我下去试验下。
qq120848369 2011-10-31
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 qq120848369 的回复:]

引用 3 楼 qq120848369 的回复:

C/C++ code
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>

void alr……
[/Quote]

哦哦,还有一点要提到的,楼主写pipe的数据量小于pipe_buf,所以属于原子写,也就是父子进程写的数据不会穿插,这是很重要的一点。 楼主可以看一下以下帮助:



Write requests to a pipe or FIFO shall be handled in the same way as a regular file with the following exceptions:

* There is no file offset associated with a pipe, hence each write request shall append to the end of the pipe.

* Write requests of {PIPE_BUF} bytes or less shall not be interleaved with data from other processes doing writes on the same pipe. Writes of
greater than {PIPE_BUF} bytes may have data interleaved, on arbitrary boundaries, with writes by other processes, whether or not the O_NONBLOCK
flag of the file status flags is set.
qq120848369 2011-10-31
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 qq120848369 的回复:]

C/C++ code
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>

void alrmFunc(int signo)
{
printf("……
[/Quote]

忘了说楼主为什么读不到,因为你只调用了一次read,当然读不到那么多字符,循环读。
qq120848369 2011-10-31
  • 打赏
  • 举报
回复
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>

void alrmFunc(int signo)
{
printf("alarm\n");
exit(2);
}

void chldFunc(int signo)
{
pid_t pid;

while(true)
{
pid=waitpid(-1,NULL,WNOHANG);

if(pid==0)
{
break;
}
else if(pid<0)
{
if(errno==ECHILD)
{
break;
}
}
else
{
printf("pid:%d exit!\n",pid);
}
}
}

int main(int argc, char **argv)
{
int fd[2];
pid_t pid;
char get[13];

if(signal(SIGCHLD,chldFunc)==SIG_ERR)
{
perror("signal");
exit(1);
}

if(pipe(fd) < -1){
printf("Pipe Error!\n");
return -1;
}
if((pid = fork()) < 0){
printf("Fork Error!\n");
return -1;
}else if(pid > 0){
/* parent */
if(signal(SIGALRM,alrmFunc)==SIG_ERR)
{
perror("signal");
exit(1);
}

printf("P\n");
write(fd[1], "tttttt", 6);
int n;

alarm(3);

while( (n=read(fd[0], get, 12))>0 )
{
get[n]='\0';
printf("%s",get);
fflush(stdout);
}
//父进程读光父子进程所有数据
//但是父进程阻塞在read,没法close pipe[1]
//所以read也得不到EOF,这就是很纠结的一件事了
//设置read非阻塞说不定运气不好数据没读全就EAGAIN了
//可以使用select pipe[0],设置一个超时值,超时则关闭pipe[1]退出
//这里我在alarm里退出父进程
printf("parent exit\n");
//让父进程退出自动关闭pipe[0] and pipe[1]
}else{
/* child */
printf("C\n");
write(fd[1], "kkkkkkkkkkkk", 12);
}

//子进程退出自动关闭pipe[1],pipe[0].
//但子进程不是唯一的pipe[1]持有者,所有不发送EOF
return 0;
}
dongjiawei316 2011-10-31
  • 打赏
  • 举报
回复
[Quote=引用楼主 cumirror 的回复:]
我的问题是,能否在父子进程中对于读写端都不关闭,从而可以实现父子的全双工通讯。
[/Quote]
这样是不行的,当父进程和子进程都从一个pipe中读操作,读出来的可能是自己刚刚写进去的。
如果你想全双工的话,使用两个pipe就好了。
本PDF电子书包含上下两册,共1576页,带目录,高清非扫描版本。 作者: 毛德操 胡希明 丛书名: Linux内核源代码情景分析 出版社:浙江大学出版社 目录 第1章 预备知识 1.1 Linux内核简介. 1.2 Intel X86 CPU系列的寻址方式 1.3 i386的页式内存管理机制 1.4 Linux内核源代码的C语言代码 1.5 Linux内核源代码的汇编语言代码 第2章 存储管理 2.1 Linux内存管理的基本框架 2.2 地址映射的全过程 2.3 几个重要的数据结构和函数 2.4 越界访问 2.5 用户堆栈的扩展 2.6 物理页面的使用和周转 2.7 物理页面的分配 2.8 页面的定期换出 2.9 页面的换入 2.10 内核缓冲区的管理 2.11 外部设备存储空间的地址映射 2.12 系统调用brk() 2.13 系统调用mmap() 第3章 断、异常和系统调用 3.1 X86 CPU对断的硬件支持 3.2 断向量表IDT的初始化 3.3 断请求队列的初始化 3.4 断的响应和服务 3.5 软断与Bottom Half 3.6 页面异常的进入和返回 3.7 时钟断 3.8 系统调用 3.9 系统调用号与跳转表 第4章 进程与进程调度 4.1 进程四要素 4.2 进程三部曲:创建、执行与消亡 4.3 系统调用fork()、vfork()与clone() 4.4 系统调用execve() 4.5 系统调用exit()与wait4() 4.6 进程的调度与切换 4.7 强制性调度 4.8 系统调用nanosleep()和pause() 4.9 内核的互斥操作 第5章 文件系统 5.1 概述 5.2 从路径名到目标节点 5.3 访问权限与文件安全性 5.4 文件系统的安装和拆卸 5.5 文件的打开与关闭 5.6 文件的写与读 5.7 其他文件操作 5.8 特殊文件系统/proc 第6章 传统的Unix进程间通信 6.1 概述 6.2 管道和系统调用pipe() 6.3 命名管道 6.4 信号 6.5 系统调用ptrace()和进程跟踪 6.6 报文传递 6.7 共享内存 6.8 信号量 第7章基于socket的进程间通信 7.1系统调用socket() 7.2函数sys—socket()——创建插口 7.3函数sys—bind()——指定插口地址 7.4函数sys—listen()——设定server插口 7.5函数sys—accept()——接受连接请求 7.6函数sys—connect()——请求连接 7.7报文的接收与发送 7.8插口的关闭 7.9其他 第8章设备驱动 8.1概述 8.2系统调用mknod() 8.3可安装模块 8.4PCI总线 8.5块设备的驱动 8.6字符设备驱动概述 8.7终设备与汉字信息处理 8.8控制台的驱动 8.9通用串行外部总线USB 8.10系统调用select()以及异步输入/输出 8.11设备文件系统devfs 第9章多处理器SMP系统结构 9.1概述 9.2SMP结构的互斥问题 9.3高速缓存与内存的一致性 9.4SMP结构断机制 9.5SMP结构的进程调度 9.6SMP系统的引导 第10章系统引导和初始化 10.1系统引导过程概述 10.2系统初始化(第一阶段) 10.3系统初始化(第二阶段) 10.4系统初始化(第三阶段) 10.5系统的关闭和重引导

23,222

社区成员

发帖
与我相关
我的任务
社区描述
Linux/Unix社区 应用程序开发区
社区管理员
  • 应用程序开发区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧