[英]C fork/exec with non-blocking pipe IO
這似乎是一件相當普遍的事情,而且我已經設法自學了我需要的一切,除了我現在有一個問題,這是我的故障排除。
int nonBlockingPOpen(char *const argv[]){
int inpipe;
pid_t pid;
/* open both ends of pipe nonblockingly */
pid = fork();
switch(pid){
case 0: /*child*/
sleep(1); /*child should open after parent has open for reading*/
/*redirect stdout to opened pipe*/
int outpipe = open("./fifo", O_WRONLY);
/*SHOULD BLOCK UNTIL MAIN PROCESS OPENS FOR WRITING*/
dup2(outpipe, 1);
fcntl(1, F_SETFL, fcntl(1, F_GETFL) | O_NONBLOCK);
printf("HELLO WORLD I AM A CHILD PROCESS\n");
/*This seems to be written to the pipe immediately, blocking or not.*/
execvp(*argv, argv);
/*All output from this program, which outputs "one" sleeps for 1 second
*outputs "two" sleeps for a second, etc, is captured only after the
*exec'd program exits!
*/
break;
default: /*parent*/
inpipe = open("./fifo", O_RDONLY | O_NONBLOCK);
sleep(2);
/*no need to do anything special here*/
break;
}
return inpipe;
}
為什么子進程每次生成一行時都不會將其stdout寫入管道? 在execvp或dup2的工作方式中是否有一些我缺少的東西? 我知道我對這一切的處理方式有點奇怪,但我找不到另一種以編程方式捕獲閉源二進制文件輸出的方法。
我猜你只有在exc'd程序退出后得到它的輸出,因為它在每條消息后都不會刷新 。 如果是這樣,你無法從外面做任何事情。
我不太確定這與你的問題中的阻塞和非阻塞I / O之間的選擇有什么關系。 非阻塞寫入可能會完全或部分失敗:不會阻塞程序,直到管道中有空間,調用立即返回並說它無法寫入應該擁有的所有內容。 非阻塞I / O既不會使緩沖區變大,也不會強制刷新輸出,並且某些程序可能會嚴重支持它。
你不能強制你正在執行的二進制程序刷新。 如果您認為非阻塞I / O是該問題的解決方案,抱歉,但我擔心這是非常正交的。
編輯:好吧,如果exec'd程序只使用libc提供的緩沖(沒有實現它自己的)並且是動態鏈接的,你可以強制它通過將它鏈接到刷新每次寫入的修改過的libc來刷新。 這將是一個絕望的措施。 只有在其他一切都失敗時才嘗試。
啟動進程時(通過示例中的execvp()),標准輸出的行為取決於輸出設備是否為終端。 如果不是(並且FIFO不是終端),那么輸出將被完全緩沖,而不是線路緩沖。 你無能為力; (標准)C庫就是這樣做的。
如果你真的想讓它工作線緩沖,那么你必須為程序提供一個偽終端作為其標准輸出。 這進入了有趣的領域 - 偽終端或ptys並不是那么容易處理。 有關POSIX函數,請參閱:
grantpt()
- 授予對從屬偽終端設備的訪問權限 posix_openpt()
- 打開一個偽終端設備 ptsname()
- 獲取slave偽終端設備的名稱 unlockpt()
- 解鎖偽終端主/從對 為什么子進程每次生成一行時都不會將其stdout寫入管道?
你怎么知道? 你甚至沒有嘗試從fifo讀取輸出。
NB通過文件名我假設你正在使用fifo 。 或者它是一個普通的文件?
孩子的小錯誤:在dup2()
,你需要close(outpipe)
。
fcntl(1,F_SETFL,fcntl(1,F_GETFL)| O_NONBLOCK);
根據你exec()的程序,你可能會丟失一些輸出或導致程序失敗,因為寫入stdout現在可能會因EWOULDBLOCK而失敗。
IIRC fifos具有與管道相同的緩沖區大小。 每POSIX最小值為512字節,通常為4K或8K。
您可能想要解釋為什么需要它。 與阻塞IO相比,非阻塞IO具有不同的語義,除非您的子進程期望您遇到各種問題。
printf(“HELLO WORLD我是一個兒童過程\\ n”);
stdout是緩沖的,我會在那之后fflush(stdout)
。 (無法找到文件,exec()本身是否會刷新stdout。)
在execvp或dup2的工作方式中是否有一些我缺少的東西? 我知道我對這一切的處理方式有點奇怪,但我找不到另一種以編程方式捕獲閉源二進制文件輸出的方法。
我不會玩非阻塞IO - 並將其保留為阻塞模式。
我會使用pipe()而不是fifo。 Linux的man管道有一個方便的例子,fork()。
否則,這是一種非常正常的做法。
sleep()
不保證父進程會首先打開管道 - 正如Dummy00001所說,你應該使用pipe()
管道,而不是命名管道。 您還應該檢查execvp()
和fork()
失敗,並且您不應該將子端設置為非阻塞 - 這是子進程的決定。
int nonBlockingPOpen(char *const argv[])
{
int childpipe[2];
pid_t pid;
pipe(childpipe);
pid = fork();
if (pid == 0)
{
/*child*/
/*redirect stdout to opened pipe*/
dup2(childpipe[1], 1);
/* close leftover pipe file descriptors */
close(childpipe[0]);
close(childpipe[1]);
execvp(*argv, argv);
/* Only reached if execvp fails */
perror("execvp");
exit(1);
}
/*parent*/
/* Close leftover pipe file descriptor */
close(childpipe[1]);
/* Check for fork() failing */
if (pid < 0)
{
close(childpipe[0]);
return -1;
}
/* Set file descriptor non-blocking */
fcntl(childpipe[0], F_SETFL, fcntl(childpipe[0], F_GETFL) | O_NONBLOCK);
return childpipe[0];
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.