Avançar para a área de conteúdo

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

A primeira vez que acessar o developerWorks, um perfil será criado para você. Informações do seu perfil (tais como: nome, país / região, e empresa) estarão disponíveis ao público, que poderá acompanhar qualquer conteúdo que você publicar. Seu perfil no developerWorks pode ser atualizado a qualquer momento.

Todas as informações enviadas são seguras.

  • Fechar [x]

Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

Todas as informações enviadas são seguras.

  • Fechar [x]

Desempenho de Aplicativos Boost Usando E/S Assíncrona

Saiba quando e como Usar a API POSIX AIO

M. Tim Jones, Consultant Engineer, Emulex Corp.
M. Tim Jones
M. Tim Jones é um arquiteto de firmwares embarcados e autor de Inteligência Artificial: Sistemas de Abordagem, GNU/Linux, Programação de Aplicativos AI (atualmente em sua segunda edição), Programação de Aplicativos AI (em sua segunda edição) e BSD Sockets Programming from a Multilanguage Perspective. Sua formação em engenharia vai desde o desenvolvimento de kernels para nave espacial geossincrônica até a arquitetura de sistema embarcado e o desenvolvimento de protocolos de interligação de redes. Tim é um Engenheiro Consultor para a Emulex Corp. em Longmont, Colorado.

Resumo:  O modelo de entrada/saída (E/S) mais comum usado no Linux® é a E/S síncrona. Depois que um pedido é feito nesse modelo, o aplicativo é bloqueado até que o pedido seja atendido. Isso é um grande paradigma, pois o aplicativo de chamada não exige nenhuma Unidade Central de Processamento (CPU) enquanto espera pela conclusão do pedido de E/S. Mas, em alguns casos, há necessidade de sobrepor um pedido de E/S com outro processamento. A Interface de Programação do Aplicativo (API) de E/S assíncrona (AIO) de Portable Operating System Interface (POSIX) fornece esse recurso. Neste artigo, obtenha uma visão geral da API e veja como usá-la.

Data:  29/Ago/2006
Nível:  Intermediário
Atividade:  1914 visualizações
Comentários:  


Introdução à AIO

A E/S assíncrona do Linux é uma inclusão relativamente recente no kernel Linux. Ela é um recurso padrão do kernel 2.6, mas é possível encontrar correções para a versão 2.4. A ideia básica por trás da AIO é permitir que um processo inicie diversas operações de E/S sem precisar bloquear ou esperar a conclusão de alguma. Posteriormente, ou após a notificação da conclusão da E/S, o processo pode recuperar os resultados da E/S.

Modelos de E/S

Antes de falarmos da API AIO, vamos explorar os diferentes modelos de E/S que estão disponíveis para Linux. Não pretendemos fazer uma revisão cansativa, mas sim abranger os modelos mais comuns para ilustrar suas diferenças em relação à E/S assíncrona. A Figura 1 mostra os modelos síncronos e assíncronos, bem como os modelos com e sem bloqueio.


Figura 1. Matriz Simplificada dos Modelos Básicos de E/S do Linux
Matriz Simplificada dos Modelos Básicos de E/S do Linux

Cada um desses modelos de E/S tem padrões de uso que são vantajosos para determinados aplicativos. Esta seção explora brevemente cada um deles.

E/S com Bloqueio Síncrono

Limite de E/S versus Processos de Limite de CPU

Um processo de limite de E/S é aquele que executa mais E/S que processamento. Um processo de limite de CPU efetua mais processamento que a E/S. O planejador Linux 2.6 prefere os processos de limite de E/S porque geralmente eles iniciam uma E/S e depois um bloqueio, o que siginifica que outros trabalhos podem ser interligados de modo eficiente entre eles.

Um dos modelos mais comuns é o modelo de E/S de bloqueio síncrono. Neste modelo, o aplicativo de espaço de usuário executa uma chamada do sistema que resulta no bloqueio do aplicativo. Isso significa que o aplicativo é bloqueado até que a chamada do sistema seja concluída (dados transferidos ou com erros). O aplicativo de chamada está em um estado no qual não consome CPU e simplesmente espera pela resposta, assim, se torna eficiente em uma perspectiva de processamento.

