pila de kernel y pila de espacio de usuario

110

¿Cuál es la diferencia entre la pila del kernel y la pila del usuario? ¿Por qué se usa la pila de kernel? Si se declara una variable local en un ISR, ¿dónde se almacenará? ¿Cada proceso tiene su propia pila de kernel? Entonces, ¿cómo se coordina el proceso entre ambas pilas?

jkv
fuente

Respuestas:

188
  1. ¿Cuál es la diferencia entre la pila del kernel y la pila del usuario?

En resumen, nada, aparte de usar una ubicación diferente en la memoria (y, por lo tanto, un valor diferente para el registro de puntero de pila) y, por lo general, diferentes protecciones de acceso a la memoria. Es decir, cuando se ejecuta en modo de usuario, la memoria del kernel (parte de la cual es la pila del kernel) no será accesible incluso si está mapeada. Viceversa, sin ser solicitado explícitamente por el código del kernel (en Linux, a través de funciones como copy_from_user()), la memoria del usuario (incluida la pila de usuarios) no suele ser directamente accesible.

  1. ¿Por qué se usa [una pila de kernel separada]?

Separación de privilegios y seguridad. Por un lado, los programas del espacio de usuario pueden hacer que su pila (puntero) sea lo que quieran y, por lo general, no hay ningún requisito arquitectónico para tener uno válido. Por lo tanto, el kernel no puede confiar en que el stackpointer del espacio de usuario sea válido o utilizable y, por lo tanto, requerirá un conjunto bajo su propio control. Las diferentes arquitecturas de CPU implementan esto de diferentes maneras; Las CPU x86 cambian automáticamente los punteros de pila cuando ocurren los cambios de modo de privilegio, y los valores que se utilizarán para los diferentes niveles de privilegio se pueden configurar mediante código privilegiado (es decir, solo el kernel).

  1. Si se declara una variable local en un ISR, ¿dónde se almacenará?

En la pila de kernel. El kernel (es decir, el kernel de Linux) no engancha los ISR directamente a las puertas de interrupción de la arquitectura x86, sino que delega el envío de interrupciones a un mecanismo común de entrada / salida de interrupciones del kernel que guarda el estado de registro previo a la interrupción antes de llamar a los controladores registrados . La CPU misma al enviar una interrupción puede ejecutar un privilegio y / o cambio de pila, y esto es usado / configurado por el kernel para que el código de entrada de interrupción común ya pueda depender de una pila del kernel presente.
Dicho esto, las interrupciones que ocurren mientras se ejecuta el código del kernel simplemente (continuarán) usarán la pila del kernel en su lugar en ese momento. Esto puede, si los manejadores de interrupciones tienen rutas de llamadas profundamente anidadas, provocar desbordamientos de pila (si se interrumpe una ruta de llamada profunda del kernel y el manejador causa otra ruta profunda; en Linux, el código RAID del sistema de archivos / software es interrumpido por el código de red con iptables activo es conocido por desencadenarlo en kernels antiguos no optimizados ... la solución es aumentar el tamaño de la pila del kernel para tales cargas de trabajo).

  1. ¿Cada proceso tiene su propia pila de kernel?

No solo cada proceso, cada hilo tiene su propia pila de kernel (y, de hecho, también su propia pila de usuario). Recuerde que la única diferencia entre procesos y subprocesos (para Linux) es el hecho de que varios subprocesos pueden compartir un espacio de direcciones (formando un proceso).

  1. ¿Cómo se coordina el proceso entre ambas pilas?

En absoluto, no es necesario. La programación (cómo / cuándo se ejecutan diferentes subprocesos, cómo se guarda y se restaura su estado) es la tarea del sistema operativo y los procesos no necesitan preocuparse por esto. A medida que se crean los subprocesos (y cada proceso debe tener al menos un subproceso), el kernel crea pilas de kernel para ellos, mientras que las pilas de espacio de usuario se crean / proporcionan explícitamente mediante cualquier mecanismo que se utilice para crear un subproceso (funciones como makecontext()o pthread_create()permiten que el llamador especificar una región de memoria que se utilizará para la pila de subprocesos "secundarios"), o heredada (mediante clonación de memoria en el acceso, generalmente llamada "copia en escritura" / COW, cuando se crea un nuevo proceso).
Dicho eso(estado, entre eso está el puntero de pila del hilo). Hay varias formas para esto: las señales de UNIX, setcontext(), pthread_yield()/ pthread_cancel(), ... - pero esto es disgressing un poco de la pregunta original.

