Está en la página 1de 44

Sistemas Operativos

Introducción

Es un programa de control que se ocupa de:

Administrar los recursos de la computadora.

Administrar la ejecución de los diferentes programas, incluso de diferentes usuarios.

Facilitar la tarea del programador permitiéndole acceso a los recursos de manera independiente del hardware.

Proveer servicios a los programas de aplicación a través de un conjunto de llamadas standard.

Estas acciones se resuelven a través de una implementación que puede representarse en capas

Modelo de capas

Modelo de capas

Clasificación: Sistemas de Tiempo Real

Se utilizan para sistemas de control industrial, centrales telefónicas, instrumentación científica.

Por lo general tienen una interfaz con el usuario limitada o no tienen utilitarios.

Su fortaleza consiste en administrar los recursos de la computadora, de modo de ejecutar una operación en particular en la misma cantidad de tiempo cada vez que ocurre el evento que la dispara. Concepto event-driven.

En el tipo de aplicaciones que resuelven estos sistemas operativos, si una parte se mueve mas rápido solo porque los recursos están disponibles puede generar resultados tan catastróficos como si no se mueve porque el recurso está ocupado.

Ejemplos: QNX, RT-Linux.

Clasificación: Monotarea - Monousuario

Están preparados para ejecutar sólo una tarea a la vez. No puede ejecutar mas de una tarea en forma concurrente

Interfaz para un sólo usuario, (una sola sesión de trabajo) Transfiere el control de la máquina a la aplicación que va ejecutarse, y sólo interviene a demanda de esta mediante alguna llamada a los servicios de su kernel, o cuando la aplicación finaliza y devuelve el control.

Ejemplo: MS-DOS, CPM/86, algunas versiones de Palm OS

Clasificación de Multitarea - Monousuario

Interfaz para un usuario, pero pueden mantener en memoria múltiples aplicaciones en forma estable y dentro de un entorno de protección.

Es habitual descargar correo electrónico de Internet o bajar un archivo extenso durante minutos mientras se trabaja en la redacción de un documento, o en la escritura de un programa de aplicación, y hasta se chequea el estado de una unidad de disco, y se realizar un backup de

información

todo a la vez.

Ejemplos: Windows XP, Windows NT WorkStation, Windows 2000, Cualquier UNIX corriendo en monousuario.

Clasificación: Multiusuario

Esta es la forma mas avanzada de los Sistemas Operativos, y que primó en los Sistemas pioneros.

La falta de capacidad del hardware de por entonces (1969) hizo que se implementasen versiones mas simplificadas para usuarios individuales.

Aquí la interfaz de usuario soporta múltiples sesiones. Esto por extensión implica que tiene capacidades de Sistema Multitarea.

Estos Sistemas son los mas poderosos y normalmente los mas eficientes: MVS, UNIX.

Microsoft tiene versiones similares, Windows 2003 Server entre otras.

Funciones de un Sistema Operativo

Gestión del procesador

Gestión del tiempo de procesamiento para cada tarea (scheduling de procesos)

Gestión de la Memoria

Asignación de memoria RAM para las aplicaciones aplicando criterios basados en la MMU del procesador

Gestión de la Memoria Virtual

Gestión de la Memoria Cache

Funciones de un Sistema Operativo (2)

Gestión de los dispositivos de E/S

Acceso al hardware de manera transparente para las aplicaciones.

Manejo de la concurrencia de acceso en sistemas multiusuario/multitarea especialmente.

Gestión del storage

File Systems en los medios masivos de almacenamiento:

discos rígidos, CD-ROM's. DVD's.

Funciones de un Sistema Operativo (3)

Interfaz para las aplicaciones

System Call's

En los multitarea se manejan mediante este subsistema, los diferentes niveles de privilegio que posea el Sistema Operativo (y que dependen del procesador utilizado en el sistema)

Interfaz para los usuarios

Menejo de interfaces sencillas para usuarios no expertos:

GUI, Texto, Combinación de ambas.

Seguridad

Scheduler Preemtivo o Cooperativo

Scheduler Preemptivo

Esto significa que una tarea puede ser interrumpida por otra de mayor prioridad, en cualquier momento y luego continuar su ejecución.

Scheduler Cooperativo

Cuando una tarea tiene la ejecución otra tarea de mayor prioridad sólo puede tomar el control del CPU en determinados momentos.

Modelo derivado de las funciones

Modelo derivado de las funciones

Modelo derivado de las funciones

Hardware

Kernel

