Sistemas Operativos - Linux - Electronica - FRBAgjoyuela/archivos/linux/Presentac... · Autor:...

219
Autor: Alejandro Furfaro 1 Sistemas Operativos Universidad Tecnológica Nacional - FRBA Técnicas Digitales III

Transcript of Sistemas Operativos - Linux - Electronica - FRBAgjoyuela/archivos/linux/Presentac... · Autor:...

Autor: Alejandro Furfaro 1

Sistemas OperativosUniversidad Tecnológica Nacional - FRBA

Técnicas Digitales III

Autor: Alejandro Furfaro 2

IntroducciónSistema Operativo ¿Que es?Es un programa de control que se ocupa de:2 Administrar los recursos de la computradora.2 Administrar la ejecución de los diferentes programas en muchos casos

pueden ser de diferentes usuarios.2 Facilitar la tarea del programador permitiendole acceso a los recursos de

manera independiente del hardware.2 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:

Hardware

Sistema Operativo

Utilidades / Servicios

AplicacionesProgramador de Aplicaciones

Programador de Sistemas

Autor: Alejandro Furfaro 3

Clasificación de los Sistemas Operativos

Sistemas Real Time:2Se utilizan para sistemas de control industriales, centrales de

conmutación, instrumentación científica. 2Por lo general tienen una muy pobre capacidad de interfaz con el

usuario, no tienen utilitarios.2Su 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 even-driven. 2En el tipo de aplicaciones que resuelven estos sistemas operativo, 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.2Ejemplos de implementaciones

iQNXi RT-Linux

Autor: Alejandro Furfaro 4

Clasificación de los Sistemas Operativos

Monotarea - Monousuario2 Están preparados para ejecutar solo una tarea a la vez. no puede

ejecutar mas de una en forma concurrente. 2 Interfaz para un solo usuario, (una sola sesión de trabajo). 2 Transfiere el control de la máquina a la aplicación que va a

ejecutarse, y solo interviene a demanda de ésta mediante alguna llamada a los servicios de su kernel, o cuando la aplicación finaliza y devuelve el control.

2 El viejo MS-DOS (sucesor del mas viejo aún CPM/86), es el mas difundido de este tipo de sistemas.

2 Un ejemplo muchísimo mas actual, útil, y eficiente de este tipo de sistemas es el Palm OS que corre en las computadoras de mano Palm Pilot. Aquí no hay programas residentes y se tiene un sistema operativo con una interfaz de usuario muy cómoda que permite ejecutar aplicaciones de a una por vez.

Autor: Alejandro Furfaro 5

Clasificación de los Sistemas Operativos

Multitarea Monousuario2 Hoy en día es habitual utilizar estos sistemas operativos en las PC de

escritorio.2 Interfaz para un solo usuario, pero pueden mantener en memoria

múltiples aplicaciones en forma estable y dentro de un entorno de protección (algunos con mas suerte que otros...)

2 Es habitual descargar correo 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 realiza un backup de información, todo a la vez.

2 Ejemplos habituales de este tipo de sistemas.i Windows XP, i NT Workstation, i 2000 Workstation, i OS/2, i Machintosh, i Linux o cualquier UNIX instalado como Workstation

Autor: Alejandro Furfaro 6

Clasificación de los Sistemas Operativos

Multiusuario2 Esta es la forma mas avanzada de los sistemas operativos, y curiosamente

la que primó en los sistemas pioneros como UNIX.2 La falta de capacidad del hardware de por entonces (1969) hizo que se

implementasen versiones mas simplificadas para usuarios individuales.2 Aquí la interfaz de usuario soporta múltiples sesiones. Esto por extensión

implica que tiene capacidades multitarea, ya que con solo ejecutarse un proceso por usuario se tiene mas de una tarea en ejecución en la memoria del sistema.

2 Estos sistemas son los mas poderosos y normalmente los mas eficientes: MVS, para los mainframes, UNIX (o cualquiera de sus versiones free como LINUX, o free BSD, por ejemplo) son los mejores exponentes de este tipo de sistemas.

2 Microsoft tiene versiones denominadas Server de Windows XP 2000, y una evolución de 2000, denominada 2003 que soporta al procesador Itanium.

Autor: Alejandro Furfaro 7

Funciones de un Sistema OperativoGestión del procesador 2 Gestión del tiempo de procesamiento para cada tarea (scheduling de

procesos).

Gestión de la Memoria.2 Asignación de memoria RAM para las aplicaciones aplicando

criterios basados en la MMU del procesador.2 Gestión de la Memoria Virtual2 Gestión de la memoria cache

Gestión de los dispositivos de E/S. 2 Acceso al hardware de manera transparente para las aplicaciones.2 Manejo de la concurrencia de acceso en sistemas multiusuario o

multitarea especialmente

Autor: Alejandro Furfaro 8

Funciones de un Sistema OperativoGestión del storage2 (File Systems en los medios masivos de almacenamiento: discos

rígidos, CD-ROMs, DVDs).

Interfaz para las Aplicaciones.2 Colección de llamadas para ejecutarse desde los programas de

aplicación para acceder a servicios brindados pro código del sistema operativo. Se las conoce como System Calls.

2 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.2 Manejo de interfaces sencillas para usuarios no expertos

iGUIi Textoi Combinación de ambas

Autor: Alejandro Furfaro 9

Modelo derivado de las funciones

Hardware

DeviceDrivers

Buffe

rsde

E/S

File

Syst

emMan

ager

Gestió

n de

Pro

ceso

s

Gestión de Memoria

Interfaz de aplicaciones (API o System Call)

Aplicaciones de usuario Programas de Aplicacióny UtilitariosEjecutan en el menor nivel de privilegio

Capa de interfaz para acceso a los servicios del S.O. Por parte de las aplicaciones

KERNEL

Hardware(lo que golpeamos a causa de los estándares de calidad de algunos S.O.’s ....)

Servicios de E/S

Sche

dule

r

Autor: Alejandro Furfaro 10

Caso Práctico

Linux

Autor: Alejandro Furfaro 11

Linux: IntroducciónSistema Operativo Unix-likeBasado en POSIX (Portable OS based on UNIX)Kernel monolítico (programa único +módulos)2 Opuesto a los Sistemas MicroKernel que reinaron en los 70’s.

Diseñado bajo el concepto Lightweight Processes (LWP)Preemptive Kernel2 Hasta la versión 2.4 el kernel no puede intercalar arbitrariamente flujos de ejecución mientras está

en modo privilegiado (PL=00).2 La versión 2.6, compilada con la opción “Preemptive Kernel”, sí puede .2 Solaris 2.x y Mac 3.0 son full preemptive

Soporta SMP (Symetric MultiProcessing)Soporta varios File Systems (IBM AIX, SGI Irix, FAT32, etc)Puede ser muy pequeño. Se puede ajustar a un floppy 3”1/2Puede ejecutarse un web server o proxy server basado en Linux en una PC basada en 80386.Es libre. Podemos intslarlo y modificar su código sin otra limitación que nuestro hardware.Versiones. Se representan con tres números separados por puntos. Ej: 2.4.18, o 2.5.22. El primero es la Versión. El segundo indica si es un kernel estable (par) o en desarrollo (impar). El tercero es el release.

Autor: Alejandro Furfaro 12

El KernelEs el principal programa del SOActualmente soportado por las siguientes arquitecturas:2 ARM (ARM based Computers)2 ARM262 Alpha (Compaq)2 Intel (ia32 e ia64-Itanium)2 Familia 68K (Motorola)2 Parisc (WS basadas en procesadores RISC HP-9000 de Hewlett Packard)2 MIPS (Silicon Graphics)2 Power PC y PowerPC 642 Sparc y Ultra Sparc (32 y 64 bits Sun Microsystems)2 S390 (IBM)2 SuperH. Línea SuperH de Procesadores de Hitachi y STMicroelectronics2 V850. Microcontroladores NEC basados en un RISC de 32 bits Arquitectura Harvard.

Aprovecha las capacidades del hardware2 Maneja el acceso a los recursos hardware específicos. A través de Device Drivers.2 Provee servicios de acceso al hardware a los programas de usuario.Es reentrante2 Múltiples procesos acceden al kernel de manera simultánea.

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 1

Autor: Alejandro Furfaro 13

El KernelManeja niveles de Protección2 Ejecución en modo Kernel.2 Ejecución en modo User. 2 Maneja Stacks separados.

Inte

rrup

ción

de

timer

Fallo

de

Pági

na

Serv

icio

del

Si

stem

a

Req

uerim

ient

o a

un

driv

er/ I

nter

rupc

ión

desd

e un

dis

posi

tivo

Modo Usuario

Modo Kernel

System Call Handler

System Call Handler

SchedulerSchedulerException Handler

Exception Handler

Device Driver

Device Driver

Proceso 1 Proceso 2

Proceso 3(Accede a una

página de memoria no presente)

Proceso 4

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 1

Autor: Alejandro Furfaro 14

Manejo de MemoriaModelo:

Unidad dePaginación

Unidad dePaginación

Dirección LógicaSegmento:Desplazamiento

Dirección Lineal

Unidad de Segmentación

Dirección Física

Unidad de Segmentación

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 2

Autor: Alejandro Furfaro 15

Manejo de Memoria: Tabla GDTUna GDT por cada procesador presente en el sistemaSe almacenan en un array denominado cpu_gdt_tablecpu_gdt_table

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 2

Autor: Alejandro Furfaro 16

Manejo de Memoria: Tabla GDTSegmentación: Basada en Modelo FLAT

Segment Base G Limit S Type DPL D/B Puser code 0x00000000 1 0xfffff 1 10 3 1 1user data 0x00000000 1 0xfffff 1 2 3 1 1kernel code 0x00000000 1 0xfffff 1 10 0 1 1kernel data 0x00000000 1 0xfffff 1 2 0 1 1

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 2

Autor: Alejandro Furfaro 17

Manejo de Memoria: Tabla GDT

Una TSS por cada Procesador.– No usa Task Switch a través del procesador. (lo realiza mediante código)– Porque mantiene entonces una TSS?

