C 中的 getline() 信号中断如何工作?

hac*_*cks 2 c linux operating-system

我有一个最小的工作代码,它应该读取用户输入并处理任何 SIGINT (ctrl+c)。我用了sigaction。如果按下 ctrl+c,它将$在新行上打印 a 并等待用户输入。

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

volatile sig_atomic_t sigint_received = 0;

static void handler(int signum)
{
    sigint_received = 1;
}


int main()
{
    struct sigaction sa;

    sa.sa_handler = handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sigaction(SIGINT, &sa, NULL);

    char *line = NULL;
    size_t n = 100;
    line = malloc(n);

    while(1){
        if(sigint_received){
            sigint_received = 0;
            printf("\n");
        }

        printf("$ ");
        getline(&line, &n, stdin);

        if(!strcmp(line, "exit\n")){
            break;
        }
    }

    if(line){
        free(line);
    }

    return 0;
} 
Run Code Online (Sandbox Code Playgroud)

我做了预期的事情(无意中)。我的问题是操作系统处理中断后为什么getline不等待输入?程序不应该从中断处继续吗?


免责声明:我不擅长 Linux 编程,如果这是一个菜鸟问题,很抱歉。

hac*_*cks 5

感谢@user3386109@Emanuel P的富有洞察力的评论。sigaction我检查了 Mac上的手册页getline,发现如下:

man sigaction

SA_RESTART 请参阅下面的段落。

进而

如果在下面列出的系统调用期间捕获到信号,则调用可能会因错误而被迫终止EINTR,调用可能会返回比请求的数据传输短的数据传输,或者调用可能会重新启动。SA_RESTART通过设置中的位来请求重新启动挂起的呼叫sa_flags。受影响的系统调用包括通信通道或慢速设备(例如终端,但不是常规文件)上的open(2)read(2)write(2)sendto(2)recvfrom(2)和,以及在或期间。...sendmsg(2)recvmsg(2)wait(2)ioctl(2)

并在失败时getline返回-1。它在这里说:

如果发生故障,errno则设置为指示错误。

在这种情况下errno将被设置为评论EINTR中指出的。

处理完中断后程序将恢复运行getline并且不会等待用户输入。相反,如上所述,它将因错误而终止EINTR

固定代码:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <errno.h>

volatile sig_atomic_t sigint_received = 0;

static void handler(int signum)
{}


int main()
{
    struct sigaction sa;

    sa.sa_handler = handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sigaction(SIGINT, &sa, NULL);

    char *line = NULL;
    size_t n = 100;
    line = malloc(n);

    while(1){  
        printf("$ ");
        ssize_t ret = getline(&line, &n, stdin);

        if(ret == -1 && errno == EINTR){
            printf("\n");
            errno = 0;  // Reset errno flag
            continue;
        }

        if(!strcmp(line, "exit\n")){
            break;
        }
    }

    if(line){
        free(line);
    }

    return 0;
} 
Run Code Online (Sandbox Code Playgroud)

  • 在调用某些函数之前将其设置为 0 是完全可以的,甚至是必要的。例如,POSIX 规范是这样规定的:***“由于 0、{LONG_MIN} 或 {LLONG_MIN} 以及 {LONG_MAX} 或 {LLONG_MAX} 会在错误时返回,并且在成功时也是有效返回,因此应用程序希望检查错误情况应将 errno 设置为 0,然后调用 strtol() 或 strtoll(),然后检查 errno。[选项结束]。"*** (3认同)