我正在通过管道系统调用来练习C代码,它可以很好地处理少量数据。但是当数据超出管道容量时,就会发生死锁。
我的测试系统是Debian Sid,但我相信它与其他Linux发行版具有共同点。当输入文件'/tmp/a.out'足够小以适合管道时,这段代码可以很好地工作,但是由于文件最大为1M而被阻止。
#include <sys/errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <stdio.h>
#define CHUNK 2048
int main() {
int fd=open("/tmp/a.out",O_RDONLY);
int pin[2];
int pout[2];
int nread;
char buff[CHUNK];
pipe(pin);
pipe(pout);
int rc;
pid_t pid=fork();
if (pid == 0) {
close(pin[1]);
dup2(pin[0],STDIN_FILENO);
close(pout[0]);
dup2(pout[1],STDOUT_FILENO);
execlp("cat","cat",(char *)0);
} else if (pid > 0) {
close(pin[0]);
close(pout[1]);
/* I think dead lock occurs here, but i can't figure out a way to avoid it */
while ( (nread=read(fd,buff,CHUNK)) > 0) write(pin[1],buff,nread);
close(pin[1]);
while ( (nread=read(pout[0],buff,CHUNK)) >0) write(STDOUT_FILENO,buff,nread);
waitpid(pid,&rc,0);
exit(rc);
} else {
perror("fork");
exit(errno);
}
}
有什么建议吗?我知道Python的子进程类具有subprocess.communicate()之类的东西可以避免这种死锁,但是我不知道如何在C语言中处理它。
非常感谢。
答案 0 :(得分:2)
第一个进程通过管道cat
和cat
管道回到第一个进程。因此,为了使cat
不阻塞在回管上,第一个过程还必须排空该管。例如:
fcntl(pout[0], F_SETFL, fcntl(pout[0], F_GETFL) | O_NONBLOCK);
while((nread=read(fd, buff, CHUNK)) > 0) {
write(pin[1], buff, nread); // TODO: check errors and partial writes here.
while((nread=read(pout[0],buff,CHUNK)) > 0) // pout[0] must be set into non-blocking mode.
write(STDOUT_FILENO, buff, nread);
}
一种更可靠的方法是将pin[1]
和pout[0]
都设置为非阻塞模式,使用select
确定pin[1]
是否准备好写,而{{1 }}进行读取,然后进行相应的写入/读取,并处理部分读取和写入。
答案 1 :(得分:0)
根据您的建议,至少我有2种方法可以解决此问题 1.通过'fcntl'或'select / poll / epoll'设置'NON-BLOCK'模式
struct data {
int from_fd;
int to_fd;
};
管道的代码应类似于
pthread_t t;
struct data d;
d.from_fd=fd;
d.to_fd=pin[1];
pthread_create(&t,NULL,&fd_to_pipe,(void*) &d);
while ( (nread=read(pout[0],buff,CHUNK)) >0) write(STDOUT_FILENO,buff,nread);
waitpid(pid,&rc,0);
pthread_join(t,NULL);
谢谢!