A Figura 2 ilustra o modelo de E/S de bloqueio tradicional, que também é o modelo mais comum usado atualmente em aplicativos. Seus comportamentos são bem entendidos e seu uso é eficiente para aplicativos típicos. Quando a chamada do sistema read é invocada, o aplicativo é bloqueado e o contexto alterna para o kernel. A chamada read é então iniciada e quando a resposta é retornada (do dispositivo do qual a leitura está sendo feita), os dados são movidos para o buffer de espaço do usuário. Depois, o aplicativo é desbloqueado (e a chamada read é retornada).


Figura 2. Fluxo Típico do Modelo de E/S de Bloqueio Síncrono
Fluxo Típico do Modelo de E/S de Bloqueio Síncrono

Da perspectiva do aplicativo, a chamada read tem longa duração. Mas, na verdade, o aplicativo é bloqueado enquanto a chamada read é multiplexada com outro trabalho no kernel.

E/S sem Bloqueio Síncrono

Uma variante menos eficiente de bloqueio síncrono é a E/S sem bloqueio síncrono. Neste modelo, um dispositivo é aberto como sem bloqueio. Isso significa que em vez de concluir uma E/S imediatamente, uma chamada read pode retornar um código de erro indicando que o comando não pode ser atendido imediatamente (EAGAIN ou EWOULDBLOCK), como mostrado na Figura 3.


Figura 3. Fluxo Típico do Modelo de E/S sem Bloqueio Síncrono
Fluxo Típico do Modelo de E/S sem Bloqueio Síncrono

A implicação da falta de bloqueio é que um comando de E/S pode não ser atendido imediatamente, exigindo que o aplicativo faça várias chamadas para aguardar a conclusão. Isso pode ser extremamente ineficaz porque, em muitos casos, o aplicativo deve aguardar ocupado até que os dados fiquem disponíveis ou tentar fazer outro trabalho enquanto o comando é executado no kernel. Também como mostrado na Figura 3, esse método pode apresentar latência na E/S pois qualquer diferença entre os dados que estão sendo disponibilizados no kernel e o usuário que chama read para retorno pode reduzir o processamento geral dos dados.

E/S de Bloqueio Assíncrono

Outro paradigma de bloqueio é a E/S sem bloqueio, com notificações de bloqueio. Neste modelo, a E/S sem bloqueio é configurada e depois a chamada do sistema select de bloqueio é usada para determinar quando há qualquer atividade para um descritor de E/S. Isso torna a chamada select interessante, pois ela pode ser usada para fornecer notificação não apenas para um descritor, mas para muitos. Para cada descritor, é possível solicitar notificação da capacidade do descritor em gravar dados, da disponibilidade para ler dados e também da ocorrência de um erro.


Figura 4. Fluxo Típico do Modelo de E/S de Bloqueio Assíncrono (select)
Fluxo Típico do Modelo de E/S de Bloqueio Assíncrono

O principal problema da chamada select é que ela não é muito eficiente. Embora seja um modelo conveniente para notificação assíncrona, seu uso para E/S de alto desempenho não é aconselhado.

E/S sem Bloqueio Assíncrono (AIO)

Finalmente, o modelo de E/S sem bloqueio assíncrono é um dos processamentos de sobreposição com E/S. O pedido read é retornado imediatamente, indicando que read foi inicializado com êxito. O aplicativo pode então desempenhar outro processamento enquanto a operação de leitura é concluída em segundo plano. Quando a resposta read chega, um sinal ou retorno de chamada baseado no encadeamento pode ser gerado para concluir a transação de E/S.


Figura 5. Fluxo Típico do Modelo de E/S sem Bloqueio Assíncrono
Fluxo Típico do Modelo de E/S sem Bloqueio Assíncrono (AIO)

A capacidade de sobreposição de computação e processamento de E/S em um único processo para que, potencialmente, vários pedidos de E/S explore a diferença entre a velocidade do processamento e a velocidade da E/S. Embora um ou mais pedidos de E/S lentos estejam pendentes, a CPU pode executar outras tarefas ou, mais comumente, operar em E/Ss já concluídas enquanto outras E/Ss são iniciadas.

A próxima seção examina mais profundamente esse modelo, explora a API e depois demonstra diversos comandos.


Motivação para E/S Assíncrona

