【问题标题】:How to write a test case to verify EINTR returned by sem_wait function in linux如何编写测试用例来验证 linux 中 sem_wait 函数返回的 EINTR
【发布时间】:2018-08-07 23:10:15
【问题描述】:

我刚刚编写了以下例程来处理 EINTR 错误。 例程如下,

while((s = sem_wait(&w4compl)) == -1)
{
        if (errno == EINTR)
        {
                perror("call interrupted by sig. handler\n");
                continue;
        }
        else
                printf("Other Error Generated\n");
}

所以,在这里我看不到打印“调用被 sig.handler\n 中断”语句。如何测试它以使其打印相同(我如何执行 if (errno == EINTR) 的部分)。

【问题讨论】:

  • HI P.P.如果这个问题已经回答,你能给我链接吗?
  • 它已在您的问题中链接。
  • @P.P.如果你仔细观察,这实际上不是一个好的复制品。当然,标题很好听,很笼统,但最重要的答案都说“不要再打电话给fclose()”,这在这里并没有真正的帮助。
  • 同意乔纳森。这对我没有多大帮助。
  • @JonathonReinhart 链接的问题是针对单元测试 EINTR 的,这也是 OP 在这里提出的问题。那里有不错的答案。当然,fclose 被用作示例(这里是 sem_post)。我不确定我们是否需要针对每个可能返回 EINTR 的系统调用提出不同的问题以及如何对其进行单元测试。

标签: c linux semaphore


【解决方案1】:

安装信号处理程序,并导致信号被传递(使用alarm()setitimer()timer_create()+timer_settime()),这样信号的传递将中断sem_wait() 调用。


考虑这个示例程序:

#define  _POSIX_C_SOURCE  200809L
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <semaphore.h>
#include <stdio.h>
#include <errno.h>

static void dummy_handler(int signum)
{
}

static int install_dummy_handler(int signum)
{
    struct sigaction  act;
    memset(&act, 0, sizeof act);
    sigemptyset(&act.sa_mask);
    act.sa_handler = dummy_handler;
    act.sa_flags = 0;
    return sigaction(signum, &act, NULL);
}

static const char *errname(const int errnum)
{
    switch (errnum) {
    case EINTR:  return "EINTR";
    case EINVAL: return "EINVAL";
    default:     return "(other)";
    }
}

int main(void)
{
    sem_t  s;

    if (install_dummy_handler(SIGALRM) == -1) {
        fprintf(stderr, "Cannot install ARLM signal handler: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    sem_init(&s, 0, 0);

    alarm(1);

    if (sem_wait(&s) == -1) {
        const int  errnum = errno;
        printf("sem_wait() failed with errno == %s (%d; %s).\n",
               errname(errnum), errnum, strerror(errnum));
    } else
        printf("sem_wait() succeeded.\n");

    return EXIT_SUCCESS;
}

main() 中,我们为 SIGALRM 信号安装了一个信号处理程序。信号处理函数是否做任何事情都没有关系,因为正是 信号的传递 导致“慢”系统调用返回 EINTR 错误。 (只要在安装该处理程序时使用 SA_RESTART 标志。如果您查看install_dummy_handler() 中的act.sa_mask,您会发现我们根本没有使用任何标志。所有标志和sigaction() 的用法在 man 2 sigaction 手册页中有描述。)

main()中,我们首先初始化我们的信号量,然后设置一秒钟的闹钟。当真正的挂钟时间过去时,会发出 SIGALRM 信号。 请注意,虽然 SIGALRM 非常适合本示例和类似目的,但您可能希望使用 POSIX per-process interval timers 代替。

接下来,我们只需在信号量上调用sem_wait(),并检查结果。实际上,如果您使用例如编译和运行上述 example.c

gcc -Wall -O2 example.c -lpthread -o example
./example

程序将输出

sem_wait() failed with errno == EINTR (4; Interrupted system call).

一秒钟后。

【讨论】:

  • @NiravPatel:请注意,如果您有多个线程,则信号必须指向执行sem_wait() 调用的线程,并且它是将信号传递给用户空间处理函数,不是信号本身,它会导致阻塞调用的中断。在某些情况下,您可能需要一个信号对,主要信号在除一个线程之外的所有线程中都被阻塞,以便其信号处理程序可以将信号“转发”(使用未阻塞的辅助信号)到每个现有的其他线程,以中断几个线程。如果您需要更多详细信息或示例,请告诉我。
【解决方案2】:

如果系统调用被中断,几乎 Linux 上的任何系统调用都可以返回 EINTR

来自man page(强调我的):

sem_wait() 递减(锁定)sem 指向的信号量。如果 信号量的值大于零,则递减 继续,函数立即返回。如果信号量 当前值为零,然后调用阻塞,直到它 变得可以执行递减(即,信号量值 高于零),或信号处理程序中断调用

要触发这种情况,你应该确保sem_wait系统调用被阻塞(等待),然后向线程发送一个信号(有一个处理程序)。

一些伪代码:

sigint_handler:
    return

thread2:
    <Your while loop from the question>

main:
    signal(SIGINT, sigint_handler)  // Setup signal handler

    sem_wait(&w4compl)
    t2 = start_thread(thread2)
    sleep(5)                        // Hack to make sure thread2 is blocked

    pthread_kill(t2, SIGINT)

【讨论】:

  • 感谢@Jonathan Reinhart。这里的例程在其中一个线程中调用。那么如何给线程发送 SIGINT 信号呢?
  • 使用pthread_kill 向线程发送信号。
  • 只是为了理解,OP应该在这里阻止哪个系统调用?为什么是 SIGINT?
  • @Jonathan 我将在我现有的代码中尝试伪代码并检查。
  • 1.使用signal 不是一个好主意(应该首选sigaction)。 2.除了sleep作为同步机制是一个糟糕的选择之外,sleep本身可以被信号中断(我仍然不明白sleep在main中是如何确保它在线程中被阻塞的)。
猜你喜欢
  • 1970-01-01
  • 2021-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多