domingo, 23 de setembro de 2012

Desafio em C no Unix – Simulando o comando: cat | more


Considere um programa que apresenta a sua saída página por página. Ao invés de reinventar a paginação feita por vários utilitários do sistema Unix, desejamos chamar o paginador favorito do usuário (more, less, …). Faça um programa que use um pipe para redirecionar a saída para o paginador. O programa recebe o nome do arquivo a ser mostrado como entrada e repassa linha a linha deste arquivo a um processo filho através de um pipe. O processo filho irá executar o paginador.

Vamos inicialmente conceituar alguns comandos a serem utiliados:

more filtro de arquivo para leitura e visualização em monitores

Descrição: é um filtro para  paginar textos em uma tela de cada vez. Este paginador é simples. Os usuários devem perceber que o comendo less (1) prover mais de emulação (1) e mais melhorias.

less - O oposto do more

Descrição: less é um programa semelhante ao more , mas que permite o movimento para trás no arquivo, bem como o movimento para frente. Além disso, menos não tem que ler o arquivo de entrada inteiro antes de começar, portanto, com grandes entradas começa-se mais rápido do que editores de texto como vi.

pipe - Cria um pipe

Descrição

O Pipe, utilizado através do comando “|” permite que a saída de um comando seja diretamente utilizado em outro comando. O pipe tem seu nome inspirado nos tubos (pipes), de transporte de fluídos. A ideia do PIPE é encadear um conjunto de processos, de modo que a saída de cada um seja a entrada do seguinte. 

 O exemplo abaixo mostra claramente como utilizar o Pipe:


$ ls -l | grep imagem | sort -r
O primeiro comando ls -l lista o conteúdo do diretório em formato longo, a seguir o comando Pipe faz com que a saída do primeiro comando entre no segundo comando, grep imagem que procura na listagem do diretório qualquer arquivo que possua a palavra imagem, por final, a saída do comando grep imagem é utilizada no comando sort -r que faz a classificação de forma crescent
Conding in C

 Vamos ao Primeiro Código:  

 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/wait.h>
 int main(int argc, char *argv[]) {
 int fd[2], pid1, pid2;
 char *my_program1[3] = {“/bin/cat”, argv[1], NULL};
 char *my_program2[2] = {“/bin/more”, NULL};
 pipe(fd);
 if((pid1 = fork()) == 0) {
 close(STDOUT_FILENO);
 dup(fd[1]);
 close(fd[0]); close(fd[1]);
 execv(my_program1[0], my_program1);
 }
 if((pid2 = fork()) == 0) {
 close(STDIN_FILENO);
 dup(fd[0]);
 close(fd[0]); close(fd[1]);
 execv(my_program2[0], my_program2);
 }
 close(fd[0]); close(fd[1]);
 waitpid(pid1, NULL, 0);
 waitpid(pid2, NULL, 0);
 return 0;
 }

Executando
Para executar o código, não se esqueça de gerar a compilação do arquivo, como por exemplo:
gcc -o CatMore CatMore.c
Após, para execução, o programinha espera que você passe o nome do arquivo que deve ser lido por parâmetro, por exemplo:
./CatMore CatMore.c
O resultado disso será a leitura do próprio arquivo CatMore.c.