Na taxonomia anterior de modelos de E/S, é possível ver a motivação da AIO. Os modelos de bloqueio exigem que o aplicativo de inicialização seja bloqueado quando a E/S é iniciada. Isso significa que não é possível sobrepor o processamento e a E/S ao mesmo tempo. O modelo sem bloqueio síncrono permite sobrepor o processamento e a E/S, mas requer que o aplicativo verifique o status da E/S constantemente. Isso não é feito para a E/S sem bloqueio assíncrono, que permite a sobreposição do processamento e da E/S, incluindo a notificação da conclusão da E/S.

A funcionalidade fornecida pela função select (E/S de bloqueio assíncrono) é semelhante à AIO, exceto pelo fato de que ela ainda é bloqueada. No entanto, ela é bloqueada nas notificações, não na chamada de E/S.


Introdução à AIO para Linux

Esta seção explora o modelo de E/S assíncrono para Linux, para ajudá-lo a entender como aplicá-lo em seus aplicativos.

Em um modelo tradicional de E/S, há um canal de E/S identificado por um identificador exclusivo. No UNIX®, são descritores de arquivos (que são iguais para arquivos, canais, soquetes, etc). Na E/S de bloqueio, inicialize uma transferência e a chamada do sistema é retornada quando ela é concluída ou quando ocorre um erro.

AIO para Linux

A AIO foi inserida no kernel Linux pela primeira vez na versão 2.5 e agora é um recurso padrão dos kernels de produção 2.6.

Na E/S sem bloqueio assíncrono, é possível inicializar várias transferências ao mesmo tempo. Para isso, é necessário um contexto exclusivo para cada transferência, de modo que seja possível identificá-la após a conclusão. Na AIO, isso é uma estrutura aiocb (Bloco de Controle de E/S da AIO). Essa estrutura contém todas as informações sobre uma transferência, incluindo um buffer de usuário para dados. Quando a notificação para uma E/S ocorre (chamada de conclusão) a estrutura aiocb é fornecida para identificar exclusivamente a E/S concluída. A demonstração da API mostra como fazer isso.


API AIO

A API AIO é muito simples, mas fornece as funções necessárias para a transferência de dados com alguns modelos de notificação diferentes. A Tabela 1 mostra as informações da interface AIO, que são explicadas posteriormente nesta seção.


Tabela 1. APIs AIO
Função da APIDescrição
aio_read Solicita uma operação de leitura assíncrona
aio_error Verifica o status de um pedido assíncrono
aio_return Obtém o status de retorno de um pedido assíncrono completo
aio_write Solicita uma operação assíncrona
aio_suspend Suspende o processo de chamada até que um ou mais pedidos assíncronos sejam concluídos (ou falhem)
aio_cancel Cancela um pedido de E/S assíncrono
lio_listio Inicia uma lista de operações de E/S

Cada uma dessas funções da API usa a estrutura aiocb para a inicialização ou verificação. Essa estrutura tem diversos elementos, mas a Lista 1 mostra apenas aqueles que serão necessários usar.


Lista 1. A Estrutura aiocb Mostrando os Campos Relevantes
                
struct aiocb {

  int aio_fildes;               // Descritor de Arquivo
  int aio_lio_opcode;           // Válido somente para lio_listio (r/w/nop)
  volatile void *aio_buf;       // Buffer de Dados
  size_t aio_nbytes;            // Número de Bytes no Buffer de Dados
  struct sigevent aio_sigevent; // Estrutura de Notificação

  /* Campos internos */
  ...

};

A estrutura sigevent informa à AIO o que fazer quando a E/S for concluída. Será explorada essa estrutura na demonstração da AIO. Agora, eu mostrarei como as funções individuais da API para AIO funcionam e como usá-las.

aio_read

A função aio_read solicita uma operação de leitura assíncrona para um descritor de arquivo válido. O descritor de arquivo pode representar um arquivo, um soquete ou até mesmo um canal. A função aio_read possui o seguinte protótipo:

int aio_read( struct aiocb *aiocbp );

A função aio_read é retornada imediatamente depois que o pedido é enfileirado. O valor de retorno é zero para êxito ou -1 para erro, em que errno é definido.

Para executar read, o aplicativo deve inicializar a estrutura aiocb. O seguinte breve exemplo ilustra o preenchimento na estrutura de pedido aiocb e o uso de aio_read para executar um pedido read assíncrono (agora ignore a notificação). Ele também mostra o uso da função aio_error, mas eu a explicarei posteriormente.


Lista 2. Código de Amostra para um Pedido read Assíncrono com aio_read
                
