V JOLIM C e Linux Dicas e Truques Márcio Fuckner marciofk@ppgia.pucpr.br
Agenda Introdução Entrada e Saída C em Redes Multithreading Utilidades
Introdução Apresentação Márcio Fuckner Bacharel em Sistemas de Informação Professor do Departamento de Computação da PUCPR Mestre em Informática Sun Certified Java Programmer Sun Certified Java Web Component Developer Instrutor do HSBC Bank
Por que os temas? Linux é um sistema operacional importante no cenário mundial e nasceu da participação ativa da comunidade open source. É a plataforma de desenvolvimento predileta dos pesquisadores da área de ciência da computação A linguagem C tem uma estreita ligação com o mundo UNIX (suas histórias se confundem) Programas em C/C++ tem ótimo desempenho Pesquisadores precisam extrair o melhor do ambiente para atingir seus objetivos Muitas vezes um shell script nao traz o mesmo desempenho e versatilidade que programas escritos em C
Entrada e Saída A linguagem C possui diversas system calls que permitem interagir com o file system do sistema operacional Todos os shells conhecidos fazem uso destas funções (bash, ksh e csh por exemplo) Alguns exemplos que serão apresentados Como obter informações sobre um arquivo? Como saber as permissões de um arquivo? Como caminhar na estrutura de um diretório? Como trabalhar com pipes? Como criar um arquivo temporário?
Informações sobre arquivos (1 de 3) Função int stat(char * arquivo, struct stat *) stat permite consultar diversas informações do arquivo Campo st_mode st_ino st_nlink st_uid st_gid st_size st_atime st_mtime st_ctime Descrição Tipo e modo Número do i node Quantidade de links Id do owner Id do grupo owner Tamanho em bytes Data de último acesso Data de modificação Data de criação ExemploES01.c
Informações sobre arquivos (2 de 3) Unix possui diversos tipos de arquivos. É possível determinar o tipo usando uma das seguintes macros descritas abaixo Macro S_ISREG ( ) S_ISDIR ( ) S_ISCHR ( ) S_ISBLK ( ) S_ISFIFO ( ) S_ISLNK ( ) S_ISSOCK ( ) Descrição Arquivo comum (regular file) Diretório Arquivo orientado a caractere Arquivo orientado a bloco Arquivo de pipe ou fifo Arquivo de link Arquivo de socket ExemploES02.c
Informações sobre arquivos (3 de 3) O st_mode também codifica as permissões do arquivo. Para decodificá lo, é necessário realizar uma operação AND binária com as seguintes constantes que estão na include <sys/stat.h> Constante S_IRUSR S_IWUSR S_IXUSR S_IRGRP S_IWGRP S_IXGRP S_IROTH S_IWOTH S_IXOTH Descrição Leitura Usuário Gravação Usuário Execução Usuário Leitura Grupo Gravação Grupo Execução Grupo Leitura Outros Gravação Outros Execução Outros ExemploES03.c
Lendo arquivos de um diretório Diretórios podem ser lidos por qualquer usuário com permissões de leitura. Em sistemas UNIX com padrão POSIX é possível ler diretórios usando as seguintes funções DIR * opendir ( char * path ) struct dirent * readdir ( DIR * dp) rewinddir( DIR * dp ) closedir ( DIR * dp ) ExemploES04.c
Trabalhando com pipes Pipe é um recurso poderoso comum em sistemas Unix Com ele é permitido que a saída de um processo seja a entrada de outro Comandos podem ser combinados e programas podem se comunicar através de pipes Exemplo find. name db* sort find sort
Fazendo um programa C ler um pipe Comandos como grep, wc e sort quando não recebem nenhuma opção e argumentos fazem leitura da entrada padrão. Isso permite usarmos esses comandos em conjunto com outros Para fazer um programa em C que leia um pipe podemos usar as seguintes técnicas Ler a entrada padrão (stdin), convertendo a para um ponteiro FILE * através da função fdopen Usar a função popen, que permite executar um comando em um processo filho e devolvendo a saída para o programa
Lendo a entrada padrão em C Em Unix três descritores ficam abertos para utilização São eles STDIN 0 Entrada padrão STDOUT 1 Entrada padrão STDERR 2 Erro padrão Através da função fdopen podemos transformar esses descritores em um FILE * (stream) IMPORTANTE Um pipe é um fluxo contínuo. Não é possível voltar a ler partes anteriores de um pipe. ExemploES05.c
Usando a função popen Também é possível criar um processo filho e comunicar se com ele através de uma stream, que pode ser de saída ou de entrada Sintaxe FILE * popen(char * cmd, char * modo) ExemploES06.c
Criando arquivos temporários seguros Arquivos temporários são elementos comuns em programação Em aplicações multitarefa precisamos ainda garantir que estes arquivos sejam únicos para evitar problemas de sobreposição de nomes Em sistemas POSIX é possível usar a função tmpfile que devolve um descritor de arquivo para atualização, equivalente ao fopen com a opção w+ Sintaxe FILE * tmpfile ( ) ExemploES07.c
Onde usar? Comandos de manipulação do sistema de arquivos Processamento de arquivos de dados Criação de utilitários de disco Criação de instaladores Comunicação entre processos
Redes Necessidade de distribuição de processamento Balanceamento de carga Conectividade Alguns cenários: Como criar um servidor de socket TCP? Como criar um cliente de socket TCP?
Criando um servidor Socket (1 de 2) Este exemplo apresenta um servidor de socket TCP que fica ouvindo requisições em uma porta Para se criar um servidor de socket é necessário: Executar a função socket, que tem por objetivo criar um descritor. Sintaxe: int socket ( int domain, int type, int protocol ) Executar a função bind, que permite associar o programa a um endereço e porta. Sintaxe: int bind ( int s, struct sockaddr, int addrlen ) ExemploRede01.c
Criando um servidor Socket (2 de 2) Para se criar um servidor de socket é necessário (cont.) Indicar que o programa ficará ouvindo por requisições usando a função listen e ficar aguardando por conexões usando a função accept Sintaxe: int accept ( int fd, struct sockaddr *, int addrlen ) Enviar e receber mensagens através das funções send e recv respectivamente. Fechar a conexão com a função close ( int fd ) ExemploRede01.c
Criando um cliente Socket Para se criar uma aplicação que se conecte com um servidor de socket, o desenvolvedor precisa: Executar a função socket, que tem por objetivo criar um descritor de comunicação. Sintaxe: int socket( int domain, int type, int protocol ) Executar a função connect, que tem por objetivo realizar a conexão com o servidor. Sintaxe: int connect ( int fd, struct sockaddr *, int addrlen ) Enviar e receber mensagens através das funções send e recv respectivamente. Fechar a conexão com a função close ( int fd ) ExemploRede02.c
Onde usar? Criação de servidores de serviços comuns como: Bancos de dados Servidores web Servidores de aplicação
Multithreading Limitações físicas dos processadores Ultimamente o mercado adotou modelos de processadores com múltiplos núcleos (multicore) Quais são as alternativas? Computação quântica, computadores de DNA, chips de carbono (nanotubos ou grafeno) ou uso de fotons E enquanto essas tecnologias nao chegam? Paralelizar ao máximo as aplicações So quando usamos multithreading e multiprocessamento é que aproveitamos mais de um núcleo
Multiprocessamento Em sistemas UNIX é possível criar processos a partir de outro, viabilizando o paralelismo No entanto, há um custo alto devido a inicialização e duplicação da área de memória, além do gerenciamento de troca de mensagens necessário Processo A Variáveis globais código pilha fork() Processo B Variáveis globais código pilha
Multithreading Thread é um fluxo de controle leve e independente dentro de um processo Permite o compartilhamento de áreas de memória, descritores de arquivos, sinais e variáveis globais Possuem dados próprios: thread ID, registradores, pilha, contadores e variáveis locais Processo A Variáveis globais cõdigo pilha1 pilha2 pilha3
Posix Threads Permite a criação de threads pthread_create Permite identificar qual é a thread em execução pthread_self Permite passar argumentos para a thread Permite especificar regiões críticas pthread_mutex_lock e pthread_mutex_unlock Permite sincronizar execuções que são pré requisitos pthread_join
Criando uma Thread Para criar uma thread eh necessário executar a função pthread_create Uma vez que a thread foi criada, ela torna se elegível para execução no processo de escalonamento de processos do sistema operacional ExemploThread01.c
Criando varias Threads Como a thread pode começar a executar assim que ela é criada, a thread corrente é liberada, configurando assim uma chamada assíncrona A thread corrente pode disparar quantas outras forem necessárias O exemplo 02 apresenta este cenário ExemploThread02.c
Passando parametros para Threads Eh possivel passar parâmetros para threads através da mesma função pthread_create Qualquer tipo de dado pode ser passado uma vez que a assinatura da função recebe um ponteiro para void Para flexibilizar a passagem de parâmetros o ideal é criar uma estrutura contendo os parâmetros necessários e enviar o ponteiro desta estrutura como parâmetro para a thread A mesma estrutura pode ser usada para retorno de valores da thread filha para a thread mãe ExemploThread03.c
Sincronizando Threads Processos A,B e C podem rodar em paralelo O processo D so pode iniciar se os três terminarem A função pthread_join permite realizar este tipo de controle A B C D ExemploThread04.c
Sincronizando Threads (exemplo) O programa precisa saber se um ponto esta em dois polígonos Em um cenário tradicional, o programa verificaria se o ponto está em um polígono, depois em outro O exemplo 04 apresenta a execução paralelizada da verificação usando a função pthread_join ExemploThread04.c
Onde usar? Cenários de uso massivo de cálculo Aplicações de engenharia Simuladores Problemas NP completos de busca e otimização Jogos Servidores Serviços de sistema operacional Mineração distribuída de dados Reconhecimento de padrões Dentre vários outros
Utilidades Como criar comandos compatíveis com o padrão UNIX? Como ler múltiplos argumentos como ocorre em printf? Como ler e gravar variáveis de ambiente? Como interceptar sinais em um programa C? Como criar um daemon?
Criando comandos Unix Like Em geral os comandos Unix obedecem uma padronização da passagem de parâmetros para facilitar o uso e aprendizado Os comandos tem o seguinte formato geral <comando> [ [+ ] opção ]... [ args de opção ] args A ordem e a disposição das opções nao afetam no resultado final. Exemplos: ls la é equivalente a ls l a ou ls a l ou ls al Sistemas compatíveis com POSIX possuem a função getopt que permite ler opções e argumentos no padrão Unix Sintaxe int getopt(int argc, char ** argv, char * opções) ExemploUtil01.c
Lendo múltiplos argumentos Funções como printf permitem que o usuário informe múltiplos parâmetros para a função Em C é possível obter o mesmo resultado usando as funções da família va_args Funções va_start ( va_list params, int qtd ) va_arg ( va_list params, void * ) va_end ( va_list params ) ExemploUtil02.c
Manipulando variaveis de ambiente Em sistemas operacionais Unix é comum a utilização de variáveis de ambiente, que podem ser personalizadas para cada usuário ou ambiente. Estas variáveis permitem flexibilidade na configuração da aplicação Exemplos típicos de utilização de variáveis de ambiente são: nomes de servidores, portas e diretórios padrões Em C é possível Recuperar variáveis de ambiente com a função getenv Atualizar valores de variáveis através da função setenv ExemploUtil03.c
Interceptar sinais em um programa C Processos podem se comunicar através de sinais IPC Alguns sinais no UNIX são bem famosos, como por exemplo o kill 9 (SIGKILL) e o kill HUP (SIGHUP) Um programa C pode especificar um tratador de sinais (signal handler) Neste exemplo, será apresentado um signal handler para o sinal kill HUP, que é uma das maneiras comuns de derrubar servidores de forma amigável ExemploRede03.c
Criando um Daemon Um daemon é um programa especial que executa no servidor em modo de serviço ou background, não necessitando interação direta com usuários Tipicamente são serviços de indexação, de banco de dados, servidores web ou de aplicação Criar um daemon significa tornar o processo filho independente do processo pai. Lembre se que em Unix, quando o processo pai morre, o filho também deve morre, senão fica orfão (defunct ou zombie). Para tornar um processo daemon é necessário executar a função setsid ExemploRede04.c
Unificando... ExemploRede01.c
Obrigado... Para saber mais: Posix Threads TCP sockets em C https://computing.llnl.gov/tutorials/pthreads/ http://www.linuxhowtos.org/c_c++/socket.htm Unix Advanced Programming Stevens