c linux-kernel semaphore

Semáforo compartido entre el usuario y los espacios del núcleo.



linux-kernel semaphore (7)

Bueno, estabas en la dirección correcta, pero no del todo.

Linux llamado POSIX semaphore se basa en FUTex, que significa Fast User-space Mutex. Como su nombre lo indica, mientras que su implementación es asistida por el kernel, una gran parte de ella es realizada por el código de usuario. Compartir tal semáforo entre el kernel y el espacio de usuario requeriría volver a implementar esta infraestructura en el kernel. Posible, pero ciertamente no es fácil.

Por otra parte, los semáforos SysV se implementan completamente en el kernel y solo son accesibles para el espacio del usuario a través de llamadas al sistema estándar (por ejemplo, sem_timedwait() y amigos).

Esto significa que todas las operaciones relacionadas con SysV (creación, toma o liberación de semáforos) se implementan realmente en el kernel y simplemente puede llamar a la función del kernel subyacente desde su código para tomar el mismo semáforo del kernel que se necesita.

Por lo tanto, su código de usuario simplemente llamará sem_timedwait() . Esa es la parte fácil.

La parte del kernel es un poco más complicada: tiene que encontrar el código que implementa sem_timedwait() y las llamadas relacionadas en el kernel (están todas en el archivo ipc / sem.c) y crear una réplica de cada uno de los funciones que hacen lo que hace la función original sin las llamadas a copy_from_user(...) y copy_to_user(..) y amigos.

La razón de esto es que esas funciones del kernel esperan ser llamadas desde una llamada del sistema con un puntero a un búfer del usuario, mientras que usted quiere llamarlas con parámetros en los búferes del kernel.