#include <aio.h>

...

  int fd, ret;
  struct aiocb my_aiocb;

  fd = open( "file.txt", O_RDONLY );
  if (fd < 0) perror("open");

  /* Zerar estrutura aiocb (recomendado) */
  bzero( (char *)&my_aiocb, sizeof(struct aiocb) );

  /* Alocar um buffer de dados para o pedido aiocb */
  my_aiocb.aio_buf = malloc(BUFSIZE+1);
  if (!my_aiocb.aio_buf) perror("malloc");

  /* Inicializar os campos necessários no aiocb */
  my_aiocb.aio_fildes = fd;
  my_aiocb.aio_nbytes = BUFSIZE;
  my_aiocb.aio_offset = 0;

  ret = aio_read( &my_aiocb );
  if (ret < 0) perror("aio_read");

  while ( aio_error( &my_aiocb ) == EINPROGRESS ) ;

  if ((ret = aio_return( &my_iocb )) > 0) {
    /* obter bytes ret em read */
  } else {
    /* read com falha, consultar errno */
  }


Na Lista 2, depois do arquivo do qual os dados de leitura estão sendo abertos, limpe sua estrutura aiocb e depois aloque um buffer de dados. A referência para o buffer de dados é colocada em aio_buf. Subsequentemente, inicie o tamanho do buffer em aio_nbytes. O aio_offset é configurado como zero (o primeiro deslocamento no arquivo). Configure o descritor de arquivo do qual a leitura está sendo feito em aio_fildes. Depois de configurar esses campos, chame aio_read para solicitar a leitura. É possível então fazer uma chamada para aio_error para determinar o status de aio_read. Assim que o status for EINPROGRESS, aguarde até que ele mude. Aqui, seu pedido terá sido bem-sucedido ou terá falhado.

Criando com a Interface AIO

É possível localizar os protótipos de funções e outros símbolos necessários no arquivo de cabeçalho aio.h. Ao criar um aplicativo que usa esta interface, é necessário utilizar a biblioteca de extensões em tempo real POSIX (librt).

Observe as similaridades ao ler a partir do arquivo com as funções da biblioteca padrão. Além da natureza assíncrona de aio_read, outra diferença é configurar o deslocamento para read. Em uma chamada read típica, o deslocamento é mantido para você no contexto do descritor de arquivo. Para cada read, o deslocamento é atualizado para que reads subsequentes destinem o próximo bloco de dados. Isso não é possível com a E/S assíncrona porque é possível executar muitos pedidos read simultaneamente e, portanto, deve especificar o deslocamento para cada pedido read específico.

aio_error

A função aio_error é utilizada para determinar o status de um pedido. Seu protótipo é:

int aio_error( struct aiocb *aiocbp );

Esta função pode retornar o seguinte:

  • EINPROGRESS, indicando que o pedido ainda não foi concluído
  • ECANCELLED, indicando que o pedido foi cancelado pelo aplicativo
  • -1, indicando que ocorreu um erro ao qual é possível consultar o errno

aio_return

Outra diferença entre a E/S assíncrona e a E/S de bloqueio padrão é que você não tem acesso imediato ao status de retorno de sua função porque não está fazendo o bloqueio na chamada read. Em uma chamada read padrão, o status de retorno é fornecido no retorno da função. Com a E/S assíncrona, você usa a função aio_return. Essa função tem o seguinte protótipo:

ssize_t aio_return( struct aiocb *aiocbp );

Esta função é chamada somente depois que a chamada aio_error determinar que seu pedido foi concluído (com êxito ou com erro). O valor de retorno de aio_return é idêntico aos sitemas de chamadas read ou write em contexto assíncrono (número de bytes transferidos ou -1 para erro).

aio_write

A função aio_write é utilizada para solicitar uma gravação assíncrona. Seu protótipo de função é:

int aio_write( struct aiocb *aiocbp );

A função aio_write retorna imediatamente, indicando que o pedido foi enfileirado (com um retorno de 0 para êxito e -1 para falha, com errno corretamente definido).

Isso é semelhante à chamada do sistema read, embora uma diferença de comportamento seja digna de observação. Lembre-se de que o deslocamento a ser utilizado é importante com a chamada read. No entanto, com write, o deslocamento só será importante se utilizado em um contexto de arquivo no qual a opção O_APPEND não está configurada. Se O_APPEND estiver configurada, então o deslocamento será ignorado e os dados serão anexados ao final do arquivo. Caso contrário, o campo aio_offset determina o deslocamento no qual os dados serão gravados no arquivo.

