Está en la página 1de 42

Procesos

Fernando R. Rannou
Departamento de Ingenieria Informatica
Universidad de Santiago de Chile

October 10, 2013

Concepto de proceso

Un programa es un conjunto de instrucciones en un lenguaje en


particular. Cuando un programa es compilado, linkeado y cargado en
memoria se transforma en un proceso
En terminos simples, un proceso es
un programa en ejecucion
una instancia de un programa corriendo en un computador
unidad de actividad caracterizada por la ejecucion de una secuencia
de instrucciones, un estado actual, y un conjunto asociado de
recursos del sistema
La labor principal del SO esta basada en el control de los procesos

De programa a proceso
Considere el siguiente programa en C, hello.c:
#include <stdio.h>
#define N 10
main() {
int i;
for (i=0; i < N; i++)
printf("Hola %d\n", i);
}

Las etapas que transforman este codigo en un programa ejecutable son:

Usando gcc podemos obtener los resultados de algunas de estas etapas:


$ gcc -o hello -save-temps hello.c
$ ls hello*
hello
hello.c
hello.i
hello.o

hello.s

El preprocesador
El preprocesador de gcc, llamado cpp se encarga de expandir los
#include<> y reemplazar la macros y #define<>, entre otras cosas.
El archivo transformado tiene extension .i
$ cat hello.i
...
typedef long unsigned int size_t;
...
extern int printf (__const char *__restrict __format, ...);
main() {
int i;
for (i=0; i < 10; i++)
printf("Hola %d\n", i);
}

El compilador
EL compilador trasforma el codigo a lenguaje assembler
$ cat hello.s
.file "main.c"
.section .rodata
.LC0:
.string "Hola %d\n"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl $0, -4(%rbp)
jmp .L2

.L3:
movl
movl
movl
movq
movl
call
addl

$.LC0, %eax
-4(%rbp), %edx
%edx, %esi
%rax, %rdi
$0, %eax
printf
$1, -4(%rbp)

.L2:

cmpl $9, -4(%rbp)


jle .L3
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.6.3-1ub
.section .note.GNU-stack,"",@progbits

El ensamblador

El ensamblador traduce el programa assembler a lenguaje de


maquina
El producto resultante es llamado objeto reubicable, o simplemente
objeto.
El m
odulo a
un no contiene el codigo de printf

El linkeador

Finalmente, el linker ld se encarga se resolver todas las referencias


pendientes
En este ejemplo, el linker mezcla los m
odulos hello.o con el
m
odulo de printf.o
El resultado es un codigo ejecutable
Este objeto esta listo para ser cargado en memoria y transformarse
en un proceso
Un programa ejecutable NO es un proceso

Creacion de procesos
Situaciones tpicas que crean nuevos procesos:
1
Solicitud de un usuario mediante la ejecucion de un comando:
$ ./hello
2

Solicitud de un proceso ya existente, mediante la invocaci


on de un
llamado al sistema
main() {
...
pid = fork();
...
}

3
4

Cuando se abre un aplicaci


on mediante la interacci
on en una GUI
El SO puede crear varios procesos al momento de bootear

El SO se encarga de crear a los procesos y administralos. Debe:


1
Procurar memoria principal para alojar al proceso, y
2
Crear una estructura o registro que contenga informacion acerca del
proceso

Bloque de control de proceso (PCB)


El PCB es el nombre generico dado a la estructura de datos que el Kernel
maneja acerca del proceso. Contiene toda la informacion que describe al
proceso en el sistema.

PID: Identificador del proceso


Otros identificadores (padre, grupo,
etc)
Estado: que esta haciendo el
proceso
Prioridad
Tiempos de procesador
Informacion de I/O
Informacion de memoria
Contexto

El PCB de Linux
El descriptor de procesos en Linux es task struct
(include/linux/sched.h). Algunos de los elementos mas
importantes son:

struct task_struct {
volatile long state;
// estado del proceso
long counter;
// Usada junto con prioridad
long priority;
// prioridad
unsigned long blocked; // se
nales mascaradas
struct task_struct *next_task, *prev_task; // punteros, anterior y p
int pid;
// Process ID
struct task_struct *p_opptr, *p_pptr, *p_cptr;
// punteros al padre original, actual
// hijo m
as joven, etc
unsigned short uid,euid,suid,fsuid; // ID usuarios
long utime, stime, cutime, cstime, start_time; // usos de procesador
struct thread_struct tss;
// Thread state stack
struct fs_struct *fs;
// info de sistema de archivos
struct files_struct *files; // info sobre archivos abiertos
struct mm_struct *mm;
// punero a estructura de admin memoria
struct signal_struct *sig; // manejadores de se
nales
};

La lista de descriptores

Linux mantiene una lista doblemente enlazada de task struct

Componentes de un proceso en memoria


En memoria, un proceso se organiza en segmentos. Los principales son:
Memoria
Texto

Texto: codigo ejecutable


Datos globales: inicializados y
no inicializados

Datos globales
Heap

Heap: memoria dinamica


Stack de usuario: usado para
paso de parametros en llamados
a funciones y variables locales

Stack usuario

Intel Task State Segment (TSS)


Independientemente del PCB definido por un SO, este debe finalmente
ajustarse al descriptor de tareas del procesador estructura.
struct tss_struct
unsigned short
unsigned long
unsigned short
unsigned long
unsigned short
unsigned long
unsigned short
unsigned long
unsigned long
unsigned long
unsigned long
unsigned long
unsigned long
unsigned long
unsigned long
unsigned short
unsigned short
unsigned short
unsigned short
unsigned short
unsigned short
unsigned short
unsigned short
unsigned long
};

