You are on page 1of 16

ESCOLA POLITCNICA DA UNIVERSIDADE DE SO PAULO

Departamento de Engenharia de Computao e Sistemas Digitais


J ulho
2008
PCS2042 Sistemas Operacionais
Fase 3 Implementao de um driver simples no
Minix 3, tratamento de DEV_WRITE e DEV_READ
Autores:
Raphael Mielle Trintinalia
Vitor Araujo Romera
Wilson Leo Neto
1
ndice
1 Objetivo ........................................................................................................................2
2 Estudo..........................................................................................................................2
3 Criao do Driver no Minix...........................................................................................4
3.1 Instalando drivers..................................................................................................4
3.2 Nosso Driver teste.c...........................................................................................6
4 Resultados e Concluses...........................................................................................11
4.1 Execuo de echo lixo >/dev/teste.................................................................11
4.2 Redirecionando a sada de um printf para o driver .............................................13
4.3 Execuo de cat /dev/teste................................................................................15
5 Referncias ................................................................................................................15
2
1 Objetivo
O objetivo principal deste exerccio o estudo do funcionamento dos drivers no Minix,
observando a troca de mensagens feita entre o device e o kernel e marcando os pontos de
abertura do driver, configurao, escrita, leitura, envio de status e cancelamento da ao pelo
usurio.
Para a concluso deste estudo, foi proposta a criao de um novo driver no Minix,
simplificado de forma a retornar uma mensagem na tela (printf) a cada recebimento de
mensagem relacionada s aes citadas no pargrafo anterior, desconsiderando as rotinas e os
comandos responsveis pelas aes no dispositivo.
2 Estudo
O estudo comea pela analise dos drivers de dispositivo no MINIX 3,para cada classe de
dispositivo de E/S presente em um sistema MINIX3, existe um driver de dispositivo de E/S
separado. Esses drivers so processos completos, cada um com seu prprio estado,
registradores, pilha, etc. Os drivers de dispositivo se comunicam com o sistema de arquivos
usando o mecanismo de passagem de mensagens padro utilizado por todos os processos do
MINIX3. Um driver de dispositivo simples pode ser escrito como um nico arquivo-fonte.
O principio de projeto do MINIX3, de executar componentes do sistema operacional como
processos completamente separados em espao de usurio so, altamente modular e
moderadamente eficiente. Diferentemente do Unix, no MINIX3 um processo l um arquivo
enviando uma mensagem para o processo de sistema de arquivos. O sistema de arquivos por
sua vez, pode enviar uma mensagem para o driver de disco pedindo para que ele leia o bloco
necessrio. O driver de disco usa chamadas de ncleo para pedir tarefa de sistema para que
faa a E/S real e copie dados entre os processos.
A funo de cada driver aceitar requisies de outros processos e execut-los. Todos os
drivers de dispositivo de bloco recebem uma mensagem, executa esta mensagem e envia uma
resposta. Isso significa que os drivers so estritamente seqenciais e no contem nenhuma
multiprogramao interna, para mante-los simples. Quando uma requisio em hardware feita,
o driver executa uma operao receive especificando que est interessado apenas em aceitar
mensagens de interrupo e no em novos pedidos de trabalho. Todas as novas mensagens de
requisio ficam apenas esperando, at que o trabalho corrente tenha terminado.
As definies necessrias para todos os drivers de dispositivos de bloco esto localizadas em
drivers/libdrivers/driver.h. O cdigo-fonte do lao principal e as funes comuns de todos os
drivers de dipositivo de bloco esto em driver.c. Aps fazer toda inicializao especifica do
hardware necessria, cada driver chama driver_task, passando uma estrutura driver como
argumento da chamada.
Na instruo de switch do lao principal, os primeiros cinco tipos de mensagem, DEV_OPEN,
DEV_CLOSE, DEV_IOCTL, DEV_CANCEL e DEV_SELECT, resultam em chamadas indiretas
usando os endereos passados na estrutura driver. As mensagens DEV_READ e DEV_WRITE
resultam em chamadas diretas para do_rdwt, as mensagens DEV_GATHER e DEV_SCATTER
resultam em chamadas diretas para do_vrdwt. A estrutura driver passada como argumento por
todas as chamadas dentro do comando switch, sejam diretas ou indiretas, portanto, todas as
3
funes chamadas podem fazer mais uso dela conforme necessrio. Do_rdwt e do_vrdwt
realizam algum processamento preliminar, mas ento tambm fazem chamadas indiretas para
rotinas especificas do dispositivo.
Os outros casos, HARD_INT, SYS_SIG e SYN_ALARM, respondem s notificaes. Elas
tambm resultam em chamadas indiretas, mas ao terminar, cada uma delas executa um comando
continue. Isso faz o controle retornar para o inicio do lao, ignorando os passos da limpeza e da
mensagem de resposta.
Figura 1 Exemplo de troca de mensagens entre o User
Process, o File System e o driver.
4
3 Criao do Driver no Minix
3.1 Instalando drivers
Primeiramente, foi necessrio um estudo do funcionamento de um driver, principalmente
no que diz respeito comunicao do programa gerenciador com o Sistema Operacional.
Fizemos tambm uma anlise de drivers j existentes para que pudssemos observar o
tratamento das mensagens e buscar um cdigo mais simples para trabalhar. Optamos, ento, por
basear o nosso driver no driver de impresso (printer), pois este parecia ser mais simples e
tratava um menor nmero de sinais.
Criamos o cdigo para o driver teste.c, contendo o tratamento das requisies enviadas
e alteramos o cdigo dos arquivos .depend e Makefile, adaptando-os ao nosso driver. Para
compilar o cdigo, executamos o comando make que leu o Makefile de forma a criar o
executvel do nosso driver.
Para adicionar o novo device, tivemos de modificar o arquivo /usr/src/servers/fs/dmap.c
que contm o mapeamento de todos os devices utilizados pelo Minix, acrescentando uma entada:
DT(0, no_dev, 0, NONE, DMAP_MUTABLE)
A figura 2 mostra a adio do nosso device /dev/teste no arquivo dmap.c. O nmero 18
no comentrio se refere ao major number utilizado pelo Minix para reconhecer o driver a partir de
uma requisio.
Figura 2 Adio da entrada para /dev/teste em dmap.c
5
Agora, precisvamos criar o device na pasta /dev a partir do nosso cdigo na pasta
usr/src/drivers. Para isso, utilizamos o comando mknod, criando uma juno entre os dois
diretrios:
mknod /dev/<nome> c <int manor> <int mi nor>
Os argumentos inteiros major e minor fazem o reconhecimento do driver que envia a
mensagem. No caso, o Minix reconhece um driver pelo seu major number. Caso estes nmeros
sejam iguais em dois drivers, o Sistema Operacional ir considerar que se tratam do mesmo
dispositivo, mas ainda assim verifica o minor number, que identifica o driver requisitor. Se estes
dois valores forem iguais, o SO observa os dois drivers como um nico.
O argumento c, informa que o driver no ir lidar com requisies de acesso randmicas,
ou seja, as requisies de acesso devem ser especficas e organizadas em uma ordem.
Aps a execuo do comando, pudemos observar o nosso device no diretrio /dev, assim
como mostrado na figura 3 abaixo:
Para finalizar a criao do driver bastava apenas inicializ-lo. Para isso utilizamos o
comando service up. Este comando executa o cdigo que, ligado ao device pelo mknod, inicializa
o driver:
service up <service> [-args args] [-dev special] [-peri od ticks]
Figura 3 Adio da entrada para /dev/teste em dmap.c
6
3.2 Nosso Driver teste.c
Observando agora o cdigo teste.c, mostraremos seus pontos principais, principalmente
o tratamento dos sinais e mensagens.
Primeiramente, vamos observar em alto nvel o tratamento de sinais que deve ser
realizado pelo nosso driver simples:
Figura 4 Tratamento de sinais em caso de escrita
Figura 5 Tratamento de sinais em caso de leitura
7
Abaixo so relacionados os sinais e outras variveis utilizadas pelo driver, entre eles o
buffer de sada (o array obuf) e o ponteiro optr para os dados no buffer.
PRI VATE i nt cal l er ; / * pr ocess t o t el l when pr i nt i ng done( FS) */
PRI VATE i nt ol ef t ; / * byt es of out put l ef t i n obuf */
PRI VATE char obuf [ 128] ; / * out put buf f er */
PRI VATE char *opt r ; / * pt r t o next char i n obuf t o pr i nt */
PRI VATE i nt or i g_count ; / * or i gi nal byt e count */
PRI VATE i nt pr oc_nr ; / * user r equest i ng t he pr i nt i ng */
PRI VATE i nt user _l ef t ; / * byt es of out put l ef t i n user buf */
PRI VATE vi r _byt es user _vi r ; / * addr ess of r emai nder of user buf */
O programa iniciado na rotina teste_task, que verifica o sinal mandado pelo File System
e realiza a tarefa correspondente. Incluimos comandos printf no incio da rotina para verificar o
valor de cada tipo de mensagem.
No loop, chamada a System Call receive para que seja recebida a mensagem enviada
pelo File System e a partir desse ponto, verificado o tipo de mensagem recebida (DEV_OPEN,
DEV_CLOSE, DEV_READ, DEV_WRITE, DEV_STATUS, CANCEL ou outro tipo no tratado pelo
driver).
i nt mai n( voi d)
{
t est e_t ask( ) ;
}
PUBLI C voi d t est e_t ask( )
{
message m;
pr i nt f ( " DEV_READ: %d \ n" , DEV_READ) ;
pr i nt f ( " DEV_OPEN: %d \ n" , DEV_OPEN) ;
pr i nt f ( " DEV_CLOSE: %d \ n" , DEV_CLOSE) ;
pr i nt f ( " HARD_I NT: %d \ n" , HARD_I NT) ;
pr i nt f ( " DEV_WRI TE: %d \ n" , DEV_WRI TE) ;
whi l e( TRUE) {
r ecei ve( ANY, &m) ; / *Bl oquei a ver i f i cando mensagens envi adas par a
est e dr i ver */
swi t ch ( m. m_t ype) {
case DEV_OPEN: pr i nt f ( " DEV_OPEN \ n" ) ;
r epl y( TASK_REPLY, m. m_sour ce, m. I O_ENDPT, OK) ;
br eak;
case DEV_CLOSE: pr i nt f ( " DEV_CLOSE \ n" ) ;
r epl y( TASK_REPLY, m. m_sour ce, m. I O_ENDPT, OK) ;
br eak;
case DEV_READ: pr i nt f ( " DEV_READ \ n" ) ;
do_r ead( &m) ; / * Tr at a a mensagemdo t i po DEV_READ */
br eak;
case DEV_WRI TE: pr i nt f ( " DEV_WRI TE \ n" ) ;
do_wr i t e( &m) ; / * Tr at a a mensagemdo t i po DEV_WRI TE */
br eak;
case DEV_STATUS: pr i nt f ( " DEV_STATUS \ n" ) ;
8
do_st at us( &m) ; / * Tr at a a mensagemdo t i po DEV_STATUS */
br eak;
case CANCEL: pr i nt f ( " CANCEL \ n" ) ;
do_cancel ( &m) ; / * Tr at a a mensagemdo t i po DEV_CANCEL*/
def aul t :
pr i nt f ( " TESTE: mensagemt ype %d f r om%d. \ n" ,
m. m_t ype, m. m_sour ce) ;
br eak;
}
}
}
A rotina reply simplesmente retorna uma mensagem ao File System aps realizar as
tarefas requisitadas. So 4 os parmetros de entrada: code indica o tipo de mensagem para que
seja identificado o retorno; replyee representa o destino da mensagem, geralmente o mesmo
processo que enviou a requisio; process indica o usurio que fez a requisio; status o
resultado da operao feita pelo driver, se houveram ou no erros durante o processo. A funo
reply simplesmente monta a mensagem com os parmetros status e process e envia a
mensagem de volta ao File System. Se houver erro no envio da mensagem, a rotina retorna uma
exceo, indicando a falha.
PRI VATE voi d r epl y( i nt code, i nt r epl yee, i nt pr ocess, i nt st at us)
{
message m;
i nt s;
m. m_t ype = code; / * TASK_REPLY or REVI VE */
m. REP_STATUS = st at us; / * r esul t of devi ce oper at i on */
m. REP_ENDPT = pr ocess; / * whi ch user made t he r equest */
i f ( OK ! = ( s=send( r epl yee, &m) ) )
pani c( " CMOS" , " sendi ng r epl y f ai l ed" , s) ;
}
Como nosso driver no realiza a leitura em nenhum dispositivo, a funo do_read apenas
imprime um printf indicando a requisio de leitura e retorna uma resposta ao File System.
PRI VATE voi d do_r ead( message *m_pt r ) {
pr i nt f ( " Fui l i do\ n" ) ;
r epl y( TASK_REPLY, m_pt r - >m_sour ce, m_pt r - >I O_ENDPT, OK) ;
}
Na funo do_write, atribuimos valores em variveis definidas no incio do cdigo que so
utilizadas na preparao e na impresso na saida - caller representa o processo que pediu a
requisio; proc_nr, o usurio que fez a requisio; user_left e orig_count representam o
tamanho da mensagem, no buffer do usurio; user_vir faz com que o buffer do usurio no seja
sobrescrito. A funo prepare_output instancia o frame buffer chunk com o tamanho do buffer do
9
usurio e verifica se este maior que o buffer de saida. Nesta situao, chunk configurado
com o tamanho do buffer de saida (obuf). feita ento uma cpia de chunk no buffer de saida
atravs de sys_datacopy. Na funo do_printer_output ns registramos os caracteres a serem
impressos a partir do buffer de saida e mostramos na tela, utilizando printf. De volta a do_write
mandamos a mensagem de retorno atravs da rotina reply.
PRI VATE voi d do_wr i t e( message *m_pt r ) {
cal l er = m_pt r - >m_sour ce;
pr oc_nr = m_pt r - >I O_ENDPT;
user _l ef t = m_pt r - >COUNT;
or i g_count = m_pt r - >COUNT;
user _vi r = ( vi r _byt es) m_pt r - >ADDRESS;
pr epar e_out put ( ) ;
do_pr i nt er _out put ( ) ;
r epl y( TASK_REPLY, m_pt r - >m_sour ce, m_pt r - >I O_ENDPT, OK) ;
}
PRI VATE voi d pr epar e_out put ( )
{
/ * St ar t next chunk of pr i nt er out put . Fet ch t he dat a f r omuser space. */
r egi st er i nt chunk;
i f ( ( chunk = user _l ef t ) > si zeof obuf ) chunk = si zeof obuf ;

sys_dat acopy( pr oc_nr , user _vi r , SELF, ( vi r _byt es) obuf , chunk) ;

opt r = obuf ;
ol ef t = chunk;
}
PRI VATE voi d do_pr i nt er _out put ( )
{
r egi st er i nt j = 0;
pr i nt f ( " Numde char s que ser ao i mpr essos: %d, I ni ci ando i mpr essao. . . \ n" ,
user _l ef t ) ;
f or ( j ; j < user _l ef t ; j ++) {
unsi gned char c1 = ( unsi gned char ) *opt r ++;
pr i nt f ( " %c" , c1) ;
}
pr i nt f ( " \ t <- - FI M! \ n" ) ;
}
A rotina do_status altera a mensagem recebida, setando o tipo de mensagem como
DEV_REVIVE (resposta de DEV_STATUS) e inserindo o status como 0 e proc_nr como o
usuriode retorno. Essa nova mensagem enviada de volta ao File System.
10
PRI VATE voi d do_st at us( m_pt r )
r egi st er message *m_pt r ; / * poi nt er t o t he newl y ar r i ved message */
{
m_pt r - >m_t ype = DEV_REVI VE; / * bui l d message */
m_pt r - >REP_ENDPT = pr oc_nr ;
m_pt r - >REP_STATUS = 0;

send( m_pt r - >m_sour ce, m_pt r ) ; / * send t he message */
}
A funo do_cancel chamada no caso de um cancelamento da tarefa pelo usurio,
interrompendo o processo. Esta recebe como parmetro a mensagem enviada pelo File System,
verifica se o usurio que requisitou o cancelamento o mesmo que iniciou o processo e em caso
afirmativo zera o restante do buffer de saida. A confirmao do cancelamento enviada pela
funo reply.
PRI VATE voi d do_cancel ( m_pt r )
r egi st er message *m_pt r ; / * poi nt er t o t he newl y ar r i ved message */
{
i f ( m_pt r - >I O_ENDPT == pr oc_nr ) {
ol ef t = 0; / * cancel out put by i nt er r upt handl er */
}
r epl y( TASK_REPLY, m_pt r - >m_sour ce, m_pt r - >I O_ENDPT, EI NTR) ;
}
11
4 Resultados e Concluses
Pudemos observar o recebimento das mensagens atravs dos printfs inseridos no cdigo,
assim como a ordem de recebimento delas, verificando assim o funcionamento do nosso driver e
concluindo nosso estudo.
As telas reproduzidas abaixo mostram estes resultados obtidos com o exerccio:
4.1 Execuo de echo lixo > /dev/teste
Figura 6 Resultado de uma chamada de escrita
Como observado na figura, obtivemos vrios sinais de DEV_WRITE em virtude de como o
echo foi desenvolvido no minix, para tratar isto executamos uma lgia para apenas tratar o
primeiro DEV_WRITE.
12
Figura 7 Nova chamada de escrita com o tratamento
13
4.2 Redirecionando a sada de um printf para o driver
Pensando em uma forma de apenas executar uma escrita desenvolvemos um programa
simples em C que apenas realiza um printf e redirecionamos a sada do programa para o driver
teste.
Figura 8 Programa simples em C para redirecionar a sada para o nosso driver
Para que nosso driver funcionasse corretamente foi preciso implementar o tratamento da
mensagem DE_IOCTL, confome novo fluxo abaixo.
Figura 9 Novo fluxo de mensagens em caso de escrita
14
Figura 10 Novo loop principal do nosso driver
Figura 11 Execuo do programa simples redirecionando a sada para /driver/teste
15
4.3 Execuo de cat /dev/teste
Para comprovar o funcionamento do nosso driver em caso de leitura, realizamos uma
chamada simples utilizando a funo cat, obtivemos o seguinte resuldado.
Figura 12 Chamada de leitura do nosso driver
5 Referncias
BRONWASSER, Laurens Audio Driver in Minix Development of a user space
driver for the ES1371 based soundcard. Documento PDF. 2005.

You might also like