Linux Devices Drivers Desarrollo de un char device

Anuncio
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Linux Devices Drivers
Desarrollo de un char device
Alejandro Furfaro
Noviembre 2010
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Agenda
1
Prerequisitos.
2
Primer Ejemplo.
3
Registro y funcionalidades.
4
Script de Instalación, No mayor
5
Poniendo al proceso a dormir
6
Manejando Interrupciones
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
¿Que tenemos que instalar antes de empezar?
Instalando los headers del kernel
¿Que más?
Linux Headers
Dijimos que un driver es código de kernel.
En consecuencia, un diver va a invocar funciones internas
del kernel, y utilizará sus esctructuras de datos.
Necesitamos contar con las definiciones de los prototipos
de estas funciones, y estructuras. ¿Donde encontramos
estas definiciones?
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
¿Que tenemos que instalar antes de empezar?
Instalando los headers del kernel
¿Que más?
Linux headers
En los headers del sistema operativo
Los fuentes del Sistema Operativo proporcionan en sus
archivos headers, las definiciones de prototipos de funciones y
de estructuras de datos, que emplea el kernel. Y son las que
empleará nuestro device driver. Por lo tanto hay que instalar los
headers.
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
¿Que tenemos que instalar antes de empezar?
Instalando los headers del kernel
¿Que más?
Versión exacta
Para saber exactamente que versión de kernel tenemos
instalada en el sistema, el comando uname es la respuesta:
˜ $ uname −r
2.6.26−1−686
˜$
Usando la propiedad de encerrar un comando entre ‘ ‘
˜ $ sudo apt−g e t i n s t a l l l i n u x −headers −‘uname −r ‘
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
¿Que tenemos que instalar antes de empezar?
Instalando los headers del kernel
¿Que más?
Ya tenemos los headers ¿y ahora?
˜ $ sudo apt−g e t i n s t a l l l i n u x −manual
E instalamos las man pages del kernel, en donde
econtraremos las funciones internas del kernel mas comunes.
Mucas otras habrá que rebuscar en otros medios.
˜ $ sudo apt−g e t i n s t a l l k e r n e l −i n t e r n a l −guide
Alguna doc, adicional del kernel.
˜ $ sudo apt−g e t i n s t a l l ‘ ‘ L i n u x Device D r i v e r s −3e r . E d i c i ó n
−Alessandro Rubini− Ed . O’ r r e i l y ’ ’
:) Faltarı́a que lo incluyan en el repo. “El libro” para aprender a
programar drivers.
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Tiny example
Manos a la obra
includes y macros
# include <l i n u x / module . h>
# include <l i n u x / i n i t . h>
# include <l i n u x / k e r n e l . h> / ∗ p r i n t k ( ) ∗ /
/∗∗
∗Macro que d e c l a r a e l a u t o r d e l m ódulo
∗∗/
MODULE AUTHOR( ” Fulano−de−t a l ” ) ;
/∗∗
∗Macro que d e c l a r a e l t i p o de l i c e n c i a d e l m ódulo .
∗∗/
MODULE LICENSE( ” Dual BSD/ GPL” ) ;
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Tiny example
Manos a la obra
init y exit (muy básicos)
i n t h e l l o i n i t ( void )
{
p r i n t k ( KERN INFO ” Hola mundo\n ” ) ;
return 0;
}
void h e l l o e x i t ( void )
{
p r i n t k ( KERN INFO ” Chau mundo ! ! ! \ n ” ) ;
}
module init ( h e l l o i n i t ) ;
module exit ( h e l l o e x i t ) ;
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Tiny example
Manos a la obra
Makefile
obj−m : = h e l l o . o
KERNELDIR ?= / l i b / modules / $ ( s h e l l uname −r ) / b u i l d
PWD : = $ ( s h e l l pwd )
default :
$ (MAKE) −C $ ( KERNELDIR ) M=$ (PWD) modules
clean :
rm h e l l o . mod . c h e l l o . o
rm h e l l o . ko h e l l o . mod . o
modules . o r d e r
Module . symvers
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Tiny example
Manos a la obra
compilando...
1
Necesitamos solamente los archivos hello.c anterior y
Makefile.
˜ / DeviceDriverK2 . 6 / h e l l o $ l s −l s
total 8
4 −rw−r−−r−− 1 a l e j a n d r o a l e j a n d r o 589 o c t 19 2009 h e l l o . c
4 −rw−r−−r−− 1 a l e j a n d r o a l e j a n d r o 246 nov 2 15:33 M a k e f i l e
˜ / DeviceDriverK2 . 6 / h e l l o $
2
¿Compilamos?
˜ / DeviceDriverK2 . 6 / h e l l o $ sudo make
[ sudo ] password f o r a l e j a n d r o :
make −C / l i b / modules /2.6.26 −1 −686/ b u i l d M= /home / a l e j a n d r o / DeviceDriverK2 . 6 / h e l l o modules
make [ 1 ] : se i n g r e s a a l d i r e c t o r i o ‘ / u s r / s r c / l i n u x−headers −2.6.26−1−686’
CC [M]
/ home / a l e j a n d r o / DeviceDriverK2 . 6 / h e l l o / h e l l o . o
B u i l d i n g modules , stage 2 .
MODPOST 1 modules
CC
/ home / a l e j a n d r o / DeviceDriverK2 . 6 / h e l l o / h e l l o . mod . o
LD [M]
/ home / a l e j a n d r o / DeviceDriverK2 . 6 / 2 0 0 9 / h e l l o / h e l l o . ko
make [ 1 ] : se s a l e d e l d i r e c t o r i o ‘ / u s r / s r c / l i n u x−headers −2.6.26−1−686’
˜ / DeviceDriverK2 . 6 / h e l l o $
3
Listar el directorio y ver los archivos que se generaron.
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Registrando el dispositivo
Capabilities
Pasando parámetros al driver
haciendo cosas útiles en init y exit
i n t h e l l o i n i t ( void )
{
int result ;
r e s u l t = r e g i s t e r c h r d e v ( h e l l o m a j o r , ” h e l l o 2 ” ,& h e l l o f o p s ) ;
p r i n t k ( KERN INFO ” h e l l o 2 : e l n úmero mayor es %d\n ” , r e s u l t ) ;
hello major = result ;
return 0;
}
void h e l l o e x i t ( void )
{
unregister chrdev ( hello major , ” hello2 ” ) ;
}
module init ( h e l l o i n i t ) ;
module exit ( h e l l o e x i t ) ;
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Registrando el dispositivo
Capabilities
Pasando parámetros al driver
variables importantes
/∗∗
∗ v a r i a b l e donde se almacena e l Major number d e l m ódulo .
∗ Se i n i c i a l i z a a 0 para p e d i r un número a l sistema .
∗/
int hello major = 0;
/∗∗
∗ D e c l a r a c i ó n de l a s f u n c i o n e s que usar á e l d r i v e r
∗ cuando sea accedido .
∗/
struct f i l e o p e r a t i o n s hello fops = {
. open =
hello open ,
. release = hello release ,
. owner =
THIS MODULE
};
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Registrando el dispositivo
Capabilities
Pasando parámetros al driver
Manejando funciones...
/ ∗ Esta f u n c i ó n es l a que c o n t e s t a r á l o s llamados a l a f u n c i ó n
∗ open ( char ∗ , i n t ) cuyo p r i m e r par ámetro corresponda a l nodo
∗ en / dev cuyo major number corresponda a l de e s t e d r i v e r ∗ /
i n t h e l l o o p e n ( s t r u c t inode ∗ inode , s t r u c t f i l e ∗ f i l p )
{
p r i n t k ( KERN INFO ” H e l l o 2 : A p e r t u r a de A r c h i v o \n ” ) ;
return 0;
}
/ ∗ Esta f u n c i ó n es l a que c o n t e s t a r á l o s llamados a l a f u n c i ó n
∗ open ( char ∗ , i n t ) cuyo p r i m e r par ámetro corresponda a l nodo
∗ en / dev cuyo major number corresponda a l de e s t e d r i v e r ∗ /
i n t h e l l o r e l e a s e ( s t r u c t inode ∗ inode , s t r u c t f i l e ∗ f i l p )
{
return 0;
}
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Registrando el dispositivo
Capabilities
Pasando parámetros al driver
Como se hace...
Se pasan por lı́nea de comandos al instalar el driver
insmod . / h e l l o 2 . ko h e l l o m a j o r =355
Se reciben en el código del módulo en una variable
/∗∗
∗ D e c l a r a c i ó n de par ámetro d e l m ódulo , \ r e f t d 3 m a j o r .
∗ Puede s e r seteado por e l user a l hacer e l insmod .
∗ ∗/
module param ( h e l l o m a j o r , i n t , 0 ) ;
module param tiene tres argumentos: nombre, tipo de dato, y la
máscara de permisos con la que se creará la entrada en sysfs
El tipo de dato es bool, asociado a una variable que debe ser de
tipo int, (evaluada como TRUE o FALSE), o invbool que
invierte el valor de la variable, o charp, para manejar strings.
luego los tipos clásicos del C: int, long, short, uint,
ulong, ushort
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Automatizando la instalación
Como obtenemos en No Mayor
Alojando dinámicamente el No Mayor
Al fuente hello3.c y a Makefile, en este tercer ejemplo
sumamos un shell script para obtener dinámicamente el
Número Mayor, y con él crear automáticamente los nodos
necesarios en el /dev.
˜ / DeviceDriverK2 . 6 / d r i v e r 3 $ cat h e l l o 3 d r i v e r . i n i t
En particular importan las lı́neas 59 a 63 del script (activarlas
en el editor que estén usando!!)
i f $INSMOD $devpath $OPTIONS ; then
MAJOR= ‘awk ” \\$2==\ ” $DEVICE\ ” { p r i n t \\$1} ” / proc / devices ‘
r e m o v e f i l e s $FILES
c r e a t e f i l e s $FILES
else
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Automatizando la instalación
Como obtenemos en No Mayor
Alojando dinámicamente el No Mayor
Al fuente hello3.c y a Makefile, en este tercer ejemplo
sumamos un shell script para obtener dinámicamente el
Número Mayor, y con él crear automáticamente los nodos
necesarios en el /dev.
˜ / DeviceDriverK2 . 6 / d r i v e r 3 $ cat h e l l o 3 d r i v e r . i n i t
En particular importan las lı́neas 59 a 63 del script (activarlas
en el editor que estén usando!!)
i f $INSMOD $devpath $OPTIONS ; then
MAJOR= ‘awk ” \\$2==\ ” $DEVICE\ ” { p r i n t \\$1} ” / proc / devices ‘
r e m o v e f i l e s $FILES
c r e a t e f i l e s $FILES
else
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Automatizando la instalación
Como obtenemos en No Mayor
Alojando dinámicamente el No Mayor
Al fuente hello3.c y a Makefile, en este tercer ejemplo
sumamos un shell script para obtener dinámicamente el
Número Mayor, y con él crear automáticamente los nodos
necesarios en el /dev.
˜ / DeviceDriverK2 . 6 / d r i v e r 3 $ cat h e l l o 3 d r i v e r . i n i t
En particular importan las lı́neas 59 a 63 del script (activarlas
en el editor que estén usando!!)
i f $INSMOD $devpath $OPTIONS ; then
MAJOR= ‘awk ” \\$2==\ ” $DEVICE\ ” { p r i n t \\$1} ” / proc / devices ‘
r e m o v e f i l e s $FILES
c r e a t e f i l e s $FILES
else
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Automatizando la instalación
Como obtenemos en No Mayor
awk... mucho mas que un comando
La base es el potente awk (o gawk en Linux, por GNU awk).
Veamos un ejemplo desde el shell:
˜ / $ awk ” \$2==\ ” sound\ ” { p r i n t \$1} ” / proc / d e v i c e s
14
˜ / $ l s −l a s / dev / dsp
0 crw−rw−−−− 1 r o o t audio 14 , 3 nov 8 13:58 / dev / dsp
˜/$
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Antes que nada...
recursos del kernel
Mandando a dormir al proceso desde el kernel
Manejando eficientemente los bloqueos a procesos
Escritura del dispositivo
el método que pone el proceso a dormir es,
tı́picamente, read
Primer paso: agregamos los métodos en File Operations.
struct f i l e o p e r a t i o n s hello fops = {
. read =
h e l l o r e a d , / / read ( ) es bloqueante .
. open =
hello open ,
. release = hello release ,
. owner =
THIS MODULE
};
Segundo paso: Escribimos la función read.
s s i z e t h e l l o r e a d ( s t r u c t f i l e ∗ f i l p , char
u s e r ∗ buf ,
s i z e t count , l o f f t ∗ f p o s ) {
int result ;
r e s u l t = c o p y t o u s e r ( buf , k b u f f e r , s t r l e n ( k b u f f e r ) ) ;
p r i n t k ( KERN INFO ” h e l l o 4 : d i s p o s i t i v o l e ı́ d o con número menor
%d\n ” , i m i n o r ( f i l p −>p r i v a t e d a t a ) ) ;
return s t r l e n ( k b u f f e r ) ;
}
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Antes que nada...
recursos del kernel
Mandando a dormir al proceso desde el kernel
Manejando eficientemente los bloqueos a procesos
Escritura del dispositivo
el método que pone el proceso a dormir es,
tı́picamente, read
Primer paso: agregamos los métodos en File Operations.
struct f i l e o p e r a t i o n s hello fops = {
. read =
h e l l o r e a d , / / read ( ) es bloqueante .
. open =
hello open ,
. release = hello release ,
. owner =
THIS MODULE
};
Segundo paso: Escribimos la función read.
s s i z e t h e l l o r e a d ( s t r u c t f i l e ∗ f i l p , char
u s e r ∗ buf ,
s i z e t count , l o f f t ∗ f p o s ) {
int result ;
r e s u l t = c o p y t o u s e r ( buf , k b u f f e r , s t r l e n ( k b u f f e r ) ) ;
p r i n t k ( KERN INFO ” h e l l o 4 : d i s p o s i t i v o l e ı́ d o con número menor
%d\n ” , i m i n o r ( f i l p −>p r i v a t e d a t a ) ) ;
return s t r l e n ( k b u f f e r ) ;
}
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Antes que nada...
recursos del kernel
Mandando a dormir al proceso desde el kernel
Manejando eficientemente los bloqueos a procesos
Escritura del dispositivo
el método que pone el proceso a dormir es,
tı́picamente, read
Primer paso: agregamos los métodos en File Operations.
struct f i l e o p e r a t i o n s hello fops = {
. read =
h e l l o r e a d , / / read ( ) es bloqueante .
. open =
hello open ,
. release = hello release ,
. owner =
THIS MODULE
};
Segundo paso: Escribimos la función read.
s s i z e t h e l l o r e a d ( s t r u c t f i l e ∗ f i l p , char
u s e r ∗ buf ,
s i z e t count , l o f f t ∗ f p o s ) {
int result ;
r e s u l t = c o p y t o u s e r ( buf , k b u f f e r , s t r l e n ( k b u f f e r ) ) ;
p r i n t k ( KERN INFO ” h e l l o 4 : d i s p o s i t i v o l e ı́ d o con número menor
%d\n ” , i m i n o r ( f i l p −>p r i v a t e d a t a ) ) ;
return s t r l e n ( k b u f f e r ) ;
}
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Antes que nada...
recursos del kernel
Mandando a dormir al proceso desde el kernel
Manejando eficientemente los bloqueos a procesos
Escritura del dispositivo
copy to user y copy from user
A copy from user la utilizaremos cuando nos ocupemos del
método write
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Antes que nada...
recursos del kernel
Mandando a dormir al proceso desde el kernel
Manejando eficientemente los bloqueos a procesos
Escritura del dispositivo
Hace falta retocar el resto
Tercer paso: en el método open (), conseguimos la referencia
a filp->private data.
/∗∗
∗ p u n t e r o a b u f f e r de cada d e v i c e .
∗ ∗/
char ∗ k b u f f e r ;
i n t h e l l o o p e n ( s t r u c t inode ∗ inode , s t r u c t f i l e ∗ f i l p ) {
unsigned i n t minor number = i m i n o r ( inode ) ;
p r i n t k ( KERN INFO ” h e l l o 4 : d e v i c e %d opened\n ” , minor number ) ;
/∗∗
∗ Para que l o s o t r o s metodos puedan o b t e n e r e l numero menor ,
∗ l o guardo en e l campo p r i v a t e d a t a de l a e s t r u c t u r a f i l e .
∗ ∗/
f i l p −>p r i v a t e d a t a = inode ;
k b u f f e r = k m a l l o c ( s t r l e n ( INIT STRING ) , GFP KERNEL ) ;
memcpy ( ( char ∗ ) k b u f f e r , INIT STRING , s t r l e n ( INIT STRING ) + 1 ) ;
return 0;
}
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Antes que nada...
recursos del kernel
Mandando a dormir al proceso desde el kernel
Manejando eficientemente los bloqueos a procesos
Escritura del dispositivo
Semáforos del kernel
Primero instanciamos la estructura semaphore que define un semáforo
del kernel, e inicializamos el semáforo en el método open().
/∗∗
∗ p u n t e r o a b u f f e r de cada d e v i c e .
∗ ∗/
char ∗ k b u f f e r ;
/∗∗
∗ Sem áforos para s i n c r o n i z a r e l acceso a l b u f f e r .
∗ ∗/
s t r u c t semaphore sem ;
i n t h e l l o o p e n ( s t r u c t inode ∗ inode , s t r u c t f i l e ∗ f i l p ) {
unsigned i n t minor number = i m i n o r ( inode ) ;
p r i n t k ( KERN INFO ” h e l l o 5 : d e v i c e %d opened\n ” , minor number ) ;
k b u f f e r =NULL ;
s e m a i n i t (&sem , 1 ) ; / / l i b e r a d o , para c o n t r o l de acceso a k b u f f e r
return 0;
}
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Antes que nada...
recursos del kernel
Mandando a dormir al proceso desde el kernel
Manejando eficientemente los bloqueos a procesos
Escritura del dispositivo
Tomando el semáforo
Una vez creado lo manejamos desde el método read (). Usamos la
familia de funciones down () del kernel
s s i z e t h e l l o r e a d ( s t r u c t f i l e ∗ f i l p , char
u s e r ∗ buf ,
s i z e t count , l o f f t ∗ f p o s )
{
i f ( ! d o w n i n t e r r u p t i b l e (&sem ) )
{
k b u f f e r = k m a l l o c ( s t r l e n ( INIT STRING ) + 1 , GFP KERNEL ) ;
memcpy ( ( char ∗ ) k b u f f e r , INIT STRING , s t r l e n ( INIT STRING ) + 1 ) ;
/∗ copio e l contenido a l b u f f e r del usuario ∗/
i f ( c o p y t o u s e r ( buf , k b u f f e r , s t r l e n ( k b u f f e r ) ) )
{
up(&sem ) ;
p r i n t k ( KERN INFO ” h e l l o 5 : E r r o r de L e c t u r a en c o p y t o u s e r
r e t u r n −EFAULT ;
}
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Antes que nada...
recursos del kernel
Mandando a dormir al proceso desde el kernel
Manejando eficientemente los bloqueos a procesos
Escritura del dispositivo
liberando el semáforo
La función de liberación de un semáforo es: void
up(struct semaphore *sem);
else
{
up(&sem ) ;
p r i n t k ( KERN INFO ” h e l l o 5 : L e c t u r a e x i t o s a , %d b y t e s \n ” , s
return s t r l e n ( k b u f f e r ) ;
}
}
else
{
p r i n t k ( KERN INFO ” h e l l o 5 :
L e c t u r a con e r r o r toma de sem áforo \n ” ) ;
r e t u r n −EFAULT ;
}
}
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Antes que nada...
recursos del kernel
Mandando a dormir al proceso desde el kernel
Manejando eficientemente los bloqueos a procesos
Escritura del dispositivo
funciones down
En el mundo Linux, la función P se llama down (o alguna
variedad de ese nombre).
‘‘down’’ se refiere al hecho que su operación consiste
en:
decrementar el valor del semáforo,
por lo general pone a “dormir” al proceso que invocó al
método del módulo, hasta que el semáforo se libere,
permitir el acceso a un recurso de manera segura
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Antes que nada...
recursos del kernel
Mandando a dormir al proceso desde el kernel
Manejando eficientemente los bloqueos a procesos
Escritura del dispositivo
funciones down
Hay tres variantes o versiones de down
void down(struct semaphore *sem);
int down interruptible(struct semaphore
*sem);
int down trylock(struct semaphore *sem);
down decrementa el valor del semáforo y espera lo que
resulte necesario.
down interruptible hace lo mismo pero la operación
es interrumpible.
down trylock nunca duerme. Si el semáforo no
está disponible al momento de la llamada, retorna
immediatamente un valor no nulo.
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Antes que nada...
recursos del kernel
Mandando a dormir al proceso desde el kernel
Manejando eficientemente los bloqueos a procesos
Escritura del dispositivo
Versiones Interrumpibles vs. No Interrumpibles
Debemos siempre tratar de utilizar la versión interrumpible
de cualquier llamada para dormir un proceso, ya que permite
..que desde el espacio de usuario, el proceso que está dormido esperando (por un semáforo en este caso), pueda ser
interrumpido por el usuario.
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Antes que nada...
recursos del kernel
Mandando a dormir al proceso desde el kernel
Manejando eficientemente los bloqueos a procesos
Escritura del dispositivo
Versiones Interrumpibles vs. No Interrumpibles
Regla general
No usar operaciones No interrumpibles, a menos que sea
absolutamente necesario. Las operaciones No Interrumpibles
son una excelente forma de crear procesos que no puedan ser
terminados de ninguna manera posible (aparecen en estado D
por Dreaded en la salida de ps).
El uso de down interruptible requiere algunas
precaucionea adicionales, sin embargo, al ser interrumpida la
operación, la función retorna un valor no nulo, y el proceso
llamante no mantiene el semaforo.
Por lo tanto, el uso correcto de down interruptible requiere
siempre chequear el valor retornado, y responder de manera
acorde al proceso invocante.
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Antes que nada...
recursos del kernel
Mandando a dormir al proceso desde el kernel
Manejando eficientemente los bloqueos a procesos
Escritura del dispositivo
Durmiendo y despertando al escribir
Antes de mirar este tramo de código, conviene abrir
hello5.c y mirar la definición de file operations ;)
s s i z e t h e l l o w r i t e ( s t r u c t f i l e ∗ f i l p , const char
u s e r ∗ buf ,
s i z e t count , l o f f t ∗ f p o s ) {
i f ( ! d o w n i n t e r r u p t i b l e (&sem ) ) {
kfree ( k b u f f e r ) ; /∗ destruyo e l b u f f e r actual ∗/
/ / p i d o ( count ) bytes , e s p e c i f i c a d o s por e l u s u a r i o .
k b u f f e r = k m a l l o c ( count , GFP KERNEL ) ;
i f ( ! kbuffer ) {
/ ∗ s i hubo un e r r o r , devuelvo −ENONMEM∗ /
p r i n t k ( KERN INFO ” h e l l o 5 : E s c r i t u r a con e r r o r acceso a memoria \
k b u f f e r = NULL ;
up(&sem ) ;
r e t u r n −ENOMEM;
}
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Antes que nada...
recursos del kernel
Mandando a dormir al proceso desde el kernel
Manejando eficientemente los bloqueos a procesos
Escritura del dispositivo
Durmiendo y despertando al escribir
/∗ copio a k b u f f e r e l buf del usuario ,
∗ s i hay e r r o r devuelvo −EFAULT ∗ /
i f ( c o p y f r o m u s e r ( k b u f f e r , buf , count ) ) {
p r i n t k ( KERN INFO ” h e l l o 5 : E s c r i t u r a con e r r o r en c o p y f r o m u s e r
kfree ( kbuffer ) ;
up(&sem ) ;
r e t u r n −EFAULT ;
}
else {
p r i n t k ( KERN INFO ” h e l l o 5 : E s c r i t u r a k m a l l o c para %d b y t e s \n ” , c
p r i n t k ( KERN INFO ” h e l l o 5 : %s \n ” , k b u f f e r ) ;
up(&sem ) ;
r e t u r n count ;
}
}
else {
p r i n t k ( KERN INFO ” h e l l o 5 : E s c r i t u r a con e r r o r toma de semaforo \n ”
r e t u r n −EFAULT ;
}
return 0;
}
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Para usar una IRQ se la pedimos al kernel
el handler
bloquenado procesos
¿Donde se pide?
Abrir holla6.c e ir al método open ().
i n t h e l l o o p e n ( s t r u c t inode ∗ inode , s t r u c t f i l e ∗ f i l p ) {
unsigned i n t minor number = i m i n o r ( inode ) ;
p r i n t k ( KERN INFO ” h e l l o 6 : d e v i c e %d opened\n ” , minor number ) ;
k b u f f e r =NULL ;
s e m a i n i t (&sem , 0 ) ; / / tomado , para c o n t r o l de acceso a k b u f f e r
t i c k c o u n t e r = TICK RELOAD ;
p r i n t k ( KERN INFO ” h e l l o 6 : Opening i r q d e v i c e \n ” ) ;
i f ( r e q u e s t i r q ( IRQ USED , &my handler , IRQF SHARED , ” h e l l o 6 ” , ” u f a ”
r e t u r n −EBUSY;
p r i n t k ( KERN INFO ” h e l l o 6 : i r q t i c k c o u n t e r = %d\n ” , t i c k c o u n t e r ) ;
p r i n t k ( KERN INFO ” h e l l o 6 : Salgo d e l Opening i r q d e v i c e \n ” ) ;
return 0;
}
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Para usar una IRQ se la pedimos al kernel
el handler
bloquenado procesos
que hacemos en el hadnler?
Accedemos al hardware, y
despertamos al proceso que ha sido bloqueado cuando
solicitó los datos disponibles
i r q r e t u r n t my handler ( i n t i r q , void ∗ d e v i d ) / / , s t r u c t p t r e g s ∗ re
{
t i c k c o u n t e r −−;
p r i n t k ( KERN INFO ” h e l l o 6 : my handler con t i c k c o u n t e r= %d \n ” , t i c k c o
if (! tick counter ) {
p r i n t k ( KERN INFO ” h e l l o 6 : t i c k c o u n t e r =0 l i b e r o sem \n ” ) ;
t i c k c o u n t e r = TICK RELOAD ;
up(&sem ) ;
w a k e u p i n t e r r u p t i b l e (& t d i g w a i t q u e u e ) ;
}
r e t u r n IRQ HANDLED ;
}
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Para usar una IRQ se la pedimos al kernel
el handler
bloquenado procesos
No lo habı́amos hecho con semáforos? que mas?
Utilizamos colas de mensaje
/∗∗
∗ Waitqueue que usaremos en e l modo de espera .
∗/
DECLARE WAIT QUEUE HEAD( t d i g w a i t q u e u e ) ;
Cuando el proceso requiere los datos (invoca el método
read () de nuestro módulo), lo bloqueamos hasta que los
datos estén disponibles, utilizando otras funciones algo
mas poderosas que los semáforos
s s i z e t h e l l o r e a d ( s t r u c t f i l e ∗ f i l p , char
u s e r ∗ buf , s i z e t cou
p r i n t k ( KERN INFO ” h e l l o 6 : L e c t u r a i n g r e s o \n ” ) ;
i f ( ! d o w n i n t e r r u p t i b l e (&sem ) )
{
k b u f f e r = k m a l l o c ( s t r l e n ( INIT STRING ) + 1 , GFP KERNEL ) ;
memcpy ( ( char ∗ ) k b u f f e r , INIT STRING , s t r l e n ( INIT STRING ) + 1
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Para usar una IRQ se la pedimos al kernel
el handler
bloquenado procesos
No lo habı́amos hecho con semáforos? que mas?
Utilizamos colas de mensaje
/∗∗
∗ Waitqueue que usaremos en e l modo de espera .
∗/
DECLARE WAIT QUEUE HEAD( t d i g w a i t q u e u e ) ;
Cuando el proceso requiere los datos (invoca el método
read () de nuestro módulo), lo bloqueamos hasta que los
datos estén disponibles, utilizando otras funciones algo
mas poderosas que los semáforos
s s i z e t h e l l o r e a d ( s t r u c t f i l e ∗ f i l p , char
u s e r ∗ buf , s i z e t cou
p r i n t k ( KERN INFO ” h e l l o 6 : L e c t u r a i n g r e s o \n ” ) ;
i f ( ! d o w n i n t e r r u p t i b l e (&sem ) )
{
k b u f f e r = k m a l l o c ( s t r l e n ( INIT STRING ) + 1 , GFP KERNEL ) ;
memcpy ( ( char ∗ ) k b u f f e r , INIT STRING , s t r l e n ( INIT STRING ) + 1
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Para usar una IRQ se la pedimos al kernel
el handler
bloquenado procesos
No lo habı́amos hecho con semáforos? que mas?
Utilizamos colas de mensaje
/∗∗
∗ Waitqueue que usaremos en e l modo de espera .
∗/
DECLARE WAIT QUEUE HEAD( t d i g w a i t q u e u e ) ;
Cuando el proceso requiere los datos (invoca el método
read () de nuestro módulo), lo bloqueamos hasta que los
datos estén disponibles, utilizando otras funciones algo
mas poderosas que los semáforos
s s i z e t h e l l o r e a d ( s t r u c t f i l e ∗ f i l p , char
u s e r ∗ buf , s i z e t cou
p r i n t k ( KERN INFO ” h e l l o 6 : L e c t u r a i n g r e s o \n ” ) ;
i f ( ! d o w n i n t e r r u p t i b l e (&sem ) )
{
k b u f f e r = k m a l l o c ( s t r l e n ( INIT STRING ) + 1 , GFP KERNEL ) ;
memcpy ( ( char ∗ ) k b u f f e r , INIT STRING , s t r l e n ( INIT STRING ) + 1
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Prerequisitos
Primer Ejemplo
Registro y parámetros
Script de Instalación, No mayor
Poniendo al proceso a dormir
Manejando Interrupciones
Para usar una IRQ se la pedimos al kernel
el handler
bloquenado procesos
No lo habı́amos hecho con semáforos? que mas?
i f ( c o p y t o u s e r ( buf , k b u f f e r , s t r l e n ( k b u f f e r ) ) ) / ∗ c o p i o e l c o n t e
{
up(&sem ) ;
p r i n t k ( KERN INFO ” h e l l o 6 : L e c t u r a con e r r o r en c o p y t o u s e r \n ”
r e t u r n −EFAULT ;
}
else
{
//
up(&sem ) ;
p r i n t k ( KERN INFO ” h e l l o 6 : L e c t u r a e x i t o s a , %d b y t e s \n ” , s t r l e n
return s t r l e n ( k b u f f e r ) ;
}
}
else
{
p r i n t k ( KERN INFO ” h e l l o 6 : L e c t u r a con e r r o r toma de semaforo \n ” ) ;
r e t u r n −EFAULT ;
}
}
Alejandro Furfaro
Linux Devices DriversDesarrollo de un char device
Descargar