• El procesador busca aquí, el stack de PL=00 cuando sube el privilegio de una tarea (ya sea por llamada al kernel, o por entrada de una interrupción

• Si 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.

LDT Descriptor: uno genérico compartido por todos los procesos. El kernel de Linux no usa LDT.

– Contiene un descriptor Null.– Dirección base: Se almacena en la default_ldtdefault_ldt.– Límite 7– Los procesos pueden crear su propia LDT mediante una función especial del

kernel: modify_ldtmodify_ldt( )( ).

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 2

Autor: Alejandro Furfaro 18

Manejo de Memoria : Tabla GDT

init_tssCPU#0_TSSDCPU#1_TSSD

CPU#n_TSSD

Descriptores de Sistema2 Task State Segment Descriptor (TSSD): Uno por cada procesador.

i Limite =0xEB (236 bytes).i S = 1, DPL = 00, Tipo = 9 u 11 (TSS de 32 bits Available / Busy )i Dirección Basei Se apilan las TSS de cada CPU en un array llamado init_tssinit_tss

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 2

Autor: Alejandro Furfaro 19

Manejo de Memoria: Tabla GDT

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 PnPde Linux invoque funciones del BIOS para detectar recursos utilizados por dispositivos PnP, usará estos segmentos de código y datos.

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 2

Autor: Alejandro Furfaro 20

Manejo de Memoria: Tabla GDT

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

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 2

Autor: Alejandro Furfaro 21

Manejo de MemoriaPaginación:2 Paginas de tamaño fijo (4 u 8 KB)2 Definiciones:

i PAGE = Es el rango de direcciones lineales mapeados dentro de esa página, junto con los datos contendidos por dichas direcciones (En la jerga, “data chunk”)

i Page Frame = Es el área de memoria que contiene una página, por eso también se la puede encontrar bajo el nombre de phisical page, o pagecontainer

2 Paging Unit:i Convierte direcciones lineales en físicas

2 Extended Paging:i A partir del pentium se tiene la posibilidad de definir páginas de 4Mbytes.

2 Three-level pagingi Procesadores de 64 bit.

Ubicación de las páginas del Kernel

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 2

Autor: Alejandro Furfaro 22

Paginación (Hardware)Paginación

Paginación Extendida

Paginación en tres niveles

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 2

Autor: Alejandro Furfaro 23

Paginación en LINUX

Se eliminan para Paginación de 32 bits Sin Extensión de Direcciones Físicas

Se elimina para Paginación de 32 bits

Con Extensión de Direcciones Físicas

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 2

Autor: Alejandro Furfaro 24

Paginación en LINUXRef: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 2

Autor: Alejandro Furfaro 25

Modelo Kernel/ProcesoProceso: Instancia de un programa en ejecución, o un contexto de ejecución. Ej: Si 24 usuarios ejecutan el programa octave, hay 26 procesos diferentes ejecutando una instancia c/u del programa octave.2 Tienen un espacio de direccionamiento determinado (direcciones de

memoria que tienen permitidas para acceder)2 Normalmente trabajan en modo User. 2 Cuando requieren servicios del kernel, switchean a modo kernel. Una

vez resuelto el requerimiento regresan a modo UserEl kernel hace que cada proceso “vea” una CPU dedicada. Es un administrador de procesosExiste un grupo de programas privilegiado llamados “kernel threads”.2 Corren en modo Kernel en el espacio de direccionamiento del kernel.2 No interactúan con el usuario. 2 No tienen terminal asociada.2 Se crean durante el startup y mueren en el shutdown

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 3

Autor: Alejandro Furfaro 26

ProcesosEl kernel debe saber para cada proceso:2 Su prioridad2 Su estado 2 Su espacio de direcciones asignado2 Sus archivos abiertos y en acceso

Descriptores de Proceso, estructura task_structtask_struct, 2 EL kernel define un tipo de dato alias de strucstruc task_structask_struc: task_ttask_t2 El kernel define un array de estas estructuras denominado

task_arraytask_array.Parentezco entre procesos.2 Los procesos crean copias de si mismos -> childs2 Los childs comparten el espacio de memoria de los padres2 Pero conservan independencia en los datos. Si un proceso modifica

un dato el resto conserva su propia copia y no ve la modificación

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 3

Autor: Alejandro Furfaro 27

ThreadsEl approach 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”2 Hilos de procesamiento paralelo que compartan no solo el código

sino las áreas de datos2 Son procesos mas livianos2 Se llaman Threads (hilos)2 POSIX Threads, los estandariza 2 pthread es la librería que contiene su implementación para generar

aplicaciones Multithreading.

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 3

Autor: Alejandro Furfaro 28

Threads: implementaciones en LinuxHasta el kernel 2.4 inclusive la implementación no era 100% Posix compatible2 El scheduler no trata a los threads de manera independiente.2 Si un thread hace una llamada bloqueante el resto queda también

bloqueado.2 Se los trata como un proceso único

A partir de la implementación 2.6, se lleva el modelo al standard.2 Se manejan desde el scheduler como procesos independientes2 Se conserva su agrupación como un único proceso a efectos de las

sistema call globales, como por ejemplo getpidgetpid ()(), exitexit ()(), killkill ()(), entre otras.

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

2 El scheduler del kernel 2.6 maneja threads…. (como el scheduler de las versiones mas serias de windows: NT, 2000, 2003 server, XP o Vista).

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 3

Autor: Alejandro Furfaro 29

Descriptor de ProcesosTiene por objeto darle al Sistema Operativo la visión clara de que está haciendo el proceso.Definido en la declaración de task_structtask_structDada la complejidad de esta estructura algunos campos son referencias a otras estructuras como la tabla de file descriptors del proceso

VER

statethreadsusageflags

run_list

tasks

mm

real parent parent

tty

thread

fsfiles

signalspending

tty_structLista de tty’s asociadas al proceso

fs_structDirectorio actual

files_struct

mm_struct

signal_structSeñales recibidas

Punteros a file descriptors

Punteros a descriptores de áreas de memoria

thread_infoInformación de bajo nivel del proceso

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 3

Autor: Alejandro Furfaro 30

Identificando un procesoCada proceso tiene un Descriptor que lo define unívocamente.Los punteros a los descriptores de proceso sirven para identificarlos. Son Números de 32 bitsLos UNIX identifican a los procesos con un ProcessID (PID). Los PID van desde 0 a 32767.El Process ID corresponde al campo pidpid en el descriptor de proceso (task_structtask_struct)

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 3

Autor: Alejandro Furfaro 31

Estados de un procesoCorresponden al campo statestate de task_structtask_struct. Valores posibles (flags)2 TASK_RUNNING2 TASK_INTERRUPTIBLE2 TASK_UNINTERRUPTIBLE2 TASK_STOPPED2 TASK_TRACED2 EXIT_ZOMBIE2 EXIT_DEAD

#define TASK_RUNNING 0#define TASK_INTERRUPTIBLE 1#define TASK_UNINTERRUPTIBLE2#define TASK_STOPPED 4#define TASK_TRACED 8#define EXIT_ZOMBIE 16#define EXIT_DEAD 32

Para setear el estado de un proceso el kernel usa las macros set_task_stateset_task_state o set_current_stateset_current_state

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 3

Autor: Alejandro Furfaro 32

Descriptor de Proceso: Alocando memoria

En Kernel Mode, los procesos usan un Stack del Segmento de Datos del Kernel.El uso de este stack no es intensivo.Los stacks expanden hacia la base de memoria (el registro espespse decrementa)Conociendo el registro espesp se deduce el comienzo del Descriptor de Proceso

union thread_union {

struct thread_info thread_info;

unsigned long stack[2048]; /* 1024 for 4KB stacks */

};

struct thread_info {

struct task_struct *task; /* main task structure */

struct exec_domain *exec_domain; /* execution domain */

unsigned long flags; /* low level flags */

unsigned long status; /* thread-synchronous flags */

__u32 cpu; /* current CPU */

__s32 preempt_count; /* 0 => preemptable, <0 => BUG */

mm_segment_t addr_limit; /* thread address space:

0-0xBFFFFFFF for user-thead

0-0xFFFFFFFF for kernel-thread*/

struct restart_block restart_block;

unsigned long previous_esp; /* ESP of the previous stack in case

of nested (IRQ) stacks*/

__u8 supervisor_stack[0];

};

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 3

Autor: Alejandro Furfaro 33

Kernel 2.6 Función current_thread_infocurrent_thread_info( )( )

Sirve para que el kernel pueda ubicar el inicio del Descriptor de Proceso.El código es:movl $0xffffe000,%ecx /* or 0xfffff000 for 4KB stacks */andl %esp,%ecxmovl %ecx,p

A la salida de este código, p contiene un puntero a strucstructhread_infothread_info. El primer miembro de thread_infothread_info es el puntero a task_structtask_struct.Por compatibilidad con los kernels anteriores se dispone del alias a la macro currentcurrent

currentcurrent-->>pidpid es una sentencia de lo mas común en todo el código del kernel para obtener el pid del proceso, por ejemplo.

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 3

Autor: Alejandro Furfaro 34

Lista de ProcesosTaskTask array: Lista doblemente enlazada de punteros a estructuras de tipo task_structtask_structLa cantidad de elementos se guarda en la variable N_TASKSN_TASKS..

El campo pidpid es el índice de este arreglo.

Los campos prev_taskprev_task y nextnext tasktask de los descriptores de procesos (task_structtask_struct), vinculan los elementos.

El primer elemento es el puntero a task_structtask_struct del proceso 0 llamado swapper.

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 3

Autor: Alejandro Furfaro 35

Implementación de listas doblemente enlazadas en Linux

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-[version]/includes/linux/list.h

nextprev

nextprev

nextprev

List_head List_head List_head

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 3

Autor: Alejandro Furfaro 36

Lista de procesos en ejecuciónPor una cuestión de eficiencia,el kernel mantiene una lista separada de los procesos que están en estado TASK_RUNNING.

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

Hasta la versión 2.4 el kernel manejaba una lista llamada runqueuerunqueue.2 El campo run_listrun_list de task_structtask_struct es una estructura de tipo list_headlist_head para

implementar esta lista.

2 La lista está encabezada por init_taskinit_task (descriptor de proceso del proceso 0 o swapper )

2 Funciones:•• add_to_runqueueadd_to_runqueue()(): Inserta un proceso a la lista

•• del_from_runqueuedel_from_runqueue()(): remueve un proceso de la lista

•• move_first_runqueuemove_first_runqueue()(): Mueve un proceso al principio de la lista

•• move_last_runqueuemove_last_runqueue()(): Mueve un proceso al final de la lista

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 3

Autor: Alejandro Furfaro 37

Lista de procesos en ejecuciónA partir del kernel 2.6, se implementa con algunas diferencias.No hay mas una única lista, sino una lista por cada valor de piroridad. (runqueuerunqueue ahora es una lista de listas!!!)Existen 140 valores de prioridad almacenables en una variable k k (0 a 139).=> hasta 140 listas de procesosEl campo run_listrun_list de task_structtask_struct apunta a una estructura de tipo list_headlist_head que ahora corresponde a la lista de procesos con prioridad kk.En el process descriptor se incluye una estructura llamada prio_array_tprio_array_t.

enqueue_task(p,arrayenqueue_task(p,array)) encola un proceso p en una lista de ejecuciónlist_add_tail(&p->run_list, &array->queue[p->prio]);__set_bit(p->prio, array->bitmap);array->nr_active++;p->array = array;

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 3

Autor: Alejandro Furfaro 38

Parentesco entre procesosEn el descriptor de procesos se dispone de los siguientes campos para reflejar parentesco

2p_opptr (original parent)2p_pptr (parent)2p_cptr (child)2p_ysptr (younger sibling)2p_osptr (older sibling)

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 3

Autor: Alejandro Furfaro 39

Creación de procesosCada vez que un proceso crea a otro, le transfiere sus recursos. Pero no se duplicanCopy on write. El kernel detecta el acceso a modificar un recurso por parte del child y en ese momento le crea una copia propia.Lightweight processes. Comparten las estructuras del kernel, y el espacio de direcciones en modo User.

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 3

Autor: Alejandro Furfaro 40

Creación de procesos: fork ()Se emplean diferentes system calls para crear procesos. La mas utilizada es forkfork ()().

If (!fork())

Retorna PID del proceso creado Retorna 0

Proceso hijo

Proceso principal

FALSE

Internal defork fork (kernel)

TRUE

Descriptor Proceso principalDescriptor Proceso child

Proceso en memoria física

Una misma copia física del código y los datos en memoria apuntada por dos descriptores de proceso diferentes. Una copia = Dos procesos. (Lightweight Process)

Una misma copia física del código y los datos en memoria apuntada por dos descriptores de proceso diferentes. Una copia = Dos procesos. (Lightweight Process)

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 3

Autor: Alejandro Furfaro 41

Creación de procesos: vfork ()La system call vforkvfork ()() crea un child que hace suyo el espacio completo de direcciones del proceso padre, obligándolo a esperar su finalización si requiere acceso a un objeto compartido.El espacio de direcciones del proceso se compone de:2El código ejecutable del programa2El área de datos inicializados del programa.2El área de datos no inicilizados el programa2El stack inicial del programa (el stack de Modo User)2El código ejecutable y los datos de las librerías

compartidas necesarias.2El heap (la memoria que el programa puede requerir

dinámicamente)

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 3

Autor: Alejandro Furfaro 42

clone ()System Call propia e LINUX. __clone (fn, arg, flags, child_stack)2 fn es la función que ejecutará el proceso child, que finaliza cuando

dicha función ejecute return ().2 arg: Puntero a la lista de argumentos de fn.2 flags: cuatro bytes. El menos significativo es el número de la señal

que el child va a enviar al padre cuando termine (default SIGCHILD). Los otros codifican flags:i CLONE_VM: comparte descriptores de memoria y las tablas de páginasi CLONE_FS: comparte la tabla que identifica el file systemi CLONE_FILES: comparte la tabla de descriptores de archivos abiertosi CLONE_SIGHAND: comparte la tabla de handlers de señali CLONE_PID: comparte el PID (solo si el parent tiene PID 0 y en entorno

uniprocesadori CLONE_PTRACE: si el parent es traceado por ptrace(), el hijo tambiéni CLONE_VFORK:

2 child_stack: indica el stack de Modo USER que se le asignará al esp del child. Si es 0, corresponde al stack de modo USER del parent

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 3

Autor: Alejandro Furfaro 43

Conmutación de ProcesosEs responsbilidad del scheduler (sched.c) e incluye:2 Contexto de Hardware

iDirectorio de Páginas para cambiar al nuevo espacio de direcciones

iStacks de Modo User y Modo Kernel, mas los Registros propios de los procesadores IA-32

2 Registros adicionales que al igual que en el procesador de Intel hay que manejarlo en forma manual, por falta de soporte en el hardwareiRegistros de FPU

iRegistros de control y Debug

2 Utiliza una macro llamada switch_toswitch_to()() en lugar de utilizar un jump faral descriptor del TSShvoid switch_to (struct task_struct *prev, struct task_struct *next,

struct task_struct *last)

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 3

Autor: Alejandro Furfaro 44

Conmutación de ProcesosLinux mantiene una única TSS por cada CPU.A pesar del no uso de jmp far para conmutar no puede prescindir de al menos un TSS por cada CPU:2Al aumentar el nivel de privilegio de una tarea, el

procesador busca en el TSS actual (cuyo selector contiene el registro TR), los valores de SS y ESP correspondientes al mayor nivel de privilegio.

2Para acceder a E/S desde una tarea cuyo CPL no sea el adecuado debe consultarse el IO BitMap del TSS de la tarea.

Al usar un único TSS por CPU, Linux debe actualizarle determinados campos en cada processswitch.

VER

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 3

Autor: Alejandro Furfaro 45

Conmutación de ProcesosEl contexto de hardware de cada tarea se almacena en una estructura del tipo threadthread en task_structtask_struct. Esta estructura puede ser tomada por cualquier CPU presente en el sistema ya que contiene los datos que de guardarse en la TSS no podrían ser tomados por otra CPU.Observaciones:2Solo guarda los registros de propósito general

estrictamente necesarios. El resto va al stack2Se almacena el estado de la FPU, y debug registers que

no son contemplados en el TSS2Se mantiene el bitmap de E/S del proceso en esta

estructura

VER

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 3

Autor: Alejandro Furfaro 46

macro switch_to()Se invoca desde schedule () mediante la linea

switch_toswitch_to ((prevprev, , nextnext, , prevprev))

Se ejecuta el siguiente códigomovl prev,%eax ;eax = prevmovl next,%edx ;edx = nextmovl %eax,%ebx ;ebx = eax = prev

pushfl ;guarda flags y ebp en el stack de nodo kernelpush %ebp ;de prev;la estructura thread ocupa el offset 484 dentro de task_structmovl %esp, 484(%eax) ; salva esp en prev->thread.espmovl 484(%edx), %esp ; carga esp con next->thread.espmovl $1f, 480(%eax) ; guarda ret addr en prev->thread.eippushl 480(%edx) ; guarda ret addr en next->thread.eipjmp __switch_to

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 3

Autor: Alejandro Furfaro 47

rutina __ switch_toEjecuta los siguientes pasos:2 unlazy_fpu (prev_p) ;Si cambiaron, salva los registros de la FP,

MMX, y XMM.2 smp_processor_id ( ) ; macro que devuelve el índice a la CPU que

está ejecutando el proceso (lo saca de thread_info->cpu).2 Init_tss [cpu ].esp0 = next_p ->thread.esp0 ; carga stack de nivel 0 de

netx_p en la única TSS de la cpu..….. mmmm.2 Carga en la GDT los segmentos TLS de netx.

cpu_gdt_table[cpu][6] = next_p->thread.tls_array[0];cpu_gdt_table[cpu][7] = next_p->thread.tls_array[1];cpu_gdt_table[cpu][8] = next_p->thread.tls_array[2];

2 Salva en la estructura thread los registros FS y GS (El kernel no los usa, pero las aplicaciones pueden usarlos.

movl %fs, 40 (%esi) ;esi apunta a prev_p -> threadmovl %gs,44 (%esi)

2 Carga los nuevos valores de fs y gs.movl 40 (%ebx),%fs ;ebx apunta a next_p -> threadmovl 44 (%ebx),%gs

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 3

Autor: Alejandro Furfaro 48

rutina __ switch_to2 Salva los valores de los debug registers, si el proceso al que se va a

activar los había utilizado.if (next_p->thread.debugreg[7]){loaddebug(&next_p->thread, 0);loaddebug(&next_p->thread, 1);loaddebug(&next_p->thread, 2);loaddebug(&next_p->thread, 3);/* no 4 and 5 */loaddebug(&next_p->thread, 6);loaddebug(&next_p->thread, 7);}

2 Salva en el TSS de la CPU que corresponda los valores de IO BitMap, si prev_p o next_p tienen permisos customizados de E/S.

if (prev_p->thread.io_bitmap_ptr || next_p->thread.io_bitmap_ptr)handle_io_bitmap(&next_p->thread, &init_tss[cpu]);

2 Retornareturn prev_p;// El compilador genera este código:// movl %edi,%eax// ret

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 3

Autor: Alejandro Furfaro 49

Scheduling de procesosLinux divide el tiempo de la CPU en períodos (o épocas)En cada época de la CPU, Linux (como UNIX) asigna un quantum de tiempo a cada proceso -> time slicingLa duración del quantum es critica en la performanceTres clases de procesos2 Interactivos

i Interfacean con el usuario.i Requieren tiempo de respuesta aceptablemente rápido, para que el usuario no perciba bajo rendimiento ni

comportamientos erráticos (Ej: editores de texto, procesadores de texto, planillas de cálculo2 Batch

i No interactúan con el usuarioi Pueden aceptar mayores tiempos de respuesta (Ej: compiladores, renderizadores de imágenes)

2 Real Timei No pueden ser detenidos ni demorados.i Son procesos que deben garantizar tiempo de respuesta mínimo (Ej: reproductores multimedia)

2 La versión 2.6 del kernel emplea algoritmos heurísticos que analizan el comportamiento pasado de un proceso para distinguirlo entre batch o interactivo y schedularlo en consecuencia. Las anteriores versiones no disponían de mecanismos eficientes para tratar de manera diferenciada a estos tipos de procesos

El criterio de manejo de prioridades es dinámico. Si un proceso lleva mucho tiempo resignando CPU el scheduler le aumenta l aprioridad y si un proceso lleva mas tiempo en uso de la CPU se la disminuye

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 7

Autor: Alejandro Furfaro 50

System Calls asociadasTemporizacion2 time ()2 ctime ()2 ftime ()2 gettimeofday ()

Manejo de scheduler2 nice( ) Cambia la prioridad estática de un proceso convencional2 getpriority( ) Obtiene el máximo valor de prioridad estática para un grupo o proceso convencional2 setpriority( ) Ajusta la prioridad estática para un grupo o proceso convencional2 sched_getscheduler( ) Obtiene la política de scheduling de un proceso2 sched_setscheduler( ) Establece la política de scheduling y prioridad real time para un proceso2 sched_getparam( ) Obtiene la prioridad real time de un proceso2 sched_setparam( ) Establece la prioridad real time de un proceso2 sched_yield( ) Libera al procesador voluntariamente sin bloquearlo2 sched_get_ priority_min( ) Obtiene el mínimo valor de prioridad real-time para una política2 sched_get_ priority_max( ) Obtiene el máximo valor de prioridad real-time para una política2 sched_rr_get_interval( ) Obtiene el valor del time quantum para la política Round Robin2 sched_setaffinity( ) Establece la máscara de afinidad con CPU affinity de un proceso2 sched_getaffinity( ) Obtiene la máscara de afinidad con CPU affinity de un proceso

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 7

Autor: Alejandro Furfaro 51

Process PreemptionPreempción. Se maneja con el campo need_reschedneed_resched del descriptor de procesoLos procesos en Linux son interrumpibles (preempted).Cuando un proceso entra en estado TASK_RUNNING, el kernel chequea su prioridad y la compara con el corriente. Si su prioridad es mayor el proceso actual es interrumpido (preempted).Cuando a causa de una interrupción se debe despertar a un proceso (ponerlo TASK_RUNNING) y su prioridad es mayor que la del proceso en curso, el kernel setea el bit currentcurrent-->>need_reschedneed_resched y por lo tanto antes de salir de la interrupción se llamará a la función scheduleschedule ( )( ) para pasar al nuevo proceso.

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 7

Autor: Alejandro Furfaro 52

Políticas de Scheduling

Linux programa al timer tick de modo de generar una interrupción (tick) cada 1mseg.Un proceso tiene asignado un tiempo de ejecución llamado quantum, que es múltiplo entero de un tick.El valor mínimo y máximo de un quantum se establecen respetando:2 Efectividad en el tiempo asignado a la ejecución efectiva del proceso

respecto del tiempo que insume conmutar entre dos contextos (Restricción para el mínimo valor)

2 Interactividad de los programas de aplicación. Un quantum excesivamente largo puede provocar que el usuario note “pesados” a los restantes procesos.

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 7

Autor: Alejandro Furfaro 53

El algoritmo de SchedulingHasta el kernel 2.4.2 El problema era determinar el “mejor” proceso para lanzar a ejecución.

2 Dependía de la cantidad de procesos en ejecución

Desde el kernel 2.62 Cada CPU tiene su propia lista de procesos ejecutables

2 Los procesos se seleccionan para ejecución en tiempo constante, independientemente de la cantidad de procesos ejecutables.

2 Se cuidan las diferencias entre procesos interactivos y batch.

2 Siempre hay un proceso para ejecutar. Al menos swapper (pid=0). Hay uno por cada CPU presente en el sistema.

2 Tres clases de scheduling (campo policy de task_struct):iSCHED_FIFO (First In First Out Real Time)

Cuando el scheduler le asigna la CPU deja el descriptor de proceso en el mismo lugar de la cola de ejecución. El proceso continúa ejecutando todo el tiempo necesario a menos que se ponga TASK_RUNNING otro proceso de mayor prioridad. Si en la misma prioridad hay otros procesos real time schedulados no es interrumpido el actual

iSCHED_RR

Es como el anterior pero el proceso es puesto al final de la cola de ejecución de modo de asegurar un tratamiento uniforme a los que tienen la misma prioridad.

iSCHED_NORMAL

Es el algoritmo normal (idéntico tratamiento que el kernel 2.4)

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 7

Autor: Alejandro Furfaro 54

Scheduling: procesos convencionalesCada proceso tiene una prioridad estática (campo static_prio de task_struct) entre 100 (mas alta) y 139 (mas baja).

Base de tiempo de quantum: Se calcula dinámicamente de la siguiente manera:If (p-> static_prio < 120)

quantum = p->static_prio * 20

else

quantum = p->static_prio * 5

Cuando se crea un proceso, éste hereda la prioridad de su padre

El usuario puede modificarla con nice (), o setpriority ().

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 7

Autor: Alejandro Furfaro 55

Scheduling: Procesos Real Time Los procesos real time tienen una prioridad de entre 1 y 99 (mayor y menor)Cada vez que expira su quantum el scheduler lo reescribe y sigue ejecutando Solo es reemplazado por otra tarea si:2 Es interrumpido por otra tarea de mayor prioridad.2 Realiza una operación bloqueante (pasa a TASK INTERRUMPIBLE

o TASK_UNINTERRUMPIBLE)2 Es detenido o traceado (TASK_STOPPED o TASK TRACED)2 Es terminado (EXIT_ZOMBIE, o EXIT_DEAD)2 El proceso es Round Robin Real Time (SCHED_RR), y expira su

quantum

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 7

Autor: Alejandro Furfaro 56

Scheduler: Campos de task_structRef: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 7

Autor: Alejandro Furfaro 57

System CallsAPI: Formato de una llamada para obtener un servicio del kernelSystem Call: requerimiento explícito cursado al kernel para resolver un servicio.La inversa no es necesariamente cierta. Algunas API resuelven directamente en Modo Usuario si pasar a Modo kernel para resolver el pedido (math.lib por ejemplo).

AplicaciónInvoca

System Call

func () ¨{…int 80h…

}

…func ()…

Accede a una rutinaWrapper en la

librería standard libc

Modo Usuario

sys_func () {………

}

system call:…sys_func ()…

ret_from_sys_call:…iret

Modo Kernel

handler de System Call del kernel

rutina de servicio de laSystem Call

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 10

Autor: Alejandro Furfaro 58

IPC (Inter Process Communication)

Como hacer que dos o más procesos intercambien informaciónSincronización de Objetos.Primero debemos contar con más de un proceso (fork y exec).Warning: fork () -> poder, poder asociado gralmentea destruccion!!!Cuidado con esto!

Autor: Alejandro Furfaro 59

IPC, SeñalesLos procesos pueden señalizarse entre sí o puede hacerlo el kernel.Algunas señales no pueden ignorarse, otras pueden interceptarse y cambiar el handler.Conjunto de SIG signals (kill –l)Comando kill – permite enviar señales desde el prompt

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 11

Autor: Alejandro Furfaro 60

IPC, Señales# Nombre Acción Default Descripción POSIX1 SIGHUP Terminate Se desconectó la terminal asociada al procesoSi2 SIGINT Terminate Interrupción desde el teclado Si3 SIGQUIT Dump Quit desde el teclado (CTRL+C) Si4 SIGILL Dump Instrucción ilegal Si5 SIGTRAP Dump Breakpoint para debugging No6 SIGABRT Dump Abnormal termination Si6 SIGIOT Dump Equivalente a SIGABRT No7 SIGBUS Dump Bus error No8 SIGFPE Dump Floating-point exception Si9 SIGKILL Terminate Fuerza la terminación del proceso Si10 SIGUSR1 Terminate Dispponible para el proceso Si11 SIGSEGV Dump Referencia inválida a memoria Si12 SIGUSR2 Terminate Dispponible para el proceso Si13 SIGPIPE Terminate Escritura en un pipe sin procesos lectores Si14 SIGALRM Terminate Real-timer clock Si15 SIGTERM Terminate Terminación de un Proceso Si16 SIGSTKFLT Terminate Coprocessor stack error No

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 11

Autor: Alejandro Furfaro 61

IPC, Señales

17 SIGCHLD Ignore Proceso Child STOPPED o terminó Si18 SIGCONT Continue Reasume la ejecución, si estaba STOPPED Si19 SIGSTOP Stop Detiene al proceso (lo pone STOPPED) Si20 SIGTSTP Stop Idem SIGSTOP enviada por la tty (CTRL+Z) Si21 SIGTTIN Stop Proceso Background requiere entrada Si22 SIGTTOU Stop Proceso Background requiere salida Si23 SIGURG Ignore Condición Urgent en un socket No24 SIGXCPU Dump Límite de tiempo de CPU excedido No25 SIGXFSZ Dump Tamaño límite de Archivo excedido No26 SIGVTALRM Terminate Virtual timer clock No27 SIGPROF Terminate Profile timer clock No28 SIGWINCH Ignore Window resizing No29 SIGIO Terminate Ahora es posible una Operación de I/O No29 SIGPOLL Terminate Equivalente a SIGIO No30 SIGPWR Terminate Power supply failure No31 SIGSYS Dump system call errónea No31 SIGUNUSED Dump Equivalente a SIGSYS No

# Nombre Acción Default Descripción POSIX

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 11

Autor: Alejandro Furfaro 62

Campos de task_struct relacionados con las señales

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 11

Autor: Alejandro Furfaro 63

Campos de task_struct relacionados con las señales

typedef struct {unsigned long sig[2];} sigset_t;

struct sigpending {struct list_head list;sigset_t signal;}

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 11

Autor: Alejandro Furfaro 64

Control de las señales

Head

Signal

HeadHeadNext

InfoTailHeadHeadNext

Info

HeadHeadNext

Info

struct sigqueuestruct sigpending

pending

signal

struct task_struct

count

shared_pending

struct signal_struct

struct sigactionsa_handler

sa_masksa_flags

Descriptor de Proceso

Descriptor de Señal

Ref: Understanding the Linux Kernel 3erd. Ed. D. Bovet. Cap 11

sighand

count

action

siglock

struct sighand_struct

Descriptor de Handlers de Señal

struct sigqueue struct sigqueue

Head

Signal

HeadHeadNext

InfoTailHeadHeadNext

Info

struct sigqueuestruct sigpending struct sigqueue

Autor: Alejandro Furfaro 65

IPC, mecanismosAlgunas operaciones con IPC:2Pipes2Named Pipes (FIFO’s)2System V IPC's

iMessage queuesiSemaphoresiShared Memory

2Sockets

Autor: Alejandro Furfaro 66

PipesSe crean mediante la system call pipe ()Como resultado se obtienen dos file descriptors (o mejor dicho un array de dos file descriptors

PIPEPIPEfd[0] fd[1]

read () write ()

Lector Escritor

Ref: Interprocess Comunications in UNIX: The Nooks & Crannies. John Shapley Gray. Cap 5Beej Interprocess Comunications.

Autor: Alejandro Furfaro 67

PipesFile descriptors predeterminados2 0 stdin ; Standard input (teclado)2 1 stdout ; Standard Output (pantalla)2 2 stderr ; Standard Error (generalmente pantalla)

Uso en el shell, operador |2 Conecta la salida de un comando con la entrada del siguiente.2 En otras palabras aplica una redirección del stdout del proceso

pasado como primer parámetro, al stdin del siguiente2 Ej:

i En el home directory de un usuario ejecutar cat .bash_profile, y analizar la salida por pantalla

i Seguidamente ejecutar cat .bash_profile | grep PATH, y analizar la salida por pantalla

Ref: Interprocess Comunications in UNIX: The Nooks & Crannies. John Shapley Gray. Cap 5Beej Interprocess Comunications.

Autor: Alejandro Furfaro 68

Pipes

Autor: Alejandro Furfaro 69

Pipes

Autor: Alejandro Furfaro 70

Como se logra?Ejemplo de concatenación de los comandos ls –ls y wc -l#include <stdio.h>#include <stdlib.h>#include <unistd.h>int main(){

int pfds[2];pipe(pfds);if (!fork()) {

close(1); /* cerramos stdout del proceso*/dup(pfds[1]); /* duplicamos stdout como pfds[1] */close(pfds[0]); /* no lo necesitamos en este proceso */execlp("ls", "ls", NULL);

} else {close(0); /* cerramos stdin del proceso*/dup(pfds[0]); /* duplicamos stdin como pfds[0] */close(pfds[1]); /* no lo necesitamos en este proceso */execlp("wc", "wc", "-l", NULL);

}}

Ref: Beej Interprocess Comunications.

Autor: Alejandro Furfaro 71

Otra opción…Ejemplo de concatenación de los comandos ls –ls y wc -l#include <stdio.h>#include <stdlib.h>#include <unistd.h>int main(){

int pfds[2];pipe(pfds);if (!fork()) {

dup2(pfds[1],fileno(stdout));/* duplicamos stdout como pfds[1] */close(pfds[0]); /* no lo necesitamos en este proceso */execlp("ls", "ls", NULL);

} else {dup2(pfds[0],fileno(stdin)); /* duplicamos stdin como pfds[0] */close(pfds[1]); /* no lo necesitamos en este proceso */execlp("wc", "wc", "-l", NULL);

}}

Ref: Interprocess Comunications in UNIX: The Nooks & Crannies. John Shapley Gray. Cap 5Beej Interprocess Comunications.

Autor: Alejandro Furfaro 72

Que pasó?

stdinstdoutstderr

stream N° Estado

fd[0]fd[1]

01234

Parent (ls)

stdinstdoutstderr

stream N° Estado

fd[0]fd[1]

01234 X

Child (wc -l)

X

ls -ls | wc -l

PIPEPIPE fd[0]fd[1]

wc -llsstdin stdout

Ref: Interprocess Comunications in UNIX: The Nooks & Crannies. John Shapley Gray. Cap 5Beej Interprocess Comunications.

Autor: Alejandro Furfaro 73

Named FIFO’sLos pipes tienen limitaciones2 Solo pueden utilizarse entre procesos padres e hijos2 Solo se pueden crear desde la instancia padre de los procesos involucrados2 Se identifican mediante un par de file descriptors.

Desde el shell podemos crear un Nodo del tipo named pipe o namedFIFO, mediante la siguiente línea:mknod –m 660 myfifo pLos permisos se setean igual que en chmodEn este caso es lectura y escritura para el dueño y su grupo

Desde un programa con la system call homónimamknod("myfifo", S_IFIFO | 0660 , 0);Primer argumento: path del nodo, Tipo OR permisos (en sys/stat.h están las macros que definen los tipos)El tercer argumento lo utilizamos para crear dispositivos, no en FIFO’s.

El resto del tratamiento es mediante un solo file descriptor que se obtiene mediante la system call open ();Vamos a ejecutar la pareja speak y tick para comprobarlo …

Ref: Interprocess Comunications in UNIX: The Nooks & Crannies. John Shapley Gray. Cap 5Beej Interprocess Comunications.

Autor: Alejandro Furfaro 74

System V IPC’sEn los 70’s se incluyeron estos IPC’s para darle mayor flexibilidad de la que se tendría si se dejasen solo las señales y los pipes.Tres mecanismos2 Colas de Mensajes

i Estructuras de mensajes predefinidasi Se puede acceder en un esquema de múltiples lectores y escritores al

mismo recurso2 Memoria Compartida

i Área de memoria accesible asincrónicamente por dos o mas procesos.2 Semáforos

iMecanismo para la sincronización de acceso a recursos por parte de dos o mas procesos

1 El comando ipcs permite visualizar estos recursos desde el shell.

Ref: Interprocess Comunications in UNIX: The Nooks & Crannies. John Shapley Gray. Cap 6

Autor: Alejandro Furfaro 75

Sinopsis de System Calls

Para crear un IPC hay que obtener una clave para identificarlokey_t ftok ( char *pathname, char proj );pathname: nodo del file system accesible al procesoproj: típicamente un carácter

Como resultado de un algoritmo interno devuelve un long (64 bits) que se utilizará para identificar unívocamente al IPC

Ref: Interprocess Comunications in UNIX: The Nooks & Crannies. John Shapley Gray. Cap 6.

Autor: Alejandro Furfaro 76

Estructura de una message queuesRef: Interprocess Comunications in UNIX: The Nooks & Crannies. John Shapley Gray. Cap 6.

Permisos

struct msg_queue {struct kern_ipc_perm q_perm;time_t q_stime; /* last msgsnd time */time_t q_rtime; /* last msgrcv time */time_t q_ctime; /* last change time */unsigned long q_cbytes; /* current number of bytes on queue */unsigned long q_qnum; /* number of messages in queue */unsigned long q_qbytes; /* max number of bytes on queue */pid_t q_lspid; /* pid of last msgsnd */pid_t q_lrpid; /* last receive pid */struct list_head q_messages;struct list_head q_receivers;struct list_head q_senders;

};

struct msg { struct msg *msg_next; /* ptr to next message on q */ long msg_type; /* message type */ ushort msg_ts; /* message text size */ short msg_spot; /* address of text message */

};

q_stimeq_rtimeq_ctimeq_cbytesq_qnumq_qbytesq_lspidq_lrpid

q_messagesq_receiversq_senders

msg_nextmsg_typemsg_ts

msg_spot

msg_nextmsg_typemsg_ts

msg_spot

msg_nextmsg_typemsg_ts

msg_spot

list_head.nextlist_head.next

list_head.lastlist_head.last

Mensaje 1Mensaje 1 Mensaje 2Mensaje 2 Mensaje NMensaje N

Autor: Alejandro Furfaro 77

Messages Queues: System CallsCreación de la message queue

int msgget (key_t key,int msgflg);

Ej:key = ftok(".", proj);

if ((mid[i] = msgget ( key, IPC_CREAT | 0660)) == -1)

{

perror("Queue create");

return 1;

}

Control de una message queueint msgctl (int msqid, int cmd, struct msqid_ds *buf);

cmd: IPC_RMID para borrar la message queue, IPC_STAT, hace que msqidretorne los valores de la estructura de datos de la message queue, y IPC_SET hace que msqid_ds, se use para modificar parámetros de la estructura de datosde la message queue.Ej:

msgctl( mid, IPC_RMID, (struct msqid_ds *) 0 );

Ref: Interprocess Comunications in UNIX: The Nooks & Crannies. John Shapley Gray. Cap 6.

Autor: Alejandro Furfaro 78

Messages Queues: System CallsEscribiendo en la message queue

int msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, intmsgflg);

Lectura de una message queuessize_t msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg);

msgbuf es una estructura genéricastruct msgbuf {

long int mtype; /* type of received/sent message */char mtext[1]; /* text of the message */

};

msgtyp: define el modo de lectura. = 0. Retira el primer mensaje ingresado con cualquier valor en msgtyp> 0. Retira el primer mensaje con el mismo valor de msgtyp. Si se especificó MSG_EXCEPT, retira el primer mensaje de la lista con diferente msgtyp.< 0. Retira el primer mensaje del menor tipo cuyo valor absoluto sea menor o igual a msgtyp.

Ej: kirk y spok

Ref: Interprocess Comunications in UNIX: The Nooks & Crannies. John Shapley Gray. Cap 6.

Autor: Alejandro Furfaro 79

SemáforosConceptualmente se trata de una estructura de datos compartida por varios procesos.

Uso: Sincronizar el acceso desde múltiples procesos a un mismo recurso que no acepta accesos concurrentes.

Ref: Interprocess Comunications in UNIX: The Nooks & Crannies. John Shapley Gray. Cap 7.

Autor: Alejandro Furfaro 80

Pasos para usar un semáforo1. Crear un Juego de Semáforos1 Crear un semáforo implica generar una única instancia de una

estructura de datos que el sistema identificará en forma unívoca y utilizará para acceder al set de semáforos creado

1 La definición de esta estructura está en <bits/sem.h>, aunque incluyendo el archivo <sys.sem.h>, que referencia al primero es mas que suficiente.

1 La estructura en cuestión es semid_ds:struct semid_ds {

struct ipc_perm sem_prem;//puntero a la estructura de permisoskernel_time_t sem_otime;//hora de la última semopkernel_time_t sem_ctime;//hora del último cambiostruct sem *sem_base;//puntero al primer semáforo del arraystruct sem_queue *sem_pending;//operaciones pendientes de procesarstruct sem_queue *sem_pending_last;//última operación pendiente de

//procesarstruct sem_undo *undo; //requerimientos UNDO a este arrayunsigned short sem_nsems; //Cantidad de semáforos en el array.

}

Ref: Interprocess Comunications in UNIX: The Nooks & Crannies. John Shapley Gray. Cap 7.

Autor: Alejandro Furfaro 81

Pasos para usar un semáforoAlgunos de los campos de semid_ds se completan en el momento de ejecutar la función de creación del set de semáforos, y otros quedan para el momento de la operación de cada semáforo desde la aplicación.

Una estructura fundamental es semstruct sem {

int semval; // valor actual del semáforo.

int sempid; // pid del proceso que efectuó la última

// operación.

}

Ref: Interprocess Comunications in UNIX: The Nooks & Crannies. John Shapley Gray. Cap 7.

Autor: Alejandro Furfaro 82

Pasos para usar un semáforo2.Estructuras de datos para un set de semáforosAsí queda en memoria del sistema el set de semáforos y su estructura de control, luego de ejecutar la función de creación del set

Ref: Interprocess Comunications in UNIX: The Nooks & Crannies. John Shapley Gray. Cap 7.

Estructura de Permisos Hora del último semop Hora del último cambio

Array de semáforos Head de Req. PendientesTail de Req. Pendientes

Operaciones UNDO Nº de semáforos (n)

0 1 2 n

semval sempid

array de semáforos

Fig.1 Estructura de control de un set de semáforos.

Autor: Alejandro Furfaro 83

Pasos para usar un semáforo3.Creaciónint semget (key_t key, int nsems, int semflg);

key : se genera con ftocknsems: es el número de semáforos del set. Va al último

miembro de la estructura semid_dssemflg: contiene los permisos del set. (va a parar al primer

miembro de semid_ds)

Resultado: Se crea una estructura del tipo semid_ds como la ilustrada en la filmina previa, con los datos de permisos, y cantidad de semáforos pasados en la línea de argumentos.

Ref: Interprocess Comunications in UNIX: The Nooks & Crannies. John Shapley Gray. Cap 7.

Autor: Alejandro Furfaro 84

Pasos para usar un semáforo4. Operación del set: Se completan los campos restantes de semid_ds.int semctl (int semid, int semnum, int cmd, union senum arg);

semid: el valor devuelto por semget

semnum: Nº del semáforo sobre el que se va a efectuar la operación

cmd: operación o acción a efectuar sobre el semáforo.

arg: Unión con los valores asociados a la acción.union senum {

int val; // valor que se pasa si cmd = SETVAL

struct semid_ds; // buffer para envío o recepción de

// valores si cmd = IPC_STAT, o IPC_SET

unsigned short int *array //array de valores para cmd = GETALL

// o SETALL

struct seminfo *_buf // buffer para almacenar valores si

// cmd = IPC_INFO

}

Ref: Interprocess Comunications in UNIX: The Nooks & Crannies. John Shapley Gray. Cap 7.

Autor: Alejandro Furfaro 85

Pasos para usar un semáforo5. Operar sobre el semáforo.int semop (int semid, struct sembuf *sops, unsigned

nsops);

semid: el valor devuelto por semget ()sops: estructura tipo sembuf que se utiliza para operar sobre cada

semáforo del setstruct sembuf {

unsigned short int sem_num;// semaphore #: 0 = first

short int sem_op;// semaphore operation

short int sem_flg;// operation flags

};

nsops:

cantidad de operaciones a realizar sobre el semáforo (normalmente 1)

Ref: Interprocess Comunications in UNIX: The Nooks & Crannies. John Shapley Gray. Cap 7.

Autor: Alejandro Furfaro 86

Pasos para usar un semáforo5. En la siguiente figura vemos como se opera la función semop () sobre el set

de semáforos de la estructura sem cuyo puntero sale de semid_ds creada al crear el set de semáforos.

Ref: Interprocess Comunications in UNIX: The Nooks & Crannies. John Shapley Gray. Cap 7.

semval sempid

array de semáforos sem

0 1 2 N

1 -1

SEM_UNDO

N 2

SEM_NOWAIT

2 0

SEM_UNDO

sembuf

sem_numsem_opsem_flg

Estructura de Permisos Hora del último semop Hora del último cambio

Array de semáforos Head de Req. Pendientes Tail de Req. Pendientes

Operaciones UNDO Nº de semáforos (n)

Autor: Alejandro Furfaro 87

Pasos para usar un semáforoRef: Interprocess Comunications in UNIX: The Nooks & Crannies. John Shapley Gray. Cap 7.

Comportamiento de semop () cuando el valor pasado en sembuf.sem_op es Negativo

Condición sem_flg Acción ejecutada por semop () semval >= abs(semop) semval = semval.- abs(sem_op)

semval >= abs(semop) SEM_UNDO semval = semval.- abs(sem_op)

Actualiza el contador undo del semáforo. semval < abs(semop) Incrementa semncnt del semáforo y espera

(bloqueado) hasta que: 2 semval >= abs(semop), en cuyo caso

ajusta semncnt y semval = semval.- abs(sem_op)

2 remueve semid, retorna -1 y pone EIDRM en errno.

2 El proceso reciba una señal, en cuyo caso ajusta semncnt y pone EINTR en errno.

semval < abs(semop) IPC_NOWAIT Retorna -1 sin esperar y pone EAGAIN en errno.

Autor: Alejandro Furfaro 88

Pasos para usar un semáforoComportamiento de semop () cuando el valor pasado en sembuf.sem_op es Positivo

Condición sem_flg Acción ejecutada por semop () semval = semval + sem_op.

SEM_UNDO semval = semval + sem_op. Actualiza el contador UNDO del semáforo

Comportamiento de semop () cuando el valor pasado en sembuf.sem_op es Zero

Ref: Interprocess Comunications in UNIX: The Nooks & Crannies. John Shapley Gray. Cap 7.

Condición sem_flg Acción ejecutada por semop () semval == 0 Retorna de inmediato. semval != 0 IPC_NOWAIT Retorna de inmediato devolviendo -1 y pone EAGAIN en

errno. semval != 0 Incrementa semzcnt para el semáforo, y espera

(bloqueado) hasta que: 2 semval == 0, en cuyo caso ajusta semzcnt y retorna. 2 Se remueve semid, en cuyo caso retorna -1 y pone

EIDRM en errno. 2 El proceso reciba una señal, en cuyo caso ajusta

semzcnt y pone EINTR en errno.

Autor: Alejandro Furfaro 89

Pasos para usar un semáforoRef: Interprocess Comunications in UNIX: The Nooks & Crannies. John Shapley Gray. Cap 7.

6. Forma simplificada para sincronizar accesos....

Se arma un set de la cantidad de semáforos que se necesite.

Se los inicializa a todos en 1.

Se definen dos estructuras sembuf para dos operaciones: tomar el semáforo, y liberarlo.

static struct sembuf tomar_sem = {0,-1,SEM_UNDO},

liberar_sem = {0,1,SEM_UNDO};

Se llama a semop () pasándole en cada caso el argumento correspondiente

Autor: Alejandro Furfaro 90

Shared MemoryRef: Interprocess Comunications in UNIX: The Nooks & Crannies. John Shapley Gray. Cap 8.

Proceso 1

Proceso 2 Proceso 3

Autor: Alejandro Furfaro 91

Shared MemorySystem Callsint shmget (key_t key, int size,int shmflg);

int shmctl (int shmid, int cmd, struct shmid_ds *buf);

void *shmat (int shmid, const void *shmaddr, int shmflg);

int shmdt ( const void *shmaddr);

Ref: Interprocess Comunications in UNIX: The Nooks & Crannies. John Shapley Gray. Cap 8.

Autor: Alejandro Furfaro 92

Linux Devices Drivers

Guía para desarrollo de DeviceDrivers en Linux

Autor: Alejandro Furfaro 93

Introducción

Conceptos básicos

Autor: Alejandro Furfaro 94

“We're back to the times when men where men andwrote their own device drivers...”

Linus TorvaldsDevice Drivers

Basicamente es código que se ejecuta en modo Kernel.

Es la mediación entre los dispositivos hard y los procesos del sistema o de usuario.

Linux puede incluirlo:2 En el kernel monolítico (rapido y compacto)

2 Como modulos run time linkeables (flexible pero lento la primera vez que se usa).

Se tiende cada vez mas a estructura modular.

El driver se ocupa de resolver el mecanismo de acceso al hardware. No se concentra en la política de manejo de la información, aspecto que queda para el software de usuario

Autor: Alejandro Furfaro 95

Dispositivos en Linux Char DevicesBlock DevicesNetwork DevicesMiscellaneous (Bus) Devices

Autor: Alejandro Furfaro 96

Char DevicesSon los mas simplesSe acceden como un stream de bytes, tal como si fuesen nodos del File System. Ej.: TTY's (/dev/console). Serial ports (/dev/ttyS0)A diferencia de los archivos comunes, no nos podemos desplazar hacia atrás y hacia adelante. Accede a los datos en forma secuencial.Registran sus prestaciones a través de objetos del FS que responden a las funciones standard de acceso a archivos: 2 open ( ), 2 read ( ), 2 write ( ), 2 close ( ), etc.

Autor: Alejandro Furfaro 97

Block DevicesLos block devices agregan complejidad a su interfazAl igual que los char devices, se mapean como Nodos del File System en el directorio /dev.Registran sus prestaciones en un array del tipo blk_dev, y demás estructuras del kernel.La diferencia pasa por como el kernel maneja internamente los datos. Por lo regular es de a bloques (512 o 1024 bytes)Transmiten o reciben bloques de bytes a demanda del kernel mediante la función request. Algo diferente de la simple interfaz de los char devicesSon dispositivos que pueden hostear un File System. Ej.: Discos, Cintas.

Autor: Alejandro Furfaro 98

Network Devices

Los network devices parecen ser iguales a los block devices. Pero solo en aparienciaPero solo en aparienciaControlan las interfaces durante las transacciones de paquetes de datos en red contra un equipo remoto, pero sin conocer en detalle la composición de las transacciones que conforman esos paquetes. No siempre se relacionan con dispositivos de hardware (loopback por ejemplo)No constituyen dispositivos orientados a stream, por lo cual, no son fácilmente mapeables en el /dev

Autor: Alejandro Furfaro 99

Dispositivos Misceláneos

En general esta categoría agrupa a cualquier dispositivo o subsistema cuyas características le impiden clasificar en alguna de las tres categorías anteriores.Algunos autores clasifican en esta categoría especial, a los drivers de los controladores de buses, ya que son bastante particulares.2PCI2USB2SCSI

Autor: Alejandro Furfaro 100

Device Drivers: Inserción en el kernel

Autor: Alejandro Furfaro 101

Relación con el kernelAlcance2Kernel 2.4. (Implementaciones Red Hat 7.1 a 9.0)2Kernel 2.6. (Fedora, o Red Hat Enterprise edition)

Aclararemos las diferencias en donde existan para salvar los usos en una y otra versiónCausa de las diferencias: Linux Kernel Device Model2Unificación de todos los modelos de driver dispersos

hasta la versión 2.4 del kernel. 2Pensado mas para los drivers específicos de buses para

bridges y dispositivos, consolidando un sistema de datos y de operaciones en estructuras de datos globalmente accesibles.

Autor: Alejandro Furfaro 102

Linux Device Model

Kernel 2.6.xHacia un modelo unificado de dispositivos

Autor: Alejandro Furfaro 103

Kernel 2.6: Linux Device ModelHasta el kernel 2.6, el Device Model consistía simplemente de una colección de estructuras no relacionadas del tipo árbol de dispositivos (a veces apenas listas).

Para agrupar a estos modelos dispersos se recurre a una estructura de datos común que pueda relacionarlos con poco overhead, en un Modelo único y abarcativo.2 Los campos de datos comunes migran del antiguo modelo de bus local a un

modelo global de dispositivos.

2 Se estandarizan algunas funciones de manipulación de estos campos. i Las funciones de manipulación se convierten en un sistema de funciones

auxiliares.

i Los drivers de bus las utilizarán para incluir cualquier ítem específico del bus.

2 Cuando un driver de bus “descubre” un dispositivo particular, lo inserta en el árbol global de dispositivos y en su árbol local de dispositivos.

2 El árbol local del bus no es mas que un subconjunto del árbol global.

Autor: Alejandro Furfaro 104

Kernel 2.6: Linux Device ModelLas interfaces existentes entre el bridge y los dispositivos de E/S responden a las metas de la PC moderna: 2 Capacidad plug and play,

2 Manejo de energía,

2 Soporte hot plug.

Los buses modernos (USB, PCI-X, PCMCIA) soportan la mayoría de estas operaciones.

En el futuro un bus que no soporte una operación de este tipo será la excepción.

La Especificación ACPI (Advanced Configuration and Power Interface) de Intel, Hewlett Packard, Microsoft, fija los requisitos para que un dispositivo se adapte a cualquiera de los criterios anteriormente enumerados. 2 Describe las estructuras y mecanismos necesarios para diseñar motherboards cuyas

funcionalidades de power management y configuración avanzada puedan ser gestionadas por los sistemas operativos.

2 Aplica a toda clase de computadoras.

Autor: Alejandro Furfaro 105

Kernel 2.6: Linux Device Model

Tres objetos básicos:• Bus• Device• Class

Lleva la cuenta de que hay conectado a cada bus.Estructura bus_type

Lleva la cuenta de que hay conectado a cada bus.Estructura bus_type

Muestra la forma en que un dispositivo está conectado al sistema.Estructura device

Muestra la forma en que un dispositivo está conectado al sistema.Estructura device

Registra la función que provee el dispositivo, independientemente de donde esté conectadoEstructura device_class

Registra la función que provee el dispositivo, independientemente de donde esté conectadoEstructura device_class

Autor: Alejandro Furfaro 106

BusesHay una estructura estática definida por cada tipo de bus en el sistema (struct bus_type)Cada estructura de bus contiene la lista de dispositivos (devices) que están conectados a este tipo de bus en el sistemaCada vez que se invoca device_register para registrar un dispositivo, se lo incluye en la lista correspondiente a la estructura del bus al que está conectado.Cada estructura de bus contiene una lista de todos los device drivers de ese tipo de bus.Cada vez que se invoca a la función driver_register para registrar un driver, se lo incluye al final de esta lista.

Autor: Alejandro Furfaro 107

Estructura bus_typestruct bus_type {

char * name;

struct subsystem subsys;struct kset drivers;struct kset devices;

struct bus_attribute * bus_attrs;struct device_attribute * dev_attrs;struct driver_attribute * drv_attrs;

int (*match)(struct device * dev, struct device_driver * drv);int (*hotplug) (struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size);int (*suspend)(struct device * dev, pm_message_t state);int (*resume)(struct device * dev);

};int bus_register(struct bus_type * bus);

Autor: Alejandro Furfaro 108

Estructura bus_typeCada tipo de bus (PCI, USB, etc.) se declara como un objeto estático bus_type en el kernel. Una vez definida bus_type, se deben inicializar mínimamente el nombre y opcionalmente el puntero a la función callback match.struct bus_type pci_bus_type = {

.name = "pci",

.match = pci_bus_match,

};

El kernel exporta estructuras del tipo bus_type.extern struct bus_type pci_bus_type;

Cuando se inicializa un driver, se llama a la función bus_register del kernel, que se encargará de inicializar el resto de los campos del objeto bus e insertarlo en una lista global de tipos de bus.Una vez registrado, los campos del objeto bus_type están disponibles para que los acceda el driver del bus.

Autor: Alejandro Furfaro 109

Estructura bus_type

match (): Conectando Drivers a Dispositivos2 El formato de las estructuras de device ID y la semántica para

compararlos, son aspectos inherentes y muy específicos de cada bus.

2 Los drivers típicamente generan un array de los device ID’s que soportan y que residen en estructuras driver bus específicas.

2 La callback match ( ) permite al bus determinar si un driverdeterminado soporta a un dispositivo determinado, comparando losdevice ID soportados por el driver con el ID del dispositivo.

2 Cuando se registra un driver con el bus, se despliega la lista de dispositivos y se llama a la función match ( ) de cada dispositivo que no tenga un driver asociado

Autor: Alejandro Furfaro 110

Estructura bus_typePara reemplazar las listas locales que todos los buses mantenían por su cuenta se introdujeron al kernel 2.6 un conjunto de listas de dispositivos y de Drivers2 struc device,

2 struc device_drivers.

Cada driver de bus puede usar otro tipo de lista que le resulte adecuada y que corresponda al modelo anterior.

Es conveniente la conversión a este nuevo tipo para ser soportado en versiones futuras del kernel.

Autor: Alejandro Furfaro 111

Estructura bus_typeEl core del Linux Driver Model provee funciones auxiliares para iterar sobre estas listas.int bus_for_each_dev(struct bus_type * bus, struct device* start, void * data,int (*fn)(struct device *, void *));

int bus_for_each_drv(struct bus_type * bus, structdevice_driver * start, void * data, int (*fn)(structdevice_driver *, void *));

Estas funciones recorren la lista respectiva e invocan a la función callback para cada dispositivo o driver en la lista

Previamente a la llamada a la función callback se incrementa la cuenta de referencia del objeto correspondiente en la lista, y lo decrementan al obtener el siguiente objeto.

Se lockea el bus para sincronizar los accesos (no se mantiene el lockcuando se llama a la función callback)

Autor: Alejandro Furfaro 112

Devices

Mediante una estructura (struct device) se define para cada dispositivo: 2Sus características

2El bus al que está conectado (siempre se conectan por un bus, y cada bus está definido en el sistema)

2Su driver.

2Su clase (Que función cumple el dispositivo en el sistema).

Autor: Alejandro Furfaro 113

Estructura devicestruct device {

struct list_head g_list;struct list_head node;struct list_head bus_list;struct list_head driver_list;struct list_head intf_list;struct list_head children;struct device * parent;char name[DEVICE_NAME_SIZE];char bus_id[BUS_ID_SIZE];spinlock_t lock;atomic_t refcount;struct bus_type * bus;struct driver_dir_entry dir;u32 class_ num;struct device_driver *driver;void *driver_data;void *platform_data;u32 current_state;unsigned char *saved_state;void (*release)(struct device * dev);

};

Autor: Alejandro Furfaro 114

Estructura device: camposg_list: Nodo en la lista global de dispositivos.node: Nodo en la lista de hijos del mismo nodo padre de este dispositivo.bus_list: Nodo en la lista de dispositivos del driver de bus al que está conectado este dispositivo.driver_list: Nodo en la lista de device drivers del dispositivo.intf_list:Lista de intf_data. (Contiene una estructura alocada por cada interfaz que soporta el dispositivo).children:Lista de child devices.parent: *** FIXME ***name: Descripción ASCII del dispositivo. Ej.:" 3Com Corporation 3c905 100BaseTX [Boomerang]” bus_id: Representación ASCII de la posición del dispositivo en el bus.Este campo debe ser un nombre único entre todos los dispositivosconectados el mismo bus que éste. Ej.: PCI bus_ids se da en la forma de <bus number>:<slot number> . <function number>. Este nombre es único entre todos los dispositivos PCI en el sistema.

Autor: Alejandro Furfaro 115

lock: Spinlock para el dispositivo.

refcount: Cuenta de referencias al dispositivo.bus: Puntero a la struct bus_type a la que pertenece

el dispositivo.dir: Directorio sysfs del dispositivo. class_num: Valor

enumerado a clase del dispositivo.

driver: Puntero a la estructura device_driver que controla el dispositivo.

driver_data: Datos específicos del driver.platform_data: Datos de Plataforma específicos del dispositivocurrent_state: Estado actual de energía del dispositivo.saved_state: Puntero al estado salvado del dispositivo. Lo usa

el device driver para controlar al dispositivo. release: Callback para liberar el dispositivo una vez que

todas las referencias al mismo caducaron.

Estructura device: campos

Autor: Alejandro Furfaro 116

Estructura deviceEl driver de Bus que descubra al dispositivo, lo registrará en el coremediante:int device_register (struct device * dev);

Además el bus debe inicializar los siguientes campos:2 parent2 name2 bus_id2 bus

Un dispositivo cuya cuenta de referencias llegue a cero se remueve del core. La cuenta de referencia se puede ajustar usando:struct device * get_device (struct device * dev);void put_device (struct device * dev);

get_device() retorna un puntero a la estructura device que se le pasó como argumento, si la referencia no es 0.Un driver accede al lock en la estructura device usando: void lock_device (struct device * dev);void unlock_device (struct device * dev);

Autor: Alejandro Furfaro 117

Clases

Una clase de dispositivo describe el tipo de dispositivo, por ejemplo:2 audio, 2 network, etc.

La clase describe un juego de reglas semánticas e interfaces de programación, que permiten interactuar con esa clase de dispositivos, por ejemplo:2HID para el caso de los dispositivos USB dedicados a

interactuar con el usuario.Todos los dispositivos de una clase adhieren a las definiciones descriptas en el párrafo anterior.

Autor: Alejandro Furfaro 118

Estructura device _classtypedef int (*devclass_add)(struct device *);typedef void (*devclass_remove)(struct device *);

struct device_class {char * name;rwlock_t lock;u32 devnum;struct list_head node;struct list_head drivers;struct list_head intf_list;struct driver_dir_entry dir;struct driver_dir_entry device_dir;struct driver_dir_entry driver_dir;devclass_add add_device;devclass_remove remove_device;

};Definición típica de device_classstruct device_class input_devclass = {

.name = "input",

.add_device = input_add_device,

.remove_device = input_remove_device,};

Autor: Alejandro Furfaro 119

Estructura device_classDevices2 Al estar delimitados por su driver, se relacionan con la misma clase

a la que corresponde el driver2 Cada vez que un nuevo dispositivo se agrega a una clase se lo

enumera: se incrementa el campo devnum y se le asigna al dispositivo.

2 Si el dispositivo se desconecta o se remueve, este campo no se decrementa.

Device Drivers2 Cada clase de dispositivo guarda una lista de los device drivers que

corresponden a esa clase particular2 Los device drivers cuando se inicializan escriben en el campo

devclass de la estructura device_driver, la clase a la que pertenecen

Autor: Alejandro Furfaro 120

Estructura device _driverstruct device_driver {

char * name;struct bus_type * bus;rwlock_t lock;atomic_t refcount;list_t bus_list;list_t devices;struct driver_dir_entry dir;int (*probe)(struct device * dev);int (*remove)(struct device * dev);int (*suspend) (struct device *dev, pm_message_t

state, u32 level);int (*resume)(struct device *dev, u32 level);void (*release)(struct device_driver *drv);

};

Autor: Alejandro Furfaro 121

Se alojan en forma estática.

Cada device driver está referenciado en el kernelpor medio de una estructura única, independientemente de la cantidad de instancias de devices que pueda soportar.

Inicialización. Campos mínimos a inicializar: 2 name,

2 bus,

2 las funciones callback mas necesarias

Estructura device_driver:

Autor: Alejandro Furfaro 122

La estructura del driver se registra mediante:int driver_register (struct device_driver * drv);

Para drivers que no tienen campos bus específicos (y por ende una estructura bus específica), se usa driver_registerpasándole un puntero a su estructura device_driver.

La estructura del driver debe registrarse lo antes posible (ideal: durante la instalación del driver) ya que registrarlo en el core inicializa varios campos en la estructura device_driver, incluyendo la cuenta de referencia y el lock. Así estos campos se asumen como válidos y podrán ser usados por el bus o por el device model core.

Estructura device_driver:

Autor: Alejandro Furfaro 123

La mayoría de los drivers, tienen una estructura bus específica y se registrarán con el bus utilizando funciones del tipo pci_driver_register, mca_ driver_register, por ejemplo:int mca_register_driver(struct mca_driver *mca_drv) {

int r;

if (MCA_bus) {

mca_drv->driver.bus = &mca_bus_type;

if ((r=driver_register(&mca_drv->driver)) < 0)

return r;

}

return 0;

}

EXPORT_SYMBOL(mca_register_driver);

Estructura device_driver:

Autor: Alejandro Furfaro 124

/sys : La Interfaz con el usuarioEl Linux Driver Model se pone visible a través de un filesystem ram based: sysfs.Se define en /etc/fstab mediante la línea siguiente:none /sys sysfs defaults 0 0

El tope de sysfs contiene las siguiente entradas2 block/2 bus/

|-- /devices (es un link al nodo devices del tope)-- /drivers (contiene un directorio por cada driver que se registra

en el sistema)2 class/2 devices/2 firmware/2 net/

Autor: Alejandro Furfaro 125

Linux Device Model: Kernel orientado a objetos

La infraestructura de kobjects realiza una manejo básico de objetos del que sacan provecho las estructuras de datos grandes y los subsistemas, evitando la implementación de funciones similares.

Comprende2Cuenta de referencias a objetos.

2Mantenimiento de listas (sets) de objetos.

2 Lockeo de objetos.

2Representación del User space.

Autor: Alejandro Furfaro 126

Linux Device Model: Kernel orientado a objetos

Los tipos de objetos que soportan este funcionalidad son:2 kobjects: objeto simple.

2 kset: set de objetos de un cierto tipo.

2 ktype: set de funciones auxiliares para objetos de un tipo común.

2Subsystem: objeto que controla un numero de ksets.

Se relaciona con el sysfs. (Cada kobject registrado tiene un directorio en /sys)

Autor: Alejandro Furfaro 127

kobject

struct kobject es un tipo de datos simple que constituye la fundación de tipos de objeto mas complejos

Provee un set de campos básicos casi todos tipos de datos complejos compartidos

Están pensados para embeberse en grandes estructuras de datos reemplazando campos que, de otro modo, éstas duplicarían.

Autor: Alejandro Furfaro 128

kobject: definiciónstruct kobject {char name[KOBJ_NAME_LEN];atomic_t refcount;struct list_head entry;struct kobject * parent;struct kset * kset;struct kobj_type * ktype;struct dentry * dentry;

};

void kobject_init(struct kobject *);int kobject_add(struct kobject *);int kobject_register(struct kobject *);

void kobject_del(struct kobject *);void kobject_unregister(struct kobject *);

struct kobject * kobject_get(struct kobject *);void kobject_put(struct kobject *);

Autor: Alejandro Furfaro 129

kobjects: Interfaz de programaciónLos kobjects se pueden agregar y remover del kobject coredinámicamente con:2 kobject_register()2 kobject_unregister(). 2 El registro implica insertar el kobject en la lista de su kset dominante

y crearle un directorio en sysfs.2 Puede usarse un kobject sin agregarlo a su lista de ksets o sin

exportarlo vía sysfs llamando a kobject_init(). 2 Un kobject inicializado puede agregarse mas adelante a la jerarquía

de objetos mediante la función kobject_add(). 2 Un kobject no inicializado puede ser utilizado para cuenta de

referencia.

Nota: llamar a kobject_init() mas kobject_add() es igual a llamar a kobject_register() solamente.

Autor: Alejandro Furfaro 130

kobjects: Interfaz de programaciónDes registrar un kobject, implica:2 removerlo de su lista de ksets, con kobject_del()2 removerlo del file system sysfs, con kobject_del()2 y decrementar su cuenta de referencia, con kobject_put()2 Ambas funciones pueden ser invocadas manualmente.

Cuentas de referencia2 kobject_get()

i Incrementa la cuenta de referencia de kobjectsi Retorna una referencia válida a un kobject

2 kobject_put()i decrementa la cuenta de referencia de kobjectsi La cuenta de referencia de un objeto solo se puede incrementar cuando es

positivo. 2 método struct kobj_type::release()

i El kernel lo invoca al llegar a 0 la cuenta de referencia del kobjecti Es apuntado por el kset del kobjecti Libera la memoria alojada para el objeto.

Autor: Alejandro Furfaro 131

kobjects: aspectos importantesCuando se usan kobjects con cuenta de referencia.2 Si se lo alojó dinámicamente es imperativo suministrar una función

destructora para liberarlo. 2 La cuenta de referencia lleva la cuenta del tiempo de vida del objeto.

Cuando llega a 0, se asume que el objeto ha sido liberado y no puede volver a utilizarse.

2 Es importante liberar al objeto en esta instancia y no luego de llamar a unregister. i Si alguien mas referencia a ese objeto (por ejemplo a través de un

archivo en sysfs), obtendrá una referencia a ese objeto, asumirá que es válida, y tratará de operar sobre este.

i Si al mismo tiempo el objeto se des registra liberandose la memoria, la operación anterior hará referencia a memoria no asignada y .... ya sabemos que ocurre.

i Se puede prevenir definiendo al menos un método release y liberando el objeto solo desde ese método.

Autor: Alejandro Furfaro 132

kobjects y sysfs

Cada kobject corresponde a un directorio en sysfs. Este

directorio se crea bajo el directorio padre del kobject.

Si el kobject no tiene un padre cuando se lo registra, su

kset dominante se transforma en su padre.

Si un kobject no tiene un padre ni un kset dominante, Su

directorio se crea en el nivel mas alto de la partición sysfs.

Esto ocurre solo para kobjects embebidos en una estructura

del tipo subsystem.

Autor: Alejandro Furfaro 133

ksetsEs un set de kobjects embebidos en el mismo tipo. struct kset {

struct subsystem * subsys;struct kobj_type * ktype;struct list_head list;struct kobject kobj;

};void kset_init(struct kset * k);int kset_add(struct kset * k);int kset_register(struct kset * k);void kset_unregister(struct kset * k);

struct kset * kset_get(struct kset * k);void kset_put(struct kset * k);

struct kobject * kset_find_obj(struct kset *, char *);

Autor: Alejandro Furfaro 134

ksets

* ktype: describe el tipo de kobject que está embebido en

este kset

* subsys: apunta al subsistema al que el pertenece el

kobject

Además un kset contiene un kobject de sí mismo2 puede ser registrado en la jerarquía de memoria y exportado de

sysfs.

2 Un kset puede embeberse en un tipo de dato mas grande, y puede

ser parte de otro kset (de ese tipo de kobject).

Autor: Alejandro Furfaro 135

ksetsPor ejemplo, un dispositivo de bloque es un objeto (struct

gendisk) que está contenido en un set de block devices.

También puede contener un set de particiones (structhd_struct) que se encuentran en el dispositivo. El siguiente código ilustra como expresar esto apropiadamente.

struct gendisk * disk;

...

disk->kset.kobj.kset = &block_kset;

disk->kset.ktype = &partition_ktype;

kset_register(&disk->kset);

Autor: Alejandro Furfaro 136

ksets

El kset al que pertenece el objeto embebido disk es el

block_kset, apuntado por disk->kset.kobj.kset.

Los tipos de objetos en la lista de subordinados de disk son

las particiones, y se setean en disk->kset.ktype.

Luego se registra el kset, que manejará la inicialización y

agregará el kobject embebido a la jerarquía

Autor: Alejandro Furfaro 137

ktypesstruct kobj_type {void (*release)(struct kobject *);struct sysfs_ops * sysfs_ops;struct attribute ** default_attrs;

};

Los tipos de objetos requieren de funciones específicas de

conversión de objetos genéricos a tipos de objetos mas

complejos.

Autor: Alejandro Furfaro 138

ktypesstruct kobj_type incluye campos objeto específicos:2 release: Se la llama al llegar a 0 la cuenta de

referencia del kobject. Convierte al objeto en otro de tipo mas complejo y lo libera.

2 sysfs_ops: Provee una función de conversión para acceso a sysfs.

2 default_attrs: Atributos default a exportar vía sysfs al registrar el objeto. El último atributo se debe

inicializar a NULL (ver drivers/block/genhd.c)

Las instancias de struct kobj_type no se registran. Solo se las referencia en el kset. Un kobj_type se puede referenciar mediante un número arbitrario de ksets, o también se puede disparar un set de objetos idénticos.

Autor: Alejandro Furfaro 139

subsistemasUn subsistema representa una entidad de código significativa quemantiene un número. arbitrario de sets de objetos de varios tipos.

Puesto que el número de ksets y el tipo de objetos que contienen son variables, la representación genérica de un subsistema es mínima. struct subsystem {

struct kset kset;

struct rw_semaphore rwsem;

};

int subsystem_register(struct subsystem *);

void subsystem_unregister(struct subsystem *);

struct subsystem * subsys_get(struct subsystem * s);

void subsys_put(struct subsystem * s);

Autor: Alejandro Furfaro 140

subsistemas

Un subsistema contiene embebido un kset. Entonces:2 Se puede representar en la jerarquía de objetos vía el kobject

embebido en el kset.

2 Puede mantener una lista default de objetos de un tipo dado.

Se pueden conectar ksets adicionales al sistema creando

una referencia al subsistema antes de registrarlos. (Esta

referencia unidireccional implica que no hay forma de

determinar que ksets están conectados al subsistema.)

Todos los ksets conectados a un subsistema comparten los

mismos semáforos R/W (rwsem).

Autor: Alejandro Furfaro 141

Conceptos de programación de drivers

El Howto?

Autor: Alejandro Furfaro 142

Programación de Módulos

Escribir un device driver, es escribir código de kernel

En modo kernel se dispone de un tipo especial de programa denominado Módulo (kernel module)

Una aplicación convencional realiza una tarea única del principio hasta el fin.

Un módulo, en cambio, se registra a si mismo a fin de prestar servicios a futuro. Su función principal es efímera, pero queda “instalado” en el sistema.

Autor: Alejandro Furfaro 143

Kernel 2.4

#define MODULE#include <linux/module.h>int init_module(void){ printk("<1>¡Hola mundo! \n"); return 0; }void cleanup_module(void) {printk("<1>¡Adiós mundo

cruel!\n"); }

Kernel 2.6

#include <linux/init.h>#include <linux/module.h>MODULE_LICENSE("Dual BSD/GPL");

static int hello_init(void) {printk(KERN_ALERT "Hola,

mundo!\n");return 0; }static void hello_exit(void) { printk(KERN_ALERT “Adiós, Mundo

cruel!\n");}module_init(hello_init);module_exit(hello_exit);

Programación de Módulos

Autor: Alejandro Furfaro 144

¿y la función main??????No usa. ¿entonces?...... Veamos como se compila y ejecuta un módulo en Linux (atentos con el prompt....)

root# gcc -c hello.croot# insmod ./hello.o¡Hola mundo!root# rmmod hello¡Adiós mundo cruel!root#

insmod y rmmod, se utilizan para manejar nuestro módulo. insmod lo instala, y quedará disponible hasta que se ejecute rmmodinsmod hace que se ejecute la función init_module () (en kernels 2.4.x) o module_init () (en kernels 2.6.x).Hace las veces de “función main” del módulormmod hace que se ejecute la función cleanup_module () (en kernels2.4.x) o module_exit () (en kernels 2.6.x).Hacen las veces de funciones constructora y destructora.

Programación de Módulos

Autor: Alejandro Furfaro 145

Enlace de un módulo al kernel 2.4

Autor: Alejandro Furfaro 146

Enlace de un módulo al kernel 2.6

Autor: Alejandro Furfaro 147

Device Drivers: Recursos

El kernel de LINUX es concurrente, por lo tanto un driverdebe estar escrito con la idea que en un mismo instante ocurren varias cosas. Debe ser re entrante.

Desde el kernel no tenemos los recursos que usamos en las aplicaciones:2 No se accede a las system call standard

2 No están disponibles los IPCs!!!!

Ejemplo. Para averiguar el proceso que invocó alguna de las funciones el driver, vamos a task_struct..... printk("The process is \"%s\" (pid %i)\n", current->comm, current->pid);

Autor: Alejandro Furfaro 148

Char devicesDeben existir como file system node en /devSe crean con un comando especial:“mknod <nombre> <type> <Mn> <mn>”

Numero mayor y menor.crw-rw-rw- 1 root root 1, 3 Feb 23 1999 nullcrw------- 1 root root 10, 1 Feb 23 1999 psauxcrw------- 1 rubini tty 4, 1 Aug 16 22:22 tty1crw-rw-rw- 1 root dialout 4, 64 Jun 30 11:19 ttyS0crw-rw-rw- 1 root dialout 4, 65 Aug 16 00:00 ttyS1crw------- 1 root sys 7, 1 Feb 23 1999 vcs1crw------- 1 root sys 7, 129 Feb 23 1999 vcsa1crw-rw-rw- 1 root root 1, 5 Feb 23 1999 zero

El kernel usa el Major number para despachar la ejecución del driver correcto en el momento en que se ejecuta la función open () desde el proceso que lo desea acceder.El Minor number es usado por el driver. El kernel solo lo pasa al driver para que este lo utilice si lo necesita.

Autor: Alejandro Furfaro 149

Representación del número de device

El kernel usa un tipo definido en ‹linux/types.h›, como dev_t.

32 bits: 12 para el major number y 20 para el minor number

Para desentendernos de esta estructura (todo evoluciona y

cambia) hay dos macros en ‹linux/kdev_t.h›.2 Conociendo el número de device (dev_t), obtenemos major y minor

MAJOR(dev_t dev);

MINOR(dev_t dev);

2 Conociendo major y minor obtenemos el número de device

MKDEV(int major, int minor);

Autor: Alejandro Furfaro 150

Mas novedades del kernel 2.6

Funciones para reservar el major number, de modo de evitar conflictos y recompilacionesDefinidas en <linux/fs.h>:2Reserva un rango de major numbers

int register_chrdev_region (dev_t first, unsigned intcount, char *name);

2Si conocemos exactamente el major number a utilizarint alloc_chrdev_region(dev_t *dev, unsigned intfirstminor, unsigned int count, char *name);

2Devuelve los major numbers reservados de una u otra formavoid unregister_chrdev_region(dev_t first, unsigned intcount);

Autor: Alejandro Furfaro 151

/procFile System RAM based en el que hay mucha info. En particular para drivers:Character devices: Block devices:

1 mem 2 fd2 pty 8 sd3 ttyp 11 sr4 ttyS 65 sd6 lp 66 sd7 vcs10 misc13 input14 sound21 sg180 usb

Autor: Alejandro Furfaro 152

Script para ejecutar la instalación#!/bin/shmodule=“midriver"device=“midriver"mode="664"# invoca insmod con todos los argumentos# usa pathname (las modutils nuevas no miran en ‘.’ por default)/sbin/insmod ./$module.ko $* || exit 1# remueve nodos viejosrm -f /dev/${device}[0-3]major=$(awk "\\$2= =\"$module\" {print \\$1}" /proc/devices)mknod /dev/${device}0 c $major 0mknod /dev/${device}1 c $major 1mknod /dev/${device}2 c $major 2mknod /dev/${device}3 c $major 3# give appropriate group/permissions, and change the group.# Not all distributions have staff, some have "wheel" instead.group="staff"grep -q '^staff:' /etc/group || group="wheel"chgrp $group /dev/${device}[0-3]chmod $mode /dev/${device}[0-3]

Autor: Alejandro Furfaro 153

Manejo del major numberif (midriver_major){

dev = MKDEV(midriver_major, midriver_minor);result = register_chrdev_region(dev, midriver_nr_devs, “midriver");

} else {

result = alloc_chrdev_region(&dev, midriver_minor, midriver_nr_devs, "midriver");midriver_major = MAJOR(dev);

}if (result < 0) {

printk(KERN_WARNING "midriver: can't get major d\n", midriver_major);return result;

}

Autor: Alejandro Furfaro 154

Char Devices: Esquema de llamadas al sistema

open()write()

close()

Dev_open()Dev_write()

Dev_close()

outb()

Memoria

User Mode KernelDevice

Ports

File_ops

Autor: Alejandro Furfaro 155

Char Devices: File Operations (1)struct module *owner

– Es el primer campo de file_operations– No es en sí mismo una operación– Es un puntero al módulo “dueño” de la estructura. – Se usa para evitar que el módulo sea cargado mientras sus operaciones

están en uso.– A menudo se lo inicializa sencillamente con la macro THIS_MODULE,

definida en <linux/module.h>.loff_t (*llseek) (struct file *, loff_t, int);

– El método llseek se usa para cambiar la posición actual de lectura/ escritura en un archivo

– La nueva posición se retorna como un valor positivo– loff_t es un “long offset” y tiene al menos un ancho de 64 bits aún en

plataformas de 32-bit. – Si se produce algún error en su ejecución retorna un valor negativo– Si este puntero se inicializa en NULL en file_operations, seek () modificará

el contador de posición en la estructura file (de formas potencialmente impredecibles).

Autor: Alejandro Furfaro 156

Char Devices: File Operations (2)ssize_t (*read) (struct file *, char __user *, size_t,

loff_t *);– Lee datos desde un archivo o device. – Un puntero NULL en esta posición hace que la system call read () sobre este

device devuelva -EINVAL (“Invalid argument”). – Un valor de retorno no negativo representa el número de bytes leídos

ssize_t (*aio_read)(struct kiocb *, char __user *, size_t, loff_t);

– Inicia una lectura asincrónica (puede no completarse antes de retornar).– Si es NULL, todas las operaciones serán ejecutadas en forma sincrónica por

read ().ssize_t (*write) (struct file *, const char __user *,

size_t, loff_t *);– Envía datos a un archivo o device. – Si este puntero es NULL, la system call write () retorna -EINVAL al programa

que la invoca– Un valor de retorno, no negativo, es el número de bytes escritos.

Autor: Alejandro Furfaro 157

Char Devices: File Operations (3)ssize_t (*aio_write)(struct kiocb *, const char __user *,

size_t, loff_t *);– Inicia una operación de escritura asincrónica sobre el device.

int (*readdir) (struct file *, void *, filldir_t);– Se usa para leer directorios. Solo lo usan los file systems. Debe ser NULL

para cualquier device.unsigned int (*poll) (struct file *, struct

poll_table_struct *);– El método poll es el back end de tres system calls: poll (), epoll (), y select ().– Se usa para saber si un read () o un write () a uno o mas descriptores de

archivo va a bloquear. – El método poll () debe retornar una máscara de bits que indica si son factibles

lecturas o escrituras no bloqueantes. – El kernel con esta información pone un proceso en estado sleeping hasta que

sea posible la operación de E/S.– Si un driver deja NULL este método, se asume que puede ser leído o escrito

sin bloqueo.

Autor: Alejandro Furfaro 158

Char Devices: File Operations (4)int (*ioctl) (struct inode *, struct file *, unsigned

int, unsigned long);– La system call ioctl () envía comandos device específicos. – El kernel generalmente procesa ioctl () por medio del método definido en

file_operations. – Si no hay un method ioctl (), la system call retorna error para cualquier

requerimiento no predefinido (-ENOTTY, “No such ioctl for device”).int (*mmap) (struct file *, struct vm_area_struct *);

– mmap requiere el mapeo de un device de memoria al espacio de direcciones del proceso.

– Si este método es NULL, la system call mmap () retorna -ENODEV.int (*open) (struct inode *, struct file *);

– Como SIEMPRE es la primer operación realizada sobre el archivo o device, no es necesario declararlo

– Si es NULL, el device siempre se abre, pero no se notifica al driver.

Autor: Alejandro Furfaro 159

Char Devices: File Operations (5)int (*flush) (struct file *);

– La operación flush () se invoca cuando un proceso cierra su copia del file descriptor de un device

– Ejecuta (y espera por) cualquier operación excepcional sobre el device. – No confundir con la operación fsync () requerida por un programa.– flush () se usa en muy pocos drivers: el driver SCSI de cinta lo use, por

ejemplo, para asegurar que todos los datos escritos estén en la cinta antes de cerrar el dispositivo

– Si es NULL, el kernel simplemente ignora el requerimiento.int (*release) (struct inode *, struct file *);

– Se invoca cuando se desea liberar la estructura.– Igual que open () puede ser NULL. – release () no se invoca cada vez que un proceso llama a close (). Si una

estructura file se comparte (como resultado de fork () o dup() ), release () se invoca cuando todas las copias ejecutan close ().

Autor: Alejandro Furfaro 160

Char Devices: File Operations (6)int (*fsync) (struct file *, struct dentry *, int);

– Es el back end de la system call fsync (), que es llamada por un programa para flushear cualquier dato pendiente. Si es NULL, retorna -EINVAL.

int (*aio_fsync)(struct kiocb *, int);– Es la versión asincrónica del método fsync.

int (*fasync) (int, struct file *, int);– Se usa para notificar al device que cambió su flag FASYNC.– Puede ser NULL si el driver no soporta notificación asincrónica.

int (*lock) (struct file *, int, struct file_lock *);– Se usa para implementar file locking.– Es indispensable en archivos, pero rara vez se usa en drivers.

Autor: Alejandro Furfaro 161

Char Devices: File Operations (7)ssize_t (*readv) (struct file *, const struct iovec *,

unsigned long, loff_t *);ssize_t (*writev) (struct file *, const struct iovec *,

unsigned long, loff_t *);– Implementan operaciones de lectura escritura fragmentada, que

ocasionalmente necesitan involucrar múltiples áreas de memoria – Estas system calls fuerzan operaciones extra de copia sobre los datos.– Si estos punteros se dejan NULL, se llaman en su lugar los métodos read () y

write () (quizá mas de una vez).ssize_t (*sendfile)(struct file *, loff_t *, size_t,

read_actor_t, void *);– Implementa el lado read de la system call sendfile (), que mueve los datos

desde un file descriptor hacia otro con mínima copia– Se usa por ejemplo en un web server que necesita enviar los contenidos de

un archivo fuera hacia la red. – Los device drivers normalmente la dejan en NULL.

Autor: Alejandro Furfaro 162

Char Devices: File Operations (8)ssize_t (*sendpage) (struct file *, struct page *, int,

size_t, loff_t *, int);– sendpage es la otra mitad de sendfile; – El kernel la llama para enviar datos al archivo correspondiente, una página a

la vez. – Los device drivers normalmente no implementan sendpage.

unsigned long (*get_unmapped_area) (struct file *, unsigned long, unsigned long, unsigned long, unsignedlong);

– El objetivo de este método es encontrar una ubicación adecuada en el espacio de direcciones del proceso para mapearla sobre un segmento de memoria del device.

– Normalmente es el código de manejo de la memoria quien realiza esta tarea– Este método permite a los drivers forzar los requerimientos de alineamiento

que pueda tener cualquier device. La mayoría de los drivers dejan este método NULL.

Autor: Alejandro Furfaro 163

Char Devices: File Operations (9)int (*check_flags)(int)

– Permite al módulo chequear los flags que se le pasan en una llamada fcntl(F_SETFL...).

int (*dir_notify)(struct file *, unsigned long);– Se invoca cuando una aplicación usa fcntl () para pedir modificaciones en un

directorio. – Sólo es útil en file systems– Los drivers no necesitan implementar dir_notify.

Autor: Alejandro Furfaro 164

Char Devices: File Operations (10)Es la estructura principal para mapear el sistema de system calls del sistema operativo sobre el hardwareDeclaradas en <linux/fs.h>

struct file_operationsmidriver_fops =

{.owner = THIS_MODULE,.read = scull_read,.write = scull_write,.ioctl = scull_ioctl,.open = scull_open,.release = scull_release,

};

struct file_operationsmidriver_fops = {

NULL, //lseekmidriver_read,midriver_write,NULL, //readdirNULL, //pollmidriver_ioctl,NULL, //mmapmidriver_open,NULL, //flushmidriver_release,NULL, //fsyncNULL, //fasyncNULL, //check_media_changeNULL, //revalidateNULL, //lock

};

Autor: Alejandro Furfaro 165

Otras estructuras del sistema a considerar: struc file

Definida en <linux/fs.h>Contiene la información lógica de un archivo abierto con open (). Campos de interés para un char device2 mode_t f_mode; //Modo en que se abrió el archivo (FMODE_READ,

FMODE_WRITE)2 loff_t f_pos; //Puntero de 64 bits offset dentro del archivo2 unsigned int f_flags; //O_RDONLY, O_NONBLOCK, O_SYNC.2 struct file_operations *f_op;2 void *private_data;

i open () la carga con NULL antes de llamar al método open propio del driver.

i Se puede utilizar para guardar datos propios del driver2 struct dentry *f_dentry;

i Directory entry.i Normalmente no es necesario tenerla en cuenta, salvo si necesitan

acceder al inodo del directorio.

Autor: Alejandro Furfaro 166

Otras estructuras del sistema a considerar: struct inode

Definida en <linux/fs.h>

Contiene la información de un nodo del file system (no de un archivo abierto)2 Campos de interés para un char device

i dev_t i_rdev; //contiene el número de device (32 bits: 12 major number 20 minor number)

i struct cdev *i_cdev; //es una estructura del LDM que representa a un char device. Si el inodo no contiene un char device este campo es NULL.

2 Para obtener el major y el minor number a partir de inodeunsigned int iminor (struct inode *inode);

unsigned int imajor (struct inode *inode);

Autor: Alejandro Furfaro 167

Registro de un char device en kernel2.6: struc cdev

Definida en <linux/cdev.h>.

La usa el kernel para identificar a los char devices

Previo a usar file_operations hay que allocar en el kernel

una estructura de este tipo. Hay dos formas:2 Para obtener una estructura cdev standalone en tiempo de ejecución

struct cdev *my_cdev = cdev_alloc( );

my_cdev->ops = &my_fops;

2 Si queremos embeber una cdev en una estructura propietaria de

nuestro driver, se inicializa una estructura ya alojada convoid cdev_init(struct cdev *cdev, struct file_operations

*fops);

Autor: Alejandro Furfaro 168

Registro de un char device en kernel2.6: struc cdev

Una vez inicializada cdev, debe ser declarada al kernel.int cdev_add (struct cdev *dev, dev_t num, unsigned int

count);

void cdev_del (struct cdev *dev);

Consideraciones a tener presentes: 2 Si falla, cdev_add devuelve un código de error negativo.

2 Si no falló significa que el dispositivo está vivo y puede recibir llamadas a las operaciones declaradas

2 cdev_add debe llamarse una vez declaradas las operaciones que el dispositivo puede realizar

Para remover del sistema a un char device se ejecutavoid cdev_del (struct cdev *dev);

Autor: Alejandro Furfaro 169

Archivo <linux/cdev.h>#ifndef _LINUX_CDEV_H#define _LINUX_CDEV_H#ifdef __KERNEL__

struct cdev {struct kobject kobj;struct module *owner;struct file_operations *ops;struct list_head list;dev_t dev;unsigned int count;

};

void cdev_init(struct cdev *, struct file_operations *);struct cdev *cdev_alloc(void);void cdev_put(struct cdev *p);int cdev_add(struct cdev *, dev_t, unsigned);void cdev_del(struct cdev *);void cd_forget(struct inode *);#endif#endif

Autor: Alejandro Furfaro 170

Requerimiento de una IRQ al kernelint request_irq (unsigned int irq, void (*handler) (int, void *, struct pt_regs *), unsigned long flags, const char *dev_name, void *dev_id);

2 Retorna 0 o un código negativo de error. (-EBUSY si otro driver está usando la IRQ pedida por ejemplo).

2 Argumentos i unsigned int irq: Número de la IRQ requerida.i void (*handler) (int, void *, struct pt_regs *): Puntero a la función que se

desea instalar como handler.i unsigned long flags: Máscara de bits relacionada al manejo de

interrupciones.4 SA_INTERRUPT, Indica que es un ‘‘fast’’ interrupt handler: Se ejecutará aún con

las interupciones deshabilitadas4 SA_SHIRQ. Indica que la interrupción puede compartirse entre varios devices.

i const char *dev_name: Nombre del device en /dev. Lo usa en /proc/interrupts para mostrar el dueño de la IRQ.

i void *dev_id: Puntero usado para compartir IRQ’s. Cuando no se comparte IRQ se lo deja en NULL

void free_irq (unsigned int irq, void *dev_id);

Autor: Alejandro Furfaro 171

ANEXOS

Estructuras y piezas de código

Autor: Alejandro Furfaro 172

task_struct(/usr/src/linux--[version][version]/includes/linux/sched.h)

struct task_struct {volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */struct thread_info *thread_info;atomic_t usage;unsigned long flags; /* per process flags, defined below */unsigned long ptrace;

int lock_depth; /* BKL lock depth */

int prio, static_prio;struct list_head run_list;prio_array_t *array;

unsigned long sleep_avg;unsigned long long timestamp, last_ran;unsigned long long sched_time; /* sched_clock time spent running */int activated;

unsigned long policy;cpumask_t cpus_allowed;unsigned int time_slice, first_time_slice;

Autor: Alejandro Furfaro 173

task_struct(/usr/src/linux--[version][version]/includes/linux/sched.h)

#ifdef CONFIG_SCHEDSTATSstruct sched_info sched_info;

#endifstruct list_head tasks;/** ptrace_list/ptrace_children forms the list of my children* that were stolen by a ptracer.*/

struct list_head ptrace_children;struct list_head ptrace_list;

struct mm_struct *mm, *active_mm;/* task state */

struct linux_binfmt *binfmt;long exit_state;int exit_code, exit_signal;int pdeath_signal; /* The signal sent when the parent dies *//* ??? */unsigned long personality;unsigned did_exec:1;pid_t pid;pid_t tgid;

Autor: Alejandro Furfaro 174

task_struct(/usr/src/linux--[version][version]/includes/linux/sched.h)

/* * pointers to (original) parent process, youngest child, younger sibling,* older sibling, respectively. (p->father can be replaced with * p->parent->pid) */

struct task_struct *real_parent; /* real parent process (when being debugged) */struct task_struct *parent; /* parent process */

/* * children/sibling forms the list of my children plus the* tasks I'm ptracing. */

struct list_head children; /* list of my children */struct list_head sibling; /* linkage in my parent's children list */struct task_struct *group_leader; /* threadgroup leader */

/* PID/PID hash table linkage. */struct pid pids[PIDTYPE_MAX];struct completion *vfork_done; /* for vfork() */int __user *set_child_tid; /* CLONE_CHILD_SETTID */int __user *clear_child_tid; /* CLONE_CHILD_CLEARTID */unsigned long rt_priority;cputime_t utime, stime;unsigned long nvcsw, nivcsw; /* context switch counts */struct timespec start_time;

Autor: Alejandro Furfaro 175

task_struct(/usr/src/linux--[version][version]/includes/linux/sched.h)

/* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */unsigned long min_flt, maj_flt;

cputime_t it_prof_expires, it_virt_expires;unsigned long long it_sched_expires;struct list_head cpu_timers[3];

/* process credentials */uid_t uid,euid,suid,fsuid;gid_t gid,egid,sgid,fsgid;struct group_info *group_info;kernel_cap_t cap_effective, cap_inheritable, cap_permitted;unsigned keep_capabilities:1;struct user_struct *user;

#ifdef CONFIG_KEYSstruct key *thread_keyring; /* keyring private to this thread */

#endifint oomkilladj; /* OOM kill score adjustment (bit shift). */char comm [TASK_COMM_LEN]; /* executable name excluding path

- access with [gs]et_task_comm (which lockit with task_lock())

- initialized normally by flush_old_exec */

Autor: Alejandro Furfaro 176

task_struct(/usr/src/linux--[version][version]/includes/linux/sched.h)

/* file system info */int link_count, total_link_count; struct sysv_sem sysvsem; /* ipc stuff */struct thread_struct thread; /* CPU-specific state of this task */struct fs_struct *fs; /* filesystem information */struct files_struct *files; /* open file information */struct namespace *namespace; /* namespace */

/* signal handlers */struct signal_struct *signal;struct sighand_struct *sighand;

sigset_t blocked, real_blocked;struct sigpending pending;

unsigned long sas_ss_sp;size_t sas_ss_size;int (*notifier)(void *priv);void *notifier_data;sigset_t *notifier_mask;

void *security;struct audit_context *audit_context;seccomp_t seccomp;

Autor: Alejandro Furfaro 177

task_struct(/usr/src/linux--[version][version]/includes/linux/sched.h)

/* Thread group tracking */u32 parent_exec_id;u32 self_exec_id;

/* Protection of (de-)allocation: mm, files, fs, tty, keyrings */spinlock_t alloc_lock;

/* Protection of proc_dentry: nesting proc_lock, dcache_lock, write_lock_irq(&tasklist_lock); */spinlock_t proc_lock;

/* context-switch lock */spinlock_t switch_lock;

/* journalling filesystem info */void *journal_info;

/* VM state */struct reclaim_state *reclaim_state;struct dentry *proc_dentry;struct backing_dev_info *backing_dev_info;struct io_context *io_context;unsigned long ptrace_message;siginfo_t *last_siginfo; /* For ptrace use. */

Autor: Alejandro Furfaro 178

task_struct(/usr/src/linux--[version][version]/includes/linux/sched.h)

/* * current io wait handle: wait queue entry to use for io waits* If this thread is processing aio, this points at the waitqueue* inside the currently handled kiocb. It may be NULL (i.e. default* to a stack based synchronous wait) if its doing sync IO. */

wait_queue_t *io_wait;/* i/o counters(bytes read/written, #syscalls */

u64 rchar, wchar, syscr, syscw;#if defined(CONFIG_BSD_PROCESS_ACCT)

u64 acct_rss_mem1; /* accumulated rss usage */u64 acct_vm_mem1; /* accumulated virtual memory usage */clock_t acct_stimexpd; /* clock_t-converted stime since last update */

#endif#ifdef CONFIG_NUMA

struct mempolicy *mempolicy;short il_next;

#endif#ifdef CONFIG_CPUSETS

struct cpuset *cpuset;nodemask_t mems_allowed;int cpuset_mems_generation;

#endif}; VOLVER

Autor: Alejandro Furfaro 179

mm_struct(/usr/src/linux--[version][version]/includes/linux/sched.h)

struct mm_struct {struct vm_area_struct * mmap; /* list of VMAs */struct rb_root mm_rb;struct vm_area_struct * mmap_cache; /* last find_vma result */unsigned long (*get_unmapped_area) (struct file *filp,

unsigned long addr, unsigned long len,unsigned long pgoff, unsigned long flags);

void (*unmap_area) (struct vm_area_struct *area);unsigned long mmap_base; /* base of mmap area */unsigned long free_area_cache; /* first hole */pgd_t * pgd;atomic_t mm_users; /* How many users with user space? */atomic_t mm_count; /* How many references to "struct mm_struct" (users count as 1) */int map_count; /* number of VMAs */struct rw_semaphore mmap_sem;spinlock_t page_table_lock; /* Protects page tables and some counters */struct list_head mmlist; /* List of maybe swapped mm's. These are globally strung

* together off init_mm.mmlist, and are protected by mmlist_lock */unsigned long start_code, end_code, start_data, end_data;unsigned long start_brk, brk, start_stack;unsigned long arg_start, arg_end, env_start, env_end;unsigned long total_vm, locked_vm, shared_vm;unsigned long exec_vm, stack_vm, reserved_vm, def_flags, nr_ptes;

Autor: Alejandro Furfaro 180

mm_struct(/usr/src/linux--[version][version]/includes/linux/sched.h)

/* Special counters protected by the page_table_lock */mm_counter_t _rss;mm_counter_t _anon_rss;unsigned long saved_auxv[42]; /* for /proc/PID/auxv */unsigned dumpable:1;cpumask_t cpu_vm_mask;/* Architecture-specific MM context */mm_context_t context;/* Token based thrashing protection. */unsigned long swap_token_time;char recent_pagein;/* coredumping support */int core_waiters;struct completion *core_startup_done, core_done;/* aio bits */rwlock_t ioctx_list_lock;struct kioctx *ioctx_list;struct kioctx default_kioctx;

unsigned long hiwater_rss; /* High-water RSS usage */unsigned long hiwater_vm; /* High-water virtual memory usage */

};

Autor: Alejandro Furfaro 181

list_head (/usr/src/linux--[version][version]/includes/linux/list.h)

struct list_head {struct list_head *next, *prev;

};

Autor: Alejandro Furfaro 182

timer_list(/usr/src/linux--[version][version]/includes/linux/timer.h)

/** In Linux 2.4, static timers have been removed from the kernel.* Timers may be dynamically created and destroyed, and should be initialized* by a call to init_timer() upon creation.** The "data" field enables use of a common timeout function for several* timeouts. You can use this field to distinguish between the different* invocations.*/struct timer_list {

struct list_head list;unsigned long expires;unsigned long data;void (*function)(unsigned long);

};

Autor: Alejandro Furfaro 183

tms(/usr/src/linux--[version][version]/includes/linux/times.h)

struct tms {clock_t tms_utime;clock_t tms_stime;clock_t tms_cutime;clock_t tms_cstime;

};

Autor: Alejandro Furfaro 184

user_struct(/usr/src/linux--[version][version]/includes/linux/sched.h)

/** Some day this will be a full-fledged user tracking system..*/struct user_struct {

atomic_t __count; /* reference count */atomic_t processes; /* How many processes does this user have? */atomic_t files; /* How many open files does this user have? */

/* Hash table maintenance information */struct user_struct *next, **pprev;uid_t uid;

};

Autor: Alejandro Furfaro 185

rlimit(/usr/src/linux--[version][version]/includes/linux/resources.h)

struct rlimit {unsigned long rlim_cur;unsigned long rlim_max;

};

Autor: Alejandro Furfaro 186

tty_struct(/usr/src/linux--[version][version]/includes/linux/tty.h)

struct tty_struct {int magic;struct tty_driver driver;struct tty_ldisc ldisc;struct termios *termios, *termios_locked;int pgrp, session;kdev_tdevice;unsigned long flags;int count;struct winsize winsize;unsigned char stopped:1, hw_stopped:1, flow_stopped:1, packet:1;unsigned char low_latency:1, warned:1;unsigned char ctrl_status;struct tty_struct *link;struct fasync_struct *fasync;struct tty_flip_buffer flip;int max_flip_cnt;int alt_speed; /* For magic substitution of 38400 bps */wait_queue_head_t write_wait;wait_queue_head_t read_wait;struct tq_struct tq_hangup;void *disc_data;

Autor: Alejandro Furfaro 187

tty_struct(/usr/src/linux--[version][version]/includes/linux/tty.h)

void *driver_data;struct list_head tty_files;

#define N_TTY_BUF_SIZE 4096/* The following is data for the N_TTY line discipline. For historical reasons, this is included in the

tty structure./unsigned int column;unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;unsigned char closing:1;unsigned short minimum_to_wake;unsigned long overrun_time;int num_overrun;unsigned long process_char_map[256/(8*sizeof(unsigned long))];char *read_buf;int read_head, read_tail, read_cnt;unsigned long read_flags[N_TTY_BUF_SIZE/(8*sizeof(unsigned long))];int canon_data;unsigned long canon_head;unsigned int canon_column;struct semaphore atomic_readstruct semaphore atomic_write;spinlock_t read_lock;struct tq_struct SAK_tq; /* If the tty has a pending do_SAK, queue it here - akpm */

};

Autor: Alejandro Furfaro 188

thread_struct(/usr/src/linux--[version][version]/includes/asm-i386/processor.h)

struct thread_struct {unsigned long esp0;unsigned long eip;unsigned long esp;unsigned long fs;unsigned long gs;

/* Hardware debugging registers */unsigned long debugreg[8]; /* %%db0-7 debug registers */

/* fault info */unsigned long cr2, trap_no, error_code;

/* floating point info */union i387_union i387;

/* virtual 86 mode info */struct vm86_struct * vm86_info;unsigned long screen_bitmap;unsigned long v86flags, v86mask, saved_esp0;

/* IO permissions */int ioperm;unsigned long io_bitmap[IO_BITMAP_SIZE+1];

};

Autor: Alejandro Furfaro 189

fs_struct(/usr/src/linux--[version][version]/includes/linux/processor.h)

struct fs_struct {atomic_t count;rwlock_t lock;int umask;struct dentry * root, * pwd, * altroot;struct vfsmount * rootmnt, * pwdmnt, * altrootmnt;

};

Autor: Alejandro Furfaro 190

files_struct(/usr/src/linux--[version][version]/includes/linux/sched.h)

/** Open file table structure*/struct files_struct {

atomic_t count;rwlock_t file_lock; /* Protects all the below members. Nests inside tsk->alloc_lock */int max_fds;int max_fdset;int next_fd;struct file ** fd; /* current fd array */fd_set *close_on_exec;fd_set *open_fds;fd_set close_on_exec_init;fd_set open_fds_init;struct file * fd_array[NR_OPEN_DEFAULT];

};

Autor: Alejandro Furfaro 191

signal_struct(/usr/src/linux--[version][version]/includes/linux/sched.h)

struct signal_struct {atomic_t count;struct k_sigaction action[_NSIG];spinlock_t siglock;

};

Autor: Alejandro Furfaro 192

sigpending(/usr/src/linux--[version][version]/includes/linux/signal.h)

struct sigpending {struct sigqueue *head, **tail;sigset_t signal;

};

Autor: Alejandro Furfaro 193

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)

/** include/asm-i386/processor.h** Copyright (C) 1994 Linus Torvalds*/

#ifndef __ASM_I386_PROCESSOR_H#define __ASM_I386_PROCESSOR_H

#include <asm/vm86.h>#include <asm/math_emu.h>#include <asm/segment.h>#include <asm/page.h>#include <asm/types.h>#include <asm/sigcontext.h>#include <asm/cpufeature.h>#include <asm/msr.h>#include <asm/system.h>#include <linux/cache.h>#include <linux/config.h>#include <linux/threads.h>#include <asm/percpu.h>

/* flag for disabling the tsc */extern int tsc_disable;

Autor: Alejandro Furfaro 194

struct desc_struct {unsigned long a,b;

};

#define desc_empty(desc) \(!((desc)->a + (desc)->b))

#define desc_equal(desc1, desc2) \(((desc1)->a == (desc2)->a) && ((desc1)->b == (desc2)->b))

/** Default implementation of macro that returns current* instruction pointer ("program counter").*/

#define current_text_addr() ({ void *pc; __asm__("movl $1f,%0\n1:":"=g" (pc)); pc; })

/** CPU type and hardware bug flags. Kept separately for each CPU.* Members of this structure are referenced in head.S, so think twice* before touching them. [mj]*/

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)

Autor: Alejandro Furfaro 195

struct cpuinfo_x86 {__u8 x86; /* CPU family */__u8 x86_vendor; /* CPU vendor */__u8 x86_model;__u8 x86_mask;char wp_works_ok; /* It doesn't on 386's */char hlt_works_ok; /* Problems on some 486Dx4's and old 386's */char hard_math;char rfu;

int cpuid_level; /* Maximum supported CPUID level, -1=no CPUID */unsigned long x86_capability[NCAPINTS];char x86_vendor_id[16];char x86_model_id[64];int x86_cache_size; /* in KB - valid for CPUS which support this

call */int x86_cache_alignment; /* In bytes */int fdiv_bug;int f00f_bug;int coma_bug;unsigned long loops_per_jiffy;unsigned char x86_num_cores;

} __attribute__((__aligned__(SMP_CACHE_BYTES)));

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)

Autor: Alejandro Furfaro 196

#define X86_VENDOR_INTEL 0#define X86_VENDOR_CYRIX 1#define X86_VENDOR_AMD 2#define X86_VENDOR_UMC 3#define X86_VENDOR_NEXGEN 4#define X86_VENDOR_CENTAUR 5#define X86_VENDOR_RISE 6#define X86_VENDOR_TRANSMETA 7#define X86_VENDOR_NSC 8#define X86_VENDOR_NUM 9#define X86_VENDOR_UNKNOWN 0xff/** capabilities of CPUs*/

extern struct cpuinfo_x86 boot_cpu_data;extern struct cpuinfo_x86 new_cpu_data;extern struct tss_struct doublefault_tss;DECLARE_PER_CPU(struct tss_struct, init_tss);

#ifdef CONFIG_SMPextern struct cpuinfo_x86 cpu_data[];#define current_cpu_data cpu_data[smp_processor_id()]#else#define cpu_data (&boot_cpu_data)#define current_cpu_data boot_cpu_data#endif

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)