aio_suspend

É possível utilizar a função aio_suspend para suspender (ou bloquear) o processo de chamada até que um pedido de E/S assíncrono seja concluído, um sinal seja mostrado ou ocorra um tempo de espera opcional. O responsável pela chamada fornece uma lista de referências aiocb à qual a conclusão de pelo menos uma fará com que aio_suspend seja retornado. O protótipo de função para aio_suspend é:

int aio_suspend( const struct aiocb *const cblist[],
                  int n, const struct timespec *timeout );

Utilizar aio_suspend é muito simples. Uma lista de referências aiocb é fornecida. Se alguma delas for concluída, a chamada será retornada com 0. Caso contrário, -1 será retornado, indicando que ocorreu um erro. Consulte a Lista 3.


Lista 3. Utilizando a Função aio_suspend para Bloqueio em E/Ss Assíncronas
                
struct aioct *cblist[MAX_LIST]

/* Limpar a lista. */
bzero( (char *)cblist, sizeof(cblist) );

/* Carregar uma ou mais referências na lista */
cblist[0] = &my_aiocb;

ret = aio_read( &my_aiocb );

ret = aio_suspend( cblist, MAX_LIST, NULL );

Observe que o segundo argumento de aio_suspend são diversos elementos em cblist, não diversas referências aiocb. Qualquer elemento NULL em cblist é ignorado por aio_suspend.

Se um tempo de espera for fornecido para aio_suspend e ocorrer esse tempo de espera, -1será retornado e errno conterá EAGAIN.

aio_cancel

A função aio_cancel permite cancelar um ou todos os pedidos de E/S pendentes para um determinado descritor de arquivo. Seu protótipo é:

int aio_cancel( int fd, struct aiocb *aiocbp );

Para cancelar um único pedido, forneça o descritor de arquivo e a referência aiocb. Se o pedido for cancelado com êxito, a função retorna AIO_CANCELED. Se o pedido for concluído, a função retorna AIO_NOTCANCELED.

Para cancelar todos os pedidos para um determinado descritor de arquivo, forneça esse descritor de arquivo e uma referência NULL para aiocbp. A função retorna AIO_CANCELED se todos os pedidos forem cancelados, AIO_NOT_CANCELED se pelo menos um pedido não puder ser cancelado e AIO_ALLDONE se nenhum dos pedidos puder ser cancelado. É possível então avaliar cada pedido individual da AIO utilizando aio_error. Se o pedido tiver sido cancelado, aio_error retornará -1 e errno será configurado como ECANCELED.

lio_listio

Finalmente, a AIO permite iniciar várias transferências ao mesmo tempo utilizando a função de API lio_listio. Essa função é importante porque significa que é possível iniciar muitas E/Ss no contexto de uma única chamada do sistema (o que significa um comutador de contexto do kernel). Isso é ótimo em uma perspectiva de desempenho, portanto, vamos explorá-la. A função de API lio_listio tem o seguinte protótipo:

int lio_listio( int mode, struct aiocb *list[], int nent,
                   struct sigevent *sig );

O argumento mode pode ser LIO_WAIT ou LIO_NOWAIT. LIO_WAIT bloqueia a chamada até que todas as E/S sejam concluídas. LIO_NOWAIT retorna depois que as operações são enfileiradas. A lista é uma lista de referências aiocb, com o número máximo de elementos definidos por nent. Observe que os elementos da lista podem ser NULL, que lio_listio ignora. A referência sigevent define o método para a notificação de sinal quando todas as E/Ss estiverem concluídas.

O pedido para lio_listio é um pouco diferente do pedido read ou write no qual a operação deve ser especificada. Isso é ilustrado na Lista 4.


Lista 4. Utilizando a Função lio_listio para Iniciar uma Lista de Pedidos
                

struct aiocb aiocb1, aiocb2;
struct aiocb *list[MAX_LIST];

...

/* Preparar o primeiro aiocb */
aiocb1.aio_fildes = fd;
aiocb1.aio_buf = malloc( BUFSIZE+1 );
aiocb1.aio_nbytes = BUFSIZE;
aiocb1.aio_offset = next_offset;
aiocb1.aio_lio_opcode = LIO_READ;

