Mudanças entre as edições de "SOP29005-2020-1"

De MediaWiki do Campus São José
Ir para navegação Ir para pesquisar
Linha 214: Linha 214:
 
#Destruir todos
 
#Destruir todos
 
   killall yes
 
   killall yes
 +
 +
 +
{{collapse top| bg=lightyellow | expandir=true | Processos no Linux}}
 +
 +
== Processos no Linux - Modificado por Eraldo ==
 +
 +
<blockquote style="background: lime; border: 1px solid black; padding: 1em;">
 +
  Processos no Linux
 +
</blockquote>
 +
 +
*Exercícios propostos pelo Prof.Arliones e Modificados por Eraldo
 +
 +
 +
[[imagem:DesenhoForkExec.png|border|650px]]
 +
 +
 +
;Syscall FORK
 +
 +
* Em um terminal, execute "man fork"
 +
** A função da API POSIX '''fork()''' aciona uma chamada de sistema (system call - syscall) que cria um novo processo duplicando o processo que realiza a chamada. O novo processo, chamado de filho, é uma cópia exata do processo criador, chamado de pai, exceto por alguns detalhes listados na manpage. O principal destes detalhes para nós agora é o fato de terem '''PIDs diferentes'''.
 +
** O '''código''' dos dois processos (pai e filho) são '''idênticos''';
 +
** Os '''dados''' dos dois processos (pai e filho) são '''idênticos NO MOMENTO DA CRIAÇÃO''';
 +
** Execução do processo filho inicia na próxima instrução do programa (no retorno da chamada FORK);
 +
** Não é possível saber qual dos processos (pai ou filho) retormará a execução primeiro - isto fica a cargo do excalonador do SO;
 +
** Valores de retorno da chamada FORK:
 +
*** (-1): erro na criação do processo (ex.: memória insuficiente);
 +
*** (0): em caso de sucesso, este é o valor de retorno recebido pelo processo filho;
 +
*** (>0): em caso de sucesso, este é o valor de retorno recebido pelo processo pai;
 +
 +
;Syscall JOIN
 +
 +
* A syscall JOIN é implementada no POSIX pela função '''wait()'''. Execute "man wait".
 +
** Além da função '''wait()''', há também '''waitpid()''' e '''waitid()''';
 +
** Todas estas syscalls são utilizadas para aguardar por mudanças no estado de um processo filho e obter informações sobre o processo filho cujo estado tenha mudado. São consideradas mudanças de estado: o filho terminou; o filho foi finalizado por um sinal (ex.: kill); o filho foi retomado por um sinal (ex.: alarme);
 +
** A chamada wait também libera os recursos do processo filho que termina;
 +
** '''wait()''': esta função suspende a execução do processo chamador até que UM DOS SEUS FILHOS finalize;
 +
** '''waitpid()''': suspende a execução do processo chamador até que UM FILHO ESPECÍFICO finalize;
 +
 +
;Syscall EXEC
 +
 +
* A syscall EXEC é implementada no POSIX pela família de funções '''exec()'''. Execute "man exec".
 +
** As principais funções da família são '''execl()''', '''execlp()''' e '''execvp()''';
 +
** Todas estas funções são, na realidade, front-ends (abstrações) para a syscall '''execve'''. Esta syscall substitui a imagem do processo corrente (aquele que chama a syscall) pela a imagem de um novo processo;
 +
** Os parâmetros passados a estas funções são, basicamente, o nome de um arquivo com a imagem do programa a ser executado (um binário de um programa), e uma lista de parâmetros a serem passados a este novo programa;
 +
 +
 +
;Exemplos POSIX utilizando fork/wait/exec
 +
 +
*Exemplo 1: fork/wait básico
 +
<syntaxhighlight lang=c>
 +
// ex1: fork/wait básico
 +
#include <sys/types.h>
 +
#include <stdlib.h>
 +
#include <stdio.h>
 +
#include <unistd.h>
 +
#include <sys/wait.h>
 +
 +
