- Aprender
- Informática
- Ciencias de la computación
- Sistemas operativos
- Procesos y llamadas al sistema
Procesos y llamadas al sistema
IntermedioUnidad 1 de 9 del curso Curso de Sistemas operativos
Lo que aprenderás
- Describe las funciones principales del sistema operativo y la historia y las ideas de diseño que explican por qué comparte y protege la máquina como lo hace.
- Describe las funciones principales de un sistema operativoExplica cómo el sistema operativo convierte el hardware en recursos más fáciles de usar, compartidos y protegidos para los programas.
- Explica cómo la multiprogramación y el tiempo compartido dieron forma al sistema operativoSigue los pasos desde el procesamiento por lotes hasta la multiprogramación y el tiempo compartido, descubre por qué cada paso necesitó nuevas protecciones y compara las principales formas de organizar un núcleo.
- Describe las funciones principales de un sistema operativo
- Explica qué es un proceso, qué información registra el sistema operativo sobre cada uno y cómo los procesos pasan entre los estados de ejecución, listo y bloqueado.
- Explica qué es un proceso y qué información mantiene sobre él el sistema operativoDistingue un programa de un proceso, describe el estado de la máquina y el espacio de direcciones de un proceso, y explica cómo el SO crea un proceso y lleva su seguimiento.
- Sigue las transiciones de estado de un procesoUsa los estados de ejecución, listo y bloqueado y sus transiciones para seguir cómo los procesos comparten una CPU, y reconoce los estados adicionales que muestran los sistemas reales.
- Explica qué es un proceso y qué información mantiene sobre él el sistema operativo
- Usa la API de procesos de Unix en C para crear procesos hijos, ejecutar nuevos programas en ellos, recoger sus resultados, terminarlos y combinar estas llamadas en un intérprete de comandos sencillo.
- Crea un proceso hijo con forkLlama a fork, distingue al padre del hijo por su valor de retorno y predice qué ve e imprime cada proceso.
- Ejecuta otro programa con execSustituye el programa de un proceso con exec, pasa los argumentos correctamente y explica por qué Unix separa la creación de un proceso de la carga de un programa.
- Espera a los procesos hijos, consulta su estado de salida y termina procesosTermina procesos con exit y señales, recoge el resultado de un hijo con wait y waitpid, decodifica su estado y evita que los procesos zombis y huérfanos causen problemas.
- Construye un intérprete de comandos sencillo con fork, exec, wait y redirecciónCombina las llamadas de proceso en un bucle de intérprete de comandos que ejecuta comandos, redirige la entrada y la salida, conecta comandos con tuberías y ejecuta trabajos en segundo plano.
- Crea un proceso hijo con fork
- Explica cómo el hardware separa el código privilegiado del núcleo de los programas de usuario y cómo los programas cruzan esa frontera de forma controlada mediante llamadas al sistema, excepciones e interrupciones.
- Distingue el modo usuario del modo núcleoExplica qué permite cada modo de la CPU, qué operaciones son privilegiadas y qué ocurre cuando un programa excede los límites de su modo.
- Explica paso a paso cómo funciona una llamada al sistemaSigue una llamada al sistema desde la función envoltorio de biblioteca hasta el núcleo mediante una trampa y de vuelta, e interpreta su valor de retorno y sus errores.
- Distingue las trampas, las excepciones y las interrupcionesClasifica los eventos que hacen que la CPU entre en el núcleo, explica cómo funciona la tabla de manejadores del núcleo y predice qué ocurre después de cada tipo de evento.
- Distingue el modo usuario del modo núcleo
- Explica cómo el sistema operativo ejecuta programas directamente en la CPU a toda velocidad sin perder el control y cómo cambia la CPU de un proceso a otro.
- Explica la ejecución directa limitadaExplica los dos problemas de ejecutar programas directamente en la CPU y cómo los resuelven el modo usuario, las trampas y la interrupción del temporizador, paso a paso desde el arranque hasta la terminación del proceso.
- Sigue un cambio de contexto y estima su costeSigue cada paso del cambio de la CPU de un proceso a otro, distingue un cambio de contexto de un cambio de modo y estima cuánto tiempo de CPU cuesta el cambio.
- Explica la ejecución directa limitada
Guía de estudio de Procesos y llamadas al sistema
1Explica qué hace un sistema operativo
El SO ofrece a los programas normales formas útiles de realizar cálculos, almacenar datos y usar dispositivos mientras comparten la máquina de forma segura. Sus responsabilidades explican por qué una aplicación de escritorio no necesita su propio controlador de disco y por qué un programa defectuoso no debería detenerlo todo. Sigue el paso de los computadores que ejecutaban un trabajo a la vez a los sistemas interactivos y luego compara cómo los distintos diseños de núcleo dividen el trabajo privilegiado.
1.1 Describe las funciones principales de un sistema operativo
Un navegador, un compilador y un reproductor de música pueden avanzar en un mismo computador. Cada uno necesita tiempo de CPU, memoria y acceso a dispositivos. El sistema operativo (SO) proporciona esos recursos mediante servicios que los programas pueden usar sin controlar toda la máquina.
Proporcionar recursos útiles a los programas
La virtualización convierte un recurso físico en un recurso virtual cómodo de usar. La virtualización de la CPU permite que muchos procesos se comporten como si cada uno tuviera su propio procesador. El SO ejecuta cada uno durante un tiempo y conserva su estado entre turnos. La virtualización de la memoria da a cada proceso su propio espacio de direcciones: las direcciones que pueden usar sus instrucciones.
Una abstracción oculta detalles detrás de una interfaz útil. Un archivo es una abstracción de bytes almacenados. Un programa puede llamar a write sin saber si esos bytes van a una unidad SSD o a una unidad de red. Una llamada al sistema proporciona una forma controlada de solicitar un servicio del núcleo. El trabajo específico del dispositivo queda detrás de la interfaz.
Compartir recursos y proteger programas
Como gestor de recursos, el SO decide quién obtiene tiempo de CPU, memoria y servicio de los dispositivos. Intenta mantener el hardware en uso y, al mismo tiempo, dar a los programas un acceso razonable. Compartir también requiere protección: un programa no debe sobrescribir la memoria privada de otro ni desactivar el temporizador para retener la CPU.
El hardware ayuda a imponer este límite. Las instrucciones de las aplicaciones se ejecutan en modo usuario con permisos limitados. El código del núcleo se ejecuta con los privilegios necesarios para gestionar la máquina. Un temporizador puede interrumpir un programa aunque este nunca solicite nada al SO.
Para una computadora portátil que descarga un archivo mientras compila código, sigue tres tareas distintas:
- El dispositivo de red informa de la llegada de datos. El núcleo los pone a disposición del proceso que realiza la descarga.
- El SO comparte el tiempo de CPU entre el navegador, el compilador y otros procesos listos.
- El compilador solicita operaciones de archivo para almacenar su salida. Su código no controla directamente el controlador de almacenamiento.
Separar el núcleo de la interfaz
El núcleo es la parte central privilegiada. El intérprete de comandos, el escritorio y la mayoría de las bibliotecas del sistema se ejecutan como programas normales, incluso cuando se distribuyen con el SO. Cerrar una ventana del escritorio no elimina el núcleo. Un servidor sin interfaz gráfica sigue necesitando gestión de procesos, protección de memoria y servicios de dispositivos.
En una CPU lógica, el código de usuario y el código del núcleo se turnan. Mientras el compilador ejecuta instrucciones normales de usuario, el núcleo no se está ejecutando también en esa CPU. Las llamadas al sistema, las excepciones y las interrupciones de hardware le devuelven el control.
Elegir cómo y después a quién
Un mecanismo proporciona una operación; una política elige cómo usarla. Guardar los registros de un proceso y restaurar los de otro es un mecanismo. Elegir qué proceso listo obtiene el siguiente turno es una política. Mantenerlos separados permite al SO cambiar sus decisiones de planificación sin reemplazar el mecanismo de cambio.
El diseño de un SO equilibra abstracciones útiles, rendimiento, protección, fiabilidad, seguridad y consumo de energía. El tiempo de CPU y la memoria adicionales dedicados a la gestión son sobrecarga. En toda la materia aparecen tres temas recurrentes: la virtualización, la concurrencia (actividades en curso al mismo tiempo) y la persistencia, que conserva los datos almacenados más allá de la vida de un programa o de una pérdida de alimentación.
Puntos clave
- La virtualización da a los programas vistas cómodas del hardware compartido.
- Las abstracciones, como los archivos, ocultan los detalles de los dispositivos detrás de operaciones útiles.
- El núcleo es privilegiado; el intérprete de comandos y el escritorio son programas normales.
- Un mecanismo proporciona una operación; una política elige cuándo o para quién se usa.
- La protección y una sobrecarga baja son objetivos del diseño de un SO.
1.2 Explica cómo la multiprogramación y el tiempo compartido dieron forma al sistema operativo
Los sistemas operativos evolucionaron a medida que los computadores pasaron de ejecutar trabajos aislados a atender muchas actividades a la vez. Cada cambio resolvió un problema práctico: hardware inactivo, interacción lenta o fallos que se propagaban entre programas.
De los lotes al trabajo solapado
En el procesamiento por lotes, los trabajos enviados se ejecutan uno tras otro sin interacción con quien los envió. Los primeros programas de soporte ayudaban principalmente a cargar trabajos y realizar operaciones comunes de entrada/salida. Si el trabajo actual tenía que esperar a un dispositivo, la CPU podía quedar inactiva.
La multiprogramación mantiene varios trabajos en memoria. Cuando uno se bloquea por una operación de entrada/salida, otro puede realizar cálculos. Supón que un trabajo calcula repetidamente durante 20 ms y espera 80 ms a una operación de entrada/salida de disco. Por sí solo, su utilización de CPU es 20 / 100 = 20 %. Con dos trabajos así y un solapamiento adecuado, los cálculos útiles pueden ocupar el 40 % del tiempo transcurrido. La demora del dispositivo no se ha reducido; se ha ocupado tiempo de CPU que, de otro modo, habría quedado inactivo.
La ganancia depende de que haya trabajo listo. Añadir trabajos no puede elevar la utilización de la CPU por encima del 100 %, y los trabajos que esperan al mismo dispositivo aún pueden encontrarse con un cuello de botella. Varios trabajos en memoria también necesitan protección: uno no debe sobrescribir a otro ni dañar los datos del propio SO.
Hacer que la interacción sea rápida
El tiempo compartido da a los procesos turnos cortos en la CPU. Una terminal o un escritorio puede responder mientras otros programas siguen trabajando. Esperar a que termine un trabajo por lotes antes de atender la pulsación de una tecla de otro usuario daría una experiencia muy distinta.
| Método de compartición | Qué se divide | Ejemplo |
|---|---|---|
| Tiempo compartido | Uso de un recurso en intervalos sucesivos | Turnos de CPU |
| Compartición espacial | Capacidad que distintos usuarios poseen a la vez | Regiones de memoria o espacio en disco |
La concurrencia significa que varias actividades están en curso durante el mismo periodo. Sus instrucciones pueden intercalarse en una CPU lógica. El paralelismo significa que las instrucciones se ejecutan realmente de forma simultánea en distintos recursos de procesamiento. La multiprogramación no requiere hardware paralelo.
Situar los servicios dentro o fuera del núcleo
| Diseño | Dónde se ejecuta la mayoría de los servicios | Contrapartida principal |
|---|---|---|
| Núcleo monolítico | Los sistemas de archivos, controladores y funciones de red se ejecutan en modo núcleo | Las llamadas directas son eficientes; un error en un controlador puede dañar el núcleo |
| Micronúcleo | Un núcleo privilegiado pequeño da soporte a procesos de servicio en modo usuario | El aislamiento de los servicios ayuda a contener fallos; la comunicación añade trabajo |
| Núcleo híbrido | Combina elementos de ambos enfoques | El límite exacto entre servicios depende del diseño |
Linux es monolítico y puede cargar módulos del núcleo. MINIX 3, QNX y seL4 ilustran diseños de micronúcleo. Windows NT y XNU de Apple suelen describirse como diseños híbridos. A menudo puede reiniciarse un controlador en modo usuario después de un fallo, mientras que un controlador privilegiado tiene acceso que puede hacer que su fallo afecte a todo el sistema.
Los primeros sistemas para computadores personales, como MS-DOS y el Mac OS clásico, carecían de la protección de memoria de los procesos que se espera hoy. Los sistemas modernos combinan las reglas del SO con la aplicación de esas reglas mediante hardware. Los turnos cortos de CPU por sí solos no impedirían que un programa modificara la memoria de otro; la compartición y la protección resuelven partes distintas del problema.
Puntos clave
- La multiprogramación ocupa las esperas de entrada/salida con los cálculos de otro trabajo.
- El tiempo compartido permite el uso interactivo con turnos cortos de CPU.
- El trabajo concurrente puede intercalarse en una CPU; el trabajo paralelo se ejecuta simultáneamente.
- El tiempo compartido divide los turnos; la compartición espacial divide la capacidad.
- La organización del núcleo sopesa las llamadas directas entre servicios frente a un mayor aislamiento de estos.
- Varios trabajos residentes requieren protección de memoria y de privilegios.
2Describe los procesos y los estados por los que pasan
Un archivo de programa es solo el punto de partida. Para ejecutarlo, el SO construye y supervisa un proceso activo con memoria, registros, descriptores abiertos y un lugar en la lista de procesos. Aprende a interpretar ese estado y luego sigue a un par de procesos mientras el trabajo de CPU se solapa con las esperas de dispositivos. La diferencia entre esperar una CPU y esperar un evento explica qué proceso puede elegir el planificador.
2.1 Explica qué es un proceso y qué información mantiene sobre él el sistema operativo
Un ejecutable en disco puede iniciarse repetidamente. Cada ejecución necesita su propio estado cambiante para que una instancia pueda pausarse, reanudarse o fallar de forma independiente. Esa instancia activa es un proceso.
Identificar la instancia en ejecución
Un programa consiste en instrucciones y datos estáticos almacenados. Un proceso combina un programa con su memoria, registros de CPU y estado de entrada/salida actuales. Tres intérpretes de comandos pueden ejecutar el mismo archivo ejecutable mientras mantienen distintas variables, directorios actuales e historiales.
El contador de programa (PC) identifica la siguiente instrucción. El puntero de pila localiza la pila activa. Los registros generales contienen valores intermedios. Cuando un proceso deja de ejecutarse, debe conservarse suficiente estado para que continúe en la instrucción correcta con los valores correctos.
Interpretar la organización del espacio de direcciones
El espacio de direcciones de un proceso es la memoria que pueden direccionar sus instrucciones. Una organización didáctica habitual sitúa el código y los datos estáticos hacia las direcciones más bajas, un montículo por encima y una pila hacia las direcciones más altas. El montículo da soporte a los objetos asignados dinámicamente; la pila da soporte a las llamadas a funciones y al estado local. En esta organización, el montículo crece hacia direcciones más altas y la pila hacia direcciones más bajas. Las organizaciones reales dependen del sistema y las implementaciones de malloc pueden obtener memoria de más de una manera.
Dos procesos pueden imprimir la misma dirección virtual para una variable y, aun así, contener valores distintos en ella. Las direcciones virtuales pertenecen a la vista de memoria de un proceso. Que los números de dirección coincidan no implica que exista almacenamiento escribible compartido.
Iniciar un programa
Para iniciar una nueva imagen de programa, el SO prepara estos elementos:
- Su código y datos ejecutables, cuyas partes se cargan cuando se necesitan en los sistemas modernos.
- Una pila de usuario inicial e información de arranque, incluidas las cadenas de argumentos y el vector de argumentos que usa el entorno de ejecución para llamar a
main. - Memoria para la asignación dinámica y el estado inicial de los registros.
- Un entorno de entrada/salida, incluidos los descriptores de archivo heredados.
- Un punto de entrada donde comienza el código de inicio del programa.
En Unix, los descriptores de archivo son enteros pequeños que identifican recursos de entrada/salida abiertos. Por convenio del intérprete de comandos, 0 es la entrada estándar, 1 la salida estándar y 2 el error estándar. A menudo están conectados a una terminal. Un proceso puede heredar descriptores adicionales; los tres números conocidos no son un límite ni una garantía de que todos los procesos los tengan siempre abiertos.
Examinar el registro del núcleo
El bloque de control de proceso (PCB) registra la información que el núcleo necesita para gestionar un proceso: PID, estado, contexto de ejecución guardado, información de memoria, descriptores abiertos, padre, propietario, directorio actual e información de contabilidad. Linux usa task_struct; xv6 usa struct proc. Una tabla o lista de procesos organiza estos registros.
Mientras un proceso se ejecuta, sus registros activos están en la CPU. Al entrar en el núcleo, sus registros de usuario se guardan en su pila del núcleo; un cambio de contexto también conserva el contexto de ejecución del núcleo. El PCB no es una copia actualizada continuamente de cada registro de usuario.
Por ejemplo, un listado de ps en Linux podría contener 8240 8200 S bash y 8291 8240 R ps. El segundo proceso tiene el PID 8291 y el PID de su padre es 8240, por lo que es hijo del intérprete de comandos. R abarca tanto los procesos en ejecución como los listos. Un PID identifica a un proceso activo dentro de su espacio de nombres de PID, pero después de liberar sus recursos ese número puede reutilizarse.
Puntos clave
- Un proceso es una instancia de un programa con estado de memoria, registros y entrada/salida.
- Las direcciones virtuales idénticas en procesos distintos pueden identificar datos diferentes.
- El PCB contiene el registro de gestión del núcleo, no los registros activos de la CPU actualizados continuamente.
- Los descriptores de archivo se heredan; 0, 1 y 2 tienen funciones establecidas por convenio.
- Los PID pueden reutilizarse después de que un proceso haya terminado y se hayan liberado sus recursos.
2.2 Sigue las transiciones de estado de un proceso
Un proceso puede existir sin estar ejecutando una instrucción en ese momento. Su estado indica al SO si puede usar una CPU o si primero debe esperar otra cosa. Esta distinción explica por qué un computador ocupado puede tener muchos procesos, pero solo unos pocos ejecutándose en un instante dado.
Determinar qué necesita el proceso
| Estado | Significado | Ejemplo |
|---|---|---|
| En ejecución | Ejecuta instrucciones en una CPU | Un compilador que genera instrucciones |
| Listo | Puede ejecutarse, espera tiempo de CPU | Un cálculo cuyo turno acaba de terminar |
| Bloqueado | Espera un evento antes de poder continuar | Una lectura de terminal que espera entrada |
Un proceso bloqueado no consume tiempo de CPU mientras está bloqueado. Su dispositivo o evento puede seguir avanzando. Un proceso listo ya tiene lo que necesita salvo una CPU.
Las transiciones básicas son listo → en ejecución cuando se selecciona, en ejecución → listo cuando se retira de la CPU, en ejecución → bloqueado cuando debe esperar y bloqueado → listo cuando ocurre su evento. La finalización del evento permite que un proceso pueda ejecutarse; no le concede la CPU inmediatamente. Elegir entre los procesos que pueden ejecutarse es tarea del planificador.
Seguir el trabajo solapado
Usa una CPU lógica e ignora la sobrecarga del cambio. El proceso A calcula durante 2 ms, espera 3 ms a una operación de entrada/salida de disco y luego calcula durante 1 ms. El proceso B necesita 4 ms de tiempo de CPU. A empieza primero.
| Intervalo | CPU | Estado de A | Estado de B |
|---|---|---|---|
| 0–2 ms | A | En ejecución | Listo |
| 2–5 ms | B | Bloqueado | En ejecución |
| 5–6 ms | B | Listo | En ejecución |
| 6–7 ms | A | En ejecución | Terminado |
A los 2 ms, A inicia la operación de entrada/salida y se bloquea. B calcula mientras trabaja el disco. A los 5 ms, termina la operación de entrada/salida de A, pero el SO deja que B complete su cuarto milisegundo de CPU antes de ejecutar A. Esta es una decisión de política, no una transición adicional directa de bloqueado a en ejecución.
El trabajo total de CPU es 2 + 1 + 4 = 7 ms y ambos terminan después de 7 ms. Si A se ejecutara hasta terminar antes de que comenzara B, el tiempo transcurrido sería 2 + 3 + 1 + 4 = 10 ms. La utilización de la CPU sería 7 / 10 = 70 %, porque ningún otro trabajo ocuparía la espera de entrada/salida.
Interpretar los estados en un sistema real
En Linux, ps combina los estados en ejecución y listo como R. Muestra el sueño interrumpible como S, el sueño ininterrumpible, a menudo por entrada/salida, como D, un proceso detenido como T y un proceso terminado pero no recolectado como Z. Un proceso detenido no está simplemente esperando la finalización de una operación normal de entrada/salida. Un zombi ha terminado de ejecutarse y solo conserva el registro necesario para comunicar su resultado.
Un proceso nuevo también pasa por la creación antes de estar listo. En un sistema con cuatro CPU lógicas, como máximo cuatro procesos de un solo hilo pueden ejecutarse simultáneamente. Las colas de procesos listos organizan el trabajo que puede ejecutarse; las listas de bloqueados registran los eventos que necesitan los procesos. Un cambio de contexto conserva el estado de ejecución de un proceso y restaura el de otro cuando el SO cambia el proceso que se ejecuta.
Puntos clave
- Listo significa que espera una CPU; bloqueado significa que espera un evento.
- La finalización de una operación de entrada/salida pasa un proceso de bloqueado a listo.
- Solapar las esperas de dispositivos con trabajo útil de CPU puede acortar el tiempo total transcurrido.
- Linux indica los estados listo y en ejecución con el mismo código,
R. - Un zombi ha terminado, pero aún no ha sido recolectado.
- El número de CPU lógicas limita la ejecución simultánea de procesos de un solo hilo.
3Crea y controla procesos con fork, exec y wait
La interfaz de procesos de Unix separa la creación de un hijo, la sustitución de su programa y la recogida de su resultado. Sigue qué cambia y qué conserva cada llamada, incluidas las variables privadas y el estado compartido de los archivos abiertos. Luego combina las llamadas para construir el comportamiento de un intérprete de comandos: comandos en primer plano, redirección, cadenas de tuberías y trabajos en segundo plano. Cada paso depende de saber qué proceso lo está ejecutando.
3.1 Crea un proceso hijo con fork
fork() crea un proceso hijo a partir del proceso padre que realiza la llamada. Ambos continúan inmediatamente después de la llamada, por lo que una ruta del código fuente da lugar a dos ejecuciones. El valor de retorno indica a cada proceso qué papel tiene.
Distinguir los tres resultados
pid_t rc = fork();
if (rc < 0) {
perror("fork");
exit(1);
} else if (rc == 0) {
printf("child %ld\n", (long) getpid());
} else {
printf("parent of %ld\n", (long) rc);
}
Usa <unistd.h> para fork y getpid, <stdio.h> para imprimir y <stdlib.h> para exit. Si la llamada tiene éxito, el padre recibe el PID positivo del hijo y el hijo recibe 0. Si falla, quien llama recibe -1, errno explica el error y no existe ningún hijo.
Si el PID del padre es 5000 y el del hijo es 5001, el padre imprime «parent of 5001» y el hijo imprime «child 5001». No se garantiza que ninguno de los dos se ejecute primero. getpid() devuelve el PID de quien realiza la llamada; getppid() devuelve el PID de su padre. El hijo no reinicia main ni repite las instrucciones anteriores a la bifurcación.
Seguir la memoria privada y la entrada/salida compartida
El padre y el hijo comienzan con el mismo contenido de memoria ordinaria en espacios de direcciones separados. Si x es 10 antes de la llamada y el hijo cambia su copia a 20, la copia del padre sigue siendo 10. Incluso la dirección impresa de x puede coincidir: esas direcciones virtuales iguales pertenecen a espacios de direcciones de procesos distintos.
La copia en escritura evita copiar todas las páginas de memoria de inmediato. El padre y el hijo comparten inicialmente páginas físicas protegidas contra escritura. Cuando uno escribe en una página privada, el núcleo prepara una copia privada antes de permitir la escritura. Esto conserva el comportamiento de memorias separadas y reduce el coste cuando un hijo llama pronto a exec.
Los descriptores de archivo se comportan de otra manera. Sus entradas copiadas hacen referencia a la misma descripción de archivo abierto subyacente, que incluye el desplazamiento actual dentro del archivo. Las lecturas secuenciales mediante un descriptor heredado hacen avanzar ese desplazamiento compartido. La tabla de descriptores se copia, pero el estado subyacente del archivo abierto en el núcleo puede seguir compartido.
Contar solo las bifurcaciones que alcanza un proceso
Si todas las llamadas tienen éxito, fork(); fork(); deja cuatro procesos: los dos procesos producidos por la primera llamada ejecutan la segunda. Un bucle de tres iteraciones que alcanzan todos los procesos deja procesos, incluido el original. Una bifurcación dentro de una rama exclusiva del padre la alcanzan menos procesos, por lo que la duplicación repetida ya no describe todo el programa.
Vaciar deliberadamente el texto almacenado en búfer
printf puede dejar texto en un búfer del espacio de usuario. fork copia ese búfer junto con el resto de la memoria. Si la salida se redirige a un flujo con almacenamiento completo en búfer, el texto impreso antes de la bifurcación puede ser vaciado por ambos procesos al terminar normalmente.
Para printf("start\n"); fork();, la terminal habitual, que almacena la salida por líneas, vacía el texto al encontrar el salto de línea antes de la bifurcación. Un archivo al que se redirige la salida puede retenerlo y recibir dos copias. fflush(stdout) antes de fork() envía el texto pendiente una sola vez. El duplicado procede de los datos copiados en el búfer, no de ejecutar dos veces el printf anterior.
Puntos clave
fork()devuelve el PID del hijo en el padre, 0 en el hijo o -1 si falla.- Si la llamada tiene éxito, ambos procesos continúan justo después de
fork(). - Los cambios en la memoria privada ordinaria del hijo no modifican los valores del padre.
- Los descriptores heredados pueden compartir una descripción de archivo abierto y su desplazamiento.
- El número de procesos depende de cuáles alcanzan cada bifurcación.
- Vacía la salida pendiente antes de una bifurcación cuando no se desee que se vacíe por duplicado.
3.2 Ejecuta otro programa con exec
Un proceso puede conservar su identidad mientras cambia el programa que ejecuta. La familia exec realiza esa sustitución. Combinarla con fork permite que un padre continúe su trabajo mientras un hijo pasa a ejecutar otro programa.
Sustituir la imagen y conservar el proceso
Una llamada a exec que tiene éxito sustituye el código, los datos estáticos, el montículo y la pila anteriores. El núcleo prepara los argumentos y el estado de entrada del nuevo programa y luego inicia su código de arranque. El lugar desde el que se hizo la llamada ya no existe, por lo que una llamada a exec que tiene éxito nunca retorna a él.
El PID, la relación con el padre y el directorio actual se conservan. Los descriptores abiertos se conservan, salvo los marcados para cierre al ejecutar exec. Las señales con un manejador instalado vuelven a su disposición predeterminada; las que se ignoran suelen seguir ignorándose. Las credenciales normalmente siguen siendo las mismas, pero los permisos set-user-ID o set-group-ID de un ejecutable pueden cambiar las credenciales efectivas cuando el sistema lo permite.
| Operación | ¿Proceso nuevo? | Efecto principal |
|---|---|---|
fork() | Sí | Crea un hijo con una copia del estado de ejecución |
| Exec con éxito | No | Sustituye la imagen de programa del proceso que llama |
Construir un vector de argumentos válido
Para ejecutar wc -l notes.txt, el hijo puede usar:
char *args[] = {"wc", "-l", "notes.txt", NULL};
execvp(args[0], args);
perror("execvp");
_exit(127);
Las cadenas son argumentos separados, no una sola línea de comandos combinada. Por convenio, args[0] identifica el programa. El NULL final es un puntero que marca el final del array; no es la cadena «NULL». Sin un terminador válido, la llamada puede leer más allá del array.
Las líneas posteriores a execvp gestionan el fallo. Si no se encuentra el programa, exec devuelve -1 y establece errno, por ejemplo en ENOENT. Después, _exit termina el hijo sin vaciar los búferes de salida de C heredados. Volver al bucle del intérprete de comandos después del fallo crearía accidentalmente otra ruta de ejecución del intérprete.
Interpretar el nombre de la variante
La v de execv significa que los argumentos llegan como un vector; las variantes l reciben una lista. Una variante p busca un nombre de programa en PATH. Una variante e proporciona un entorno explícito. La llamada al sistema subyacente de Linux es execve; la biblioteca proporciona las variantes más cómodas de usar.
execvp("wc", args) busca en PATH. execv("wc", args) trata «wc» como una ruta relativa en el directorio actual. Se puede usar una ruta absoluta como /usr/bin/wc donde esté instalado ese ejecutable. Exec no interpreta la sintaxis del intérprete de comandos: una cadena > en args es solo un argumento, salvo que un programa la interprete por sí mismo.
Usar el intervalo previo a la sustitución
En el ejemplo con el padre de PID 6000 y el hijo de PID 6001, el hijo pasa a ejecutar wc, pero conserva el PID 6001. Su padre puede esperar a ese PID. Antes de exec, el hijo puede redirigir descriptores, conectar una tubería o cambiar de directorio; el programa que lo sustituye hereda el entorno preparado.
Esta separación es fundamental para el intérprete de comandos de Unix. CreateProcess de Windows combina la creación y la carga del programa, mediante parámetros que configuran el nuevo proceso. POSIX también ofrece posix_spawn para una operación combinada.
Puntos clave
- Exec sustituye una imagen de programa sin crear un proceso ni cambiar su PID.
- Si exec tiene éxito, nunca retorna; las instrucciones posteriores gestionan el fallo.
- Termina los vectores de argumentos con un puntero
NULL. - Las variantes
pbuscan enPATH; una variante que solo acepta una ruta no lo hace. - Preparar los descriptores antes de exec permite que un programa sin modificar participe en redirecciones y tuberías.
3.3 Espera a los procesos hijos, consulta su estado de salida y termina procesos
Un padre necesita saber si un hijo ha terminado y cómo lo ha hecho. La terminación del proceso y la recogida del resultado son pasos separados: el hijo termina y luego su padre recolecta el registro que queda en el núcleo.
Terminar un proceso
Retornar de main o llamar a exit(status) termina la ejecución normal. exit vacía los búferes de salida de C y ejecuta los manejadores de salida registrados. _exit(status) termina sin esa liberación de recursos de la biblioteca, lo que es útil después de un exec fallido en un hijo creado mediante fork. Por convenio, 0 indica éxito y un valor distinto de cero indica fallo.
Una señal también puede terminar un proceso. SIGTERM solicita la terminación y puede ser gestionada por un manejador; SIGKILL no se puede capturar ni ignorar. SIGSTOP detiene la ejecución y tampoco se puede capturar ni ignorar; SIGCONT reanuda un proceso detenido. Una terminal normalmente envía SIGINT por Ctrl-C y SIGTSTP por Ctrl-Z al trabajo en primer plano. Una señal es una notificación de software a un proceso, no una interrupción de hardware.
Esperar un resultado
wait(&status) espera a un hijo y devuelve su PID. waitpid(pid, &status, 0) selecciona un PID positivo de un hijo concreto. Un hijo que ha terminado y no ha sido recolectado puede recogerse inmediatamente. En caso contrario, una espera bloqueante normal suspende al padre hasta que haya un resultado disponible que cumpla el criterio.
Con WNOHANG, waitpid devuelve 0 cuando existen hijos que cumplen el criterio, pero ninguno tiene un resultado listo. Un resultado positivo identifica al hijo recogido. Un resultado de -1 indica un error: ECHILD significa que no queda ningún hijo que cumpla el criterio; EINTR significa que un manejador de señales interrumpió la espera.
Decodificar el estado antes de usarlo
int status;
pid_t r;
do {
r = waitpid(child, &status, 0);
} while (r == -1 && errno == EINTR);
if (r == -1) {
perror("waitpid");
} else if (WIFEXITED(status)) {
printf("exit %d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("signal %d\n", WTERMSIG(status));
}
Esto usa <sys/wait.h>, <errno.h> y <stdio.h>. El estado almacenado codifica tanto la forma de terminación como un resultado. WEXITSTATUS tiene sentido después de que WIFEXITED sea verdadero; WTERMSIG se usa después de WIFSIGNALED.
Solo están disponibles los ocho bits menos significativos de un valor de salida normal. Por tanto, exit(300) produce 44, mientras que exit(-1) produce 255. En Linux, exit(3) suele producir el estado devuelto por wait sin decodificar 768; imprimir ese entero no equivale a decodificar el código de salida.
Recolectar zombis y reconocer huérfanos
Un zombi ha terminado, pero conserva su PID y su resultado hasta que una espera los recoge. No ejecuta código, por lo que enviarle SIGKILL no puede eliminarlo. Un padre de larga duración debe recolectar a cada hijo. Para recoger a todos los hijos, sigue esperando después de cada PID positivo, vuelve a intentarlo ante EINTR y detente ante ECHILD; informa de los demás errores.
Un huérfano ha perdido a su padre y puede seguir ejecutándose. Linux le asigna como nuevo padre un proceso recolector de descendientes adecuado o el proceso init del espacio de nombres, que puede recoger su resultado más adelante. Los permisos normales para enviar señales dependen de las identidades y los privilegios de los usuarios. Ejecutarse como root concede permisos, no ejecución en modo núcleo.
Puntos clave
- Cada espera que tiene éxito recoge el resultado de un hijo.
- Decodifica la salida normal y la terminación por señal con sus macros de estado correspondientes.
WNOHANGdevuelve 0 cuando existe un hijo que cumple el criterio, pero aún no tiene un resultado listo.- Vuelve a intentar una espera interrumpida ante
EINTR;ECHILDsignifica que no queda ningún hijo que cumpla el criterio. - Un zombi ha terminado; un huérfano ha perdido a su padre y puede seguir ejecutándose.
SIGKILLySIGSTOPno se pueden capturar ni ignorar.exitvacía los búferes de C;_exitno lo hace.
3.4 Construye un intérprete de comandos sencillo con fork, exec, wait y redirección
Un intérprete de comandos convierte el texto de los comandos en creación de procesos, preparación de descriptores y recogida de resultados. Es un programa de usuario normal que usa los mismos servicios protegidos del SO disponibles para otros programas.
Iniciar un comando externo
Para wc -l notes.txt, el intérprete de comandos construye el vector de argumentos e inicia el comando. Las palabras analizadas forman un vector de argumentos terminado en un puntero nulo, args:
pid_t child = fork();
if (child == -1) {
perror("fork");
} else if (child == 0) {
execvp(args[0], args);
perror("execvp");
_exit(127);
} else {
int status;
pid_t result;
do {
result = waitpid(child, &status, 0);
} while (result == -1 && errno == EINTR);
if (result == -1) perror("waitpid");
else if (WIFEXITED(status)) {
printf("exit %d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("signal %d\n", WTERMSIG(status));
}
}
Esto usa <unistd.h>, <sys/wait.h>, <errno.h> y <stdio.h>. El intérprete de comandos que contiene este código lee y analiza cada comando, sigue esta ruta de inicio y luego muestra su siguiente indicador de entrada.
Solo el hijo sustituye su programa; el intérprete puede recoger el fallo de un comando como resultado de terminación. Los comandos internos como cd deben cambiar el estado del propio intérprete: cambiar el directorio de un hijo dejaría al intérprete padre donde estaba.
Redirigir un descriptor antes de exec
Para ls -l > out.txt, el hijo abre el destino para escritura, creándolo o truncándolo. Luego hace que la salida estándar haga referencia a ese archivo:
int fd = open("out.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1) { perror("open"); _exit(1); }
if (dup2(fd, STDOUT_FILENO) == -1) {
perror("dup2"); _exit(1);
}
if (fd != STDOUT_FILENO && close(fd) == -1) {
perror("close"); _exit(1);
}
open necesita <fcntl.h>; las llamadas a descriptores y _exit usan <unistd.h>. El modo de creación se filtra mediante la máscara de creación de archivos del proceso. La comprobación fd != STDOUT_FILENO mantiene abierto el descriptor 1 aunque open haya devuelto 1. Una vez completada la preparación, exec conserva el descriptor 1 y ls escribe normalmente sin conocer la redirección.
Un enfoque más antiguo cierra el descriptor 1 antes de abrir el archivo: open devuelve el descriptor libre más bajo. dup2 expresa el destino de forma explícita. La redirección de entrada conecta de forma similar un archivo legible al descriptor 0.
Conectar dos comandos
Para ls | wc -l, pipe(fds) crea un extremo de lectura fds[0] y un extremo de escritura fds[1]. El hijo productor conecta el extremo de escritura al descriptor 1. El hijo consumidor conecta el extremo de lectura al descriptor 0. Después de duplicarlos, cada uno cierra los descriptores originales de la tubería que no necesita. El padre cierra sus copias y espera a ambos hijos.
Crea ambos hijos antes de esperar a cualquiera de ellos. Un productor puede llenar la tubería y bloquearse; el lector debe existir para vaciarla. El lector solo alcanza el fin de archivo cuando se cierran todos los descriptores de escritura y se han consumido los datos almacenados en la tubería. Un extremo de escritura sin usar que el padre mantiene abierto puede hacer que wc espere indefinidamente.
Mantener disponible el indicador de entrada
Para sleep 60 &, el intérprete de comandos inicia el hijo y presenta el siguiente indicador sin esperar inmediatamente. Más tarde usa esperas no bloqueantes o responde a SIGCHLD para recolectar a los hijos terminados.
El estado de salida también controla las combinaciones de comandos. make && ./app inicia el segundo comando solo después de un resultado satisfactorio; make || echo failed inicia el segundo solo después de un fallo. Estas decisiones usan el resultado decodificado, no el estado codificado devuelto por wait sin decodificar.
Puntos clave
- Un comando externo se ejecuta en un hijo; un comando interno que cambia el estado se ejecuta en el intérprete de comandos.
- La redirección cambia los descriptores en el hijo antes de exec.
- Una tubería conecta la salida estándar de un productor con la entrada estándar de un consumidor.
- Inicia ambos lados antes de esperar y cierra los extremos de la tubería que no se usan.
- Para alcanzar el fin de archivo deben cerrarse todos los extremos de escritura de la tubería y consumirse los datos almacenados en búfer.
- La ejecución en segundo plano pospone la espera; el intérprete de comandos debe recolectar al hijo más tarde.
4Explica el modo usuario, el modo núcleo y las llamadas al sistema
Los programas de usuario necesitan servicios del SO sin obtener el control de la máquina. Los modos de privilegio del hardware crean ese límite, y las rutas de entrada controladas permiten al núcleo atender solicitudes y eventos. Sigue una llamada al sistema a través de su función envoltorio de biblioteca y su manejador en el núcleo, y luego distingue las trampas deliberadas, los fallos de instrucción y las interrupciones de dispositivos por sus causas y posibles resultados.
4.1 Distingue el modo usuario del modo núcleo
El núcleo debe permitir que los programas calculen rápidamente y, al mismo tiempo, impedir operaciones que podrían tomar el control de la máquina. El modo de privilegio de la CPU es el límite de hardware que hace posibles ambas cosas.
Separar el cálculo del control privilegiado
En modo usuario, las instrucciones pueden acceder a la memoria permitida del proceso y realizar cálculos normales. No pueden cambiar libremente la configuración de hardware que protege a otros programas. En modo núcleo, también llamado modo supervisor o privilegiado, el SO puede realizar las operaciones protegidas necesarias para gestionar la máquina.
Las operaciones privilegiadas incluyen programar el temporizador, desactivar interrupciones, instalar manejadores de interrupciones, cambiar los registros de control de traducción de memoria, controlar directamente dispositivos protegidos y detener la CPU. Si cualquier aplicación pudiera desactivar el temporizador, los turnos cortos de CPU dependerían de la cooperación de esa aplicación.
| Plataforma | Modo de aplicación | Modo núcleo |
|---|---|---|
| x86-64 en Linux o Windows | Anillo 3 | Anillo 0 |
| AArch64 | EL0 | EL1 |
AArch64 también proporciona EL2 para hipervisores y EL3 para firmware seguro. Estos nombres identifican niveles de privilegio del hardware, no distintos tipos de cuentas de usuario.
Entrar en una dirección controlada
El código de usuario no puede elevar su propio privilegio ni elegir cualquier dirección del núcleo para ejecutarla. La entrada se produce mediante una llamada al sistema, una excepción causada por una instrucción o una interrupción de hardware. La CPU transfiere el control a un punto de entrada configurado por el SO. El código de retorno del núcleo restaura el estado de usuario guardado y reduce el nivel de privilegio para reanudar el código de la aplicación.
El núcleo también protege sus datos mediante permisos de memoria. Un proceso puede tener direcciones del núcleo en su organización virtual y, aun así, carecer de permiso en modo usuario para acceder a ellas. Conocer una dirección no concede acceso a los bytes que contiene.
Considera una aplicación de Linux x86-64 que ejecuta hlt, una instrucción destinada a detener una CPU hasta que ocurra un evento. En modo usuario, la instrucción provoca una excepción. Linux normalmente envía SIGSEGV, cuya acción predeterminada termina ese proceso. La aplicación no detiene todo el computador; el límite de protección convierte el intento de operación privilegiada en un evento que gestiona el núcleo.
Separar la identidad del modo
Un proceso de root o administrador sigue siendo un programa en modo usuario mientras se ejecutan sus propias instrucciones. Su identidad de usuario le da un permiso más amplio para solicitar servicios. Sigue entrando en el núcleo mediante rutas controladas y el núcleo comprueba la solicitud.
Por ejemplo, una utilidad de mantenimiento privilegiada puede tener permiso para abrir un archivo protegido. La utilidad lo solicita mediante una llamada al sistema; no se convierte en código del núcleo solo porque se permita la solicitud. A la inversa, cuando el código del núcleo atiende una llamada a read de un usuario normal, se ejecuta en modo núcleo aunque quien llama no tenga una identidad administrativa.
En una CPU lógica, esto ocurre en secuencia: instrucciones de usuario, entrada en el núcleo, trabajo del núcleo y retorno a las instrucciones de usuario. No es una ejecución simultánea de un programa y una segunda tarea del SO en esa misma CPU. Distintas CPU lógicas pueden ejecutar distintos modos en el mismo instante.
Puntos clave
- El modo usuario permite cálculos normales mientras restringe las operaciones de control de la máquina.
- La entrada en el núcleo usa puntos de entrada configurados por el SO.
- Root es una identidad de usuario; el modo núcleo es un estado de privilegio de la CPU.
- Una instrucción prohibida en modo usuario genera una excepción para que el núcleo pueda responder.
4.2 Explica paso a paso cómo funciona una llamada al sistema
Un programa en C a menudo parece llamar a una función normal cuando necesita un servicio del SO. Debajo de la función envoltorio de una llamada al sistema, una transición controlada de la CPU permite al código del núcleo validar y realizar esa solicitud.
Distinguir una llamada de biblioteca de una solicitud al núcleo
La biblioteca de C implementa strlen en espacio de usuario sin una llamada al sistema. printf da formato a los datos y puede almacenarlos en un búfer; una llamada posterior a write transfiere bytes a través del núcleo. Una función envoltorio sencilla de read prepara los argumentos según el convenio de llamadas al sistema de la plataforma y ejecuta la instrucción de entrada.
POSIX describe interfaces para sistemas de tipo Unix. Linux implementa muchos servicios agrupados de forma general en control de procesos, operaciones de archivos y dispositivos, información, comunicación y protección. Un número de llamada al sistema selecciona un servicio de la tabla de despacho del núcleo. Los números dependen de la plataforma: Linux x86-64 usa 1 para write y 39 para getpid; AArch64 usa 64 para write.
Seguir una solicitud de escritura
Para write(1, "hi\n", 3) en Linux x86-64, sigue la solicitud en orden:
- La función envoltorio coloca el número de servicio 1 en
rax, el descriptor 1 enrdi, la dirección del búfer enrsiy la cantidad 3 enrdx. syscallconserva la dirección de retorno y los indicadores en registros, entra en modo núcleo y salta al punto de entrada configurado. No cambia de pila automáticamente.- El código de entrada del núcleo establece la pila del núcleo del proceso y guarda el resto del estado de usuario.
- El despacho del núcleo selecciona
write; la implementación comprueba el descriptor, el acceso al búfer de usuario y la solicitud antes de pasar los bytes hacia el recurso de salida. - Si se escriben todos los bytes correctamente, el resultado es 3 bytes. El código de retorno del núcleo restaura el estado de usuario y reanuda la función envoltorio, que retorna a quien la llamó.
El descriptor 1 puede identificar una terminal, un archivo o una tubería. El número por sí solo no identifica el dispositivo de salida. Una escritura también puede devolver una cantidad menor de bytes escritos sin que haya un error; quienes necesitan transferir todos los bytes llevan la cuenta de la cantidad realmente escrita.
Comprobar errores en la interfaz
El núcleo trata los punteros de usuario y los demás argumentos como no fiables. Una solicitud para copiar desde un búfer inaccesible puede fallar con EFAULT; la copia controlada evita conceder a quien llama acceso a la memoria del núcleo.
En Linux, un error del núcleo es un código de error negativo. La función envoltorio habitual de C devuelve -1 y establece errno. Por ejemplo, si open("missing.txt", O_RDONLY) falla porque el archivo no existe, errno es ENOENT. perror("missing.txt") puede mostrar «missing.txt: No such file or directory». Consulta errno después de que se indique un fallo, no como comprobación principal de éxito: una llamada que tiene éxito no tiene por qué borrar un error anterior.
Tener en cuenta el tiempo empleado en el núcleo
Una llamada que puede terminar en ese momento puede retornar al mismo proceso. Una lectura bloqueante de terminal puede, en cambio, poner ese proceso en estado bloqueado y dejar que otro se ejecute. El evento de entrada hace que el lector quede listo; una vez planificado, completa la ruta de retorno.
La entrada en el núcleo añade trabajo respecto de una llamada normal a una función: guardar el estado, cambiar de privilegio, comprobar los argumentos y restaurar la ejecución. El almacenamiento en búfer de la biblioteca puede combinar salidas pequeñas en menos escrituras. En Linux, strace ls ejecuta ls mientras informa de sus llamadas al sistema, argumentos y resultados.
Puntos clave
- Una función de biblioteca puede hacer todo su trabajo en espacio de usuario o invocar llamadas al sistema.
- Un número de servicio selecciona una operación del núcleo; quien llama no elige una dirección arbitraria del núcleo.
- El código del núcleo valida descriptores, punteros y otros argumentos.
- Usa el valor de retorno para detectar un fallo y después consulta
errno. - Una llamada al sistema bloqueante puede provocar un cambio de contexto; la entrada en el núcleo por sí sola no tiene por qué hacerlo.
- Los números de llamadas al sistema y los convenios de registros dependen del SO y de la arquitectura.
4.3 Distingue las trampas, las excepciones y las interrupciones
La entrada en el núcleo puede deberse a una solicitud de servicio, un problema de instrucción o un evento de dispositivo. Clasificar la causa te ayuda a predecir qué hará el SO y si debe volver a ejecutarse la misma instrucción.
Preguntar qué causó el evento
| Evento | Causa | Momento respecto a la instrucción actual |
|---|---|---|
| Trampa de llamada al sistema | El programa solicita deliberadamente un servicio | Síncrono |
| Excepción o fallo | La instrucción actual necesita una gestión especial | Síncrono |
| Interrupción de hardware | Un temporizador o dispositivo envía una señal a la CPU | Asíncrono |
Síncrono significa que está ligado a la instrucción que se ejecuta. No significa que la misma entrada cause exactamente los mismos fallos en cada ejecución. Una carga puede fallar cuando su página está ausente y tener éxito más tarde cuando esa página está presente en memoria. Asíncrono significa que la causa del evento es independiente de la instrucción actual, como cuando un disco completa un trabajo anterior.
Los manuales de CPU usan estos términos de distintas maneras. Sigue la causa: solicitud deliberada de servicio, evento causado por una instrucción o notificación de hardware.
Despachar al manejador correcto
Durante el arranque, el SO instala direcciones protegidas de manejadores. Una tabla de trampas o interrupciones asigna los tipos de eventos a rutinas del núcleo. En x86-64, la tabla de descriptores de interrupción (IDT) atiende las interrupciones y excepciones; syscall usa una dirección de entrada en un registro especial del procesador. Solo el código privilegiado puede configurar estos destinos.
Ante una interrupción desde el modo usuario, el hardware x86-64 guarda un marco de retorno en una pila del núcleo. El código de entrada guarda registros adicionales antes de realizar el trabajo. La ruta de syscall conserva su estado inicial de retorno en registros y depende del código de entrada para establecer la pila del núcleo. Tratar todas las rutas de entrada como acciones de hardware idénticas oculta una diferencia importante de implementación.
Elegir el resultado
Se puede cargar una página válida que no está en memoria, tras lo cual se vuelve a intentar la instrucción que provocó el fallo. Un acceso a memoria que el proceso no puede usar normalmente hace que Linux envíe SIGSEGV. Por tanto, una excepción no siempre es fatal ni siempre implica saltarse la instrucción actual.
Para una instrucción entera real idiv de x86-64 con divisor cero, la CPU genera una excepción de error de división. Linux normalmente entrega SIGFPE; su conocido mensaje menciona el punto flotante incluso para esta instrucción entera. En cambio, la división entera de AArch64 da cero cuando el divisor es cero. La división entre cero en C tiene un comportamiento indefinido, por lo que el código C portable no puede depender de ninguno de los dos resultados de la máquina.
Cuando termina una lectura de disco, el manejador de interrupciones registra su finalización y pasa el proceso que espera a listo. Ese proceso puede ejecutarse más tarde. Una interrupción del temporizador da al SO la oportunidad de reconsiderar qué proceso usa actualmente la CPU, pero el manejador también puede volver a ese mismo proceso.
Distinguir una señal de una interrupción
Una interrupción de hardware notifica a la CPU; una señal notifica a un proceso a través del SO. Una excepción puede hacer que el núcleo envíe una señal, y un proceso puede solicitar una señal para otro mediante kill. Las finalizaciones normales de dispositivos son interrupciones que no implican errores de programa.
El código del núcleo también debe proteger los datos compartidos mientras ocurren eventos. Puede desactivar brevemente las interrupciones en una CPU y usar cerrojos donde sea necesario. Los periodos largos con interrupciones desactivadas retrasan la atención a los dispositivos; desactivar solo las interrupciones locales no impide que otra CPU acceda a los datos compartidos.
Puntos clave
- Las trampas y excepciones surgen de la instrucción actual; las interrupciones de hardware tienen una causa externa.
- Un fallo recuperable puede corregirse y su instrucción puede volver a intentarse.
- Las tablas de entrada protegidas impiden que los programas de usuario sustituyan los manejadores del núcleo.
- Una señal se dirige a un proceso; una interrupción de hardware se dirige a la CPU para que la gestione.
- La finalización de un dispositivo pasa un proceso de bloqueado a listo, y una interrupción del temporizador no tiene por qué cambiar de proceso.
5Explica cómo el sistema operativo comparte la CPU mediante la ejecución directa limitada
La ejecución rápida y el control del SO deben funcionar conjuntamente. La ejecución directa limitada permite que las instrucciones normales se ejecuten en el hardware mientras los modos, los puntos de entrada protegidos y un temporizador mantienen el control en manos del núcleo. Sigue lo que ocurre cuando el SO vuelve al mismo proceso y cuando reanuda uno distinto. El estado guardado y los costes de cambio explican cómo los turnos cortos de CPU pueden facilitar el trabajo interactivo.
5.1 Explica la ejecución directa limitada
Ejecutar cada instrucción de una aplicación en un simulador de software haría costosa la compartición de la CPU. Permitir que una aplicación controle la máquina sin límites haría insegura esa compartición. La ejecución directa limitada (LDE) usa la ejecución normal en el hardware con una ruta protegida de regreso al SO.
Resolver dos problemas de control distintos
La ejecución directa significa que las instrucciones normales de un proceso se ejecutan en la propia CPU. El SO no examina ni aprueba cada instrucción aritmética. Quedan dos problemas: restringir las operaciones que podrían dañar la máquina y recuperar el control cuando un programa no se detiene voluntariamente.
El modo usuario resuelve el problema de las restricciones. Las trampas de llamadas al sistema permiten que un programa solicite servicios sin elegir código privilegiado arbitrario. Un temporizador de hardware resuelve el problema del control: puede interrumpir una aplicación que no realiza ninguna llamada. Juntos, estos mecanismos permiten virtualizar la CPU con poca sobrecarga.
Preparar el sistema antes de ejecutar aplicaciones
Durante el arranque, el núcleo configura los manejadores de eventos e inicializa el temporizador. Estas operaciones de configuración son privilegiadas. Los programas de usuario posteriores no pueden redirigir los manejadores ni desactivar el temporizador para evitar perder sus turnos.
Para iniciar un proceso, el SO crea su registro de gestión y su imagen de memoria, prepara los argumentos de arranque y una pila de usuario, y establece los valores iniciales de los registros. Prepara un contexto inicial guardado para que la ruta normal de retorno del núcleo al usuario pueda iniciar el programa. Conceptualmente, el proceso comienza retornando de una trampa que nunca realizó.
La organización detallada del marco guardado depende de la CPU y del SO. La idea de diseño útil es la reutilización: el mismo mecanismo de restauración puede reanudar un proceso suspendido o iniciar uno recién preparado.
Volver al núcleo voluntariamente
Durante una llamada al sistema, el hardware conserva el estado inicial de retorno y transfiere el control. El código de entrada del núcleo guarda los registros adicionales en la pila del núcleo del proceso. Después de completar una solicitud no bloqueante, el núcleo restaura el proceso y vuelve al modo usuario. Si la solicitud bloquea el proceso, primero puede ejecutarse otro proceso listo.
Cuando main retorna, el procesamiento de salida del entorno de ejecución acaba pidiendo al núcleo que termine el proceso. Su memoria puede liberarse mientras su registro de terminación permanece hasta que un padre lo recolecte.
Volver sin cooperación
Un sistema cooperativo depende de que los programas realicen llamadas, cedan la CPU o provoquen excepciones. Un bucle infinito que no hace ninguna de estas cosas puede monopolizar una CPU. El Mac OS clásico ilustra esta debilidad.
Un diseño no cooperativo usa una interrupción del temporizador para forzar la entrada en el núcleo. Considera un modelo didáctico con una CPU lógica y un temporizador periódico cada 4 ms. El temporizador produce 1000 / 4 = 250 pulsos por segundo. Un proceso de usuario atrapado en while (1) { } es interrumpido en el siguiente pulso. El núcleo puede entonces ejecutar otro proceso listo; el bucle no necesita alcanzar primero una llamada al sistema.
El pulso es una oportunidad para actuar, no una obligación de cambiar de proceso. El planificador puede dejar que continúe el proceso interrumpido. La gestión del temporizador y la restauración del estado guardado son mecanismos; la elección del planificador es una política. El tiempo de respuesta real también depende del trabajo del núcleo y de cuándo pueden gestionarse las interrupciones, por lo que el periodo del temporizador por sí solo no garantiza el tiempo de respuesta de una aplicación.
Puntos clave
- LDE ejecuta directamente las instrucciones normales de usuario con límites impuestos por el hardware.
- El modo usuario restringe las operaciones; las llamadas al sistema proporcionan servicios privilegiados de forma controlada.
- Un temporizador permite al SO recuperar el control sin cooperación del código de usuario.
- La configuración durante el arranque protege las direcciones de los manejadores y la configuración del temporizador.
- Un pulso del temporizador da al planificador la posibilidad de elegir; no exige un cambio de proceso.
5.2 Sigue un cambio de contexto y estima su coste
Entrar en modo núcleo no cambia necesariamente el proceso en ejecución. Un cambio de contexto ocurre cuando el núcleo conserva el estado de ejecución de un proceso y reanuda el de otro. Esto requiere tanto conservar el estado de usuario como cambiar el propio contexto de ejecución del núcleo.
Seguir la salida de A y la entrada de B
Para un cambio provocado por el temporizador en x86-64, sigue las capas:
- A ejecuta instrucciones de usuario. El temporizador lo interrumpe.
- El hardware pasa a modo núcleo y guarda el marco de retorno de interrupción de A en la pila del núcleo de A. El código de entrada guarda allí los demás registros de usuario de A.
- El planificador elige B. La rutina de cambio guarda el contexto de ejecución del núcleo de A y restaura el contexto del núcleo guardado de B, incluido su puntero de pila del núcleo.
- El núcleo cambia al contexto del espacio de direcciones de B según sea necesario. Ahora se ejecuta en nombre de B usando la pila del núcleo de B.
- El código de salida del núcleo restaura los registros de usuario de B. La instrucción de retorno restaura el estado de retorno de B y entra en modo usuario. B se reanuda donde se detuvo anteriormente.
El primer guardado conserva la ejecución de usuario interrumpida de A. El segundo conserva el punto en el que estaba el núcleo mientras atendía a A. No son dos copias completas e idénticas del mismo conjunto de registros. Un PCB o estructura de contexto asociada y la pila del núcleo de cada proceso trabajan conjuntamente para conservar el estado.
Identificar el evento y la decisión
Una llamada bloqueante, la terminación de un proceso o una decisión tras una interrupción del temporizador pueden provocar un cambio. Una interrupción de dispositivo puede hacer que otro proceso quede listo y la planificación puede seleccionarlo entonces. Todo el mecanismo de cambio se ejecuta en el núcleo; una aplicación no guarda sus propios registros para ofrecerse a ser interrumpida.
| Situación | ¿Transición de modo? | ¿Cambio de proceso? |
|---|---|---|
| Un servicio no bloqueante retorna a quien lo llamó | Usuario → núcleo → usuario | No |
| Se gestiona el temporizador y se reanuda el mismo proceso | Usuario → núcleo → usuario | No |
| A se bloquea y B se reanuda | Entrada en el núcleo y luego ruta de retorno de B | Sí |
Un cambio de contexto conserva el estado de ejecución necesario para continuar. El tiempo transcurre y el mundo exterior puede cambiar: los desplazamientos compartidos de archivos y las entradas externas no quedan congelados mientras un proceso está en pausa.
Tener en cuenta los costes directos e indirectos
El trabajo directo incluye ejecutar el planificador, guardar y restaurar registros y seleccionar un espacio de direcciones. Las cachés de traducción pueden necesitar ajustes; las traducciones etiquetadas pueden evitar que se descarten todas las entradas anteriores. Después aparecen costes indirectos cuando B debe volver a llenar las cachés de la CPU o las traducciones de direcciones almacenadas en caché que A desplazó. El coste depende del hardware y de la carga de trabajo, así que usa valores medidos o indicados al calcular la sobrecarga.
Supón que un cambio, incluidos sus efectos pertinentes, cuesta 5 µs y que los turnos útiles de CPU duran 10 ms. Convierte 10 ms en 10 000 µs. La proporción dedicada al cambio es 5 / (10 000 + 5), aproximadamente 0,05 %. Para turnos útiles de 1 ms es 5 / 1005, aproximadamente 0,50 %; con 0,1 ms es 5 / 105, aproximadamente 4,8 %.
El denominador incluye el trabajo útil más el tiempo de cambio de un ciclo repetido. Un turno más corto puede mejorar las oportunidades para el trabajo interactivo, pero dedica una fracción mayor a cambios repetidos. El temporizador y el código de cambio hacen posibles los turnos cortos; la política de planificación debe sopesar ese coste frente a la rapidez de respuesta.
Puntos clave
- La entrada en el núcleo puede retornar al mismo proceso sin un cambio de contexto.
- Guardar el estado de usuario al entrar y guardar el contexto del núcleo durante un cambio cumplen funciones distintas.
- Reanudar B requiere su pila del núcleo, sus registros guardados y el contexto de su espacio de direcciones.
- Los costes de cambio incluyen el trabajo directo de gestión y el llenado posterior de cachés o traducciones.
- La proporción de sobrecarga es el coste de cambio dividido entre el turno útil más el coste de cambio.
- Con un coste fijo por cambio, los turnos más cortos aumentan la proporción dedicada a los cambios.
Ejemplos de Procesos y llamadas al sistema
1Descubre qué aporta el SO
12 pasos
- 1IntroducciónEmpieza con varios programas
Imagina una computadora portátil que compila un proyecto mientras reproduce música y recibe datos de red. Cada aplicación necesita tiempo de CPU, memoria y entrada/salida, pero ninguna controla toda la máquina.
Sigue quién proporciona esos recursos, cómo avanzan varios trabajos y dónde se ejecutan los servicios privilegiados. La pregunta recurrente es concreta: ¿qué trabajo solicita la aplicación y qué trabajo debe gestionar el SO?
- 2ExplicaciónOculta los detalles del dispositivo
El SO ofrece abstracciones: interfaces útiles que ocultan los mecanismos de nivel inferior. Un programa abre un archivo y lee bytes sin implementar comandos para cada dispositivo de almacenamiento.
Una llamada al sistema solicita un servicio del núcleo mediante una interfaz controlada. El núcleo comprueba y realiza el trabajo protegido. La aplicación sigue decidiendo qué datos quiere; no obtiene permiso para controlar directamente el dispositivo.
- 3EjemploSigue un archivo de salida de un compilador
Un compilador produce bytes para un ejecutable y solicita una escritura. El núcleo comprueba el recurso de salida y gestiona el almacenamiento detrás de la interfaz de archivos.
Al mismo tiempo, el SO gestiona la memoria del compilador y comparte el tiempo de CPU con el reproductor de música. Una acción, guardar un archivo, se sitúa por tanto dentro de varias responsabilidades del SO: abstracción, gestión de recursos y protección de los demás programas.
- 4Idea claveDa a cada proceso una máquina utilizable
La virtualización presenta un recurso físico mediante una vista privada útil. Un proceso puede avanzar como si tuviera un procesador propio, aunque sus instrucciones se turnen con las de otros procesos.
Su vista de memoria también es privada: la dirección que usa no tiene por qué identificar los mismos bytes físicos en otro proceso. La virtualización de la CPU y la memoria da a los programas modelos cómodos mientras el SO gestiona el hardware físico compartido.
- 5DesafíoLocaliza la parte privilegiada
Un computador no tiene instalado un escritorio gráfico, pero ejecuta servicios, almacena archivos y comparte tiempo de CPU. ¿Ha perdido la parte central esencial del SO? Explícalo.
---reveal---
No. El núcleo sigue gestionando procesos, memoria y servicios de dispositivos. Un escritorio gráfico es un programa de usuario que se ejecuta sobre esos servicios. Su ausencia cambia la interfaz que ve una persona, no la necesidad del trabajo de gestión de recursos del núcleo.
- 6ExplicaciónSepara la operación de la elección
Un mecanismo hace posible una acción. Guardar los registros de un proceso y restaurar los de otro proporciona el mecanismo necesario para cambiar de proceso.
Una política elige cómo usarlo, por ejemplo, al decidir qué proceso listo obtiene el siguiente turno de CPU. La distinción importa cuando cambia un diseño: una nueva regla de elección puede reutilizar el mismo código fiable de cambio. Los mecanismos eficientes por sí solos no deciden qué es equitativo.
- 7ExplicaciónOcupa una espera que dejaría la CPU inactiva
Un sistema por lotes puede dejar la CPU inactiva mientras su trabajo actual espera una operación de entrada/salida. La multiprogramación mantiene otros trabajos disponibles y ejecuta uno durante esa espera.
El trabajo que espera no calcula y el dispositivo no se ha vuelto más rápido. La ganancia procede de solapar trabajo independiente: un proceso espera mientras otro usa la CPU. Varios programas residentes también requieren protección para que uno no pueda sobrescribir la memoria de otro.
- 8EjemploCalcula la ganancia de utilización
Un trabajo calcula durante 20 ms y luego espera 80 ms a una operación de entrada/salida. Por sí solo, un ciclo de 100 ms contiene solo 20 ms de trabajo de CPU: la utilización es del 20 %.
Con un segundo trabajo de ese tipo y un solapamiento adecuado, otros 20 ms de cálculo pueden ocupar tiempo que de otro modo quedaría inactivo, lo que da hasta un 40 % en este ejemplo. La mejora usa productivamente el tiempo de espera; añadir trabajos no puede elevar la utilización de la CPU por encima del 100 %.
- 9ConexiónDa turnos cortos al trabajo interactivo
El tiempo compartido da a los procesos turnos cortos de CPU para que el trabajo interactivo no tenga que esperar a que termine todo un trabajo por lotes. Esto es concurrencia incluso en una CPU lógica: varias actividades avanzan mediante el intercalado.
El paralelismo necesita una ejecución realmente simultánea en recursos de procesamiento separados. La capacidad de memoria y disco ilustra otra división, la compartición espacial, en la que distintos usuarios disponen de partes separadas a la vez.
- 10ExplicaciónElige un límite para los servicios
Un núcleo monolítico, como Linux, ejecuta la mayoría de los servicios juntos y con privilegios. Las llamadas directas son eficientes, pero un controlador defectuoso puede dañar el estado del núcleo.
Un micronúcleo, como seL4, deja más servicios en procesos de usuario separados. El aislamiento puede contener el fallo de un servicio, a costa de comunicación y transiciones de modo. Windows NT y XNU mezclan enfoques. El límite determina qué fallos pueden propagarse y qué servicios pueden reiniciarse.
- 11DesafíoRelaciona la compartición y la protección
Un sistema con una CPU ejecuta varios trabajos y cambia a otro cuando uno espera una operación de entrada/salida de disco. Una propuesta permitiría que cada trabajo sobrescribiera cualquier dirección de memoria. Explica el beneficio para la utilización y el defecto de esta propuesta.
---reveal---
La multiprogramación ocupa las esperas de entrada/salida con los cálculos de otro trabajo. Las escrituras sin restricciones permitirían que un trabajo dañara a otro o al SO. Solapar el trabajo mejora el uso de los recursos, pero el uso compartido sigue necesitando protección de memoria y privilegios que se haga cumplir.
- 12RepasoAnaliza un diseño con tres preguntas
Ante el diseño de un SO, pregunta qué recurso se comparte, qué interfaz ve un programa y qué límite protege el resto del trabajo.
Estas preguntas conectan la virtualización de la CPU con los turnos de los procesos, los archivos con los detalles ocultos de los dispositivos y la organización del núcleo con el aislamiento de fallos. Los mecanismos realizan operaciones protegidas; las políticas deciden cómo distribuir su uso. La concurrencia puede surgir del intercalado en una CPU.
2Sigue un proceso durante el trabajo de CPU y de entrada/salida
13 pasos
Guarda esta unidad para seguir cada Lección Interactiva paso a paso.
- 1IntroducciónSigue una instancia activa
- 2ExplicaciónConvierte un archivo en un proceso
- 3ExplicaciónConserva la siguiente instrucción
- 4EjemploInterpreta un listado de procesos
- 5DesafíoInterpreta direcciones iguales
- 6ConsejoInterpreta los descriptores como recursos heredados
- 7ExplicaciónPregunta qué falta
- 8EjemploInicia la línea de tiempo
- 9EjemploCompleta la línea de tiempo
- 10DesafíoCambia la decisión sobre el proceso listo
- 11ConexiónReconoce los nombres de estado reales
- 12DesafíoRelaciona los registros con la capacidad de CPU
- 13RepasoInterpreta el estado como posibilidad de ejecución
3Construye un comando a partir de llamadas de proceso
23 pasos
Guarda esta unidad para seguir cada Lección Interactiva paso a paso.
- 1IntroducciónAsigna una tarea a cada llamada
- 2ExplicaciónDivide una ejecución en dos
- 3EjemploSigue un valor privado
- 4EjemploCuenta una rama, no las llamadas del código fuente
- 5DesafíoCuenta las rutas que imprimen
- 6ConsejoComprueba qué se copió
- 7ExplicaciónSustituye el programa del hijo
- 8EjemploPasa cadenas de argumentos separadas
- 9DesafíoPredice una sustitución con éxito
- 10Idea claveUsa el intervalo antes de exec
- 11ExplicaciónRecoge el resultado de un hijo
- 12EjemploDecodifica la salida normal
- 13ExplicaciónNo dejes resultados sin recoger
- 14DesafíoGestiona correctamente una interrupción
- 15ConsejoElige una solicitud de terminación
- 16ExplicaciónVuelve al indicador de entrada en primer plano
- 17EjemploRedirige la salida antes de la sustitución
- 18DesafíoConserva el destino de salida
- 19EjemploConecta los dos extremos de la tubería
- 20Idea claveInicia los procesos antes de esperar
- 21ConexiónLleva el seguimiento del trabajo en segundo plano
- 22DesafíoSigue una cadena de tuberías vacía
- 23RepasoSigue la identidad, la imagen y el resultado
4Sigue una solicitud a través del límite de privilegio
15 pasos
Guarda esta unidad para seguir cada Lección Interactiva paso a paso.
- 1IntroducciónPregunta por qué se entró en el núcleo
- 2ExplicaciónLimita lo que pueden controlar las aplicaciones
- 3EjemploSepara la identidad del privilegio
- 4DesafíoPredice qué ocurre con una instrucción prohibida
- 5ExplicaciónEncuentra la llamada al sistema real
- 6EjemploPrepara una escritura
- 7EjemploEntra y valida
- 8EjemploInterpreta el retorno
- 9DesafíoSigue una lectura bloqueada
- 10ExplicaciónClasifica según la causa
- 11EjemploCorrige o termina
- 12ConsejoMantén las señales en la capa de procesos
- 13DesafíoClasifica la finalización de una operación de dispositivo
- 14DesafíoSepara dos tipos de entrada
- 15RepasoSigue la causa, las comprobaciones y el resultado
5Mantén el control mientras los programas se ejecutan directamente
15 pasos
Guarda esta unidad para seguir cada Lección Interactiva paso a paso.
- 1IntroducciónCombina velocidad y control
- 2ExplicaciónResuelve dos problemas de control
- 3EjemploPrepara la máquina y el proceso
- 4ConexiónObserva la debilidad del enfoque cooperativo
- 5EjemploInterrumpe un bucle infinito
- 6DesafíoIdentifica el mecanismo que falta
- 7ExplicaciónGuarda la ruta de usuario interrumpida
- 8EjemploGuarda también la ruta del núcleo
- 9EjemploRetorna mediante el estado guardado de B
- 10DesafíoDetecta una transición solo de modo
- 11ExplicaciónCuenta los costes después de un cambio
- 12EjemploConstruye el denominador del ciclo completo
- 13DesafíoEstima el efecto de cambiar la duración del turno
- 14DesafíoRelaciona una llamada bloqueada con su reanudación
- 15RepasoSigue a quién pertenece la ruta de retorno
Ejercicios de Procesos y llamadas al sistema
Set 1: Recursos, procesos y protección10 Preguntas de Práctica
Pregunta 1 de 10
Mostrar respuestaOcultar respuesta
Pregunta 2 de 10
Mostrar respuestaOcultar respuesta
Pregunta 3 de 10
Mostrar respuestaOcultar respuesta
Pregunta 4 de 10
Mostrar respuestaOcultar respuesta
Pregunta 5 de 10
Mostrar respuestaOcultar respuesta
Pregunta 6 de 10
Mostrar respuestaOcultar respuesta
Pregunta 7 de 10
Mostrar respuestaOcultar respuesta
Pregunta 8 de 10
Mostrar respuestaOcultar respuesta
Pregunta 9 de 10
Mostrar respuestaOcultar respuesta
Pregunta 10 de 10
Mostrar respuestaOcultar respuesta
Set 2: Crea, sustituye y recoge un proceso10 Preguntas de Práctica
Guarda esta unidad para ver todas las respuestas y practicar cada set.
Pregunta 1 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 2 de 10
Todas las llamadas a fork tienen éxito. ¿Cuántos procesos alcanzan puts("done") en este código?
if (fork() == 0) {
fork();
}
puts("done");
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 3 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 4 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 5 de 10
/opt/course/bin/checker y PATH incluye /opt/course/bin. No hay ningún archivo llamado checker en el directorio actual. ¿Por qué execv("checker", args) no consigue localizarlo?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 6 de 10
exit(515). Después de una espera que tiene éxito y de que WIFEXITED sea verdadero, ¿qué valor indica WEXITSTATUS?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 7 de 10
waitpid(child, &status, WNOHANG) devuelve 0. ¿Qué ha ocurrido?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 8 de 10
SIGKILL, la entrada sigue visible. ¿Qué acción libera el registro que conserva el resultado de este hijo?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 9 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 10 de 10
producer | consumer. El productor termina, pero el consumidor espera indefinidamente después de leer todos los bytes disponibles. El intérprete cerró sus copias de la tubería; el consumidor aún mantiene abierto el descriptor original heredado del extremo de escritura. ¿Qué soluciona el bloqueo?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Set 3: Sigue la entrada en el núcleo y los cambios de CPU10 Preguntas de Práctica
Guarda esta unidad para ver todas las respuestas y practicar cada set.
Pregunta 1 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 2 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 3 de 10
errno es ENOENT. ¿Qué debe concluir quien llama?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 4 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 5 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 6 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 7 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 8 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 9 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 10 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Set 4: Sigue el comportamiento de los procesos y la compartición de recursos10 Preguntas de Práctica
Guarda esta unidad para ver todas las respuestas y practicar cada set.
Pregunta 1 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 2 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 3 de 10
Un proceso de un solo hilo tiene una variable privada x igual a 6. Todas las llamadas a fork tienen éxito. Todos los procesos alcanzan puts. ¿Cuántas líneas contienen «9»?
pid_t p = fork();
if (p == 0) x += 3;
fork();
if (x == 9) puts("9");
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 4 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 5 de 10
execvp para un programa que no existe. Su siguiente instrucción imprime un error. ¿Por qué puede ejecutarse esa instrucción?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 6 de 10
errno en EINTR. ¿Qué debe hacer el padre para seguir recogiendo a sus hijos?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 7 de 10
chdir solo en un hijo creado mediante fork para implementar cd, espera a ese hijo y luego inicia otro comando. ¿Por qué comienza el siguiente comando en el directorio anterior?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 8 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 9 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 10 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Set 5: Diagnostica las interacciones entre procesos y núcleo10 Preguntas de Práctica
Guarda esta unidad para ver todas las respuestas y practicar cada set.
Pregunta 1 de 10
WIFSIGNALED sea verdadero después de que waitpid tenga éxito. ¿Qué macro proporciona el número de la señal que lo terminó?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 2 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 3 de 10
SIGSEGV. ¿Qué secuencia distingue los dos eventos?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 4 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 5 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 6 de 10
dup2(fd, 0), cierra fd incondicionalmente y ejecuta el comando mediante exec. ¿Por qué puede estar cerrada la entrada estándar del comando?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 7 de 10
Un programa tiene una variable privada x igual a 2. Todas las llamadas a fork tienen éxito. ¿Cuántos procesos imprimen «hit»?
pid_t p = fork();
if (p > 0) {
x = 7;
fork();
}
if (x == 7) puts("hit");
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 8 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 9 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 10 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
¿Te atascas con una pregunta tuya? Ayuda con Tareas
Términos Clave
A
Abstracción
Anillo de protección
B
Bifurcación
Bloque de control de proceso (PCB)
Búfer de salida
Búfer de traducción adelantada (TLB)
C
Cambio de contexto
Cambio de modo
Cierre al ejecutar exec
Comando interno
Compartición espacial
Concurrencia
Contador de programa
Contexto del núcleo
Control cooperativo
Copia en escritura
D
Descripción de archivo abierto
Descriptor de archivo
E
Ejecución directa limitada
errno
errno es un indicador de error de la biblioteca de C que muchas interfaces establecen cuando fallan y que se consulta después de que el valor de retorno indique un error.Espacio de direcciones
Espera no bloqueante
Estado bloqueado
Estado de ejecución
Estado de salida
Estado devuelto por wait
Estado listo
Excepción
F
Fallo de página
Fin de archivo (EOF)
Función envoltorio de biblioteca
I
Identificador de proceso (PID)
Instrucción privilegiada
Intérprete de comandos
Interrupción de hardware
Interrupción del temporizador
Intervalo de tiempo asignado
L
Llamada al sistema
M
Mecanismo
Micronúcleo
Modo núcleo
Modo usuario
Multiprogramación
N
Nivel de excepción
Núcleo
Núcleo monolítico
Número de llamada al sistema
P
Paralelismo
PATH
execvp buscan nombres de ejecutables.Pila del núcleo
Planificador
Política
Procesamiento por lotes
Proceso
Proceso hijo
Proceso huérfano
Proceso padre
Proceso zombi
Programa
Puntero de pila
R
Recolección de procesos terminados
Redirección
Retorno de trampa
S
Señal
Sistema operativo
Sobrecarga
Sustitución del programa
T
Tabla de trampas
Tiempo compartido
Trabajo en segundo plano
Trampa
Tubería
U
Utilización de la CPU
V
Vector de argumentos
Virtualización
Sobre esta unidad
Crea tus propios Materiales de Estudio
Convierte tus notas, diapositivas y documentos en Guías de Estudio, Preguntas de Práctica, Flashcards y 6 herramientas más.