...

bzero( (char *)list, sizeof(list) );
list[0] = &aiocb1;
list[1] = &aiocb2;

ret = lio_listio( LIO_WAIT, list, MAX_LIST, NULL );

A operação de leitura é anotada no campo aio_lio_opcode com LIO_READ. Para uma operação de escrita, LIO_WRITE é usado, mas LIO_NOP também é válido para nenhuma operação.


Notificações da AIO

Agora que foram vistas as funções AIO que estão disponíveis, esta seção apresenta os métodos que é possível utilizar para a notificação assíncrona. Eu explorarei a notificação assíncrona através de sinais e retornos de chamadas de funções.

Notificação Assíncrona com Sinais

O uso de sinais para a Interprocess Communication (IPC) é um mecanismo tradicional no UNIX e também é suportado pela AIO. Neste paradigma, o aplicativo define um manipulador de sinal que é chamado quando ocorre um sinal especificado. O aplicativo especifica então que um pedido assíncrono mandará um sinal quando o pedido estiver concluído. Como parte do contexto de sinal, o pedido aiocb específico é fornecido para rastrear vários pedidos possivelmente pendentes. A Lista 5 demonstra esse método de notificação.


Lista 5. Usando Sinais como Notificação para Pedidos da AIO
                
void setup_io( ... )
{
  int fd;
  struct sigaction sig_act;
  struct aiocb my_aiocb;

  ...

  /* Configurar o manipulador de sinal */
  sigemptyset(&sig_act.sa_mask);
  sig_act.sa_flags = SA_SIGINFO;
  sig_act.sa_sigaction = aio_completion_handler;


  /* Configurar o pedido da AIO */
  bzero( (char *)&my_aiocb, sizeof(struct aiocb) );
  my_aiocb.aio_fildes = fd;
  my_aiocb.aio_buf = malloc(BUF_SIZE+1);
  my_aiocb.aio_nbytes = BUF_SIZE;
  my_aiocb.aio_offset = next_offset;

  /* Vincular o pedido da AIO com o manipulador de sinal */
  my_aiocb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
  my_aiocb.aio_sigevent.sigev_signo = SIGIO;
  my_aiocb.aio_sigevent.sigev_value.sival_ptr = &my_aiocb;

  /* Mapear o sinal para o manipulador de sinal */
  ret = sigaction( SIGIO, &sig_act, NULL );

  ...

  ret = aio_read( &my_aiocb );

}


void aio_completion_handler( int signo, siginfo_t *info, void *context )
{
  struct aiocb *req;


  /* Garantir que este seja nosso sinal */
  if (info->si_signo == SIGIO) {

    req = (struct aiocb *)info->si_value.sival_ptr;

    /* O pedido foi concluído? */
    if (aio_error( req ) == 0) {

      /* Pedido concluído com êxito, obter o status de retorno */
      ret = aio_return( req );

    }

  }

  return;
}



Na Lista 5, você configura seu manipulador de sinal para capturar o sinal SIGIO na função aio_completion_handler. Você então inicializa a estrutura aio_sigevent para mostrar SIGIO para a notificação (que é especificada por meio da definição SIGEV_SIGNAL em sigev_notify). Quando read é concluído, seu manipulador de sinal extrai o aiocb específico da estrutura si_value do sinal, verifica o status do erro e retorna o status para determinar a conclusão da E/S.

Para desempenho, o manipulador de conclusão é ideal para dar continuidade à E/S, solicitando a próxima transferência assíncrona. Dessa forma, quando a conclusão é uma transferência é feita, você imediatamente inicia a próxima.

Notificação Assíncrona com Retornos de Chamada

Um mecanismo de notificação alternativo é o retorno de chamada do sistema. Em vez de mostrar um sinal para notificação, esse mecanismo chama uma função no espaço de usuário para notificação. Você inicializa a referência aiocb na estrutura sigevent para identificar exclusivamente o pedido específico que está sendo concluído; consulte a Lista 6.


Lista 6. Utilizando a Notificação de Retorno de Chamada de Encadeamento para Pedidos AIO
                