Explicação do Primeiro Código
Este código está bem simples. Foi a primeira versão que fiz para atender ao problema, porém ainda não está completamente correto. Se vocês repararem no enunciado, tem o seguinte detalhe: ‘e repassa linha a linha deste arquivo a um processo filho através de um pipe’, o que quer dizer que não podemos usar o cat para ler o arquivo desejado, devemos fazer o código que percorrerá linha a linha do arquivo passado por parâmetro e jogar no pipe para que o more possa lê-lo da forma desejada.


 Então, Vamos ao Segundo Código:  
 #include <unistd.h>  
 #include <stdio.h>  
 #include <stdlib.h>  
 #include <string.h>  
 #define MAXLINE 4096  
 int main(int argc, char *argv[]) {  
 int n, fd[2];  
 pid_t pid;  
 char line[MAXLINE];  
 char *my_program[2] = {“/bin/more”, NULL};  
 FILE *arquivo;  
 if (pipe(fd) < 0) {  
 fprintf(stderr, “pipe error”);  
 }  
 if ((pid = fork()) < 0) {  
 fprintf(stderr, “fork error”);  
 } else if (pid > 0) {  
 /* parent */  
 close(fd[0]);  
 arquivo = fopen(argv[1],”r”);  
 while (fgets(line, MAXLINE, arquivo) != NULL)  
 {  
 write(fd[1], line , (strlen(line)));  
 }  
 fclose(arquivo);  
 close(fd[1]);  
 waitpid(pid, NULL, 0);  
 exit(0);  
 } else {  
 /* child */  
 close(STDIN_FILENO);  
 dup(fd[0]);  
 close(fd[1]);  
 execv(my_program[0], my_program);  
 }  
 return 0;  
 }  




Fontes:

http://www.vivaolinux.com.br/dica/Pipes-no-Linux; http://jairofranchi.wordpress.com/2011/03/24/utilizando-o-comando-pipe-linux/ http://www.quaddro.com.br/blog/programando-em-ansi-c/estudando-bibliotecas-de-c-stdio-h
http://joelsonlucena.wordpress.com/2012/09/19/desafio-em-c-no-unix-simulando-o-comando-cat-more/






Entendendo Processos do Unix com Programas em C – Parte 2 – Criação de Processos

Continuando a replicação do trabalho do meu amigo Joelson:



Como Funciona a Criação de Processos
Os processos pai criam processos filhos, que, por sua vez, criam outros processos, formando uma árvore de processos.
Durante a execução, pai e filhos são executados concorrentemente, porém o pai espera até que os filhos terminem suas execuções.
Um ponto interessante a se observar é que quando o processo pai gera um processo filho, na verdade o processo filho é exatamente a cópia do processo pai, porém o filho pode ou não carregar um novo programa nele para que tenha outro conteúdo.
Nos sistemas Unix, as chamadas de sistema (system-call)
  • fork cria novo processo (processo filho)
  • exec que é usada após o fork substitui o espaço de memória do processo por um novo programa
  • wait faz com que o processo pai permaneça bloqueado até seus filhos terminarem suas execuções.
Quando um processo é criado, um novo bloco de controle de processo (PCB) é criado para o processo.
Quando o fork é executado, o sistema copia o processo corrente e o executa. O valor de retorno de todo processo filho é ’0′ (zero), enquanto do processo pai é retornado o identificador (process identification – pid) do processo filho. Normalmente usamos o valor de retorno para identificarmos se o processo que está executando é o pai ou o filho.
Colocando a Mão na Massa
Para testarmos os conceitos acima ditos, vamos para mais um programinha em C:


 #include <stdio.h>  
 #include <sys/types.h>  
 #include <unistd.h>  
 int main() {  
   if(fork() == 0) {  
     printf(“Eu sou o filho\n”);  
   } else {  
     printf(“Eu sou o pai\n”);  
   }  
 }  

Para compilarmos o arquivo:

gcc -o ProcessosPaiFilho ProcessosPaiFilho.c
E para executarmos:
./ProcessosPaiFilho
Repare que nossa saída foi:
Eu sou o pai
Eu sou o filho
Explicando o conteúdo do programa acima, podemos reparar o seguinte:
Quando no primeiro ‘if’ chamamos a função fork(), é gerado e processado um novo processo filho e que é cópia deste mesmo programa. Ambos são executados em paralelo. No processo filho, que o retorno da função fork() é comparado com zero (como vimos nos conceitos anteriores, o processo filho retorna zero no fork()), será impresso na tela “Eu sou o filho”. No processo pai, o bloco ‘else’ será executado, imprimindo na tela “Eu sou o pai”.
Conclusão
Vimos mais um passo de como funcionam os processos no sistema operacional com base Unix. Vamos continuar avançando e publicando evidências dos estudos!



