这个C程序为什么会比预期晚生成SIGPIPE信号?

16

这个程序在将其传输到 "head -n 1" 后会在随机时间后生成 SIGPIPE。 我知道因为我们在第一行之后向 "head -n 1" 提供了更多内容,所以我们预计它会生成 SIGPIPE,但实际上它会在退出之前达到一个随机数(通常 > 20 and < 200)。有什么想法吗?

#include <stdio.h>
#include <stdlib.h>

main()
{
  int i;
  char *s = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\n";

  i = 0;
  while (1) {
    fputs(s, stdout);
    fflush(stdout);
    fprintf(stderr, "Iteration %d done\n", i);
    i++;
  }
}

这不是作业,只是我教授笔记上的一些内容,我不理解。


额,**const** char *s = ... - user529758
2
填充烟草需要时间。 - Kaz
4个回答

8
这是调度的变幻无常。
你的生产者(我们称之为alphabeta)能够在head读取并退出之前运行一段时间(从而破坏管道)。
当然,“某段时间”是可变的。
有时候,alphabeta运行20次后,head才能读取stdin并退出。有时候是200次。在我的系统上,有时候是300次、1000次或2000次。事实上,它理论上可以循环到连接生产者和消费者的管道容量的极限。
为了演示,让我们引入一些延迟,这样我们就可以相当确定head在读取之前被卡住了,而alphabeta还没有输出单行:
so$ { sleep 5; ./alphabeta; } | head -n 1
ABCDEFGHIJKLMNOPQRSTUVWXYZ
Iteration 0 done

(注:以上并不保证 alphabeta 只会在上述情况下迭代一次。但是,在一个未经过负载的系统上,这种情况几乎总是发生的:head将准备就绪,并且其读取/退出几乎立即发生。)
相反,让我们看看当我们人为延迟head时会发生什么:
so$ ./alphabeta | { sleep 2; head -n 1; }
Iteration 0 done
...
Iteration 2415 done    # <--- My system *pauses* here as pipe capacity is reached ...
Iteration 2416 done    # <--- ... then it resumes as head completes its first read()
...
Iteration 2717 done    # <--- pipe capacity reached again; head didn't drain the pipe 
ABCDEFGHIJKLMNOPQRSTUVWXYZ

顺便提一下,@R..在他的评论中说SIGPIPE是同步的是完全正确的。在您的情况下,当head退出后,第一个由fflush引起的写操作将同步生成信号。这是记录的行为


4

我认为这仅仅是因为信号是异步的。

更新: 正如其他人指出的那样,它们(更准确地说,包括SIGPIPE在内的许多信号)并不是异步的。这是一个没有思考的答案 :)


5
SIGPIPE 是一种同步信号。 - R.. GitHub STOP HELPING ICE
@R.. stdio 不会在 fflush 之后缓存。 - Kevin
不是这个进程的标准输入输出,而是 head 的。 - R.. GitHub STOP HELPING ICE

0
Socket 写入是缓冲且异步的,因此通常不会在特定写入时出现错误,直到后续的读取或写入。

5
我不确定为什么套接字写入与管道输出的问题有关。 - Jonathan Leffler

0

head 命令使用 stdio 读取 stdin,因此第一个 getc 直到缓冲区满或 EOF 发生才返回。


你可以使用strace进行验证。 - R.. GitHub STOP HELPING ICE
1
不是,这是管道容量和调度问题。请看我的回答。 - pilcrow
正如pilcrow所说,这是调度问题。在此情况下,内核的管道缓冲区(通常为64-128 KiB)没有填满,因为只写入了约200 * 27 = 5.4 KB的数据。 - Adam Rosenfield
确实,这是正确的。head中的read不会阻塞直到填满缓冲区;如果有一些数据可用,它会立即返回。 - R.. GitHub STOP HELPING ICE

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接