¿Cuál es la motivación de esta condición si en void serial8250_tx_chars(struct uart_8250_port *up)
?
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
Ha estado ahí desde Linux 1.1.13 (mayo de 1994) y se repite en la mayoría de los controladores UART.
Antecedentes: Linux 3.4.91 personalizado, sistema integrado en ARMv7, el puerto UART 0 está configurado para 38400 baudios, FIFO de 16 bytes para E/S. Nada de esto se puede cambiar en nuestra configuración.
Al imprimirmuymucho en la consola a través de UART, el búfer interno de 4kB ( UART_XMIT_SIZE
) se llena y luegoestablosel proceso del espacio de usuario hasta que se vacía el búfer (¡lo que lleva un segundo a 38400 baudios!). Entonces este comportamiento se repite. Esto se debe a que la función n_tty_write()
entra en suspensión cuando el búfer está lleno y no se activa durante mucho tiempo debido a la condición cuestionable anterior.
Me resultaría más natural y eficaz si simplemente se eliminara este control. Entonces los printfs llenarían el búfer lo más rápido posible yluego continúe a la velocidad a la que se está vaciando el buffer, en lugar del procesamiento en ráfagas que estoy observando.
Funciona bien en mi entorno, pero seguramente me falta algo o no entiendo bien. Debe haber una razón para la implementación actual. ¿Hay algún efecto secundario si elimino esa condición?
Como pregunta paralela: ¿existen opciones de configuración para ajustar este comportamiento, por ejemplo, hacer que printf siempre regrese inmediatamente y descarte la salida si el búfer está lleno?
Respuesta1
Es una medida de eficiencia. La CPU funciona mucho más rápido que el puerto serie que si el kernel permitiera que el proceso del espacio de usuario se ejecutara cada vez que hubiera un poco de espacio en el búfer, terminaría haciendo un viaje al espacio de usuario y regresando para cada byte de datos. Eso es una gran pérdida de tiempo 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
La prueba anterior ni siquiera es leer y escribir en un dispositivo real: toda la diferencia de tiempo es la frecuencia con la que el sistema rebota entre el espacio de usuario y el espacio del kernel.
Si el espacio de usuario no quiere ser retenido, puede usar E/S sin bloqueo, o puede verificar usar una select()
llamada para ver si hay espacio para escribir en el dispositivo... y si no lo hay, puede volcar el el resto en un buffer propio y continuar con el procesamiento. Es cierto que eso complica las cosas, ya que ahora tienes un búfer que debes vaciar... pero si estás usando stdio, eso es generalmente cierto de todos modos.