sexta-feira, 21 de setembro de 2012

Atalhos do Terminal no MAC Os

Resolvir postar alguns atalhos para ajudar a trabalhar no TERMINAL do MAC. Os atalhos ajudam bastante a vida de quem trabalha com o terminal

FunçãoAtalho
Ctrl + AIr para o início da linha atual
Ctrl + EIr para o fim da linha atual
Ctrl + KApaga tudo o que estiver após o cursor
Ctrl + UApaga tudo o que estiver antes do cursor
Ctrl + WApaga a palavra antes do cursor
Ctrl + LLimpa todo o buffer do terminal atual
Ctrl + RBusca comandos previamente utilizados
Ctrl + C"Mata" a linha atual
Ctrl + Z Interromper qualquer comando em execução 

E se você sente falta de alguma tecla de atalho, ou gostaria de modificar alguma já existente, essa dica é para você. Em "System Preferences › Keyboard & Mouse › Keyboard Shortcut", você pode adicionar os atalhos para um programa específico, ou para todos!
Painel de Preferências de Teclado e Mouse


Um outra dica interessante é adicionar o Terminal nas pasta do FINDER. 


Para isso basta:


Para ativar este recurso, abra as Preferências do Sistema e vá em Teclado. Na aba Atalhos de teclado, escolha Serviços na lista à esquerda e, após isso, na subseção Arquivo, vá em New Terminal at Folder (ou New Terminal Tab at Folder).
Enquanto estiver nessa tela, é possível adicionar atalhos de teclado ao dar um clique duplo no espaço em branco ao lado do nome do comando e escolher um novo atalho. Não esqueça, ao final, de habilitar a caixa para que a função seja ativada. 
Agora, ao dar um clique direito (ou Control+clique) em uma pasta no Finder, um novo Serviço do Terminal irá aparecer na base do menu contextual. Use essa nova opção - ou atalho de teclado - e o Terminal irá abrir uma nova janela (ou aba) com a pasta que foi escolhida pelo usuário.
terminalfolder_01.jpg
Comando pode ser feito no menu contextual ou a partir de um atalho de teclado

Alternar entre Abas no Terminal do MAC Os


Dica Simples mais que ajuda bastante na agilidade do Terminal

⌘+shift+setas 

Utilizando o VIM no Linux

Estou iniciando na edição através do Linux. Portanto resolvi aqui colocar informações importantes para compartilhar com todo.

Usando o editor de texto vim: 

1) Pra abrir o vim digite "vim nomedoarquivo" (sugiro que vocês usem o tab para preencher o nome do arquivo corretamente, pois caso o arquivo não exista o editor se encarregará de criar um; 

2) Para começar a digitar no vim clique no botão "Insert ou i" que se encontra no seu teclado; 

3)Após escrever ou alterar um arquivo, aperte o botão "Esc", depois clique em "shift+: " Para habilitar o modo de opções.