Autor: Alejandro Furfaro 197

extern int phys_proc_id[NR_CPUS];extern int cpu_core_id[NR_CPUS];extern char ignore_fpu_irq;extern void identify_cpu(struct cpuinfo_x86 *);extern void print_cpu_info(struct cpuinfo_x86 *);extern unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c);#ifdef CONFIG_X86_HTextern void detect_ht(struct cpuinfo_x86 *c);#elsestatic inline void detect_ht(struct cpuinfo_x86 *c) {}#endif/* * EFLAGS bits */#define X86_EFLAGS_CF 0x00000001 /* Carry Flag */#define X86_EFLAGS_PF 0x00000004 /* Parity Flag */#define X86_EFLAGS_AF 0x00000010 /* Auxillary carry Flag */#define X86_EFLAGS_ZF 0x00000040 /* Zero Flag */#define X86_EFLAGS_SF 0x00000080 /* Sign Flag */#define X86_EFLAGS_TF 0x00000100 /* Trap Flag */#define X86_EFLAGS_IF 0x00000200 /* Interrupt Flag */#define X86_EFLAGS_DF 0x00000400 /* Direction Flag */#define X86_EFLAGS_OF 0x00000800 /* Overflow Flag */#define X86_EFLAGS_IOPL 0x00003000 /* IOPL mask */#define X86_EFLAGS_NT 0x00004000 /* Nested Task */#define X86_EFLAGS_RF 0x00010000 /* Resume Flag */#define X86_EFLAGS_VM 0x00020000 /* Virtual Mode */#define X86_EFLAGS_AC 0x00040000 /* Alignment Check */#define X86 EFLAGS VIF 0x00080000 /* Virtual Interrupt Flag */

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)