Capa de interfaz para acceso a los servicios del S.O. Aplicaciones

Caso práctico: Linux

Sistema Operativo UNIX-like

Basado en POSIX (Portable OS based on UNIX)

Kernel monolítico (programa único + módulos)

Diseñado bajo el concepto Lightweight Processes (LWP)

Preemptive Kernel

La versión 2.6 (al igual que Solaris 2.x y MAC 3.0) son full preemptive

Caso práctico: Linux (2)

Soporta SMP (Symetric MultiProcessing).

Soporta varios File Systems (EXT2, EXT3, IBM AIX, SGI Irix, FAT 32, NTFS, Raiser, entre otros).

Puede ser muy pequeño. Se puede ajusta a un floppy 3”1/2.

Puede ejecutarse un web server o un proxy server basado en Linux en una PC basada en 80386.

Es libre. Se puede instalar y modificar su código son otra limitación que el hardware.

Versiones. Son tres números separados por puntos.

El Kenel

Es el principal programa del S.O. Actualmente soportado por las siguientes arquitecturas:

ARM (ARM based Computers)

Alpha (Compaq)

IA32 e IA64-Itanium (Intel)

Familia 68000 (Motorola)

S390 (IBM)

Power PC, Power PC 64 (IBM)

Sparc (Sun)

El Kernel (2)

Aprovecha las capacidades del hardware

Maneja el acceso a los recursos de hardware específicos. A través de “Device Drivers”.

Provee servicios de acceso al hardware a los programas de usuarios.

Es reentrante: Múltiples procesos acceden al kernel de manera simultánea.

Maneja niveles de protección

Ejecución en modo kernel

Ejecución en modo User

Maneja Stacks separados.

El Kernel (3)

El Kernel (3)

GDT en Linux

Hay una GDT por procesador.

Hay un arreglo denominado cpu_gdt_table que contiene a estas GDT's.

18 Descriptores en uso:

KERNEL_CS,

KERNEL_DS

USER_CS,

USER_DS

1 TSS, 1 LDT

3 Thread-Local Storage

3 Advanced Power Management

5 Plug and Play BIOS

1 TSS especial (Double Fault)

GDT en Linux (2)

Segmentación basada en el modelo flat.

GDT en Linux (2)  Segmentación basada en el modelo flat.

Descriptores de TSS

Una TSS por cada procesador

No usa Task Switch a través del procesador TSS para:

El stack de PL=0 cuando sube el nivel de privilegio de una tarea (ya sea por llamada al kernel, o por entrada de una interrupción).

Un programa que corre en modo user, ejecuta IN o OUT, se consulta el par de flags IOPL en EFLAGS y el IO Map del TSS.

Descriptor LDT

Una genérica compartida por todos los procesos. El Kernel de linux no usa LDT.

Contiene un descriptor NULL

Dirección base: Se almacena en la variables default_ldt.

Límite 7.

Los procesos pueden crear su propia LDT mediante una función especial del kernel: modify_ldt().

Descriptores APM y PnP

Segmentos para Advanced Power Management (APM): El Código del BIOS usa estos segmentos, de modo que cuando el driver APM de Linux invoca funciones del BIOS para obtener el estado o configurar un dispositivo APM, usará estos segmentos de código y datos.

Segmentos para servicios Plug and Play (PnP) del BIOS. Como en el caso previo, el código del BIOS utiliza estos segmentos, de modo que cuando un driver PnP de Linux invoque funciones del BIOS para detectar recursos utilizados por dispositivos PnP, usará estos segmentos de código y datos.

Descriptores TLS

Segmentos Thread-Local Storage (TLS): Mecanismo que permite a las aplicaciones que hagan multithreading usar hasta tres segmentos conteniendo datos locales para cada thread. Las system call's set_thread_area() y get_thread_area(), crean y liberan respectivamente un segmento TLS para el proceso en ejecución.

Proceso

Instancia de un programa en ejecución, o un contexto de ejecución.

Tiene un espacio de direccionamiento determinado (direcciones de memoria que tienen permitidas para acceder). Normalmente trabajan en modo usuario.

Cuando requieren servicios del kernel, cambia a modo kernel. Una vez resuelto el requerimiento regresan a modo usuario.

El kernel hace que cada proceso “vea” una CPU dedicada. Es un administrador de procesos.

Proceso (2)

Existe un grupo de programas privilegiados llamados “kernel threads”.

Corren en modo Kernel en el espacio de direccionamiento del Kernel.

No interactúan con el usuario.

No tienen terminal asociada.