int main()
 +
{
 +
    int pid, status;
 +
    pid = fork();
 +
 +
    if(pid == -1) // fork falhou
 +
    {
 +
        perror("fork falhou!");
 +
        exit(-1);
 +
    }
 +
    else if(pid == 0) // Este é o processo filho
 +
    {
 +
        printf("processo filho\t pid: %d\t pid pai: %d\n", getpid(), getppid());
 +
        exit(0);
 +
    }
 +
    else // Este é o processo pai
 +
    {
 +
        wait(&status);
 +
        printf("processo pai\t pid: %d\t pid pai: %d\n", getpid(), getppid());
 +
        exit(0);
 +
    }
 +
}
 +
</syntaxhighlight>
 +
 +
<syntaxhighlight lang=bash>
 +
arliones@socrates:~/tmp$ gcc ex1.c -o ex1
 +
arliones@socrates:~/tmp$ ./ex1
 +
processo filho pid: 27858 pid pai: 27857
 +
processo pai pid: 27857 pid pai: 5337
 +
arliones@socrates:~/tmp$
 +
</syntaxhighlight>
 +
 +
* Exemplo 2: processos pai e filho compartilham código, mas não dados.
 +
<syntaxhighlight lang=c>
 +
// ex2: fork/wait "compartilhando" dados
 +
// ex2: fork/wait "compartilhando" dados
 +
#include <sys/types.h>
 +
#include <stdlib.h>
 +
#include <stdio.h>
 +
#include <unistd.h>
 +
#include <sys/wait.h>
 +
 +
int main()
 +
{
 +
    int pid, status, k=0;
 +
    printf("processo %d\t antes do fork\n", getpid());
 +
    pid = fork();
 +
    printf("processo %d\t depois do fork\n", getpid());
 +
 +
    if(pid == -1) // fork falhou
 +
    {
 +
        perror("fork falhou!");
 +
        exit(-1);
 +
    }
 +
    else if(pid == 0) // Este é o processo filho
 +
    {
 +
        k += 1000;
 +
        printf("processo filho\t pid: %d\t K: %d \t endereço K: %p\n", getpid(), k, &k);
 +
        exit(0);
 +
    }
 +
    else // Este é o processo pai
 +
    {
 +
        wait(&status);  // espera o filho terminar
 +
        k += 10;
 +
        printf("processo pai\t pid: %d\t K: %d  \t endereço K:  %p\n", getpid(), k,  &k);
 +
        exit(0);
 +
    }
 +
    k += 1000;
 +
    printf("FIM processo %d\t K: %d\n", getpid(), k);
 +
    exit(0);
 +
}
 +
</syntaxhighlight>
 +
 +
<syntaxhighlight lang=bash>
 +
processo 17056  antes do fork
 +
processo 17056  depois do fork
 +
processo 17057  depois do fork
 +
processo filho  pid: 17057      K: 1000        endereço K: 0x7ffd8923e318
 +
processo pai    pid: 17056      K: 10          endereço K:  0x7ffd8923e318
 +
</syntaxhighlight>
 +
 +
* Modificação no código: comentar linhas 23 e 30
 +
 +
<syntaxhighlight lang=bash>
 +
 +
</syntaxhighlight>
 +
 +
* Analise os resultados e busque entender a diferença.
 +
*Perguntas e desafios.:
 +
#Analisando os valores impressos de k pode-se dizer que os dados são compartilhados entre os dois processos?
 +
#O endereço de k impresso é o mesmo nos dois processos. ISto não contradiz a afirmação anterior?
 +
#Substitua o k por uma área criada dinâmicamente com o malloc (área de HEAP). Verifique se esta área é ou não compartilhada pelos processos.
 +
 +
 +
;Exercício fork/wait
 +
 +
Excrever um programa C que cria uma árvore de 3 processos, onde o processo A faz um ''fork()'' criando um processo B, o processo B, por sua vez, faz um ''fork()'' criando um processo C. Cada processo deve exibir uma mensagem "Eu sou o processo XXX, filho de YYY", onde XXX e YYY são PIDs de processos. Utilizar ''wait()'' para garantir que o processo C imprima sua resposta antes do B, e que o processo B imprima sua resposta antes do A. Utilizar ''sleep()'' (man 3 sleep) para haver um intervalo de 1 segundo entre cada mensagem impressa.
 +
 +