Autor: Alejandro Furfaro 198

/* * Generic CPUID function* clear %ecx since some cpus (Cyrix MII) do not set or clear %ecx* resulting in stale register contents being returned.*/

static inline void cpuid(unsigned int op, unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int*edx)

{__asm__("cpuid"

: "=a" (*eax),"=b" (*ebx),"=c" (*ecx),"=d" (*edx)

: "0" (op), "c"(0));}

/* Some CPUID calls want 'count' to be placed in ecx */static inline void cpuid_count(int op, int count, int *eax, int *ebx, int *ecx,

int *edx){

__asm__("cpuid": "=a" (*eax),

"=b" (*ebx),"=c" (*ecx),"=d" (*edx)

: "0" (op), "c" (count));}

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)

Autor: Alejandro Furfaro 199

/* * CPUID functions returning a single datum */static inline unsigned int cpuid_eax(unsigned int op){ unsigned int eax;

__asm__("cpuid": "=a" (eax): "0" (op): "bx", "cx", "dx");

return eax;}static inline unsigned int cpuid_ebx(unsigned int op){

unsigned int eax, ebx;__asm__("cpuid"

: "=a" (eax), "=b" (ebx): "0" (op): "cx", "dx" );

return ebx;}static inline unsigned int cpuid_ecx(unsigned int op){

unsigned int eax, ecx;__asm__("cpuid"

: "=a" (eax), "=c" (ecx): "0" (op): "bx", "dx" );

return ecx;}

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)

