Qual é a motivação desta condição if em void serial8250_tx_chars(struct uart_8250_port *up)
?
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
Ele existe desde o Linux 1.1.13 (maio de 1994) e se repete na maioria dos drivers UART.
Plano de fundo: Linux 3.4.91 customizado, sistema embarcado em ARMv7, porta UART 0 configurada para 38400 baud, FIFO de 16 bytes para E/S. Nada disso pode ser alterado em nossa configuração.
Ao imprimirmuitopesadamente no console via UART, o buffer interno de 4kB ( UART_XMIT_SIZE
) é preenchido e entãobarracaso processo do espaço do usuário até que o buffer seja esvaziado (o que leva um segundo a 38.400 baud!). Então esse comportamento se repete. Isso ocorre porque a função n_tty_write()
entra em suspensão quando o buffer está cheio e não é despertada por um longo tempo devido à condição questionável acima.
Eu acharia mais natural e eficiente se essa verificação fosse simplesmente removida. Então os printfs preencheriam o buffer o mais rápido possível eentão continue na velocidade em que o buffer está sendo esvaziado, em vez do processamento intermitente que estou observando.
Funciona bem no meu ambiente, mas certamente estou faltando ou entendendo mal alguma coisa. Deve haver uma razão para a implementação atual. Há algum efeito colateral se eu remover essa condição?
Como questão secundária: existem opções de configuração para ajustar esse comportamento, por exemplo, fazer com que printf sempre retorne imediatamente e descarte a saída se o buffer estiver cheio?
Responder1
É uma medida de eficiência. A CPU funciona muito mais rápido que a porta serial que se o kernel deixasse o processo do espaço do usuário rodar toda vez que houvesse um pouco de espaço no buffer, ele acabaria viajando para o espaço do usuário e voltando para cada byte de dados. Isso é um grande desperdício de tempo de CPU:
$ time dd if=/dev/zero of=/dev/null bs=1 count=10000000
10000000+0 records in
10000000+0 records out
10000000 bytes (10 MB, 9.5 MiB) copied, 5.95145 s, 1.7 MB/s
real 0m5.954s
user 0m1.960s
sys 0m3.992s
$ time dd if=/dev/zero of=/dev/null bs=1000 count=10000
10000+0 records in
10000+0 records out
10000000 bytes (10 MB, 9.5 MiB) copied, 0.011041 s, 906 MB/s
real 0m0.014s
user 0m0.000s
sys 0m0.012s
O teste acima nem sequer lê e escreve um dispositivo real: toda a diferença de tempo é a frequência com que o sistema oscila entre o espaço do usuário e o espaço do kernel.
Se o espaço do usuário não quiser ser retido, ele pode usar E/S sem bloqueio ou pode verificar, usar uma select()
chamada para ver se há espaço para gravar no dispositivo... e se não houver, pode despejar o restante em um buffer próprio e continue o processamento. É certo que isso complica as coisas, já que agora você tem um buffer que precisa liberar... mas se estiver usando stdio, isso geralmente é verdade de qualquer maneira.