FrankH.
fuente
Excelentes respuestas FrankH. Gracias.
Kumar
1
@FrankH Excelente respuesta ... pero tengo pequeñas preguntas relacionadas con ella, pero en arquitectura ARM ... ¿Cómo se relaciona esta pila de kernel con los diferentes modos de procesador?
Rahul
2
@Rahul: "el margen de un comentario SO es demasiado pequeño para contener esa respuesta". Cómo funcionan las pilas / registros de puntero de pila en los diferentes modos de CPU ARM (cuáles implementan un SP almacenado) es una buena pregunta, pero requiere más espacio para responder que el que puede dar un comentario. Lo mismo se aplica a cosas como puertas de tareas x86 o IST: no existe un puntero de pila de kernel "único" (al igual que no hay un puntero de pila de usuario "único"), y para qué soporte / mandato de hardware hay punteros de pila separados en diferentes modos de funcionamiento es ... muy dependiente del hardware.
FrankH.
@FrankH. Creé una nueva pregunta para el mismo ... stackoverflow.com/q/22601165/769260 Espero que ahora puedas ayudarme sin importar el espacio :)
Rahul
1
@FrankH. ¿Puede proporcionar un diagrama que muestre a dónde pertenece la pila del kernel en el diseño de memoria de un proceso?
Jithin Pavithran
19

Mi respuesta se recopila de otras preguntas SO con mis productos.

What's the difference between kernel stack and user stack?

Como programador del núcleo, sabe que el núcleo debe estar restringido a programas de usuario erróneos. Suponga que mantiene la misma pila tanto para el kernel como para el espacio de usuario, luego una simple falla de segmentación en la aplicación del usuario bloquea el kernel y necesita reiniciarse.

Hay una "pila de kernel" por CPU como ISR Stack y una "pila de kernel" por proceso. Hay una "pila de usuarios" para cada proceso, aunque cada subproceso tiene su propia pila, incluidos los subprocesos de usuario y del kernel.

http://linux.derkeiler.com/Mailing-Lists/Kernel/2004-10/3194.html

Why kernel stack is used?

Entonces, cuando estamos en modo kernel, es necesario un tipo de mecanismo de pila para tratar con llamadas a funciones, variables locales similares al espacio de usuario.

http://www.kernel.org/doc/Documentation/x86/kernel-stacks

If a local variable is declared in an ISR, where it will be stored?

Se almacenará en la pila ISR (IRQSTACKSIZE). El ISR se ejecuta en una pila de interrupciones separada solo si el hardware lo admite. De lo contrario, los marcos de la pila ISR se insertan en la pila del hilo interrumpido.

El espacio de usuario no sabe y, francamente, no le importa si la interrupción se sirve en la pila del núcleo del proceso actual o en una pila ISR separada. Como las interrupciones vienen por cpu, la pila de ISR debe ser por cpu.

 Does each process has its own kernel stack ?

Si. Cada proceso tiene su propia pila de kernel.

 Then how the process coordinates between both these stacks?

La respuesta de @ FrankH me parece genial.

Jeyaram
fuente
4
  1. ¿Cuál es la diferencia entre la pila del kernel y la pila del usuario?

Tomando como referencia el desarrollo del kernel de Linux de Robert Love, la principal diferencia es el tamaño:

El espacio de usuario puede salirse con la suya asignando estáticamente muchas variables en la pila, incluidas estructuras enormes y matrices de mil elementos.
Este comportamiento es legal porque el espacio de usuario tiene una pila grande que puede crecer dinámicamente.
La pila del núcleo no es ni grande ni dinámica; es pequeño y de tamaño fijo.
El tamaño exacto de la pila del kernel varía según la arquitectura.
En x86, el tamaño de la pila se puede configurar en tiempo de compilación y puede ser de 4 KB u 8 KB.
Históricamente, la pila del kernel tiene dos páginas, lo que generalmente implica que es de 8 KB en arquitecturas de 32 bits y de 16 KB en arquitecturas de 64 bits; este tamaño es fijo y absoluto.
Cada proceso recibe su propia pila.

Además, la pila del kernel contiene un puntero a la estructura thread_info que contiene información sobre el hilo.

arenard
fuente