Autor: Alejandro Furfaro 200

static inline unsigned int cpuid_edx(unsigned int op){

unsigned int eax, edx;__asm__("cpuid"

: "=a" (eax), "=d" (edx): "0" (op): "bx", "cx");

return edx;}

#define load_cr3(pgdir) \asm volatile("movl %0,%%cr3": :"r" (__pa(pgdir)))

/** Intel CPU features in CR4*/

#define X86_CR4_VME 0x0001 /* enable vm86 extensions */#define X86_CR4_PVI 0x0002 /* virtual interrupts flag enable */#define X86_CR4_TSD 0x0004 /* disable time stamp at ipl 3 */#define X86_CR4_DE 0x0008 /* enable debugging extensions */#define X86_CR4_PSE 0x0010 /* enable page size extensions */#define X86_CR4_PAE 0x0020 /* enable physical address extensions */#define X86_CR4_MCE 0x0040 /* Machine check enable */#define X86_CR4_PGE 0x0080 /* enable global pages */#define X86_CR4_PCE 0x0100 /* enable performance counters at ipl 3 */#define X86_CR4_OSFXSR 0x0200 /* enable fast FPU save and restore */#define X86_CR4_OSXMMEXCPT 0x0400 /* enable unmasked SSE exceptions */

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)

Autor: Alejandro Furfaro 201

