The EINTR error means a system call was interrupted by a signal before it could complete its operation.

This typically happens when a process is waiting for a system call (like reading from a file, writing to a socket, or waiting for a child process) and a signal arrives. The signal handler runs, and if the handler returns, the interrupted system call is aborted with EINTR.

Here are the common causes and how to fix them:

1. Signal Handlers Interrupting Blocking I/O:

  • Diagnosis: Examine your application’s signal handlers. If a signal handler is registered for a signal that can interrupt system calls (most signals can, except SIGKILL and SIGSTOP), and that signal can arrive while your application is blocked on I/O (e.g., read(), write(), select(), poll(), accept()), this is a prime suspect. Use strace -p <pid> to observe the system calls and see if EINTR follows a signal delivery.

  • Fix: The standard Unix approach is to retry the interrupted system call. Most modern libraries and applications do this automatically, but if you’re writing low-level code or using older libraries, you might need to implement it manually. In C, this looks like:

    int ret;
    do {
        ret = syscall(...); // Your interrupted system call
    } while (ret == -1 && errno == EINTR);
    

    This loop keeps calling the system call until it succeeds or returns an error other than EINTR.

  • Why it works: By retrying, you’re effectively telling the system, "Okay, that signal happened, but I still need to finish my original task." The system call will then attempt to complete its operation from where it left off, or restart.

2. Child Process Signals:

  • Diagnosis: If your application forks a child process and then calls wait() or waitpid(), and a signal arrives before the child terminates, the wait() call can return EINTR. Again, strace -p <pid> is your friend here.

  • Fix: Similar to I/O, retry the waitpid() call if it returns EINTR. The waitpid() system call itself can be configured to automatically restart if interrupted by a signal by setting the WUNTRACED flag (though this is less common for EINTR specifically, it’s good practice for robust waiting). More directly, just retry:

    int status;
    pid_t pid;
    do {
        pid = waitpid(child_pid, &status, 0);
    } while (pid == -1 && errno == EINTR);
    
  • Why it works: The waitpid() call is designed to be interruptible. Retrying ensures that you eventually get the child’s termination status.

3. Network Operations Interrupted by Signals:

  • Diagnosis: Applications performing network operations like recv(), send(), accept(), or connect() can encounter EINTR if a signal arrives during these operations. This is particularly common in high-concurrency servers that use select() or poll() to manage multiple connections, as these functions are themselves interruptible.

  • Fix: For socket operations, the same retry logic applies.

    ssize_t bytes_received;
    do {
        bytes_received = recv(sockfd, buffer, size, flags);
    } while (bytes_received == -1 && errno == EINTR);
    

    For select() and poll(), the standard practice is to retry them if they return EINTR.

  • Why it works: Network protocols are resilient to brief interruptions. Retrying the operation allows the underlying network stack to re-establish the communication state and complete the data transfer.

4. Timer-Related Signals (e.g., SIGALRM):

  • Diagnosis: If you’re using timers like alarm() or setitimer(), the signals they generate (SIGALRM) can interrupt other system calls. If your application sets an alarm and then performs a blocking operation, and the alarm fires before the operation completes, you’ll get EINTR.
  • Fix: Again, the solution is to retry the interrupted system call. If you are intentionally using signals for timing or control, you might need to adjust your signal handling logic to account for these interruptions rather than just blindly retrying. However, for most applications, the retry pattern is sufficient.
  • Why it works: The system call is designed to be restarted. The signal handler executes, and upon returning, the kernel can resume the interrupted system call.

5. Interrupted read() or write() on Pseudo-terminals (PTYs):

  • Diagnosis: When interacting with PTYs, signals can interrupt read() or write() calls. This is common in terminal emulators or applications that spawn interactive shells.
  • Fix: Implement the retry loop for read() and write() calls on the PTY file descriptor.
  • Why it works: PTY drivers, like other kernel I/O interfaces, are designed to be restartable after signal interruption.

6. Incorrectly Masked Signals:

  • Diagnosis: While less common, if signals are being masked (sigprocmask()) incorrectly, they might be delivered at unexpected times, potentially interrupting system calls. This is more about signal delivery timing than the EINTR error itself, but it can contribute to the problem.
  • Fix: Review your signal masking logic. Ensure signals are only masked when absolutely necessary and for the shortest duration possible. Always unmask signals correctly.
  • Why it works: Proper signal masking ensures that signals are delivered predictably and don’t interfere with critical, uninterruptible sections of code.

7. Misconfigured SA_RESTART Flag:

  • Diagnosis: When setting up signal handlers using sigaction(), you can specify the SA_RESTART flag. If this flag is not set for a handler that might interrupt a system call, the system call will return EINTR. Conversely, if it is set and you want EINTR to be returned (e.g., to perform specific cleanup), then EINTR won’t occur.

  • Fix: If you want system calls to automatically restart after a signal, ensure the SA_RESTART flag is set in the sa_flags field of the struct sigaction when registering your signal handler.

    struct sigaction sa;
    sa.sa_handler = my_signal_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART; // This is the key!
    sigaction(SIGINT, &sa, NULL);
    
  • Why it works: The SA_RESTART flag tells the kernel to automatically re-issue the system call if it was interrupted by a signal for which this flag is set. This is often the most elegant solution if you don’t need to perform specific actions in response to the interruption itself.

After fixing EINTR errors by implementing retries or using SA_RESTART, the next error you might encounter, especially in network programming, is EAGAIN or EWOULDBLOCK, which indicates that a non-blocking operation would have blocked and is therefore returning immediately.

Want structured learning?

Take the full Linux & Systems Programming course →