Ao ler as páginas de manual nas chamadas read()
e write()
, parece que essas chamadas são interrompidas por sinais, independentemente de terem ou não de bloquear.
Em particular, suponha
- um processo estabelece um manipulador para algum sinal.
- um dispositivo é aberto (por exemplo, um terminal) com o
O_NONBLOCK
não configurado (ou seja, operando no modo de bloqueio) - o processo faz uma
read()
chamada de sistema para ler do dispositivo e, como resultado, executa um caminho de controle do kernel no espaço do kernel. - enquanto o precess está executando seu
read()
no espaço do kernel, o sinal para o qual o manipulador foi instalado anteriormente é entregue a esse processo e seu manipulador de sinal é invocado.
Lendo as páginas de manual e as seções apropriadas no SUSv3 'System Interfaces volume (XSH)' , verifica-se que:
Eu. Se a read()
for interrompido por um sinal antes de ler quaisquer dados (ou seja, ele teve que bloquear porque não havia dados disponíveis), ele retornará -1 com errno
definido como [EINTR].
ii. Se a read()
for interrompido por um sinal após a leitura bem-sucedida de alguns dados (ou seja, foi possível iniciar o atendimento da solicitação imediatamente), ele retornará o número de bytes lidos.
Pergunta A):
Estou correto ao assumir que, em ambos os casos (bloco / sem bloco), a entrega e o manuseio do sinal não são totalmente transparentes para o read()
?
Caso i. parece compreensível, pois o bloqueio read()
normalmente colocaria o processo no TASK_INTERRUPTIBLE
estado, de modo que, quando um sinal é entregue, o núcleo coloca o processo no TASK_RUNNING
estado.
No entanto, quando read()
não precisa bloquear (caso ii.) E está processando a solicitação no espaço do kernel, eu pensaria que a chegada de um sinal e seu manuseio seriam transparentes, assim como a chegada e o manuseio adequado de um HW interrupção seria. Em particular, eu teria assumido que, após a entrega do sinal, o processo seria temporariamente colocado no modo de usuário para executar seu manipulador de sinal, do qual retornaria eventualmente para terminar o processamento da interrupção read()
(no espaço do kernel) para que a read()
execução fosse executada. curso até a conclusão, após o qual o processo retorna ao ponto logo após a chamada para read()
(no espaço do usuário), com todos os bytes disponíveis lidos como resultado.
Mas ii. parece implicar que o read()
item seja interrompido, já que os dados estão disponíveis imediatamente, mas ele retorna apenas alguns dos dados (em vez de todos).
Isso me leva à minha segunda (e final) pergunta:
Pergunta B):
Se minha suposição em A) está correta, por que a read()
interrupção é interrompida, mesmo que não precise ser bloqueada porque há dados disponíveis para satisfazer a solicitação imediatamente? Em outras palavras, por que o read()
não é retomado após a execução do manipulador de sinal, resultando no retorno de todos os dados disponíveis (que estavam disponíveis, afinal)?
fonte
Para responder à pergunta A :
Sim, a entrega e o manuseio do sinal não são totalmente transparentes para o
read()
.A
read()
execução no meio do caminho pode estar ocupando alguns recursos enquanto é interrompida pelo sinal. E o manipulador de sinal do sinal também pode chamar outroread()
(ou qualquer outro syscalls seguros de sinal assíncrono ). Portanto, aread()
interrupção do sinal deve ser interrompida primeiro para liberar os recursos que usa, caso contrário, osread()
chamados do manipulador de sinal acessarão os mesmos recursos e causarão problemas de reentrada.Como as chamadas do sistema que não
read()
podem ser chamadas pelo manipulador de sinal e também podem ocupar um conjunto idêntico de recursos, como oread()
fazem. Para evitar problemas de reentrada acima, o design mais simples e seguro é interromper a interrupçãoread()
toda vez que um sinal ocorre durante sua execução.fonte