Системные вызовы и код ошибки EINTR
Есть ли какой-нибудь эксперт, который может помочь мне в следующем?
У меня есть следующие системные вызовы в C:
access()
unlink()
setsockopt()
fcntl()
setsid()
socket()
bind()
listen()
Я хочу знать, могут ли они потерпеть неудачу с кодом ошибки -1 и ошибочно EINTR/EAGAIN.
Должен ли я обращаться с EINTR / EAGAIN для них?
Документация не имеет ничего общего с EINTR / EAGAIN, но многие люди, которых я вижу, справляются с этим.
Что является правильным?
Вот как я регистрирую обработчики сигналов: https://gitorious.org/zepto-web-server/zepto-web-server/source/b1b03b9ecccfe9646e34caf3eb04689e2bbc54dd:src/signal-dispatcher-utility.c
С этой конфигурацией: https://gitorious.org/zepto-web-server/zepto-web-server/source/b1b03b9ecccfe9646e34caf3eb04689e2bbc54dd:src/server-signals-support-utility.c
Также есть коммит, в который я добавил некоторую обработку EINTR / EAGAIN в некоторых системных вызовах, которые, как мне известно, возвращают EINTR или EAGAIN: https://gitorious.org/zepto-web-server/zepto-web-server/commit/b1b03b9ecccfe9646e34caf3eb04689e2bbc54dd
4 ответа
См. http://man7.org/linux/man-pages/man7/signal.7.html - начните чтение в самом низу, где говорится о "Прерывании системных вызовов и функциях библиотеки..." Это человек из Linux страницы, но информация в целом применима к любой системе с Unix / Posix / Linux.
Если вы не установите обработчик прерывистого сигнала (один установлен с sigaction
опуская SA_RESTART
флаг или тот, который установлен с signal
на некоторых системах) вы не должны ожидать увидеть EINTR
на всех.
Среди вашего конкретного списка функций я не вижу ни одной, которая могла бы испытать EINTR
в любом случае, кроме fcntl
и для него, только когда он используется для блокировки. Ссылка в ответе Джона должна быть полезна при ответах на вопросы о конкретных функциях.
На каждой странице man системного вызова *NIX есть раздел, озаглавленный "ОШИБКИ". Обратитесь к руководству, например: http://man7.org/linux/man-pages/man2/accept.2.html. Вы также можете использовать командную строку man accept
чтобы посмотреть это.
В общем, системные вызовы, для вычисления которых может потребоваться некоторое время, могут установить -1+EINTR при доставке сигнала, а короткие системные вызовы - нет. Например, accept()
может заблокировать ваш процесс, чтобы он мог быть прерван сигналом, но setsid()
настолько коротка, что записана так, чтобы не прерываться сигналами.
сигнал (7) для списков Linux
accept
connect
fcntl
flock
futex
ioctl
open
read
readv
recv
recvfrom
recvmsg
send
sendmsg
sendto
wait
wait3
wait4
waitid
waitpid
write
writev
как возможно прерываемый (EINTR) обработчиками no-SA_RESTART и
setsockopt
accept
recv
recvfrom
recvmsg
connect
send
sendto
sendmsg
pause
sigsuspend
sigtimedwait
sigwaitinfo
epoll_wait
epoll_pwait
poll
ppoll
select
lect
msgrcv
msgsnd
semop
semtimedop
clock_nanosleep
nanosleep
read
io_getevents
sleep
как прерываемый EINTR, даже обработчиками SA_RESTART.
Кроме того, в нем перечислены:
setsockopt
accept
recv
recvfrom
recvmsg
connect
send
sendto
sendmsg
epoll_wait
epoll_pwait
semop
semtimedop
sigtimedwait
sigwaitinfo
read
futex
msgrcv
msgsnd
nanosleep
как EINTR-прерывается сигналом остановки + SIGCONT
и говорит, что это конкретное поведение специфично для Linux и не санкционировано POSIX.1
,
Помимо этого, особенно если в спецификации функции нет списка EINTR
, вы не должны получить EINTR
,
Если вы не доверяете системе, чтобы выполнить ее, вы можете попытаться закинуть петлю с вашей подозреваемой системной функцией: SIGSTOP
/SIGCONT
+ сигнал с no-SA_RESTART
обработчик no-op и посмотрите, сможете ли вы вызвать EINTR.
Я попробовал это с:
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
static void chld(int Sig)
{
int status;
if(0>wait(&status))
_exit(1);
if(!WIFEXITED(status)){
//this can only interrupt an AS-safe block
assert(WIFSIGNALED(status) && WTERMSIG(status) == SIGALRM);
puts("OK");
exit(0);
} else {
switch(WEXITSTATUS(status)){
case 1: puts("FAIL"); break;
case 2: puts("EINTR"); break;
}
}
exit(0);
}
static void nop(int Sig)
{
}
int main()
{
sigset_t full;
sigfillset(&full);
sigaction(SIGCHLD, &(struct sigaction){ .sa_handler=chld, .sa_mask=full, .sa_flags=0 } , 0);
sigaction(SIGUSR1, &(struct sigaction){ .sa_handler=nop, .sa_mask=full, .sa_flags=0 } , 0);
pid_t p;
if(0>(p=fork())) { perror(0); return 1; }
if(p!=0){
//bombard it with SIGSTOP/SIGCONT/SIGUSR1
for(;;){
usleep(1); kill(p, SIGSTOP); kill(p, SIGCONT); kill(p, SIGUSR1);
}
}else{
sigaction(SIGCHLD, &(struct sigaction){ .sa_handler=SIG_DFL }, 0);
if(0>alarm(1))
return 1;
for(;;){
#if 1
/*not interruptible*/
if(0>access("/dev/null", R_OK)){
if(errno==EINTR)
return 2;
perror(0);
return 1;
}
#else
int fd;
unlink("fifo");
if(0>mkfifo("fifo",0600))
return 1;
/*interruptible*/
if(0>(fd=open("fifo", O_RDONLY|O_CREAT, 0600))){
if(errno==EINTR)
return 2;
perror(0);
return 1;
}
close(fd);
#endif
}
}
return 0;
}
а также unlink
а также access
определенно кажутся бесперебойными EINTR (в соответствии с их спецификацией), что означает EINTR
Повторная петля вокруг них была бы ненужной.