Invocar funciones específicas del kernel (llamadas del sistema) es parte natural del desarrollo aplicaciones en GNU/Linux. ¿Pero qué hay de ir en la dirección contraria, donde el espacio de kernel llama al espacio de usuario? Resulta que hay una cantidad de aplicaciones para esta función que probablemente usted usa todos los días. Por ejemplo, cuando el kernel encuentra un dispositivo para el cual se debe cargar un módulo, ¿cómo ocurre este proceso? La carga del módulo dinámico ocurre desde el kernel a través del proceso de ayuda de usuario.
Comencemos explorando ayuda de usuario, su interfaz de programación de aplicaciones (API), y algunos ejemplos de dónde se usa esta función en el kernel. Luego, usando la API, usted creará una aplicación de muestra para comprender mejor cómo funciona y sus limitaciones.
La API ayudante de usuario es una API simple con un conjunto de opciones conocido.
Por ejemplo, para crear un proceso desde el espacio de usuario, generalmente usted
brinda el nombre del ejecutable, y un conjunto de variables del entorno (consulte la
página man para ver el execve). Lo mismo se aplica para
crear un proceso desde el kernel. Pero como está iniciando el proceso desde el
espacio del kernel, hay unas pocas opciones disponibles.
La tabla 1 muestra el conjunto central de funciones del kernel disponibles en la API ayudante de usuario.
Tabla 1. Funciones centrales de la API ayudante de usuario
| Función de la API | Descripción |
|---|---|
call_usermodehelper_setup
| Preparar un controlador para una llamada user-land |
call_usermodehelper_setkeys
| Configurar las claves de sesión para un ayudante |
call_usermodehelper_setcleanup
| Configurar una función de limpieza para el ayudante |
call_usermodehelper_stdinpipe
| Crear unstdincanal para un ayudante |
call_usermodehelper_exec
| Invocar la llamada user-land |
En esta tabla se incluyen también unas funciones de simplificación que encapsulan las funciones del kernel en la Tabla 2 (que requieren una única llamada en lugar de múltiples llamadas). Estas funciones de simplificación sirven para la mayoría de los casos, así que le aconsejamos usarlas, de ser posible.
Tabla 2. Simplificaciones de la API ayudante de usuario
| Función API | Descripción |
|---|---|
call_usermodehelper
| Hacer una llamada user-land |
call_usermodehelper_pipe
| Hace una llamada user-land con un canalstdin |
call_usermodehelper_keys
| Hacer una llamada user-land con claves de sesión |
Primero veamos las funciones centrales, luego exploremos las capacidades que ofrecen
las funciones de simplificación. La API central opera usando una referencia de
controlador denominada estructura subprocess_info. Esta
estructura (que se puede encontrar en ./kernel/kmod.c) agrupa todos los elementos
necesarios para una instancia de ayuda de usuario determinada. La referencia de la
estructura es devuelta de una llamada a call_usermodehelper_setup. La estructura (y las llamadas subsiguientes)
se configure adicionalmente a través de llamadas a call_usermodehelper_setkeys(for credentials storage),call_usermodehelper_setcleanup, and call_usermodehelper_stdinpipe. Finalmente, cuando cla configuración esté
completa, puede invocar la aplicación de modo de usuario a través de una llamada
a call_usermodehelper_exec.
Las funciones centrales le dan la mayor cantidad de control, donde las funciones de
ayuda hacen la mayor parte del trabajo por usted en una llamada individual. Las
llamadas relacionadas con el canal (call_usermodehelper_stdinpipe y la función de ayuda call_usermodehelper_pipe) crean un canal relacionado para que lo utilice
el ayudante. Específicamente, se crea un canal (una estructura de archivo en el
kernel). El canal puede ser leído por la aplicación de espacio de usuario y puede
ser escrito por el lado del kernel. En cuanto a la escritura, core dumps son la
única aplicación que puede usar un canal con un ayudante de usuario. En esta
aplicación (./fs/exec.c do_coredump()), core dump es
escrito a través del canal desde el espacio del kernel al espacio del usuario.
La relación entre estas funciones ysub_processinfojunto a
los detalles de la subprocess_info estructura se muestran
en la Figura 1.
Figura 1. Relaciones de la API ayudante de usuario
La funciones de simplificación que se ven en la Tabla2 realizan
la función call_usermodehelper_setup y la función call_usermodehelper_exec internamente. Las últimas dos
llamadas que figuran en la Tabla 2 invocancall_usermodehelper_setkeys y call_usermodehelper_stdinpipe, respectivamente. Puede encontrar la
fuente para call_usermodehelper_pipe en ./kernel/kmod.c y
para call_usermodehelper y call_usermodhelper_keys en ./include/linux/kmod.h.
¿Para qué invocar una aplicación de espacio de usuario desde el kernel?
Ahora analicemos algunos de los lugares en el kernel donde se utiliza la API ayudante de usuario. La tabla 3 nos ofrece una lista exclusiva de aplicaciones sino que representa una intersección de usos interesantes.
Tabla 3. Aplicaciones de la API ayudante de usuario en el kernel
| Aplicación | Ubicación de origen |
|---|---|
| Carga de módulo kernel | ./kernel/kmod.c |
| Gestión de energía | ./kernel/sys.c |
| Grupos de control | ./kernel/cgroup.c |
| Generación de clave de seguridad | ./security/keys/request_key.c |
| Entrega de evento kernel | ./lib/kobject_uevent.c |
Una de las aplicaciones más simples de la API ayudante de usuario es cargar módulos
kernel desde el espacio de kernel. La función request_module encapsula la funcionalidad de la API ayudante de usuario y
provee una interfaz simple. En un modo de uso común, el kernel identifica un
dispositivo o servicio y hace una llamada a request_module para que se cargue el módulo. A través de la API ayudante de
usuario, el módulo se carga en el kernel vía modprobe(la
aplicación invocada en el espacio de usuario vía request_module).
Una aplicación similar a la carga de módulo es hot-plugging de dispositivos (para agregar o quitar dispositivos en tiempo de ejecución). Esta función se implementa con la API ayudante de usuario, invocando la utilidad /sbin/hotplug en espacio de usuario.
Una aplicación interesante de la API ayudante de usuario (via request_module) es la API textsearch (./lib/textsearch.c). Esta
aplicación brinda una infraestructura de búsqueda de texto configurable en el
kernel. Esta aplicación usa la API ayudante de usuario a través de la carga dinámica
de algoritmos como módulos cargables. En la versión 2.6.30 del kernel, se da soporte
a tres algoritmos, que incluyen Boyer-Moore (./lib/ts_bm.c), un enfoque de máquina
de estados finitos (./lib/ts_fsm.c), y finalmente el algoritmo Knuth-Morris-Pratt
(./lib/ts_kmp.c).
La API ayudante de usuario también soporta Linux en un apagado ordenado del sistema. Cuando es necesario apagar el sistema, el kernel invoca el comando/sbin/poweroff en el espacio de usuario para lograrlo. Otras aplicaciones se indican en la Tabla 3, con la ubicación de origen acompañante.
Internas de la API ayudante de usuario
Encontrará el origen y la API para la API ayudante de usuario en kernel/kmod.c (que
ejemplifica su uso principal como el cargador de módulo de kernel del espacio de
kernel). La implementación utiliza kernel_execve para el
trabajo sucio. Observe que kernel_execve es lafunción
utilizada para iniciar init el proceso en el tiempo de
arranque y no utiliza la API ayudante de usuario.
La implementación de la API ayudante de usuario es bastante simple y sencilla (ver
Figura 2). El trabajo del ayudante de usuario comienza con la llamada a call_usermodehelper_exec (que se utiliza para iniciar la
aplicación de espacio de usuario desde una estructura subprocess_info preconfigurada). Esta función acepta dos argumentos:
la subprocess_info referencia de estructura y un tipo
de enumeración (ya sea no esperar, esperar que se inicie el proceso, o esperar que
todo el proceso se complete). El subprocess_info(o major
dicho el work_struct elemento de esta estructura se coloca
en cola en una estructura de trabajo (khelper_wq), que
asincrónicamente realize la invocación.
Figura 2. Implementación interna de la API ayudante de usuario
Cuando se coloca un elemento en el khelper_wq, se invoca la
función de control para la cola de trabajo (en este caso,__call_usermodehelper), que se ejecuta a través del subproceso khelper. Esta función comienza a sacar de cola la
estructura subprocess_info, que contiene toda la
información necesaria para la invocación del espacio de usuario. La ruta luego
depende de la enumeración de la variable wait. Si el
solicitante espera que termine todo el proceso, inclusive la invocación del espacio
de usuario (UMH_WAIT_PROC) o no esperar nada (UMH_NO_WAIT), entonces se crea un subproceso de kernel
desde la función wait_for_helper. De lo contrario, el
solicitante puede simplemente querer esperar que se invoque la aplicación de espacio
de usuario (UMH_WAIT_EXEC) pero no que se complete. En
este caso, se crea un subproceso de kernel para ____call_usermodehelper().
En el subproceso wait_for_helper, se instala un controlador
de señal SIGCHLD, , y se crea otro subproceso de kernel para ____call_usermodehelper. Pero en el subproceso wait_for_helper, se hace una llamada a sys_wait4 para esperar la finalización del subproceso de kernel ____call_usermodehelper(indicado por una señal SIGCHLD).
Entonces el subproceso realiza toda limpieza necesaria (ya sea liberando las
estructuras para UMH_NO_WAIT O simplemente enviar una
notificación de finalización a call_usermodehelper_exec().
La función ____call_usermodehelper es donde ocurre el
verdadero trabajo para iniciar la aplicación en el espacio de usuario. Esta función
comienza desbloqueando todas las señales y configurando el círculo de clave de
sesión. También instala el canal stdin(si se solicita).
Luego de un poco más de inicialización, se invoca la aplicación de espacio de
usuario a través de una llamada a kernel_execve(fromkernel/syscall.c), que incluye la listapath,argv previamente seleccionada (inclusive
el nombre de la aplicación de espacio de usuario), y el entorno. Cuando se completa
este proceso, el subproceso sale a través de una llamada a do_exit().
Este proceso también utiliza finalizaciones Linux, que es una operación tipo
semáforo. Cuando se invoca la función call_usermodehelper_exec, se declara una finalización. Después de que la
estructurasubprocess_infose coloca en el khelper_wq, se hace una llamada a wait_for_completion(utilizando la variable finalización como su único
argumento). Observe que esta variable también se almacena en la estructura subprocess_info como el campo complete. Cuando el subproceso secundario desea despertar la
función call_usermodehelper_exec, llama al método
kernel complete, observando la variable de
finalización de la estructura subprocess_info. Esta
llamada desbloquea la función para poder continuar. Puede encontrar la
implementación de esta API en include/linux/completion.h.
Encontrará más información sobre la API ayudante de usuario siguiendo los enlaces de la secciónRecursos.
Ahora veamos un uso simple de la API ayudante de usuario. Primero verá la API común, luego aprenderá a simplificar las cosas más usando las funciones del ayudante.
Para esta demostración, desarrollará un módulo de kernel cargable simple que invoca
la API. La Lista 1 presenta las funciones del módulo boilerplate, definiendo las
funciones de entrada y salida del módulo. Estas dos funciones se invocan en modprobe o insmod del módulo
(función de entrada del módulo) y rmmod del módulo (salida
del módulo).
Listado 1. Funciones del módulo boilerplate
#include <linux/module.h> #include
<linux/init.h> #include <linux/kmod.h> MODULE_LICENSE(
"GPL" ); static int __init mod_entry_func( void ) { return umh_test(); }
static void
__exit mod_exit_func( void ) { return; } module_init( mod_entry_func );
module_exit(
mod_exit_func ); |
El uso de la API ayudante de usuario se muestra en el Listado 2,
que explorará detalladamente. La función comienza con la declaración de una variedad
de variables y estructuras necesarias. Comience con la estructura subprocess_info, que contiene toda la información necesaria para
realizar la invocación de espacio del usuario. Esta invocación se inicializa cuando
llama call_usermodehelper_setup. Luego defina su lista
de argumentos, llamada argv. Esta lista es similar a la
lista argv utilizada en programas C comunes y define la aplicación (primer elemento del conjunto) y la
lista de argumentos. Se necesita un terminador NULL para indicar el final de la
lista. Observe que la variable argc (conteo de argumentos)
está implícita, debido a que se desconoce la extensión de la lista argv. En este ejemplo, el nombre de la aplicación es
/usr/bin/logger, y su argumento es help!, seguido por su
NULL terminador. La siguiente variable requerida es el conjunto de entorno (envp). Este conjunto es una lista de parámetros que definen
el entorno de ejecución para la aplicación de espacio de usuario. En este ejemplo,
usted define unos cuantos parámetros típicos que se definen para el shell y termina
con una entrada de NULL terminador.
Listado 2. Prueba simple de API ayudante en modo de usuario
static int umh_test( void ) { struct
subprocess_info *sub_info; char *argv[] =
{ "/usr/bin/logger", "help!", NULL };
static char *envp[] = { "HOME=/", "TERM=linux",
"PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL }; sub_info
= call_usermodehelper_setup( argv[0], argv, envp, GFP_ATOMIC ); if
(sub_info == NULL) return -ENOMEM; return
call_usermodehelper_exec( sub_info,
UMH_WAIT_PROC ); } |
Luego, haga una llamada a call_usermodehelper_setup para
crear su estructura subprocess_info inicializada. Observe
que usted utiliza sus variables inicializadas previamente junto a un cuarto
parámetro que indica la máscara GFP para la inicialización de la memoria. Interno de
la función de configuración, hay una llamada kzalloc(que
asigna memoria kernel y la pone en cero). Esta función requiere GFP_ATOMIC o el indicador GFP_KERNEL(donde el
primero define que la llamada no debe entrar en espera y el Segundo que la espera es
posible). Luego de una prueba rápida de su nueva estructura (concretamente, no es
NULL), siga haciendo la llamada usando la función call_usermodehelper_exec. Esta función toma su estructura subprocess_info y una enumeración y define si espera (se
describe en la sección internos). ¡Y eso es todo! Cuando el módulo se carga, debe
ver el mensaje en su archivo /var/log/messages.
Puede simplificar este proceso aún más usando la función API call_usermodehelper, que realiza las funciones call_usermodehelper_setup y call_usermodehelper_exec juntas. Como se muestra en el Listado 3, esto no
solo elimina una función sino que también elimina la necesidad de que el llamador
gestione la estructura subprocess_info.
Listado 3. Prueba aún más simple de API ayudante en modo de usuario
static int umh_test( void ) {
char *argv[] = { "/usr/bin/logger", "help!", NULL };
static char *envp[] = {
"HOME=/", "TERM=linux", "PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL };
return call_usermodehelper ( argv[0], argv, envp, UMH_WAIT_PROC ); } |
Observe que en el Listado 3 existen los mismos requisitos para configurar y hacer la
llamada (como por ejemplo inicializar los conjuntos argv y envp). La única diferencia es que la
función ayudante realiza las funcionessetup y exec.
La API ayudante de usuario es un aspecto importante del kernel, dado su variado y amplio uso (desde carga de módulo kernel, hot-plugging de dispositivo, y distribución de evento para udev). Aunque es importante validar aplicaciones genuinas de la API, es un aspecto importante del kernel es un aspecto importante del kernel para comprender y por ende es un agregado útil para su kit de herramientas de kernel de Linux.
Aprender
- Hay poca información sobre la API ayudante de
usuario, pero la implementación es bastante clara y simple de seguir. Puede revisar
la implementación a través de LXR (el navegador fuente Linux Cross
Referencer— para todas las revisiones de fuente). Los dos archivos principales
de interés son kmod.c y kmod.h.
- El sistema de archivo /proc brinda un método de
comunicación entre el kernel y el espacio de usuario,— aunque es un sistema de
archivos virtual. Puede obtener más información sobre el sistema de archivos /proc
en "Accessthe Linux kernel using the /proc
filesystem" (developerworks, marzo de 2006).
- La interfaz de llamada del sistema Linux provee los
medios para que las aplicaciones espacio de usuario invoquen funcionalidad de
kernel. Para obtener más detalles sobre llamadas del sistema Linux, inclusive cómo
agregar nuevas llamadas del sistema, vea "Kernelcommand using system calls"
(developerworks, marzo de 2007).
- Para ejemplificar la API ayudante de usuario, este
artículo utiliza módulos de kernel creables para instalar aplicaciones de prueba en
el kernel. Para obtener más información sobre los módulos de kernel cargables y su
implementación, vea "Anatomy of Linux loadable kernel
modules" (developerworks, julio de 2008).
- Para obtener más información sobre la interfaz de
cola de trabajo del kernel 2.6, vea este antiguo artículo de Linux Journal
de2003, que ofrece una buena introducción a la API y al funcionamiento de
las colas de trabajo del kernel.
- En la zona Linux de developerWorks, encuentre más
recursos para desarrolladores de Linux.
- Siga actualizado con Eventos técnicos y transmisiones por
Internet de developerWorks.
- Siga developerWorks en Twitter.
Obtener los productos y tecnologías
- Con Software de prueba de IBM, disponible
para descargarlo directamente de developerWorks, arme su próximo proyecto de
desarrollo sobre Linux.
Comentar
- Involúcrese en la comunidad My developerWorks. Conéctese
con otros usuarios de developerWork mientras explora los blogs, foros, grupos y
wikis creados por desarrolladores.

M. Tim Jones es arquitecto de firmware incrustado y es autor de Artificial Intelligence: A Systems Approach, GNU/Linux Application Programming (actualmente en su segunda edición), AI Application Programming (en su segunda edición) y BSD Sockets Programming from a Multilanguage Perspective. Sus antecedentes en la ingeniería abarcan desde el desarrollo de kernels para naves espaciales geosincrónicas hasta la arquitectura de sistemas incrustados y el desarrollo de protocolos de redes. Tim es Consultant Engineer de Emulex Corp. en Longmont, Colorado.