Se crean durante el arranque del sistema y finalizan con su cierre.

Proceso (3): ps -ef | more

guri:~# ps ­ef | more

UID

PID PPID

C STIME TTY

TIME CMD

root

1

0

0 Aug26 ?

00:00:00 init [2]

root

2

0

0 Aug26 ?

00:00:00 [kthreadd]

root

3

2

0 Aug26 ?

00:00:00 [migration/0]

root

4

2

0 Aug26 ?

00:00:00 [ksoftirqd/0]

root

5

2

0 Aug26 ?

00:00:00 [watchdog/0]

root

9

2

0 Aug26 ?

00:00:00 [events/0]

root

11

2

0 Aug26 ?

00:00:00 [khelper]

root

44

2

0 Aug26 ?

00:00:00 [kblockd/0]

root

47

2

0 Aug26 ?

00:00:00 [kacpid]

root

48

2

0 Aug26 ?

00:00:00 [kacpi_notify]

root

130

2

0 Aug26 ?

00:00:00 [kseriod]

root

171

2

0 Aug26 ?

00:00:00 [pdflush]

root

172

2

0 Aug26 ?

00:00:00 [pdflush]

Proceso (4)

El Kernel debe saber para cada proceso:

La prioridad.

El estado.

El espacio de direcciones asignado.

Los archivos abiertos y en acceso.

Descriptores de Proceso, estructura task_struc:

El Kernel define un tipo de dato struc task_struc: task_t.

El kernel define un array de estas estructuras denominado task_array.

Proceso (5)

Parentesco entre procesos

Los procesos pueden crear copias de si mismos -> child's.

Los child's comparten el espacio de memoria de los padres.

Pero conservan independencia en los datos. Si un proceso modifica un dato el resto conserva su propia copia y no ve la modificación. A esto se lo denomina “copy on write

Proceso (6): ps -ejH | more

guri:~# ps ­ejH | more

PID

PGID

SID TTY

TIME CMD

 

2

0

0

?

00:00:00 kthreadd

3

0

0

?

00:00:00

migration/0

4

0

0

?

00:00:00

ksoftirqd/0

3774

0

0

?

00:00:00

events/1

1

1

1

?

00:00:00 init

 

930

930

930 ?

00:00:00

udevd

1897

1897

1897 ?

00:00:00

portmap

2725

2725

2725 ?

00:00:00

inetd

2884

2884

2884 ?

00:00:00

kdm

2887

2887 2887 tty7

00:03:16

Xorg

2895

2884

2884 ?

00:00:00

kdm

2953

2953

2953 ?

00:00:00

startkde

2991

2991

2991 ?

00:00:00

ssh­agent

3039

2953

2953 ?

00:00:00

kwrapper

2899

2899 2899 tty1

00:00:00

getty

3027

3027

3027 ?

00:00:00

kdeinit

3032

3027

3027 ?

00:00:00

klauncher

3062

3027

3027 ?

00:00:04

konsole

3065

3065 3065 pts/0

00:00:00

bash

3628

3628 3065 pts/0

00:00:00

su

3630

3630 3065 pts/0

00:00:00

bash

Threads

El enfoque parent-childs, permite crear flujos paralelos de ejecución.

Fue el inicio de computación paralela en los sistemas operativos antiguos.

Los sistemas operativos modernos, introducen el concepto de “Multithreaded Applications”.

Hilos de procesamiento en paralelo que compartan no lo el código sino las áreas de datos.

Son procesos mas livianos, denominados Threads (hilos).

POSIX Threads, los estandariza.

pthread es la librería que contiene su implementación para generar aplicaciones Multithreading.

Threads (2)

Hasta el kernel 2.4 inclusive la implementación no era 100% POSIX compatible.

A partir de la implementación 2.6, se lleva el modelo al standard.

Se manejan desde el scheduler como procesos independientes.

Se conserva su agrupación como un único proceso a efectos de las system call's globales, como por ejemplo getpid(), exit(), kill(), entre otras.

Pero si un thread queda bloqueado como consecuencia de llamar a una función bloqueante, el resto sigue trabajando normalmente.

El kernel 2.6 maneja threads, como las versiones de Windows:

NT, 2000, 2003 Server, XP o Vista.

Descriptor de procesos

Tiene por objeto darle al Sistema Operativo la visión clara de que está haciendo el proceso.

Definido en la declaración de task_struct.

Dada la complejidad de esta estructura algunos campos son referencias a otras estructuras como la tabla de file descriptor's del proceso.