/** Save the cr4 feature set we're using (ie* Pentium 4MB enable and PPro Global page* enable), so that any CPU's that boot up* after us can get the correct flags.*/

extern unsigned long mmu_cr4_features;

static inline void set_in_cr4 (unsigned long mask){

mmu_cr4_features |= mask;__asm__("movl %%cr4,%%eax\n\t"

"orl %0,%%eax\n\t""movl %%eax,%%cr4\n": : "irg" (mask):"ax");

}

static inline void clear_in_cr4 (unsigned long mask){

mmu_cr4_features &= ~mask;__asm__("movl %%cr4,%%eax\n\t"

"andl %0,%%eax\n\t""movl %%eax,%%cr4\n": : "irg" (~mask):"ax");

}

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)

Autor: Alejandro Furfaro 202

/** NSC/Cyrix CPU configuration register indexes*/

#define CX86_PCR0 0x20#define CX86_GCR 0xb8#define CX86_CCR0 0xc0#define CX86_CCR1 0xc1#define CX86_CCR2 0xc2#define CX86_CCR3 0xc3#define CX86_CCR4 0xe8#define CX86_CCR5 0xe9#define CX86_CCR6 0xea#define CX86_CCR7 0xeb#define CX86_PCR1 0xf0#define CX86_DIR0 0xfe#define CX86_DIR1 0xff#define CX86_ARR_BASE 0xc4#define CX86_RCR_BASE 0xdc/** NSC/Cyrix CPU indexed register access macros*/

