Maneira adequada de lidar com EINTR em bibliotecas


10

Qual é a etiqueta recomendada quando se trata de EINTRbibliotecas?

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 EINTRseria 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 EINTRe 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?)


1
O que sua biblioteca está fazendo? I tendem a aprovar a sua estratégia atual (de abortar a operação em EINTRe relatando que ao chamador), mas pode depender de qual é a sua biblioteca fazendo ...
Basile Starynkevitch

1
Nesse caso, está executando uma operação de bloqueio.
Rufflewind

Respostas:


2

Sempre deixe a decisão de como lidar EINTRcom 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 reade writehoje em dia retornam sucesso parcial - porque é muito frustrante para o usuário ser informado:

Você tentou escrever "foo"e nós escrevemos com sucesso nada, ou "f", ou "fo", e você não sabe qual . Diverta-se! Espero que o seu sistema consiga reiniciar toda a gravação depois de qualquer uma dessas!

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 EINTRocorrê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 um EINTRe 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.


4

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

again:
    if ((n = read(fd, buf, BUFFSIZE)) < 0) {
        if (errno == EINTR)
            goto again;     /* just an interrupted system call */
        /* handle other errors */
    }

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.


3
Isso não responde à pergunta. foi assim que as bibliotecas deveriam abordar o problema. Se eu souber que quero ignorar o EINTR em determinadas operações, posso fazê-lo com esta nova chamada ( goto again). Mas, como desenvolvedor de bibliotecas, não sei se é isso que meu usuário da biblioteca deseja.
M22
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.