*Use o comando pstree para verificar a árvore de processos criada.
 +
 +
{{collapse top|Solução}}
 +
<code>
 +
/*
 +
ex3: Excrever um programa C que cria uma arvore de 3 processos, onde o processo
 +
A faz um fork() criando um processo B, o processo B, por sua vez, faz um fork()
 +
criando um processo C. Cada processo deve exibir uma mensagem "Eu sou o
 +
processo XXX, filho de YYY", onde XXX e YYY sao PIDs de processos. Utilizar
 +
wait() para garantir que o processo C imprima sua resposta antes do B, e que o
 +
processo B imprima sua resposta antes do A. Utilizar sleep() (man 3 sleep) para
 +
haver um intervalo de 1 segundo entre cada mensagem impressa.
 +
*/
 +
 +
#include <sys/types.h>
 +
#include <stdlib.h>
 +
#include <stdio.h>
 +
#include <unistd.h>
 +
#include <sys/wait.h>
 +
 +
int main()
 +
{
 +
    int pid, status;
 +
    pid = fork();
 +
 +
    if(pid == -1) // fork falhou
 +
    {
 +
        perror("fork falhou!");
 +
        exit(-1);
 +
    }
 +
    else if(pid == 0) // Este é o processo filho
 +
    {
 +
        pid = fork();
 +
        if(pid == -1)
 +
        {
 +
            perror("fork falhou!");
 +
            exit(-1);
 +
        }
 +
        else if(pid == 0) // Este é o filho do filho
 +
        {
 +
            sleep(1);
 +
            printf("Eu sou o processo C (PID %d), filho de %d\n", getpid(), getppid());
 +
            exit (0);
 +
        }
 +
        else
 +
        {
 +
            wait(&status);
 +
            sleep(1);
 +
            printf("Eu sou o processo B (PID %d), filho de %d\n", getpid(), getppid());
 +
            exit(0);
 +
        }
 +
    }
 +
    else // Este é o processo pai
 +
    {
 +
        wait(&status);
 +
        sleep(1);
 +
        printf("Eu sou o processo A (PID %d), filho de %d\n", getpid(), getppid());
 +
        exit(0);
 +
    }
 +
}
 +
 +
</syntaxhighlight>
 +
{{collapse bottom}}

Edição das 07h28min de 21 de fevereiro de 2020

AULA 1 - Dia 11/02/2020

Objetivos/Conteúdos

  • Apresentação do Plano de Ensino
    • Objetivos e Conteúdo Programático da Disciplina (ver SIGAA)
    • Forma de Avaliação (ver SIGAA)
  • Introdução a Sistemas Operacionais (Cap1. do Silberschatz)
    • O que faz um sistema operacional (1.1)
    • Organização e Arquitetura de um Sistema Computacional (1.2 e 1.3)
      • Importância da Interrupção e Timers (1.2.1)
      • Estrutura de Armazenamento (1.2.2)
      • Estrutura de IO (1.2.3)
    • Estrutura e Operações de um Sistema Operacional (1.4 e 1.5)

Material de Referência

Arliones:

Leitura Recomendada

  • Cap.1 do Silberschatz principalmente:
    • 1.1 a 1.9

Exercícios Práticos de Apoio a Aula

Na sequência. com fins motivacionais, são apresentados alguns exercícios ilustrando conceitos de processos, arquivos e permissionamento.

  1. Comando para ver todos os processos no linux:
      $ ps aux
    
  2. Colocar 2 processos se executando no contexto de um terminal e verificar número dos processos e então "destruí-los":
    $ yes > /dev/null
    $ yes > /dev/null
    $ ps 
    $ kill -9 <pid_yes>
    $ kill -9 <pid_yes>
    

    Observe que dois processos foram criados a partir do programa "yes". Os processos associados ao terminal são visualizados e então destruídos. Tente destruir também o interpretador de comando (shell) associado ao terminal.

  3. Criar um terminal adicional
    $ xterm
    

    Ir no terminal criado e listar os processos que se executam associados a este terminal. Verificar qual o dispositivo de entrada e saída associado a ele. Voltar ao terminal original e enviar uma mensagem. Destruir o terminal criado:

    $ echo Alo Terminal > /dev/pts/2
    $ kill -9 <pid_terminal>
    
  4. Comunicação entre processos:
    $ cat /etc/passwd | grep home | wc -l
    
  5. Retirando a permissão de leitura de um arquivo em nível de usuário proprietário:
    $ echo Alo Mundo > alfa.txt
    $ ls -l alfa.txt
    $ cat alfa.txt
    $ chmod u-r alfa.txt
    $ cat alfa.txt
    $ chmod u+r alfa.txt
    $ cat  alfa.txt
    

AULA 2 - Dia 14/02/2020

Objetivos/Conteúdos

  • Estruturas do Sistema Operacional (cap.2)
    • Serviços do Sistema Operacional (2.1)
    • Interfaces com Usuários (2.2)
    • Chamadas do Sistema (2.3)
    • Tipos de Chamadas de Sistema (2.4) ver Fig.2.8
      • Chamadas de Controle de Processos


Material de Referência

Leitura Recomendada

  • Cap.2 do Silberschatz principalmente:
    • 2.1 a 2.8

Exercícios

  1. Estudar e executar o código em http://cs.lmu.edu/~ray/notes/gasexamples/ Detalhes das chamadas na arquitetura x86_64 ver em https://lwn.net/Articles/604287/
  2. Estudar e executar o código usando a função (em conformidade com a API POSIX) write() para o hello world:
    #include <unistd.h> 
    
    main()
    {
      write(1,"Alo Mundo\n",10);
    }
    
  3. Use o comando strace para verificar todas as chamadas de sistema dos programas acima.
  4. DESAFIO 1: Estude a seção "Mixing C and Assembly Language" da http://cs.lmu.edu/~ray/notes/gasexamples/ e construa uma função meu_hello_world() usando o código em assembly do exercício inicial. Estude como poderia disponibilizar esta e outras funções de interface (a sua API) em uma biblioteca. Note que esta função deve ser chamada da forma:
    main()
    {
      meu_hello_world();
    }
    
  5. Gere o assembly do código em C e discuta a diferença entre uma chamada de função e uma chamada de sistema.
  6. DESAFIO 2: Estude o link http://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/ e melhore a função meu_hello_world para suportar uma mensagem adicional da forma:
    #include <string.h>
    main()
    {
      char *p="Tudo bem com vocês?";
      meu_hello_world(p, strlen(p));
    }
    

    Solução:

    Solução
    modificado de http://cs.lmu.edu/~ray/notes/gasexamples/
            .global meu_hello_world
    
            .text
    meu_hello_world:
    
            push    %rdi                    # salva primeiro parâmetro
            push    %rsi                    # salva segundo parâmetro
            mov     $1, %rax                # system call 1 é write
            mov     $1, %rdi                # file handle 1 é stdout
            mov     $message, %rsi          # endereço da string
            mov     $13, %rdx               # número de bytes da string
            syscall                         # chamada ao sistema
    
            pop     %rdx                    # o que havia sido passado em rsi (número de bytes) é colocado em rdx
            pop     %rsi                    # o que havia sido colocado em rdi (endereço da string) é colocado em rsi
            mov     $1, %rax                # system call 1 is write
            mov     $1, %rdi                # file handle 1 é stdout
            syscall                         # nova chamada ao sistema
    
            ret
    
    message:
            .ascii  "Hello, World\n"
    

    AULA 3 - Dia 18/02/2020

    Objetivos/Conteúdos

      • Interfaces com Usuários (2.2)
      • Chamadas do Sistema (2.3)
      • Tipos de Chamadas de Sistema (2.4) ver Fig.2.8
        • Chamadas de Controle de Processos

    AULA 4 - Dia 21/08/2019

    Objetivos

    • PARTE 2A: Gerenciamento de Processos (cap.3)
    • 3.1.Conceito de Processo
    • 3.2.Escalonamento de Processos
    • 3.3.Operações sobre Processos (Laboratório Operações sobre Processos no Linux: Fork/Exec/Wait)

    Material de Referência

    • Slides do Cap.3 - Silberschatz
    • Laboratório Fork/Exec/Wait

    Exercícios de Demonstração

    1. Executar em um terminal o comando top. Em um outro terminal executar:
      yes > /dev/null &
      yes > /dev/null &
    
    1. Parar um dos processos yes
      kill -STOP <pid_yes>
    
    1. Continuar o processo
      kill -CONT <pid_yes>
    
    1. Destruir todos
      killall yes
    


    Processos no Linux

    Processos no Linux - Modificado por Eraldo

    Processos no Linux

    • Exercícios propostos pelo Prof.Arliones e Modificados por Eraldo


    DesenhoForkExec.png


    Syscall FORK
    • Em um terminal, execute "man fork"
      • A função da API POSIX fork() aciona uma chamada de sistema (system call - syscall) que cria um novo processo duplicando o processo que realiza a chamada. O novo processo, chamado de filho, é uma cópia exata do processo criador, chamado de pai, exceto por alguns detalhes listados na manpage. O principal destes detalhes para nós agora é o fato de terem PIDs diferentes.
      • O código dos dois processos (pai e filho) são idênticos;
      • Os dados dos dois processos (pai e filho) são idênticos NO MOMENTO DA CRIAÇÃO;
      • Execução do processo filho inicia na próxima instrução do programa (no retorno da chamada FORK);
      • Não é possível saber qual dos processos (pai ou filho) retormará a execução primeiro - isto fica a cargo do excalonador do SO;
      • Valores de retorno da chamada FORK:
        • (-1): erro na criação do processo (ex.: memória insuficiente);
        • (0): em caso de sucesso, este é o valor de retorno recebido pelo processo filho;
        • (>0): em caso de sucesso, este é o valor de retorno recebido pelo processo pai;
    Syscall JOIN
    • A syscall JOIN é implementada no POSIX pela função wait(). Execute "man wait".
      • Além da função wait(), há também waitpid() e waitid();
      • Todas estas syscalls são utilizadas para aguardar por mudanças no estado de um processo filho e obter informações sobre o processo filho cujo estado tenha mudado. São consideradas mudanças de estado: o filho terminou; o filho foi finalizado por um sinal (ex.: kill); o filho foi retomado por um sinal (ex.: alarme);
      • A chamada wait também libera os recursos do processo filho que termina;
      • wait(): esta função suspende a execução do processo chamador até que UM DOS SEUS FILHOS finalize;
      • waitpid(): suspende a execução do processo chamador até que UM FILHO ESPECÍFICO finalize;
    Syscall EXEC
    • A syscall EXEC é implementada no POSIX pela família de funções exec(). Execute "man exec".
      • As principais funções da família são execl(), execlp() e execvp();
      • Todas estas funções são, na realidade, front-ends (abstrações) para a syscall execve. Esta syscall substitui a imagem do processo corrente (aquele que chama a syscall) pela a imagem de um novo processo;
      • Os parâmetros passados a estas funções são, basicamente, o nome de um arquivo com a imagem do programa a ser executado (um binário de um programa), e uma lista de parâmetros a serem passados a este novo programa;


    Exemplos POSIX utilizando fork/wait/exec
    • Exemplo 1: fork/wait básico
    // ex1: fork/wait básico
    #include <sys/types.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/wait.h>
    
    int main()
    {
        int pid, status;
        pid = fork();
    
        if(pid == -1) // fork falhou
        {
            perror("fork falhou!");
            exit(-1);
        }
        else if(pid == 0) // Este é o processo filho
        {
            printf("processo filho\t pid: %d\t pid pai: %d\n", getpid(), getppid());
            exit(0);
        }
        else // Este é o processo pai
        {
            wait(&status);
            printf("processo pai\t pid: %d\t pid pai: %d\n", getpid(), getppid());
            exit(0);
        }
    }
    
    arliones@socrates:~/tmp$ gcc ex1.c -o ex1 
    arliones@socrates:~/tmp$ ./ex1 
    processo filho	 pid: 27858	 pid pai: 27857
    processo pai	 pid: 27857	 pid pai: 5337
    arliones@socrates:~/tmp$
    
    • Exemplo 2: processos pai e filho compartilham código, mas não dados.
    // ex2: fork/wait "compartilhando" dados
    // ex2: fork/wait "compartilhando" dados
    #include <sys/types.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/wait.h>
    
    int main()
    {
        int pid, status, k=0;
        printf("processo %d\t antes do fork\n", getpid());
        pid = fork();
        printf("processo %d\t depois do fork\n", getpid());
     
        if(pid == -1) // fork falhou
        {
            perror("fork falhou!");
            exit(-1);
        }
        else if(pid == 0) // Este é o processo filho
        {
            k += 1000;
            printf("processo filho\t pid: %d\t K: %d \t endereço K: %p\n", getpid(), k, &k);
            exit(0);
        }
        else // Este é o processo pai
        {
            wait(&status);  // espera o filho terminar
            k += 10;
            printf("processo pai\t pid: %d\t K: %d  \t endereço K:  %p\n", getpid(), k,  &k);
            exit(0);
        }
        k += 1000;
        printf("FIM processo %d\t K: %d\n", getpid(), k); 
        exit(0);
    }
    
    processo 17056   antes do fork
    processo 17056   depois do fork
    processo 17057   depois do fork
    processo filho   pid: 17057      K: 1000         endereço K: 0x7ffd8923e318
    processo pai     pid: 17056      K: 10           endereço K:  0x7ffd8923e318
    
    • Modificação no código: comentar linhas 23 e 30
    • Analise os resultados e busque entender a diferença.
    • Perguntas e desafios.:
    1. Analisando os valores impressos de k pode-se dizer que os dados são compartilhados entre os dois processos?
    2. O endereço de k impresso é o mesmo nos dois processos. ISto não contradiz a afirmação anterior?
    3. Substitua o k por uma área criada dinâmicamente com o malloc (área de HEAP). Verifique se esta área é ou não compartilhada pelos processos.


    Exercício fork/wait

    Excrever um programa C que cria uma árvore de 3 processos, onde o processo A faz um fork() criando um processo B, o processo B, por sua vez, faz um fork() criando um processo C. Cada processo deve exibir uma mensagem "Eu sou o processo XXX, filho de YYY", onde XXX e YYY são PIDs de processos. Utilizar wait() para garantir que o processo C imprima sua resposta antes do B, e que o processo B imprima sua resposta antes do A. Utilizar sleep() (man 3 sleep) para haver um intervalo de 1 segundo entre cada mensagem impressa.

    • Use o comando pstree para verificar a árvore de processos criada.
    Solução

    /* ex3: Excrever um programa C que cria uma arvore de 3 processos, onde o processo A faz um fork() criando um processo B, o processo B, por sua vez, faz um fork() criando um processo C. Cada processo deve exibir uma mensagem "Eu sou o processo XXX, filho de YYY", onde XXX e YYY sao PIDs de processos. Utilizar wait() para garantir que o processo C imprima sua resposta antes do B, e que o processo B imprima sua resposta antes do A. Utilizar sleep() (man 3 sleep) para haver um intervalo de 1 segundo entre cada mensagem impressa.

    • /
    1. include <sys/types.h>
    2. include <stdlib.h>
    3. include <stdio.h>
    4. include <unistd.h>
    5. include <sys/wait.h>

    int main() {

       int pid, status;
       pid = fork();
    
       if(pid == -1) // fork falhou
       {
           perror("fork falhou!");
           exit(-1);
       }
       else if(pid == 0) // Este é o processo filho
       {
           pid = fork();
           if(pid == -1)
           {
               perror("fork falhou!");
               exit(-1);
           }
           else if(pid == 0) // Este é o filho do filho
           {
               sleep(1);
               printf("Eu sou o processo C (PID %d), filho de %d\n", getpid(), getppid());
               exit (0);
           }
           else
           {
               wait(&status);
               sleep(1);
               printf("Eu sou o processo B (PID %d), filho de %d\n", getpid(), getppid());
               exit(0);
           }
       }
       else // Este é o processo pai
       {
           wait(&status);
           sleep(1);
           printf("Eu sou o processo A (PID %d), filho de %d\n", getpid(), getppid());
           exit(0);
       }
    

    }

    </syntaxhighlight>