由于 SIGINT 导致的部分写入
Partial write due to SIGINT
我有一个 write_full
应该将缓冲区完全写入标准输出,即使它被信号中断。
我有一个循环,它保持 write_full
一个字符串,直到 quit
被信号处理程序更改。这是代码:
#include <signal.h>
#include <unistd.h>
#include <errno.h>
volatile sig_atomic_t quit = 0;
void sigint_handler(int s)
{
quit = 1;
}
int write_full(char *buf, size_t len)
{
while (len > 0) {
ssize_t written = write(STDOUT_FILENO, buf, len);
if (written == -1) {
if (errno == EINTR) {
continue;
}
return -1;
}
buf += written;
len -= (size_t)written;
}
return 0;
}
int main(void)
{
struct sigaction act = {
.sa_handler = sigint_handler
};
sigaction(SIGINT, &act, NULL);
while (!quit) {
write_full("loop\n", 5);
}
write_full("cleanup", 7);
return 0;
}
我希望程序在打印 "cleanup" 之前完全写入 "loop",但我看到这样的输出:
loop
loop
loop
l^C
cleanup
为什么会这样?我希望它是这样的:
loop
loop
loop
l^Coop
cleanup
因为 write_full 应该继续写入 "oop\n" 部分,即使第一次写入由于中断而很短。我在信号处理程序处放置了一个断点并步进,看起来 write
报告它已经写入了 4 个字符,即使它只将 "l" 写入标准输出。所以接下来不写"oop\n",它只写“\n”。
l^C
Program received signal SIGINT, Interrupt.
Breakpoint 1, sigint_handler (s=2) at src/main.c:9
9 quit = 1;
(gdb) next
10 }
(gdb) next
write_full (buf=0x4020a0 "loop\n", len=5) at src/main.c:16
16 if (written == -1) {
(gdb) print written
= 4
为什么会这样?我该如何解决这个问题?
Ctrl-C 打乱了终端输出。程序写了它应该写的所有内容,但默认情况下,终端驱动程序会在 Ctrl-C 之后截断该行。这不受您的程序控制。如果驱动程序在将 complete 行缓冲区复制到物理设备的过程中看到 Ctrl-C,则会发生剪切。输入行缓冲区也被丢弃。这也适用于其他信号生成字符。
这在 stty(1)
和 termios(3)
手册页中有相当简要的描述。
可以使用 stty noflsh
命令禁用此行为。
您也可以重定向到一个文件以查看完整的输出。
我有一个 write_full
应该将缓冲区完全写入标准输出,即使它被信号中断。
我有一个循环,它保持 write_full
一个字符串,直到 quit
被信号处理程序更改。这是代码:
#include <signal.h>
#include <unistd.h>
#include <errno.h>
volatile sig_atomic_t quit = 0;
void sigint_handler(int s)
{
quit = 1;
}
int write_full(char *buf, size_t len)
{
while (len > 0) {
ssize_t written = write(STDOUT_FILENO, buf, len);
if (written == -1) {
if (errno == EINTR) {
continue;
}
return -1;
}
buf += written;
len -= (size_t)written;
}
return 0;
}
int main(void)
{
struct sigaction act = {
.sa_handler = sigint_handler
};
sigaction(SIGINT, &act, NULL);
while (!quit) {
write_full("loop\n", 5);
}
write_full("cleanup", 7);
return 0;
}
我希望程序在打印 "cleanup" 之前完全写入 "loop",但我看到这样的输出:
loop
loop
loop
l^C
cleanup
为什么会这样?我希望它是这样的:
loop
loop
loop
l^Coop
cleanup
因为 write_full 应该继续写入 "oop\n" 部分,即使第一次写入由于中断而很短。我在信号处理程序处放置了一个断点并步进,看起来 write
报告它已经写入了 4 个字符,即使它只将 "l" 写入标准输出。所以接下来不写"oop\n",它只写“\n”。
l^C
Program received signal SIGINT, Interrupt.
Breakpoint 1, sigint_handler (s=2) at src/main.c:9
9 quit = 1;
(gdb) next
10 }
(gdb) next
write_full (buf=0x4020a0 "loop\n", len=5) at src/main.c:16
16 if (written == -1) {
(gdb) print written
= 4
为什么会这样?我该如何解决这个问题?
Ctrl-C 打乱了终端输出。程序写了它应该写的所有内容,但默认情况下,终端驱动程序会在 Ctrl-C 之后截断该行。这不受您的程序控制。如果驱动程序在将 complete 行缓冲区复制到物理设备的过程中看到 Ctrl-C,则会发生剪切。输入行缓冲区也被丢弃。这也适用于其他信号生成字符。
这在 stty(1)
和 termios(3)
手册页中有相当简要的描述。
可以使用 stty noflsh
命令禁用此行为。
您也可以重定向到一个文件以查看完整的输出。