#define getCx86(reg) ({ outb((reg), 0x22); inb(0x23); })

#define setCx86(reg, data) do { \outb((reg), 0x22); \outb((data), 0x23); \

} while (0)

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)

Autor: Alejandro Furfaro 203

static inline void __monitor(const void *eax, unsigned long ecx,unsigned long edx)

{/* "monitor %eax,%ecx,%edx;" */asm volatile(

".byte 0x0f,0x01,0xc8;": :"a" (eax), "c" (ecx), "d"(edx));

}

static inline void __mwait(unsigned long eax, unsigned long ecx){

/* "mwait %eax,%ecx;" */asm volatile(

".byte 0x0f,0x01,0xc9;": :"a" (eax), "c" (ecx));

}

/* from system description table in BIOS. Mostly for MCA use, butothers may find it useful. */extern unsigned int machine_id;extern unsigned int machine_submodel_id;extern unsigned int BIOS_revision;extern unsigned int mca_pentium_flag;

/* Boot loader type from the setup header */extern int bootloader_type;

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)

Autor: Alejandro Furfaro 204

/** User space process size: 3GB (default).*/

#define TASK_SIZE (PAGE_OFFSET)

/* This decides where the kernel will search for a free chunk of vm* space during mmap's.*/

#define TASK_UNMAPPED_BASE (PAGE_ALIGN(TASK_SIZE / 3))

