Qual é a etiqueta recomendada quando se trata de EINTR
bibliotecas?
No momento, estou escrevendo uma função que executa algumas tarefas do sistema de arquivos com a API POSIX, mas muitas das chamadas que utilizo podem potencialmente retornar EINTR
. Além disso, a função pode bloquear em algumas circunstâncias. (Para os interessados, implementa um mecanismo de bloqueio.)
No interesse de tornar isso o mais genérico possível, gostaria de saber qual é a maneira correta de lidar com uma chamada de sistema interrompida.
Na maioria das fontes que li, as pessoas normalmente tentam novamente a ligação e continuam seus negócios. No entanto, não tenho certeza de que é a coisa certa a fazer aqui, pois pode haver razões legítimas para interromper minha função, pois isso pode levar uma quantidade significativa de tempo. Além disso, significa que a função
EINTR
seria simplesmente engolida e o chamador perderia qualquer indicação de que isso ocorreu.Minha estratégia atual é abortar imediatamente a operação se eu receber
EINTR
e notificar o chamador sobre isso. Dessa forma, o chamador pode decidir se deseja repetir minha função, certo? (Ou talvez minha compreensão dos sinais seja falha?)
EINTR
e relatando que ao chamador), mas pode depender de qual é a sua biblioteca fazendo ...Respostas:
Sempre deixe a decisão de como lidar
EINTR
com o usuário e facilite a retomada da operação, conforme apropriado.Geralmente, a melhor maneira de fazer isso é retornar da função de biblioteca para o chamador
EINTR
, mas em alguns casos um retorno de chamada ou alguma outra implementação pode ser melhor - a melhor maneira depende de outros fatores, mas sempre permita que o controle do usuário tente novamente. currículo.Isso significa que, se o código da sua biblioteca puder ser parcialmente bem-sucedido antes de obter um
EINTR
, você deve pensar cuidadosamente no que o usuário precisa saber sobre esse êxito parcial ou se pode precisar retomar a operação de onde falhou. Pode ser necessário retornar informações adicionais ou fornecer uma interface para retomar a partir de qualquer local onde possa ser apropriado.É por isso que as chamadas do sistema
read
ewrite
hoje em dia retornam sucesso parcial - porque é muito frustrante para o usuário ser informado:Obviamente, em alguns casos, devemos escrever sistemas para lidar com situações exatamente como essa - por exemplo, talvez após uma gravação parcial, você sempre recrie o arquivo, ou reabra a conexão de rede, ou use algum byte para significar "recomeçar" - portanto, depende de quais casos de uso sua biblioteca se destina.
Se uma função de biblioteca executa várias operações, e não há como saber em qual delas falhou, e essas operações não são todas seguras e eficientemente idempotentes, isso basicamente torna uma biblioteca inutilizável para código que precisa ser robusto.
Se todas as etapas de uma função de biblioteca são seguras e eficientemente idempotentes, ou a coisa toda é atômica - como a aquisição de um bloqueio -, basta informar o usuário que uma
EINTR
ocorrência é suficiente.Além disso, se tentarmos novamente
EINTR
, poderemos interromper o tratamento do sinal . No nível baixo, os manipuladores de sinal podem usar com segurança apenas um conjunto limitado de recursos e, em muitos casos, um manipulador de sinal define apenas um booleano indicando que o sinal foi recebido e depois retorna, esperando que, quando o código for retomado, sair do que estava fazendo. Se obtivermos umEINTR
e tentarmos novamente, em vez de retornar o controle ao usuário, poderemos impedir que o código faça isso.O que fazer depois de uma
EINTR
é uma decisão completa do programa - a resposta certa não pode ser conhecida sem saber o que o programa está fazendo e como o programa deve responder a um sinal, e isso afeta o restante do programa.Saber como ou se o usuário pode precisar continuar e ajudá-lo a fazê-lo, se necessário, é uma responsabilidade da biblioteca - a resposta certa não pode ser conhecida sem saber o que a biblioteca está fazendo.
fonte
Sinais no Unix
quando algum outro processo envia seu sinal de processo, seu programa interrompe o que está fazendo ...
1) Execute o código do manipulador que você escreveu. Você não tem idéia do que seu programa pode estar fazendo quando o sinal chegar. Essa é a idéia dos sinais, eles podem ser completamente assíncronos.
2) Quando o manipulador de sinal é concluído, ele geralmente retorna e seu programa continua onde parou, como se nada tivesse acontecido.
Encontrei algumas informações úteis em Richard Stevens
Uma característica dos sistemas UNIX anteriores é que, se um processo capturar um sinal enquanto o processo foi bloqueado em uma chamada de sistema "lenta", a chamada do sistema foi interrompida. A chamada do sistema retornou um erro e o número do erro foi definido como EINTR. Isso foi feito sob a suposição de que, desde que um sinal ocorreu e o processo o capturou, há uma boa chance de que algo aconteceu que acorde a chamada do sistema bloqueada.
A semântica do POSIX.1 para leituras e gravações interrompidas foi alterada com a versão 2001 do padrão. As versões anteriores deram às implementações a opção de como lidar com leituras e gravações que processaram quantidades parciais de dados. Se a leitura recebeu e transferiu dados para o buffer de um aplicativo, mas ainda não recebeu tudo o que o aplicativo solicitou e é interrompido, o sistema operacional pode falhar na chamada do sistema com o erro definido como EINTR ou permitir que a chamada do sistema seja bem-sucedida, retornando a quantidade parcial de dados recebidos. Da mesma forma, se a gravação for interrompida após a transferência de alguns dados no buffer de um aplicativo, o sistema operacional poderá falhar na chamada do sistema com o número de erro definido como EINTR ou permitir que a chamada do sistema seja bem-sucedida, retornando a quantidade parcial de dados gravados. Historicamente, implementações derivadas do System V falham na chamada do sistema, enquanto implementações derivadas do BSD retornam sucesso parcial. Com a versão 2001 do padrão POSIX.1, a semântica no estilo BSD é necessária.
O problema com as chamadas interrompidas do sistema é que agora precisamos lidar com o retorno de erro explicitamente. A sequência de código típica (assumindo uma operação de leitura e assumindo que queremos reiniciar a leitura mesmo que seja interrompida) seria
Para impedir que os aplicativos precisem lidar com chamadas de sistema interrompidas, o 4.2BSD introduziu o reinício automático de determinadas chamadas de sistema interrompidas.
fonte
goto again
). Mas, como desenvolvedor de bibliotecas, não sei se é isso que meu usuário da biblioteca deseja.