{
back_link,__blh;
esp0;
ss0,__ss0h;
esp1;
ss1,__ss1h;
esp2;
ss2,__ss2h;
__cr3;
eip;
eflags;
eax,ecx,edx,ebx;
esp;
ebp;
esi;
edi;
es, __esh;
cs, __csh;
ss, __ssh;
ds, __dsh;
fs, __fsh;
gs, __gsh;
ldt, __ldth;
trace, bitmap;
io_bitmap[IO_BITMAP_SIZE+1];

EAX, ECX, EDX, EBX:


registros prop
osito general
EFLAS: registros de status
CS: Code segment
DS: Data segment
SS: Stack segment
ES, FS, FS: otros Data segment

Proceso

Luego, el proceso se realiza con tres componentes principales:


1
2
3

Su descriptor
Su espacio de direcciones
Su contexto, que generalmente es parte del descriptor

Primer modelo de estados de un proceso

En terminos simples, un proceso puede estar en uno de dos estados:


1
Corriendo, es decir ejecutandose en el procesador
2
No corriendo; en el sistema, pero fuera del procesador

Modelo de proceso de 5 estados

El estado no-corriendo se divide en dos: listo y bloqueado


Por conveniencia, se agregan dos estados mas: Nuevo y Salir.
Despachar
Entrar
Nuevo

Termino
No corriendo

Corriendo

Time-out

Evento
ocurre

Esperar
evento

Bloqueado

Salir

Modelo de 5 estados

Corriendo (running): el proceso esta corriendo, es decir esta en el


procesador
Listo (ready): el proceso esta listo para ejecutarse
Bloqueado (blocked): no puede ejecutarse hasta que un evento
dado ocurra. Ej: operaci
on de I/O termine
Nuevo (new): esta recien creado, pero a
un no ha sido admitido en
la cola de listos. Ej. A
un no ha sido cargado en memoria
Fin (exit): un proceso que, por alg
un motivo, ya no sigue
ejecutandose en el sistema

Ejemplo transicion entre estados

Proceso A

Proceso B

Proceso C

Dispatcher

10

12

14

16

18

Uso de multiples colas de espera


El SO puede organizar los procesos en colas de espera, seg
un sus
estados
Por ejemplo una cola de listos, otra de espera por I/O de disco, otra
para I/O de interface de red, etc.

Procesos suspendidos

El procesador es mucho mas veloz que los dispositivos de I/O, y


sera posible que todos los procesos en memoria estuvieran
esperando por I/O
Swapear temporalmente algunos de estos procesos a disco y as
liberar memoria.
Procesos que han sido swapeados a disco desde el estado de
bloqueados, pasan al estado Suspendido (suspended)
Note que swapping es una operaci
on de I/O

Modelo de estados con un estado suspendido

Despachar
Entrar
Nuevo

Termino
No corriendo

Corriendo

Time-out

Activar

Evento
ocurre

Bloqueado

Suspendido
Suspender

Esperar
evento

Salir

Modelo de estados con dos estados suspendidos


Bloqueado: proceso en
memoria principal y
esperando por un evento
Nuevo

Bloqueado/Suspendido:
proceso en memoria
secundaria y esperando
por un evento
Listo/Suspendido:
proceso en memoria
secundaria, pero listo
para ejecucion en
cuanto se cargue en
memoria principal

Suspender
Admitir

Admitir

Despachar
Termino

Activar
Listo/
Suspendido

Corriendo

No corriendo
Suspender
Time-out

Evento
ocurre

Evento
ocurre

Suspender
Bloqueado/
Suspendido

Bloqueado
Activar

Esperar
evento

Salir

Cambio de contexto (process switch)


Un cambio de contexto ocurre cada vez que el procesador comienza o
reanuda la ejecucion de un proceso distinto (al actual).
Cuando hacer un cambio de contexto?
1

Trap: interrupci
on asociada a un error de la ejecucion de la
instrucci
on actual
Interrupci
on: evento externo a la ejecucion del proceso

Interrupci
on del reloj por termino de tiempo
Fallo de memoria. La direcci
on de memoria no se encuentra
actualmente en memoria principal.

Llamado al sistema (system call): por ejemplo instrucci


on de I/O

Un cambio de contexto o cambio de proceso no es lo mismo que un


cambio del modo de ejecucion del procesador

Labores en un cambio de contexto

Guardar el contexo del proceso incluyendo el PC y otros registros


Actualizar el PCB del proceso que actualmente esta en ejecucion
Mover el PCB de la cola de listo a la cola apropiada.
Seleccionar otro proceso para ejecucion
Actualizar el PCB del proceso seleccionado
Actualizar las estructuras de administracion de memoria
Restaurar el estado del procesador con el contexto del proceso
elegido

Ejecucion del sistema operativo

Ejecuci
on en el contexto de un proceso usuario
Cada vez que se solicita un servivio al SO, simplemente se realiza un
llamado a una rutina del SO, pero no hay cambio de contexto.
Hay cambio de modo usuario a modo kernel
Un n
umero peque
no de tareas, como por ejemplo cambio de
contexto, pueden ejecutarse fuera del contexto usuario

Basado en procesos
Implementa las tareas del SO como una colecci
on de procesos del
sistema
en sistemas multiprocesadores y multicomputadores, pues
Util
permiten que los servicios prestados por los procesos puedan ser
ejecutados exclusivamante en algunos procesadores y as mejorar
rendimiento.

Cambio del modo de ejecucion


Existen dos modos principales para la ejecucion de procesos:
1

Modo usuario : Es un modo no privilegiado, en el cual el proceso no


puede ejecutar instrucciones reservadas al kernel
Modo kernel : En este modo, el proceso puede ejecutar cualquier
tipo de instrucciones y tiene el control absoluto del sistema

En SO donde el kernel se ejecuta en el contexto de procesos usuarios


(Unix, por ejemplo) es necesario realizar cambio en el modo de
ejecucion cada vez que un proceso invoca alg
un llamado al sistema
Recordar que el procesador chequea por interrupciones durante el
ciclo fetch.
Si hay alguna interrupci
on pendiente, el procesador realiza lo
siguiente:
1
2

Setea el PC con la direcci


on inicial del manejador de interrupciones
Cambia el modo del procesador de modo usuario a modo kernel, de
manera que el manejador de interrupciones pueda ejecutar
instrucciones privilegiadas.

Modelo de estados en Unix

Modelo de estados en Linux

TASK ZOMBIE
TASK STOPPED

start

TASK RUNNING
(ready)

TASK RUNNING (in


processor)

TASK INTERRUPTIBLE

TASK UNINTERRUPTIBLE

TASK RUNNING: en la cola de listos o


en el procesador
TASK INTERRUPTABLE: proceso
bloqueado esperando un evento: fin de
I/O, o alguna se
nal de otro procesos
TASK UNINTERRUPTABLE: proceso
bloqueado esperando alguna condici
on
de hardware. No recibe se
nales de otros
procesos
TASK ZOMBIE: proceso finalizado
TASK STOPPED: proceso detenido y
s
olo puede reanudarse mediante una
se
nal de otro proceso (ejemplo
debugger)

Comando ps

C
odigo
R
S
D
T
Z
l
s
<
N

Estado
Runnable (TASK RUNNING)
Sleeping (TAKS INTERRUPTABLE)
Sleeping (TASK UNINTERRUPTABLE)
Stopped (TASK STOPPED).
Zombie (TASK ZOMBIE).
Multi-thread
Session leader
Alta prioridad
Baja prioridad

Creacion de procesos en Unix

fork() es el llamado al sistema para crear un nuevo proceso


#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);

fork() crea un proceso hijo, copia exacta del proceso padre. Lo


u
nico diferente es el identificador de proceso o pid
fork() retorna:
1
2
3

el pid del hijo, en el proceso padre


0, en el proceso hijo
-1 si hubo alg
un error (s
olo en el padre)

fork() es llamado una vez, pero retorna dos veces

Ejemplo 1 de fork()
#include <sys/types.h>
#include <unistd.h>
main()
{
int pid;
if ( (pid = fork()) == -1) {
printf("No se pudo crear hijo\n");
exit(-1);
}
if (pid == 0) { // soy el hijo
printf("Soy el hijo y mi pid = %d\n", getpid());
printf("Hijo: el pid de mi padre es = %d\n", getppid());
exit(0);
}
else { // soy el padre
printf("Soy el padre y mi pid = %d\n", getpid());
printf("Padre: el pid de mi hijo es %d\n", pid);
printf("Padre: el pid de mi padre es %d\n", getppid());
exit(0);
}
}

Ejemplo 2 de fork()

Identificadores

En sistemas Unix-like cada proceso tiene al menos seis


identificadores asociados con el.
pid
pid
uid
uid
gid
gid

t
t
t
t
t
t

getpid(void): ID del proceso (el que invoca)


getppid(void): ID del padre del proceso
getuid(void): ID del usuario real del proceso
geteuid(void): ID del usuario efectivo del proceso
getgid(void): ID del grupo real del proceso
getegid(void): ID del grupo efectivo del proceso

El usuario y grupo real identifican quien realmente somos y son


seteados al momento de hacer login al sistema (archivo password
/etc/passwd)
El usuario y grupo efectivo definen los permisos de acceso del
proceso a los archivos.

El 1/2 ambiente de un proceso

El medio ambiente de un proceso se refiere a todos aquellos elementos


que permite que un proceso se ejecute; incluye
C
omo comienza la ejecucion de un proceso;
Los recursos asociados y sus lmites;
El layout en memoria principal;
Los argumentos de linea de comando;
Las variables de medio ambiente.

La funcion main()

Un programa en C comienza su ejecucion cuando la funcion main()


es invocada.
int main(int argc, char *argv[]);

argc es el n
umero de argumentos en lnea de comandos
*argv[] es el vector de punteros los argumentos de lnea de
comandos Ejemplo:
$ sort -n 100 -o file.txt
argc es 5

argv[] = {sort, -n, 100, -o, file.txt

En realidad, el kernel llama a una rutina start-up la cual finalmente


invoca a main

Inicio, ejecucion y termino de un proceso

Cuando el proceso invoca exit(), el proceso realiza operaciones de


limpieza antes de retornar al kernel.
Cuando el proceso invoca exit(), el proceso retorna
inmediatamente al kernel.

La funcion atexit()
Esta funcion le permite al proceso registrar manejadores de salida, es
decir funciones que se ejecutan cuando el proceso termina.
#include <stdlib.h>
int atexit(void (*func)(void));

Cuando el proceso termina, se invocan las funciones registradas con


atexit() en el orden inverso al que fueron registradas
static void limpieza(void);
int main(int argc, char *argv[]) {
if (atexit(limpieza) != 0)
err_sys("error registrar limpieza()");
printf("eso es todo, amigos\n");
return(0);
}
static void limpieza(void) {
printf("limpiando...\n");
}

Variables de 1/2 ambiente


Un proceso puede acceder a sus variables de 1/2 ambiente mediante
el puntero environ
extern char **environ;

Cada variable consiste de un par nombre=valor y pueden ser ledas


por el proceso usando la funcion
char *getenv(const char *name);

Estas variables no tiene valor para el kernel; el significado es dado


por las aplicaciones
environ

"HOME=/home/batman"
"PATH=/bin:/usr/bin"
"SHELL=/bin/bash"
"USER=batman"
"PWD=/home/batman/lab1"

NULL

Layout de un proceso Linux en memoria

$ cat /proc/self/maps
001c2000-001c3000 r-xp
002b8000-002d1000 r-xp
002d1000-002d2000 r-xp
002d2000-002d3000 rwxp
00c87000-00dc4000 r-xp
00dc4000-00dc6000 r-xp
00dc6000-00dc7000 rwxp
00dc7000-00dca000 rwxp
08048000-0804d000 r-xp
0804d000-0804e000 rw-p
0987f000-098a0000 rw-p
b7db6000-b7fb6000 r--p
b7fb6000-b7fb7000 rw-p
b7fcf000-b7fd0000 rw-p
bfaf8000-bfb0d000 rw-p
$ size /bin/cat
text
data
18431
1036

bss
0

001c2000
00000000
00019000
0001a000
00000000
0013d000
0013f000
00dc7000
00000000
00004000
0987f000
00000000
b7fb6000
b7fcf000
bfaf8000

dec
19467

00:00
08:01
08:01
08:01
08:01
08:01
08:01
00:00
08:01
08:01
00:00
08:01
00:00
00:00
00:00

0
66950
66950
66950
67366
67366
67366
0
7294713
7294713
0
7264522
0
0
0

hex filename
4c0b /bin/cat

[vdso]
/lib/ld-2.5.so
/lib/ld-2.5.so
/lib/ld-2.5.so
/lib/i686/nosegneg/libc-2.5.so
/lib/i686/nosegneg/libc-2.5.so
/lib/i686/nosegneg/libc-2.5.so
/bin/cat
/bin/cat
/usr/lib/locale/locale-archive

[stack]

Familia exec()
Si fork() crea un clon de un proceso, como sera posible crear
procesos hijos totalmente distintos a un padre?
execve() ejecuta un nuevo programa en el proceso que realiza la
invocaci
on
execve() no crea un nuevo proceso, sino que sobre-escribe el
programa actual con uno nuevo
#include <unistd.h>
int execve(const char *filename, char *const argv [], char *const

filename es el camino absoluto del programa ejecutable


argv[] es un arreglo con los argumentos de lnea de comandos
envp[] es el medio ambiente de ejecucion del proceso
execve() no retorna si hubo exito

Familia exec()
Las siguientes funciones son front-ends de execve():
#include <unistd.h>
int
int
int
int
int

execl(const char *path, const char *arg, ...);


execlp(const char *file, const char *arg, ...);
execle(const char *path, const char *arg , ..., char * const envp[
execv(const char *path, char *const argv[]);
execvp(const char *file, char *const argv[]);

Para recordar:
l : lista uno a uno los argumentos
v : entrega un vector con los argumentos
p : usa el PATH definido en el medio ambiente del proceso para
encontrar el ejecutable
e : asigna un environment, es decir medio ambiente

Ejemplo de execve()
#include <sys/types.h>
#include <unistd.h>
main()
{
int pid;
if ( (pid = fork()) == -1) {
printf("No se pudo crear hijo\n");
exit(-1);
}
if (pid == 0) { // soy el hijo
if (execlp("ls", "ls", "/home/rannou", 0) == -1) {
printf("no se pudo hacer exec\n");
exit(-1);
}
printf("bye");
}
// soy el padre
wait(pid);
printf("mi hijo finalizo\n");
exit(0);
}

También podría gustarte