#define HAVE_ARCH_PICK_MMAP_LAYOUT

/** Size of io_bitmap.*/

#define IO_BITMAP_BITS 65536#define IO_BITMAP_BYTES (IO_BITMAP_BITS/8)#define IO_BITMAP_LONGS (IO_BITMAP_BYTES/sizeof(long))#define IO_BITMAP_OFFSET offsetof(struct tss_struct,io_bitmap)#define INVALID_IO_BITMAP_OFFSET 0x8000#define INVALID_IO_BITMAP_OFFSET_LAZY 0x9000

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)

Autor: Alejandro Furfaro 205

struct i387_fsave_struct {long cwd;long swd;long twd;long fip;long fcs;long foo;long fos;long st_space[20]; /* 8*10 bytes for each FP-reg = 80 bytes */long status; /* software status information */

};struct i387_fxsave_struct {

unsigned short cwd;unsigned short swd;unsigned short twd;unsigned short fop;long fip;long fcs;long foo;long fos;long mxcsr;long mxcsr_mask;long st_space[32]; /* 8*16 bytes for each FP-reg = 128 bytes */long xmm_space[32]; /* 8*16 bytes for each XMM-reg = 128 bytes */long padding[56];

} __attribute__ ((aligned (16)));

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)

Autor: Alejandro Furfaro 206

struct i387_soft_struct {long cwd;long swd;long twd;long fip;long fcs;long foo;long fos;long st_space[20]; /* 8*10 bytes for each FP-reg = 80 bytes */unsigned char ftop, changed, lookahead, no_update, rm, alimit;struct info *info;unsigned long entry_eip;

};

union i387_union {struct i387_fsave_struct fsave;struct i387_fxsave_struct fxsave;struct i387_soft_struct soft;

};

typedef struct {unsigned long seg;

} mm_segment_t;

struct thread_struct;

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)

Autor: Alejandro Furfaro 207

struct tss_struct {unsigned short back_link,__blh;unsigned long esp0;unsigned short ss0,__ss0h;unsigned long esp1;unsigned short ss1,__ss1h; /* ss1 is used to cache MSR_IA32_SYSENTER_CS */unsigned long esp2;unsigned short ss2,__ss2h;unsigned long __cr3;unsigned long eip;unsigned long eflags;unsigned long eax,ecx,edx,ebx;unsigned long esp;unsigned long ebp;unsigned long esi;unsigned long edi;unsigned short es, __esh;unsigned short cs, __csh;unsigned short ss, __ssh;unsigned short ds, __dsh;unsigned short fs, __fsh;unsigned short gs, __gsh;unsigned short ldt, __ldth;unsigned short trace, io_bitmap_base;

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)

Autor: Alejandro Furfaro 208

/** The extra 1 is there because the CPU will access an* additional byte beyond the end of the IO permission* bitmap. The extra byte must be all 1 bits, and must* be within the limit.*/

unsigned long io_bitmap[IO_BITMAP_LONGS + 1];/** Cache the current maximum and the last task that used the bitmap:*/

unsigned long io_bitmap_max;struct thread_struct *io_bitmap_owner;/** pads the TSS to be cacheline-aligned (size is 0x100)*/

unsigned long __cacheline_filler[35];/** .. and then another 0x100 bytes for emergency kernel stack*/

unsigned long stack[64];} __attribute__((packed));

#define ARCH_MIN_TASKALIGN 16

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)

Autor: Alejandro Furfaro 209

struct thread_struct {/* cached TLS descriptors. */

struct desc_struct tls_array[GDT_ENTRY_TLS_ENTRIES];unsigned long esp0;unsigned long sysenter_cs;unsigned long eip;unsigned long esp;unsigned long fs;unsigned long gs;

/* Hardware debugging registers */unsigned long debugreg[8]; /* %%db0-7 debug registers */

/* fault info */unsigned long cr2, trap_no, error_code;

/* floating point info */union i387_union i387;

/* virtual 86 mode info */struct vm86_struct __user * vm86_info;unsigned long screen_bitmap;unsigned long v86flags, v86mask, saved_esp0;unsigned int saved_fs, saved_gs;

/* IO permissions */unsigned long *io_bitmap_ptr;

/* max allowed port in the bitmap, in bytes: */unsigned long io_bitmap_max;

};

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)

Autor: Alejandro Furfaro 210

#define INIT_THREAD { \.vm86_info = NULL, \.sysenter_cs = __KERNEL_CS, \.io_bitmap_ptr = NULL, \

}

/** Note that the .io_bitmap member must be extra-big. This is because* the CPU will access an additional byte beyond the end of the IO* permission bitmap. The extra byte must be all 1 bits, and must* be within the limit.*/

#define INIT_TSS { \.esp0 = sizeof(init_stack) + (long)&init_stack, \.ss0 = __KERNEL_DS, \.ss1 = __KERNEL_CS, \.ldt = GDT_ENTRY_LDT, \.io_bitmap_base = INVALID_IO_BITMAP_OFFSET, \.io_bitmap = { [ 0 ... IO_BITMAP_LONGS] = ~0 }, \

}

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)

Autor: Alejandro Furfaro 211

static inline void load_esp0(struct tss_struct *tss, struct thread_struct *thread){

tss->esp0 = thread->esp0;/* This can only happen when SEP is enabled, no need to test "SEP"arately */if (unlikely(tss->ss1 != thread->sysenter_cs)) {

tss->ss1 = thread->sysenter_cs;wrmsr(MSR_IA32_SYSENTER_CS, thread->sysenter_cs, 0);

}}

#define start_thread(regs, new_eip, new_esp) do { \__asm__("movl %0,%%fs ; movl %0,%%gs": :"r" (0)); \set_fs(USER_DS); \regs->xds = __USER_DS; \regs->xes = __USER_DS; \regs->xss = __USER_DS; \regs->xcs = __USER_CS; \regs->eip = new_eip; \regs->esp = new_esp; \

} while (0)

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)

Autor: Alejandro Furfaro 212

/** This special macro can be used to load a debugging register*/

#define loaddebug(thread,register) \__asm__("movl %0,%%db" #register \

: /* no output */ \:"r" ((thread)->debugreg[register]))

/* Forward declaration, a strange C thing */struct task_struct;struct mm_struct;

/* Free all resources held by a thread. */extern void release_thread(struct task_struct *);

/* Prepare to copy thread state - unlazy all lazy status */extern void prepare_to_copy(struct task_struct *tsk);

/** create a kernel thread without removing it from tasklists*/

extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);

extern unsigned long thread_saved_pc(struct task_struct *tsk);void show_trace(struct task_struct *task, unsigned long *stack);

unsigned long get_wchan(struct task_struct *p);

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)

Autor: Alejandro Furfaro 213

#define THREAD_SIZE_LONGS (THREAD_SIZE/sizeof(unsigned long))#define KSTK_TOP(info) \({ \

unsigned long *__ptr = (unsigned long *)(info); \(unsigned long)(&__ptr[THREAD_SIZE_LONGS]); \

})#define task_pt_regs(task) \({ \

struct pt_regs *__regs__; \__regs__ = (struct pt_regs *)KSTK_TOP((task)->thread_info); \__regs__ - 1; \

})#define KSTK_EIP(task) (task_pt_regs(task)->eip)#define KSTK_ESP(task) (task_pt_regs(task)->esp)struct microcode_header {

unsigned int hdrver;unsigned int rev;unsigned int date;unsigned int sig;unsigned int cksum;unsigned int ldrver;unsigned int pf;unsigned int datasize;unsigned int totalsize;unsigned int reserved[3];

};

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)

Autor: Alejandro Furfaro 214

struct microcode {struct microcode_header hdr;unsigned int bits[0];

};

typedef struct microcode microcode_t;typedef struct microcode_header microcode_header_t;

/* microcode format is extended from prescott processors */struct extended_signature {

unsigned int sig;unsigned int pf;unsigned int cksum;

};

struct extended_sigtable {unsigned int count;unsigned int cksum;unsigned int reserved[3];struct extended_signature sigs[0];

};/* '6' because it used to be for P6 only (but now covers Pentium 4 as well) */#define MICROCODE_IOCFREE _IO('6',0)

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)

Autor: Alejandro Furfaro 215

/* REP NOP (PAUSE) is a good thing to insert into busy-wait loops. */static inline void rep_nop(void){

__asm__ __volatile__("rep;nop": : :"memory");}#define cpu_relax() rep_nop()/* generic versions from gas */#define GENERIC_NOP1 ".byte 0x90\n"#define GENERIC_NOP2 ".byte 0x89,0xf6\n"#define GENERIC_NOP3 ".byte 0x8d,0x76,0x00\n"#define GENERIC_NOP4 ".byte 0x8d,0x74,0x26,0x00\n"#define GENERIC_NOP5 GENERIC_NOP1 GENERIC_NOP4#define GENERIC_NOP6 ".byte 0x8d,0xb6,0x00,0x00,0x00,0x00\n"#define GENERIC_NOP7 ".byte 0x8d,0xb4,0x26,0x00,0x00,0x00,0x00\n"#define GENERIC_NOP8 GENERIC_NOP1 GENERIC_NOP7

/* Opteron nops */#define K8_NOP1 GENERIC_NOP1#define K8_NOP2 ".byte 0x66,0x90\n" #define K8_NOP3 ".byte 0x66,0x66,0x90\n" #define K8_NOP4 ".byte 0x66,0x66,0x66,0x90\n" #define K8_NOP5 K8_NOP3 K8_NOP2 #define K8_NOP6 K8_NOP3 K8_NOP3#define K8_NOP7 K8_NOP4 K8_NOP3#define K8_NOP8 K8_NOP4 K8_NOP4

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)

Autor: Alejandro Furfaro 216

/* K7 nops *//* uses eax dependencies (arbitary choice) */#define K7_NOP1 GENERIC_NOP1#define K7_NOP2 ".byte 0x8b,0xc0\n" #define K7_NOP3 ".byte 0x8d,0x04,0x20\n"#define K7_NOP4 ".byte 0x8d,0x44,0x20,0x00\n"#define K7_NOP5 K7_NOP4 ASM_NOP1#define K7_NOP6 ".byte 0x8d,0x80,0,0,0,0\n"#define K7_NOP7 ".byte 0x8D,0x04,0x05,0,0,0,0\n"#define K7_NOP8 K7_NOP7 ASM_NOP1

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)

Autor: Alejandro Furfaro 217

#ifdef CONFIG_MK8#define ASM_NOP1 K8_NOP1#define ASM_NOP2 K8_NOP2#define ASM_NOP3 K8_NOP3#define ASM_NOP4 K8_NOP4#define ASM_NOP5 K8_NOP5#define ASM_NOP6 K8_NOP6#define ASM_NOP7 K8_NOP7#define ASM_NOP8 K8_NOP8#elif defined(CONFIG_MK7)#define ASM_NOP1 K7_NOP1#define ASM_NOP2 K7_NOP2#define ASM_NOP3 K7_NOP3#define ASM_NOP4 K7_NOP4#define ASM_NOP5 K7_NOP5#define ASM_NOP6 K7_NOP6#define ASM_NOP7 K7_NOP7#define ASM_NOP8 K7_NOP8#else#define ASM_NOP1 GENERIC_NOP1#define ASM_NOP2 GENERIC_NOP2#define ASM_NOP3 GENERIC_NOP3#define ASM_NOP4 GENERIC_NOP4#define ASM_NOP5 GENERIC_NOP5#define ASM_NOP6 GENERIC_NOP6#define ASM_NOP7 GENERIC_NOP7#define ASM_NOP8 GENERIC_NOP8#endif

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)

Autor: Alejandro Furfaro 218

#define ASM_NOP_MAX 8/* Prefetch instructions for Pentium III and AMD Athlon *//* It's not worth to care about 3dnow! prefetches for the K6

because they are microcoded there and very slow.However we don't do prefetches for pre XP Athlons currentlyThat should be fixed. */

#define ARCH_HAS_PREFETCHextern inline void prefetch(const void *x){

alternative_input(ASM_NOP4,"prefetchnta (%1)",X86_FEATURE_XMM,"r" (x));

}#define ARCH_HAS_PREFETCH#define ARCH_HAS_PREFETCHW#define ARCH_HAS_SPINLOCK_PREFETCH/* 3dnow! prefetch to get an exclusive cache line. Useful for

spinlocks to avoid one state transition in the cache coherency protocol. */extern inline void prefetchw(const void *x){

alternative_input(ASM_NOP4,"prefetchw (%1)",X86_FEATURE_3DNOW,"r" (x));

}

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)

Autor: Alejandro Furfaro 219

#define spin_lock_prefetch(x) prefetchw(x)

extern void select_idle_routine(const struct cpuinfo_x86 *c);

#define cache_line_size() (boot_cpu_data.x86_cache_alignment)

extern unsigned long boot_option_idle_override;

#endif /* __ASM_I386_PROCESSOR_H */

Procesador específico(/usr/src/linux--[version][version]/includes/linux/processor.h)