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.