void setup_io( ... )
{
  int fd;
  struct aiocb my_aiocb;

  ...

  /* Configurar o pedido da AIO */
  bzero( (char *)&my_aiocb, sizeof(struct aiocb) );
  my_aiocb.aio_fildes = fd;
  my_aiocb.aio_buf = malloc(BUF_SIZE+1);
  my_aiocb.aio_nbytes = BUF_SIZE;
  my_aiocb.aio_offset = next_offset;

  /* Vincular o pedido da AIO com um retorno de chamada do encadeamento */
  my_aiocb.aio_sigevent.sigev_notify = SIGEV_THREAD;
  my_aiocb.aio_sigevent.notify_function = aio_completion_handler;
  my_aiocb.aio_sigevent.notify_attributes = NULL;
  my_aiocb.aio_sigevent.sigev_value.sival_ptr = &my_aiocb;

  ...

  ret = aio_read( &my_aiocb );

}


void aio_completion_handler( sigval_t sigval )
{
  struct aiocb *req;

  req = (struct aiocb *)sigval.sival_ptr;

      /* O pedido foi concluído? */
  if (aio_error( req ) == 0) {

      /* Pedido concluído com êxito, obter o status de retorno */
      ret = aio_return( req );

  }

  return;
}



Na Lista 6, depois de criar seu pedido aiocb, é possível solicitar um retorno de chamada de encadeamento usando SIGEV_THREAD para o método de notificação. Você então especifica o manipulador de notificações específico e carrega o contexto a ser transmitido para o manipulador (neste caso, uma referência ao próprio pedido aiocb). No manipulador, você simplesmente distribui o ponteiro sigval recebido e utiliza as funções da AIO para validar a conclusão do pedido.


Ajuste do Sistema para AIO

O sistema de arquivos proc contém dois arquivos virtuais que podem ser ajustados para o desempenho da E/S assíncrona:

  • O arquivo /proc/sys/fs/aio-nr fornece o número atual de pedidos de E/S assíncronos de todo o sistema.
  • O arquivo /proc/sys/fs/aio-max-nr é o número máximo de pedidos simultâneos permitidos. O máximo geralmente é 64KB, que é adequado a maioria dos aplicativos.

Resumo

Utilizar a E/S assíncrona pode ajudá-lo a criar aplicativos de E/S mais rápidos e eficientes. Se seu aplicativo puder sobrepor o processamento e a E/S, então a AIO poderá ajudá-lo a criar um aplicativo que utilize, de forma mais eficiente, os recursos da CPU disponíveis para você. Embora esse modelo de E/S seja diferente dos padrões de bloqueio tradicionais encontrados na maioria dos aplicativos Linux, o modelo de notificação assíncrono é conceitualmente simples e pode simplificar seu design.


Recursos

Aprender

Obter produtos e tecnologias

Discutir

Sobre o autor

M. Tim Jones

M. Tim Jones é um arquiteto de firmwares embarcados e autor de Inteligência Artificial: Sistemas de Abordagem, GNU/Linux, Programação de Aplicativos AI (atualmente em sua segunda edição), Programação de Aplicativos AI (em sua segunda edição) e BSD Sockets Programming from a Multilanguage Perspective. Sua formação em engenharia vai desde o desenvolvimento de kernels para nave espacial geossincrônica até a arquitetura de sistema embarcado e o desenvolvimento de protocolos de interligação de redes. Tim é um Engenheiro Consultor para a Emulex Corp. em Longmont, Colorado.

Ajuda para Relatar Abuso

Relatar abuso

Obrigado. Esta entrada foi sinalizada para atenção do moderador.


Ajuda para Relatar Abuso

Relatar abuso

Falha no envio do Relatório de abuso. Tente novamente mais tarde.


developerWorks: Registre-se


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos de uso do developerWorks.

 


Na primeira vez que você efetua sign in no developerWorks, um perfil é criado para você. Informações selecionadas do seu perfil developerWorks são exibidas ao público, mas você pode editá-las a qualquer momento. Seu primeiro nome, sobrenome (a menos que escolha ocultá-los), e seu nome de exibição acompanharão o conteúdo que postar.

Selecione seu nome de exibição

Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

(Deve possuir de 3 a 31 caracteres.)


Ao clicar em Enviar, você concorda com os termos de uso do developerWorks.

 


Classificar este artigo

Comentários

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Linux
ArticleID=382590
ArticleTitle=Desempenho de Aplicativos Boost Usando E/S Assíncrona
publish-date=08292006
author1-email=mtj@mtjones.com
author1-email-cc=