'EINTR': existe uma lógica por trás disso?

6

Conversa fiada como pano de fundo

EINTR é o erro que as chamadas chamadas do sistema interruptíveis podem retornar. Se um sinal ocorrer enquanto uma chamada de sistema está em execução, esse sinal não é ignorado e um manipulador de sinal foi definido para ele com SA_RESTART não definido e este manipulador lida com esse sinal, então a chamada do sistema retornará o código de erro EINTR .

Como uma nota lateral, recebi esse erro com muita frequência usando ncurses no Python.

A questão

Existe uma lógica por trás desse comportamento especificado pelo padrão POSIX? É possível entender que pode não ser possível retomar (dependendo do design do kernel), no entanto, qual é a razão para não reiniciá-lo automaticamente no nível do kernel? Isso é por razões legadas ou técnicas? Se isso é por razões técnicas, estas razões ainda são válidas hoje em dia? Se isso é por razões de legado, então qual é a história?

    
por Hibou57 05.01.2016 / 11:21

2 respostas

8

É difícil fazer coisas não triviais em um manipulador de sinal, já que o restante do programa está em um estado desconhecido. A maioria dos manipuladores de sinais apenas definem um flag, que é posteriormente verificado e tratado em outro lugar no programa.

Motivo de não reiniciar a chamada do sistema automaticamente:

Imagine um aplicativo que receba dados de um soquete pelo bloqueio e ininterrupto recv() chamada do sistema. Em nosso cenário, os dados são muito lentos e o programa fica muito tempo nessa chamada de sistema. Esse programa tem um manipulador de sinal para SIGINT que define um sinalizador (que é avaliado em outro lugar) e SA_RESTART é definido para que a chamada do sistema seja reiniciada automaticamente. Imagine que o programa esteja em recv() , que aguarda dados. Mas nenhum dado chega. Os blocos de chamadas do sistema. O programa agora captura ctrl - c do usuário. A chamada do sistema é interrompida e o manipulador de sinais, que apenas define o sinalizador, é executado. Então, recv() é reiniciado, ainda esperando por dados. O loop de eventos está preso em recv() e não tem oportunidade de avaliar o sinalizador e sair do programa normalmente.

Com SA_RESTART não definido:

No cenário acima, quando SA_RESTART não está definido, recv() receberia EINTR em vez de ser reiniciado. A chamada do sistema sai e, portanto, pode continuar. Claro, o programa deve então (o mais cedo possível) verificar o sinalizador (definido pelo manipulador de sinal) e fazer a limpeza ou o que ele fizer.

    
por 05.01.2016 / 12:27
1

Richard Gabriel escreveu um artigo A ascensão do 'pior é melhor' que discute o design escolha aqui no Unix:

Two famous people, one from MIT and another from Berkeley (but working on Unix) once met to discuss operating system issues. The person from MIT was knowledgeable about ITS (the MIT AI Lab operating system) and had been reading the Unix sources. He was interested in how Unix solved the PC loser-ing problem. The PC loser-ing problem occurs when a user program invokes a system routine to perform a lengthy operation that might have significant state, such as IO buffers. If an interrupt occurs during the operation, the state of the user program must be saved. Because the invocation of the system routine is usually a single instruction, the PC of the user program does not adequately capture the state of the process. The system routine must either back out or press forward. The right thing is to back out and restore the user program PC to the instruction that invoked the system routine so that resumption of the user program after the interrupt, for example, re-enters the system routine. It is called PC loser-ing because the PC is being coerced into loser mode, where 'loser' is the affectionate name for 'user' at MIT.

The MIT guy did not see any code that handled this case and asked the New Jersey guy how the problem was handled. The New Jersey guy said that the Unix folks were aware of the problem, but the solution was for the system routine to always finish, but sometimes an error code would be returned that signaled that the system routine had failed to complete its action. A correct user program, then, had to check the error code to determine whether to simply try the system routine again. The MIT guy did not like this solution because it was not the right thing.

The New Jersey guy said that the Unix solution was right because the design philosophy of Unix was simplicity and that the right thing was too complex. Besides, programmers could easily insert this extra test and loop. The MIT guy pointed out that the implementation was simple but the interface to the functionality was complex. The New Jersey guy said that the right tradeoff has been selected in Unix-namely, implementation simplicity was more important than interface simplicity.

    
por 12.11.2017 / 21:08