As Opções mais utilizadas são:



  • x - sai e salva o arquivo;

  • q - apenas sai;

  • w - salva;

  • q! - sai forçado (muito útil caso você faça uma alteração e não queira salvar).

  • quinta-feira, 20 de setembro de 2012

    Entendendo Processos do Unix com Programas em C – Parte 1


    Este Post é em homenagem ao trabalho do meu colega de turma Joelson da Cunha Lucena,

    Motivação
    Estou estudando Sistemas Operacionais com base Unix e tenho feito alguns testes em C para comprovar os estudos. Creio que possa ser útil a publicação desses programinhas criados para compartilhar o conhecimento.
    Processos em Sistemas Unix
    Um processo é um programa em execução.
    Vários conceitos permeiam o mundo dos processos, tais como a estrutura de um processo na memória, os possíveis estados de um processo, o bloco de controle de processo (PCB), como o sistema operacional trata os processos durante suas trocas de contexto, filas de escalonamento de processos, os escalonadores propriamente ditos, etc. (Talvez entremos mais a fundo em cada um desses conceitos mais pra frente)
    Como o foco é ser prático, vamos a um exemplo de como podemos gerar um processo em Unix.
    Mãos na Massa
    Estando em algum sistema com base Unix (Linux, Mac OS, BSD, Hurd, etc), vamos criar um arquivo chamado loop.c no qual conterá nosso código de programa na linguagem C. O local de trabalho fica a seu critério, mas no exemplo vou usar o home do meu usuário.
    Dentro do arquivo, criaremos o seguinte código na linguagem C:
    #include
    #include
    int main() {
        while(1);
    }
    O programa começa com a declaração dos includes, que são as bibliotecas que  nos fornecem as funções usadas no nosso código. O ‘int main()’ é a declaração do método principal a ser executado naquele determinado arquivo/programa. Repare que seu conteúdo é apenas ‘while(1)’, o que provocará um loop infinito dentro do programa. Fizemos isso para que você possa ver o processo sendo executado no sistema operacional, então se tivéssemos colocado qualquer programação finda, não seria possível acompanharmos o importante passo que o exercício propõe, que é vermos o processo que criamos em execução.
    Como os arquivos .c não podem ser executados diretamente, devemos compilar o arquivo para criarmos um executável. Para tal, posicionado no terminal na pasta onde consta o arquivo loop.c criado, digite o seguinte comando:
    gcc -o loop loop.c
    onde:
    • gcc -o – é o programa a ser chamado para que o sistema compile o arquivo
    • loop – é o nome do arquivo que será criado
    • loop.c – é o nome do nosso arquivo origem de código fonte.
    Tendo criado o nosso arquivo executável, para chamá-lo, podemos digitar no terminal:
    ./loop&
    onde:
    • ./ – é o indicador de que chamamos determinado arquivo à execução
    • loop – é o nome do arquivo executável que geramos e queremos executar
    • & – Serve para que o sistema rode esse programa em background. Se não tivéssemos especificado esse caracter, o terminal nos bloquearia naquela sessão até que finalizássemos o processo abruptamente.
    Para vermos o nosso processo em execução, podemos digitar no terminal o comando: top
    Ele te retornará uma lista de processos em execução, tal como isto:
     2160 joelson   20   0  4148  352  272 R  100  0.0   2:00.47 loop                                                                                                       
     1149 root      20   0  161m  24m  11m S    1  0.4   1:36.36 Xorg                                                                                                       
     1890 joelson   20   0 1107m 273m  36m S    1  4.7   4:26.13 firefox                                                                                                     1934 joelson   20   0  373m  37m  20m S    1  0.6   0:30.50 plugin-containe    
    Repare que o nosso processo loop é logo o primeiro da minha fila!
    Como o nosso processo é de loop infinito, ele nunca deixará de executar a não ser que o interrompemos.
    Ainda na tela de visualização dos processos em execução, para que você finalize o processo loop, digite k
    Ele aguardará uma entrada com o identificador do processo a ser finalizado. No meu caso, é 2160, então é o que eu digitarei e darei enter.
    Após, ele pede um sinal de finalização. Digite 9 e dê enter novamente. Pronto, finalizamos o processo!
    Para sair da visualização da lista de processos, digite: ctrl + c
    Conclusão
    Entender como funcionam os sistemas operacionais é muito interessante, mas vermos na prática seu funcionamento é muito melhor. Vimos como criamos e, de maneira limitada, que podemos manipular nossos processos e seus estados. Nos estudos que ainda serão postados, vamos nos aprofundando nas especificidades dos processos. Espero que seja de proveito de todos.