vi /usr/src/linux-[versión]/includes/linux/sched.h

Identificación de un proceso

Cada proceso tiene un Descriptor que lo define unívocamente.

Los punteros a los descriptores de proceso sirven para identificarlos. Son números de 32 bits.

Los UNIX identifican a los procesos son un Process ID (PID). Los PID van desde 0 a 32767.

El Process ID corresponde al campo PID en el descriptor de proceso (task_struct)

Estados de un proceso

Corresponden al campo state de task_struct. Valores posibles (flags)

Para setear el estado de un proceso el kernel usa las macros set_task_state o set_current_state.

Los estados posibles son:

TASK_RUNNING: El valor del campo state es 0, el proceso está en ejecución en alguna CPU o esperando para ser ejecutado.

TASK_INTERRUPTIBLE: El valor del campo state es 1, el proceso está suspendido hasta que se produzca una determinada condición que los vuelva a TASK_RUNNING; como por ejemplo una Interrupción, liberación de un recurso o una señal.

Estado de un proceso (2)

Los estados posibles son (continuación):

TASK_UNINTERRUPTIBLE: El valor del campo state es 2, es similar al anterior con la diferencia que la llegada de una señal no lo pone en TASK_RUNNING.

TASK_STOPPED: El valor del campo state es 4, el proceso ha sido detenido como consecuencia de la recepción de señales como SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOUT.

TASK_TRACED: El valor del campo state es 8, el proceso ha sido detenido por un debugger. El debugger lanzó el proceso utilizando ptrace().

Estado de un proceso (3)

Los estados posibles son (continuación):

EXIT_ZOMBIE: El valor del campo state es 16, el proceso terminó su ejecución pero el proceso padre no ejecutó la System Call wait4() o waitpid(), para recuperar información del proceso finalizado. El sistema no puede descartar el proceso, ni libera sus recursos hasta que se ejecuten alguna de las System Call citadas.

EXIT_DEAD: El valor del campo state es 32, es el estado final de un proceso y está siendo liberado por el sistema, como consecuencia que el proceso padre ejecutó la system call wait4() o waitpid().

Lista de Procesos

Task array: Lista doblemente enlazada de punteros a estructuras de tipo task_struct.

La cantidad de elementos se guarda en la variable N_TASKS.

El campo PID es el índice de este arreglo.

Los campos prev_task y next_task de los descriptores de procesos (task_struct), vinculan los elementos.

El primer elemento es el puntero a task_struct del proceso cero llamado swapper.

Lista doblemente enlazadas

Linux utiliza numerosas listas doblemente enlazadas.

Buscando un estilo de programación homogéneo se define la siguiente estructura genérica:

Struct list_head {

Struct list_head *next, *prev;

};

La misma se encuentra en el archivo /usr/src/linux- [versión]/includes/linux/list.h

*next, *prev;  };  La misma se encuentra en el archivo /usr/src/linux- [versión]/includes/linux/list.h

Lista de procesos en ejecución

Por una cuestión de eficiencia, el kernel mantiene una lista separa de los procesos que están en estado TASK_RUNNING.

La variable nr_running mantiene la cantidad de procesos en este estado.

Hasta la versión 2.4 el kernel manejaba una lista llamada runqueue.

Lista de procesos en ejecución (2)

El campo run_list de task_struct es una estructura de tipo list_head para implementar esta lista.

La lista está encabezada por init_task (descriptor de proceso del proceso cero o swapper).

Funciones:

add_to_runqueue(): Inserta un proceso a la lista

del_from_runqueue(): Remueve un proceso de la lista

move_first_runqueue(): Mueve un proceso al principio de la lista.

move_last_runqueue(): Mueve un proceso al final de la lista.

Lista de procesos en ejecución (3)

A partir del kernel 2.6, se implementa con algunas diferencias.

No hay mas una única lista, sino una lista por cada valor de prioridad. (runqueue ahora es una lista de listas)

Existen 140 valores de prioridad almacenables en una variable k (de 0 a 139). => hasta 140 listas de procesos.

El campo run_list de task_struct apunta a una estructura de tipo list_head que ahora corresponde a la lista de procesos con prioridad k.

Parentesco entre procesos

En el descriptor de procesos se dispone de los siguientes campos para reflejar parentesco

p_opptr (original parent)

p_pptr (parent)

p_cptr (child)

p_ysptr (younger sibling)

p_osptr (older sibling)

parent)  p_pptr (parent)  p_cptr (child)  p_ysptr (younger sibling)  p_osptr (older sibling)