¿Cómo se hace la E / S de consola sin locking en Linux en C?

¿Cómo se hace la consola sin locking IO en Linux / OS X en C?

Tu no, realmente. El TTY (consola) es un dispositivo bastante limitado, y prácticamente no realiza E / S sin locking. Lo que haces cuando ves algo que parece una E / S sin locking, por ejemplo, en una aplicación curses / ncurses, se llama E / S sin formato . En I / O sin procesar, no hay interpretación de los caracteres, no hay proceso de borrado, etc. En su lugar, necesita escribir su propio código que verifique los datos mientras hace otras cosas.

En los progtwigs modernos de C, puede simplificar esto de otra manera, al colocar las E / S de la consola en un proceso de subprocesos o de peso liviano. Luego, la E / S puede continuar de la forma habitual de locking, pero los datos pueden insertarse en una cola para ser procesados ​​en otra secuencia.

Actualizar

Aquí hay un tutorial de curses que lo cubre más.

Al igual que Pete Kirkham , encontré cc.byexamples.com , y funcionó para mí. Vaya allí para obtener una buena explicación del problema, así como la versión de ncurses.

Mi código necesitaba tomar un comando inicial de la entrada estándar o un archivo, luego observar un comando de cancelación mientras se procesaba el comando inicial. Mi código es C ++, pero debería poder usar scanf () y el rest donde utilizo la función de entrada C ++ getline ().

La carne es una función que verifica si hay alguna entrada disponible:

 #include  #include  #include  // cc.byexamples.com calls this int kbhit(), to mirror the Windows console // function of the same name. Otherwise, the code is the same. bool inputAvailable() { struct timeval tv; fd_set fds; tv.tv_sec = 0; tv.tv_usec = 0; FD_ZERO(&fds); FD_SET(STDIN_FILENO, &fds); select(STDIN_FILENO+1, &fds, NULL, NULL, &tv); return (FD_ISSET(0, &fds)); } 

Esto debe invocarse antes de cualquier función de entrada stdin. Cuando utilicé std :: cin antes de usar esta función, nunca volvió a ser verdadera. Por ejemplo, main () tiene un bucle que se ve así:

 int main(int argc, char* argv[]) { std::string initialCommand; if (argc > 1) { // Code to get the initial command from a file } else { while (!inputAvailable()) { std::cout << "Waiting for input (Ctrl-C to cancel)..." << std::endl; sleep(1); } std::getline(std::cin, initialCommand); } // Start a thread class instance 'jobThread' to run the command // Start a thread class instance 'inputThread' to look for further commands return 0; } 

En el hilo de entrada, se agregaron nuevos comandos a una cola, que fue procesada periódicamente por el jobThread. El inputThread se parecía un poco a esto:

 THREAD_RETURN inputThread() { while( !cancelled() ) { if (inputAvailable()) { std::string nextCommand; getline(std::cin, nextCommand); commandQueue.lock(); commandQueue.add(nextCommand); commandQueue.unlock(); } else { sleep(1); } } return 0; } 

Esta función probablemente podría haber estado en main (), pero estoy trabajando con una base de código existente, no en contra de ella.

Para mi sistema, no había entradas disponibles hasta que se envió una nueva línea, que era justo lo que quería. Si desea leer cada carácter cuando se escribe, debe desactivar el "modo canónico" en stdin. cc.byexamples.com tiene algunas sugerencias que no he intentado, pero el rest funcionó, así que debería funcionar.

Quiero agregar un ejemplo:

 #include  #include  #include  int main(int argc, char const *argv[]) { char buf[20]; fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK); sleep(4); int numRead = read(0,buf,4); if(numRead > 0){ printf("You said: %s", buf); } } 

Cuando ejecuta este progtwig tiene 4 segundos para proporcionar entrada a la entrada estándar. Si no se encuentra ninguna entrada, no se bloqueará y simplemente regresará.

2 ejecuciones de muestra:

 Korays-MacBook-Pro:~ koraytugay$ ./a.out fda You said: fda Korays-MacBook-Pro:~ koraytugay$ ./a.out Korays-MacBook-Pro:~ koraytugay$ 

A principios de este mes, marqué ” Entrada de usuario sin locking en bucle sin ncurses ” cuando pensé que podría necesitar una entrada de consola no bloqueada y sin búfer, pero no lo hice, por lo que no puedo garantizar si funciona o no. Para mi uso, no me importó que no ingresara hasta que el usuario presionó enter, entonces solo usé aio para leer stdin.

Aquí hay una pregunta relacionada usando C ++: multiplataforma (linux / Win32) sin locking C ++ IO en stdin / stdout / stderr

usar ncurses

Otra alternativa al uso de ncurses o threads es usar GNU Readline , específicamente la parte que le permite registrar funciones de callback . El patrón es entonces:

  1. Use select () en STDIN (entre otros descriptores)
  2. Cuando select () le dice que STDIN está listo para leer, llame a readline rl_callback_read_char ()
  3. Si el usuario ha ingresado una línea completa, rl_callback_read_char llamará a su callback. De lo contrario, volverá inmediatamente y tu otro código podrá continuar.

No estoy del todo seguro de lo que quiere decir con “consola IO”: ¿está leyendo de STDIN o es una aplicación de consola que lee de otra fuente?

Si está leyendo desde STDIN, deberá omitir fread () y usar read () y write (), con poll () o select () para evitar que las llamadas se bloqueen. Es posible que pueda desactivar el almacenamiento en búfer de entrada, lo que debería hacer que fread devuelva un EOF, con setbuf (), pero nunca lo he intentado.