Tomemos, por ejemplo, sem_timedwait() : la función relevante del kernel es sys_timedwait() en ipc / sem.c (consulte aquí: http://lxr.free-electrons.com/source/ipc/sem.c#L1537 ). Si copia esta función en el código del kernel y simplemente elimina las partes que hacen copy_from_user() y copy_to_user() y simplemente usa los punteros pasados ​​(ya que los llamará desde el espacio del kernel), obtendrá funciones equivalentes al kernel que pueden tome el semáforo de SysV del espacio del kernel, junto con el espacio del usuario, siempre que los llame desde el contexto del usuario en el kernel (si no sabe lo que significa esta última oración, le recomiendo que lea los controladores de dispositivos de Linux, tercera edición) .

La mejor de las suertes.

Version corta

¿Es posible compartir un semáforo (o cualquier otro bloqueo de sincronización) entre el espacio del usuario y el espacio del kernel? Los semáforos nombrados de POSIX tienen persistencia en el núcleo , por eso me preguntaba si es posible crearlos y / o acceder a ellos desde el contexto del núcleo.

La búsqueda en Internet no ayudó mucho debido al mar de información sobre el uso normal de los semáforos POSIX.

Versión larga

Estoy desarrollando una interfaz unificada para sistemas en tiempo real en la que tengo un poco de contabilidad adicional para cuidar, protegida por un semáforo. Estos registros se realizan en la asignación de recursos y la desasignación, que se realiza en un contexto no en tiempo real.

Con RTAI, el hilo que espera y publica un semáforo, sin embargo, debe estar en un contexto de tiempo real. Esto significa que usar el semáforo llamado RTAI significa cambiar entre el contexto en tiempo real y el no en tiempo real en cada espera / publicación en el espacio del usuario, y peor aún, crear un hilo corto en tiempo real para cada sem / espera en el espacio del kernel.

Lo que estoy buscando es una forma de compartir un semáforo de Linux o POSIX normal entre los espacios del usuario y del kernel para que pueda esperar / publicarlo de manera segura en un contexto no en tiempo real.

Cualquier información sobre este tema sería muy apreciada. Si esto no es posible, ¿tiene alguna otra idea de cómo se podría realizar esta tarea? 1

1 Una forma sería agregar una llamada al sistema, tener el semáforo en el espacio del kernel y hacer que los procesos del usuario invocen esa llamada del sistema y todo el semáforo se administre en el espacio del kernel. Sin embargo, sería más feliz si no tuviera que parchar el núcleo solo por esto.


Como estoy seguro de que saben, incluso la mejor solución de trabajo para esto probablemente sería muy fea. Si estuviera en tu lugar, simplemente concedería la batalla y utilizaría puntos de encuentro para sincronizar los procesos.


Estaba pensando en formas en que el kernel y la tierra del usuario comparten las cosas directamente, es decir, sin costo de instalación / copia. Una cosa que recordé fue el modelo RDMA donde el kernel escribe / lee directamente desde el espacio del usuario, con la sincronización, por supuesto. Es posible que desee explorar ese modelo y ver si funciona para su propósito.


He leído el README su proyecto y tengo las siguientes observaciones. Disculpas por adelantado:

En primer lugar, ya existe una interfaz universal para sistemas en tiempo real. Se llama POSIX; Ciertamente, VxWorks, Integrity y QNX son compatibles con POSIX y, según mi experiencia, existen muy pocos problemas con la portabilidad si se desarrolla dentro de la API de POSIX. Si POSIX es sano o no es otro asunto, pero es el que todos usamos.

[La razón por la que la mayoría de los RTOS son compatibles con POSIX es porque uno de los grandes mercados para ellos es el equipo de defensa. Y el DoD de EE. UU. No le permitirá utilizar un sistema operativo para su equipo que no es de TI (por ejemplo, radares) a menos que sea compatible con POSIX ... Esto ha hecho prácticamente imposible comercialmente un RTOS sin darle POSIX]

En segundo lugar, Linux puede convertirse en un sistema operativo en tiempo real bastante bueno aplicando el PREMPT_RT parches PREMPT_RT . De todos los RTOS por ahí, este es probablemente el mejor en este momento desde el punto de vista de hacer un uso eficiente de todas estas CPU de múltiples núcleos. Sin embargo, no es un sistema operativo tan difícil como el de los demás, así que es un quid pro quo.

RTAI adopta un enfoque diferente: en efecto, coloca su propio RTOS debajo de Linux y hace que Linux no sea más que una tarea ejecutada en su sistema operativo. Este enfoque está bien hasta cierto punto, pero la gran penalización de RTAI es que el bit en tiempo real es ahora (por lo que puedo decir) no compatible con POSIX (aunque la API parece que acaban de pegar rt_ en la parte frontal de Algunos nombres de funciones de POSIX) y la interacción con otras cosas es ahora, como está descubriendo, bastante complicado.

PREEMPT_RT es un conjunto de parches mucho más intrusivo que RTAI, pero la recompensa es que todo lo demás (como POSIX y valgrind) se mantiene completamente normal. Además, hay cosas buenas como FTrace disponibles. La contabilidad es entonces un caso de simplemente usar herramientas existentes, no tener que escribir nuevas. También parece que PREEMPT_RT se está introduciendo gradualmente en el núcleo de Linux de todos modos. Eso haría que otros conjuntos de parches como RTAI sean bastante inútiles.

Entonces, Linux + PREEMPT_RT nos da POSIX en tiempo real más un montón de herramientas, al igual que todos los otros RTOS que existen; elementos comunes en todos los ámbitos. Lo que suena como el objetivo de tu proyecto.

Pido disculpas por no ayudar con el "cómo" de su proyecto, y es muy poco amable de mi parte preguntarme "¿por qué?" de eso tambien Pero creo que es importante saber que hay cosas establecidas que parecen superponerse con lo que estás tratando de hacer. Invadir a King POSIX va a ser difícil.


Me gustaría responder esto de manera diferente: no quieres hacer esto. Hay buenas razones por las que no hay una interfaz para hacer este tipo de cosas y hay buenas razones por las que todos los demás subsistemas del kernel están diseñados e implementados para no necesitar nunca un bloqueo compartido entre el usuario y el espacio del kernel. La complejidad de la ordenación de bloqueos y el bloqueo implícito en lugares inesperados se descontrolará rápidamente si comienza a jugar con usuarios que pueden evitar que el núcleo haga ciertas cosas.

Permítanme recordar una sesión de depuración muy larga que realicé hace unos 15 años para, al menos, aclarar qué problemas complejos puede encontrar. Estuve involucrado en el desarrollo de un sistema de archivos donde la gran parte del código estaba en la zona de usuario. Algo como FUSIBLE.

El kernel haría una operación del sistema de archivos, lo empaquetaría en un mensaje y lo enviaría al demonio del usuario y esperaría una respuesta. El demonio de la zona de usuario lee el mensaje, hace cosas y escribe una respuesta en el kernel que se activa y continúa con la operación. Concepto simple

Una cosa que debes entender acerca de los sistemas de archivos es el bloqueo. Cuando busca el nombre de un archivo, por ejemplo "foo / bar", el núcleo de alguna manera obtiene el nodo para el directorio "foo", luego lo bloquea y le pregunta si tiene el archivo "bar". El código del sistema de archivos de alguna manera encuentra "barra", lo bloquea y luego desbloquea "foo". El protocolo de bloqueo es bastante sencillo (a menos que esté haciendo un cambio de nombre), el padre siempre se bloquea antes del niño y el niño se bloquea antes de que se libere el bloqueo principal. El mensaje de búsqueda para el archivo es lo que se enviaría a nuestro demonio de la zona de usuario mientras el directorio aún estaba bloqueado, cuando el demonio respondió que el kernel procedería a bloquear primero la "barra" y luego desbloquear "foo".

Ni siquiera recuerdo los síntomas que estábamos depurando, pero recuerdo que el problema no era reproducible de manera trivial, requería horas y horas de programas de tortura del sistema de archivos hasta que se manifestara. Pero después de unas semanas nos dimos cuenta de lo que estaba pasando. Digamos que la ruta completa a nuestro archivo era "/ a / b / c / foo / bar". Estamos en el proceso de hacer una búsqueda en "barra", lo que significa que estamos manteniendo el bloqueo en "foo". El demonio es un proceso normal de usuario, por lo que algunas operaciones que realiza pueden bloquearse y también pueden ser anuladas. En realidad, está hablando a través de la red, por lo que puede bloquear durante mucho tiempo. Mientras estamos esperando al demonio del usuario, algún otro proceso quiere buscar "foo" por alguna razón. Para hacer esto, tiene el nodo para "c", bloqueado por supuesto, y le pide que busque "foo". Se las arregla para encontrarlo e intenta bloquearlo (tiene que estar bloqueado antes de que podamos liberar el bloqueo en "c") y espera a que se libere el bloqueo en "foo". Otro proceso se produce cuando se quiere buscar "c", que por supuesto termina esperando ese bloqueo mientras se mantiene el bloqueo en "b". Otro proceso espera a "b" y mantiene "a". Otro proceso más quiere "a" y mantiene el bloqueo en "/".

Esto no es un problema, todavía no. Esto también sucede a veces en los sistemas de archivos normales, los bloqueos se pueden conectar en cascada hasta la raíz, se espera un momento para que el disco sea lento, el disco responde, las congestiones aumentan y todos se bloquean y todo sigue funcionando bien. En nuestro caso, sin embargo, la razón para mantener el bloqueo durante mucho tiempo fue porque el servidor remoto para nuestro sistema de archivos distribuido no respondió. X segundos más tarde, el daemon de la zona de usuario agota el tiempo de espera y, justo antes de responder al kernel de que la operación de búsqueda en "barra" ha fallado, registra un mensaje en syslog con una marca de tiempo. Una de las cosas que necesita la marca de tiempo es la información de la zona horaria, por lo que necesita abrir "/ etc / localtime", por supuesto, para hacerlo, debe comenzar a buscar "/ etc" y para eso necesita bloquear "/ ". "/" ya está bloqueado por otra persona, por lo que el demonio de la zona de usuario espera a que esa otra persona desbloquee "/" mientras que otra persona espera a través de una cadena de 5 procesos y bloquea a la demonio para que responda. El sistema termina en un punto muerto total.

Ahora, tal vez su código no tendrá problemas como este. Estás hablando de un sistema en tiempo real, por lo que podría haber un nivel de control que tengas que los núcleos normales no tienen. Pero no estoy seguro de si agregar una capa inesperada de complejidad de bloqueo le permita mantener las propiedades en tiempo real del sistema, o realmente asegurarse de que nada de lo que haga en la zona de usuarios creará una cascada de interbloqueo. Si no lo haces, si nunca tocas ningún descriptor de archivos, si nunca haces operaciones de memoria y un montón de otras cosas que realmente no puedo pensar en este momento, podrías salir con un bloqueo compartido entre el usuario y el kernel, pero Será difícil y probablemente encontrarás problemas inesperados.


No tengo mucha experiencia en esto de ninguna manera, pero aquí está mi opinión. Si nos fijamos en la implementación de sem_open y sem_wait , en realidad se trata simplemente de crear un archivo en / dev / shm, hacer un mapeo de la estructura y utilizar operaciones atómicas en él. Si desea acceder al semáforo nombrado desde el espacio de usuario, probablemente tendrá que parchear el subsistema tmpfs. Sin embargo, creo que esto sería difícil, ya que no sería sencillo determinar si un archivo está destinado a ser un semáforo nombrado.

Probablemente, una forma más sencilla sería simplemente reutilizar la implementación del semáforo del kernel y hacer que el kernel administre el semáforo para los procesos del espacio de usuario. Para hacer esto, escribiría un módulo de kernel que asocie con un archivo de dispositivo. Luego, defina dos ioctl para el archivo del dispositivo, uno para esperar y otro para publicar. Aquí hay un buen tutorial sobre cómo escribir módulos del kernel, que incluye la configuración de un archivo de dispositivo y la adición de operaciones de E / S. http://www.freesoftwaremagazine.com/articles/drivers_linux . No sé exactamente cómo implementar una operación ioctl, pero creo que solo puede asignar una función al miembro ioctl de la estructura file_operations. No estoy seguro de cuál debería ser la firma de la función, pero es probable que puedas averiguarlo cavando en la fuente del núcleo.


Una solución que se me ocurre es tener un archivo /proc (o /sys o lo que sea) en un módulo principal del kernel donde escribir 0 en él (o leerlo / escribir en él) causaría que se produzca un up / down en un semaphore . Exportar ese semáforo permite que otros módulos del kernel accedan directamente a él mientras las aplicaciones de usuario pasan por el sistema de archivos /proc .

Todavía esperaría a ver si la pregunta original tiene una respuesta.