简体   繁体   中英

System call interrupted by a signal still has to be completed

A lot of system calls like close( fd ) Can be interrupted by a signal. In this case usually -1 is returned and errno is set EINTR .

The question is what is the right thing to do? Say, I still want this fd to be closed.

What I can come up with is:

while( close( fd ) == -1 )
  if( errno != EINTR ) {
    ReportError();
    break;
  }

Can anybody suggest a better/more elegant/standard way to handle this situation?

UPDATE: As noticed by mux, SA_ RESTART flag can be used when installing the signal handler. Can somebody tell me which functions are guaranteed to be restartable on all POSIX systems(not only Linux )?

Some system calls are restartable, which means the kernel will restart the call if interrupted, if the SA_RESTART flag is used when installing the signal handler, the signal(7) man page says:

If a blocked call to one of the following interfaces is interrupted by a signal handler, then the call will be automatically restarted after the signal handler returns if the SA_RESTART flag was used ; otherwise the call will fail with the error EINTR:

It doesn't mention if close() is restartable, but these are:

read(2), readv(2), write(2), writev(2), ioctl(2), open(2),wait(2), wait3(2), wait4(2), waitid(2), and waitpid,accept(2), connect(2), recv(2), recvfrom(2), recvmsg(2), send(2), sendto(2), and sendmsg(2) flock(2) and fcntl(2) mq_receive(3), mq_timedreceive(3), mq_send(3), and mq_timedsend(3) sem_wait(3) and sem_timedwait(3) futex(2)

Note that those details, specifically the list of non-restartable calls, are Linux-specific

I posted a relevant question about which system calls are restartable and if it's specified by POSIX somewhere, it is specified by POSIX but it's optional, so you should check the list of non-restartable calls for your OS, if it's not there it should be restartable. This is my question: How to know if a Linux system call is restartable or not?

Update: Close is a special case it's not restartable and should not be retried in Linux, see this answer for more details: https://stackoverflow.com/a/14431867/1157444

For the record: On essentially every UNIX, close() must not be retried if it returns EINTR. DO NOT put an EINTR retry-loop in place for close like you would for waitpid() or read() . See this page for more details: http://austingroupbugs.net/view.php?id=529 On linux, Solaris, BSD and others, retrying close() is incorrect. HP-UX is the only common(!) system I could find that requires this.

EINTR means something very different for read() and select() and waitpid() and so on than it does for close() . For most calls, you retry on EINTR because you asked for something to be done which blocks, and if you were interrupted that means it didn't happen, so you try again. For close() , the action you requested was for an entry to be removed from the fd table, which is instantaneous, without error, and will always happen no matter what close() returns.[*] The only reason close() blocks is that sometimes, for special semantics (like TCP linger), it can wait until I/O is done before returning. If close returns EINTR, that means that you asked it to wait but it couldn't. However, the fd was still closed ; you just lost your chance to wait on it.

Conclusion: unless you know you can't receive signals, using close() for waiting is a very stupid thing to do. Use an application-level ACK (TCP) or an fsync (file I/O) to make sure any writes were completed before closing the fd.

[*] There is a caveat: if another thread of the process is inside a blocking syscall on the same fd, well, ... it depends.

Assuming you're after shorter code, you can try something like:

while (((rc = close (fd)) == -1) && (errno == EINTR));
if (rc == -1)
    complainBitterly (errno);

Assuming you're after more readable code in addition to shorter, just create a function:

int closeWithRetry (int fd);

and place your readable code in there. Then it doesn't really matter how long it is, it's still a one-liner where you call it, but you can make the function body itself very readable:

int closeWithRetry (int fd) {
    // Initial close attempt.

    int rc = close (fd);

    // As long as you failed with EINTR, keep trying.
    // Possibly with a limit (count or time-based).

    while ((rc == -1) && (errno == EINTR))
        rc = close (fd);

    // Once either success or non-retry failure, return error code.

    return rc;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM