Mudanças entre as edições de "SOP-EngTel 2018 2"
(84 revisões intermediárias por 2 usuários não estão sendo mostradas) | |||
Linha 4: | Linha 4: | ||
*'''Professor:''' [[André D'Amato]] | *'''Professor:''' [[André D'Amato]] | ||
*'''Encontros:''' Segundas às 15:40 no LabCad e quintas às 13:30 no LabSid. | *'''Encontros:''' Segundas às 15:40 no LabCad e quintas às 13:30 no LabSid. | ||
− | *'''Atendimento paralelo:''' | + | *'''Atendimento paralelo:''' Quarta de 11:30 ás 12:30 horas. |
*[[SOP-EngTel_2018_2_plano| Plano de Ensino]] | *[[SOP-EngTel_2018_2_plano| Plano de Ensino]] | ||
*[[SOP2018_1_Cronograma | Cronograma]] | *[[SOP2018_1_Cronograma | Cronograma]] | ||
+ | |||
+ | {| border=1 | ||
+ | ! Nome | ||
+ | ! Prova 1 | ||
+ | ! Prova 2 | ||
+ | ! Prova 3 | ||
+ | ! Atividade 1 | ||
+ | ! Atividade 2 | ||
+ | ! Média Trabalhos * 0,3 | ||
+ | ! Média Provas * 0,7 | ||
+ | ! Final | ||
+ | |- | ||
+ | | Alisson || 4,75(8,0) || 6,5 || 1,5 ||10 || 10 || 3,73 || 3 || 6,7 | ||
+ | |- | ||
+ | | André || 6,0 || 6,25 || 5,0 || 10 || 10 || 4,25 || 3 || 7 | ||
+ | |- | ||
+ | | Angelo || 5,1(6,3) || 1,25(2,3) || 6,4(4,0) || 10 || 0 || 3,5 || 1,5 || 5 | ||
+ | |- | ||
+ | | Felipe || 8,25 || 9,5 || 8,0 || 10 || 0 || 6,0 || 1,5 || 7,51 | ||
+ | |- | ||
+ | | Francisco || 7,5 || 0 || 0 || 10 || 0 || - || - || - | ||
+ | |- | ||
+ | | Gabriel || 0(7,3) || 0(4,0) || 4,0 || 10 || 10 || 3,57 || 3 || 6,6 | ||
+ | |- | ||
+ | | Guilherme || 8,25 || 5,3(7,0) || 4,7 ||10 || 10 || 4,65 || 3 || 7,7 | ||
+ | |- | ||
+ | | Luiza || 4,4(7,5) || 6,0 || 4,2 || 10 ||5 || 4,13 || 2,25 || 6,0 | ||
+ | |- | ||
+ | | Rafael || 6,75 || 6,0 || 6,4 || 10 || 0 || 4,5 || 1,5 || 6,0 | ||
+ | |- | ||
+ | | Suyan || 4,4(6,0) || 5,0(4,5) || 3,7 || 10 || 10|| 3,4 || 3 || 6,4 | ||
+ | |- | ||
+ | | Yara || 4,9(7,0) || 6,0 || 4,0 || 10 || 10 || 4 || 3 || 7,0 | ||
+ | |- | ||
+ | |} | ||
+ | |||
+ | *Recuperação Data: 17/12/2018 no horário de aula!!!! | ||
+ | |||
Linha 76: | Linha 114: | ||
{{collapse bottom}} | {{collapse bottom}} | ||
− | |||
− | |||
− | |||
{{collapse top| bg=lightyellow | expandir=true | Unidade 02: Processos}} | {{collapse top| bg=lightyellow | expandir=true | Unidade 02: Processos}} | ||
== Unidade 02: Processos == | == Unidade 02: Processos == | ||
Linha 87: | Linha 122: | ||
=== Escalonamento de tarefas === | === Escalonamento de tarefas === | ||
− | * [http://docente.ifsc.edu.br/ | + | * [http://docente.ifsc.edu.br/andre.damato/sop2018/SOP2018-parte2.pdf Apresentação sobre Escalonamento de Processos] |
* [http://courses.cs.vt.edu/csonline/OS/Lessons/Processes/index.html Animação de escalonamento de processos - Virginia Tech] | * [http://courses.cs.vt.edu/csonline/OS/Lessons/Processes/index.html Animação de escalonamento de processos - Virginia Tech] | ||
* Capítulo 5 do livro do Silberschatz. | * Capítulo 5 do livro do Silberschatz. | ||
Linha 104: | Linha 139: | ||
* Capítulos 6 e 7 do livro do Silberschatz. | * Capítulos 6 e 7 do livro do Silberschatz. | ||
* Curiosidade: [http://research.microsoft.com/en-us/um/people/mbj/mars_pathfinder/authoritative_account.html A inversão de prioridades na Mars Pathfinder] | * Curiosidade: [http://research.microsoft.com/en-us/um/people/mbj/mars_pathfinder/authoritative_account.html A inversão de prioridades na Mars Pathfinder] | ||
+ | |||
+ | |||
{{collapse bottom}} | {{collapse bottom}} | ||
− | |||
{{collapse top| bg=lightyellow | expandir=true | Unidade 03: Memória}} | {{collapse top| bg=lightyellow | expandir=true | Unidade 03: Memória}} | ||
== Unidade 03: Memória== | == Unidade 03: Memória== | ||
Linha 113: | Linha 149: | ||
* [http://docente.ifsc.edu.br/arliones.hoeller/sop/slides/SOP29005-parte5.pdf Apresentação sobre Gerenciamento de Memória] | * [http://docente.ifsc.edu.br/arliones.hoeller/sop/slides/SOP29005-parte5.pdf Apresentação sobre Gerenciamento de Memória] | ||
* Capítulo 8 do livro do Silberschatz. | * Capítulo 8 do livro do Silberschatz. | ||
+ | |||
+ | |||
=== Memória Principal === | === Memória Principal === | ||
Linha 122: | Linha 160: | ||
* Capítulo 9 do livro do Silberschatz. | * Capítulo 9 do livro do Silberschatz. | ||
+ | |||
+ | === Exercícios === | ||
+ | |||
+ | [http://docente.ifsc.edu.br/andre.damato/sop2018/exercicios_memoria1.pdf Exercícios: Introdução]. | ||
+ | |||
+ | [http://docente.ifsc.edu.br/andre.damato/sop2018/SopMem.pdf Gerenciamento de Memória 1]. | ||
+ | |||
+ | [http://docente.ifsc.edu.br/andre.damato/sop2018/exe_mem3.pdf Gerenciamento de Memória 2]. | ||
{{collapse bottom}} | {{collapse bottom}} | ||
− | |||
{{collapse top| bg=lightyellow | expandir=true | Unidade 04: Armazenamento}} | {{collapse top| bg=lightyellow | expandir=true | Unidade 04: Armazenamento}} | ||
== Unidade 04: Armazenamento == | == Unidade 04: Armazenamento == | ||
Linha 131: | Linha 176: | ||
* [http://docente.ifsc.edu.br/arliones.hoeller/sop/slides/SOP29005-parte6.pdf Apresentação sobre Gerenciamento de Arquivos] | * [http://docente.ifsc.edu.br/arliones.hoeller/sop/slides/SOP29005-parte6.pdf Apresentação sobre Gerenciamento de Arquivos] | ||
* Capítulo 10 do livro do Silberschatz. | * Capítulo 10 do livro do Silberschatz. | ||
+ | |||
+ | == Permissões de sistema de arquivos no Linux == | ||
+ | |||
+ | Neste estudo de caso são realizados alguns exercícios práticos que permitem verificar como o sistema de arquivos é organizado no Linux. | ||
+ | Acesse o estudo de caso através [http://wiki.inf.ufpr.br/maziero/doku.php?id=unix:permissoes_em_arquivos deste roteiro] do Prof. Maziero da UTFPR. | ||
+ | |||
=== Implementação do Sistema de Arquivos === | === Implementação do Sistema de Arquivos === | ||
Linha 136: | Linha 187: | ||
* Capítulo 11 do livro do Silberschatz. | * Capítulo 11 do livro do Silberschatz. | ||
− | === Estrutura de Armazenamento em Massa === | + | ==== Exercícios ==== |
− | * [http://docente.ifsc.edu.br/arliones.hoeller/sop/slides/SOP29005-parte6.pdf Apresentação sobre Gerenciamento de Arquivos] | + | |
− | * Capítulo 12 do livro do Silberschatz. | + | |
+ | 1. Qual tipo de organização de diretórios que o ubuntu utiliza, grafo cíclico, grafo acíclico, flat ou árvore, comprove seu raciocínio por meio de testes. | ||
+ | |||
+ | 2. No ubuntu o que acontece quando deletamos um hard link, e em seguida acessamos o link como um arquivo comum e alteramos seu conteúdo? | ||
+ | |||
+ | * É possível tomar tal ação? Se sim Qual o efeito? explique. | ||
+ | |||
+ | * Faça o mesmo teste, porém desta vez utilize um soft link. | ||
+ | |||
+ | === Estrutura de Armazenamento em Massa === | ||
+ | * [http://docente.ifsc.edu.br/arliones.hoeller/sop/slides/SOP29005-parte6.pdf Apresentação sobre Gerenciamento de Arquivos] | ||
+ | * Capítulo 12 do livro do Silberschatz. | ||
=== Gerenciamento de Entrada e Saída === | === Gerenciamento de Entrada e Saída === | ||
* [http://docente.ifsc.edu.br/arliones.hoeller/sop/slides/SOP29005-parte7.pdf Apresentação sobre Gerenciamento de Entrada e Saída] | * [http://docente.ifsc.edu.br/arliones.hoeller/sop/slides/SOP29005-parte7.pdf Apresentação sobre Gerenciamento de Entrada e Saída] | ||
* Capítulo 13 do livro do Silberschatz. | * Capítulo 13 do livro do Silberschatz. | ||
+ | |||
+ | === Exercícios === | ||
+ | |||
+ | [http://docente.ifsc.edu.br/andre.damato/sop2018/lista_arquivos.pdf Exercícios Arquivos]. | ||
{{collapse bottom}} | {{collapse bottom}} | ||
− | |||
− | |||
=Laboratórios= | =Laboratórios= | ||
Linha 228: | Linha 292: | ||
{{collapse bottom}} | {{collapse bottom}} | ||
− | + | {{collapse top| bg=lightyellow | expandir=true | Processos no Linux}} | |
− | |||
− | |||
− | {{collapse top| bg=lightyellow | expandir=true | Processos no Linux | ||
== Processos no Linux == | == Processos no Linux == | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
Linha 391: | Linha 445: | ||
O ''status'' passado como parâmetro à função ''wait(&status)'' é, na verdade, o mecanismo de retorno de resultado do ''wait/waitpid''. Ao retornar, esta variável contém informações sobre o resultado da execução do processo filho. Por exemplo, se um processo terminou normalmente (i.e., chamou ''exit''), o comando ''WIFEXITED(status)'' retorna ''true''. Este comando retorna ''false'' se o processo foi abortado (e.g., ''segmentation fault'') ou morto (e.g., ''kill''). Investigue no manual do wait no Linux (''man wait'') o funcionamento do comando WEXITSTATUS(status), e use-o para modificar o exercício anterior para calcular o 5!, sendo que cada processo pode executar apenas uma multiplicação. | O ''status'' passado como parâmetro à função ''wait(&status)'' é, na verdade, o mecanismo de retorno de resultado do ''wait/waitpid''. Ao retornar, esta variável contém informações sobre o resultado da execução do processo filho. Por exemplo, se um processo terminou normalmente (i.e., chamou ''exit''), o comando ''WIFEXITED(status)'' retorna ''true''. Este comando retorna ''false'' se o processo foi abortado (e.g., ''segmentation fault'') ou morto (e.g., ''kill''). Investigue no manual do wait no Linux (''man wait'') o funcionamento do comando WEXITSTATUS(status), e use-o para modificar o exercício anterior para calcular o 5!, sendo que cada processo pode executar apenas uma multiplicação. | ||
+ | |||
+ | |||
+ | |||
{{collapse bottom}} | {{collapse bottom}} | ||
− | {{collapse top| bg=lightyellow | expandir=true | Threads de aplicação | + | {{collapse top| bg=lightyellow | expandir=true | Threads de aplicação}} |
== Threads de aplicação == | == Threads de aplicação == | ||
Linha 505: | Linha 562: | ||
{{collapse bottom}} | {{collapse bottom}} | ||
− | |||
{{collapse top| bg=lightyellow | expandir=true | Exercícios sobre pipe e memória compartilhada}} | {{collapse top| bg=lightyellow | expandir=true | Exercícios sobre pipe e memória compartilhada}} | ||
== PIPE e SH_MEMORY == | == PIPE e SH_MEMORY == | ||
Linha 559: | Linha 615: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | |||
+ | |||
+ | <!-- Parou aki 20/08 | ||
+ | |||
*'''Experimento 2:''' Complete o código a seguir para que os processos pai e filho possam compartilhar um segmento de memória. O filho escreve no segmento e o pai imprime na tela o conteúdo da mensagem. | *'''Experimento 2:''' Complete o código a seguir para que os processos pai e filho possam compartilhar um segmento de memória. O filho escreve no segmento e o pai imprime na tela o conteúdo da mensagem. | ||
Linha 756: | Linha 816: | ||
{{collapse bottom}} | {{collapse bottom}} | ||
+ | --> | ||
− | {{collapse top| bg=lightyellow | expandir=true | Trocas de mensagens com pipes | + | {{collapse top| bg=lightyellow | expandir=true | Trocas de mensagens com pipes}} |
== Trocas de mensagens com pipes == | == Trocas de mensagens com pipes == | ||
Linha 796: | Linha 857: | ||
*'''Exercício 1: construa um “pipeline”'''. Crie um programa que conecta 4 processos através de 3 pipes. Utilize ''fork()'' para criar vários processos. | *'''Exercício 1: construa um “pipeline”'''. Crie um programa que conecta 4 processos através de 3 pipes. Utilize ''fork()'' para criar vários processos. | ||
− | + | <syntaxhighlight lang=cpp> | |
− | |||
− | |||
− | |||
− | |||
+ | //Solução possível | ||
− | + | #include <unistd.h> | |
+ | #include <fcntl.h> | ||
+ | #include <stdio.h> | ||
+ | #include <string.h> | ||
+ | #include <stdlib.h> | ||
− | + | char *message = "This is a message to send!!!" ; | |
− | |||
+ | main() | ||
+ | { | ||
+ | int size = strlen(message)+1; | ||
+ | char buf[size]; | ||
+ | char buf1[size]; | ||
+ | char buf2[size]; | ||
+ | int status; | ||
+ | int fd[2]; | ||
+ | pipe(fd); | ||
− | + | if (fork() != 0) { /* I am the parent */ | |
− | + | printf("Processo A PID: %d\n", getpid()); | |
+ | write(fd[1], message, size); | ||
+ | wait(&status); | ||
+ | |||
+ | } | ||
+ | else { /*Child code */ | ||
+ | |||
+ | int status1; | ||
+ | int fd1[2]; | ||
+ | pipe(fd1); | ||
+ | |||
+ | if (fork() != 0) { /* I am the parent */ | ||
− | + | printf("Processo B PID: %d\n", getpid()); | |
− | + | read(fd[0], buf, size); | |
− | + | write(fd1[1], buf, size); | |
− | + | wait(&status1); | |
− | + | ||
− | + | }else { /*Child code */ | |
− | + | ||
− | + | int status2; | |
− | { | + | int fd2[2]; |
− | + | pipe(fd2); | |
+ | |||
+ | |||
+ | if (fork() != 0) { /* I am the parent */ | ||
+ | printf("Processo C PID: %d\n", getpid()); | ||
− | + | read(fd1[0], buf1, size); | |
− | + | write(fd2[1], buf1, size); | |
+ | wait(&status2); | ||
+ | |||
+ | |||
+ | }else { /*Child code */ | ||
− | + | printf("Processo D PID: %d\n", getpid()); | |
− | printf(" | + | read(fd2[0], buf2, size); |
− | + | printf("\n Mensagem -> %s <- \n ", buf2); | |
− | + | ||
− | + | } | |
+ | |||
+ | } | ||
+ | |||
+ | } | ||
+ | exit(0); | ||
+ | } | ||
+ | |||
+ | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
</syntaxhighlight> | </syntaxhighlight> | ||
− | '''Exercício 2''': | + | *'''Exercício 2: cópia de arquivo'''. Projete um programa de cópia de arquivos chamado FileCopy usando pipes comuns. Esse programa receberá dois parâmetros: o primeiro é o nome do arquivo a ser copiado e o segundo é o nome do arquivo copiado. Em seguida, o programa criará um pipe comum e gravará nele o conteúdo do arquivo a ser copiado. O processo filho lerá esse arquivo do pipe e o gravará no arquivo de destino. Por exemplo, se chamarmos o programa como descrito a seguir: |
− | + | :<syntaxhighlight lang=bash> | |
− | + | $ FileCopy entrada.txt copia.txt | |
− | + | </syntaxhighlight> | |
+ | :o arquivo ''entrada.txt'' será gravado no pipe. O processo filho lerá o conteúdo desse arquivo e o gravará no arquivo de destino ''copia.txt''. Escreva o programa usando os pipes da API POSIX no Linux. | ||
{{collapse bottom}} | {{collapse bottom}} | ||
− | {{collapse top| bg=lightyellow | expandir=true | | + | {{collapse top| bg=lightyellow | expandir=true | Memória Compartilhada}} |
− | |||
+ | *'''Experimento Shared Memory:''' Complete o código a seguir para que os processos pai e filho possam compartilhar um segmento de memória. O filho escreve no segmento e o pai imprime na tela o conteúdo da mensagem. | ||
− | |||
− | <syntaxhighlight lang= | + | <syntaxhighlight lang=cpp> |
#include <stdio.h> | #include <stdio.h> | ||
Linha 874: | Linha 958: | ||
#include <sys/ipc.h> | #include <sys/ipc.h> | ||
#include <sys/shm.h> | #include <sys/shm.h> | ||
− | |||
− | |||
− | |||
− | |||
− | + | #define SHM_SIZE 1024 | |
− | + | ||
− | + | int main(int argc, char *argv[]) | |
− | + | { | |
− | + | key_t key; | |
− | + | int shmid; | |
− | + | char *segmento; | |
− | + | int modo,filho; | |
+ | |||
+ | /* Criar a chave: */ | ||
+ | if (key == -1) | ||
+ | { | ||
+ | perror("ftok"); | ||
+ | exit(1); | ||
} | } | ||
− | + | ||
− | + | /*Criar o segmento */ | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | if (shmid == -1) { | |
+ | perror("shmget"); | ||
+ | exit(1); | ||
+ | } | ||
− | + | /*Vincula o segmento de memória á variável segmento*/ | |
− | + | segmento = shmat(shmid, (void *)0, 0); | |
+ | if (segmento == (char *)(-1)) { | ||
+ | perror("shmat"); | ||
+ | exit(1); | ||
+ | } | ||
+ | |||
+ | if((filho = fork()) == -1) | ||
+ | { | ||
+ | perror("fork"); | ||
+ | exit(1); | ||
+ | } | ||
+ | |||
+ | if(filho == 0) //Código do filho | ||
+ | { | ||
+ | |||
+ | |||
+ | |||
+ | exit(0); | ||
+ | } | ||
+ | else //Código do pai | ||
+ | { | ||
+ | |||
+ | |||
+ | } | ||
− | + | /* detach from the segment: */ | |
− | + | if (shmdt(segmento) == -1) { | |
− | + | perror("shmdt"); | |
− | + | exit(1); | |
− | + | } | |
− | + | ||
− | + | return 0; | |
− | + | } | |
− | + | ||
− | + | </syntaxhighlight> | |
− | + | ||
− | + | {{collapse bottom}} | |
− | + | {{collapse bottom}} | |
− | + | {{collapse top| bg=lightyellow | expandir=true | Exercício (Algoritmo de Peterson)}} | |
− | + | == Exercício (Algoritmo de Peterson) == | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | '''Exercício 1''': Sincronize o código a seguir, de maneira que o processo pai imprima apenas os números impares e o processo filho os números pares. Para isso utilize o algoritmo de '''Peterson''' visto em aula. '''Utilize memória compartilhada''' para comunicação entre os processos. | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | <syntaxhighlight lang=c> | |
+ | #include <stdio.h> | ||
+ | #include <stdlib.h> | ||
+ | #include <string.h> | ||
+ | #include <sys/types.h> | ||
+ | #include <sys/ipc.h> | ||
+ | #include <sys/shm.h> | ||
+ | |||
+ | main() | ||
+ | { | ||
+ | |||
− | + | if (fork() != 0) { /* I am the parent */ | |
− | + | int i; | |
− | + | ||
− | + | for(i = 0;i < 10;i=i+2){ | |
− | + | printf("Processo pai %d \n", i); | |
− | + | ||
− | + | } | |
− | + | ||
− | |||
− | } | ||
+ | } | ||
+ | else { /*Child code */ | ||
+ | int i; | ||
+ | for(i = 1;i < 10;i=i+2){ | ||
+ | printf("Processo filho %d \n", i); | ||
+ | |||
+ | } | ||
+ | |||
+ | |||
+ | } | ||
+ | |||
+ | exit(0); | ||
− | |||
− | |||
− | |||
− | |||
− | |||
} | } | ||
+ | </syntaxhighlight> | ||
+ | '''Exercício 2''': Considerando o exercício anterior faça a mesma sincronização, no entanto desta vez utilize a modelagem em software do TSL. | ||
+ | * Em sua experiência, depois de testar diversas vezes as execuções de suas soluções baseadas no algoritmo de '''Peterson''' e '''Tsl''', qual sua opinião sobre as abordagens? | ||
+ | Explique seu raciocínio. | ||
+ | {{collapse bottom}} | ||
+ | {{collapse top| bg=lightyellow | expandir=true | Exercício (Semáforos)}} | ||
+ | == Exercício (Semáforos) == | ||
− | |||
− | + | '''Exercício 1''': Sincronize o código a seguir, de maneira que o processo pai imprima apenas os números impares e o processo filho os números pares. Para isso utilize '''Semáforos''' de acordo com a implementação em '''semaforo.h'''. '''Utilize memória compartilhada''' para comunicação entre os processos. | |
− | + | <syntaxhighlight lang=c> | |
− | + | #include <stdio.h> | |
− | + | #include <stdlib.h> | |
− | + | #include <string.h> | |
− | + | #include <sys/types.h> | |
− | + | #include <sys/ipc.h> | |
− | + | #include <sys/shm.h> | |
− | + | ||
− | + | main() | |
− | + | { | |
− | + | ||
− | < | ||
− | |||
− | |||
− | |||
− | #include < | ||
− | #include < | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
+ | if (fork() != 0) { /* I am the parent */ | ||
+ | int i; | ||
+ | |||
+ | for(i = 0;i < 10;i=i+2){ | ||
+ | printf("Processo pai %d \n", i); | ||
+ | sleep(1); | ||
+ | |||
+ | } | ||
+ | |||
+ | } | ||
+ | else { /*Child code */ | ||
+ | int i; | ||
+ | for(i = 1;i < 10;i=i+2){ | ||
+ | printf("Processo filho %d \n", i); | ||
+ | |||
+ | } | ||
+ | |||
+ | |||
+ | } | ||
+ | |||
+ | exit(0); | ||
+ | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | |||
− | + | SEMAFORO.H | |
− | |||
− | |||
− | |||
<syntaxhighlight lang=c> | <syntaxhighlight lang=c> | ||
− | |||
− | |||
− | #include < | + | #include <sys/sem.h> |
+ | |||
− | + | int criar_semaforo(int val, int chave) | |
{ | { | ||
− | + | int semid ; | |
− | + | ||
− | + | union semun { | |
+ | int val; | ||
+ | struct semid_ds *buf ; | ||
+ | ushort array[1]; | ||
+ | } arg_ctl ; | ||
+ | |||
+ | key_t ft = ftok("/tmp", chave); | ||
+ | |||
+ | semid = semget(ft,1,IPC_CREAT|IPC_EXCL|0666); | ||
+ | if (semid == -1) { | ||
+ | semid = semget(ft,1,0666); | ||
+ | if (semid == -1) { | ||
+ | perror("Erro semget()"); | ||
+ | exit(1) ; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | arg_ctl.val = val; //valor de início | ||
+ | if (semctl(semid,0,SETVAL,arg_ctl) == -1) { | ||
+ | perror("Erro inicializacao semaforo"); | ||
+ | exit(1); | ||
+ | } | ||
+ | return(semid) ; | ||
+ | } | ||
− | + | void P(int semid){ | |
− | |||
− | |||
− | + | struct sembuf *sops = malloc(10*sizeof(int)); | |
− | + | sops->sem_num = 0; | |
+ | sops->sem_op = -1; | ||
+ | sops->sem_flg = 0; | ||
+ | semop(semid, sops, 1); | ||
+ | free(sops); | ||
+ | } | ||
− | |||
− | + | void V(int semid){ | |
− | + | struct sembuf *sops = malloc(10*sizeof(int)); | |
+ | sops->sem_num = 0; | ||
+ | sops->sem_op = 1; | ||
+ | sops->sem_flg = 0; | ||
+ | semop(semid, sops, 1); | ||
+ | free(sops); | ||
+ | } | ||
− | |||
− | + | void sem_delete(int semid) | |
− | + | { | |
− | + | ||
− | + | if (semctl(semid,0,IPC_RMID,0) == -1) | |
− | + | perror("Erro na destruicao do semaforo"); | |
− | + | } | |
+ | |||
− | |||
− | |||
− | |||
− | + | </syntaxhighlight> | |
− | + | {{collapse bottom}} | |
− | { | + | {{collapse top| bg=lightyellow | expandir=true | Programação concorrente}} |
− | |||
− | |||
− | |||
− | + | == Programação concorrente == | |
− | |||
− | + | ;POSIX Threads | |
− | + | A API POSIX disponibiliza uma biblioteca de threads chamada pthread. As threads são implementadas pela estrutura ''pthread_t'', e manipuladas pelas funções (acesse as man-pages das chamadas para maiores detalhes): | |
− | + | *''pthread_create'': cria uma thread; | |
− | + | *''pthread_kill'': força a terminação de uma thread; | |
− | + | *''pthread_join'': sincroniza o final de uma thread (qual a diferença/semelhança com o ''wait'' que usamos para processos?); | |
− | + | *''pthread_exit'': finaliza uma thread. | |
− | + | Para utilizar estas funções é necessário linkar o programa à libpthread (''-lpthread''). A classe C++ abaixo abstrai estas operações: | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
<syntaxhighlight lang=cpp> | <syntaxhighlight lang=cpp> | ||
− | + | #ifndef __thread_h | |
− | + | #define __thread_h | |
+ | #include <pthread.h> | ||
+ | #include <signal.h> | ||
− | + | class Thread | |
+ | { | ||
+ | public: | ||
+ | Thread(int ( * const entry)(int), int arg) { | ||
+ | if(pthread_create(&thread, 0, (void*(*)(void*))entry, (void *)arg)) | ||
+ | thread = 0; | ||
+ | } | ||
+ | ~Thread() {} | ||
+ | int join(int * status) { return pthread_join(thread, (void **)status); } | ||
+ | friend void exit(int status = 0) { pthread_exit((void *) status); } | ||
+ | private: | ||
+ | pthread_t thread; | ||
+ | }; | ||
+ | #endif | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | </syntaxhighlight> | |
− | |||
− | + | ;POSIX pthread mutex | |
− | + | A biblioteca pthread implementa um tipo ''pthread_mutex_t'', que garante a exclusão mútua entre threads. Estes mutex são manipulados através das funções (acesse as man-pages das chamadas para maiores detalhes): | |
− | + | *''pthread_mutex_lock'': acessa um mutex. | |
− | + | *''pthread_mutex_trylock'': tenta acessar um mutex (retorna valor indicando sucesso ou falha no lock). | |
− | + | *''pthread_mutex_unlock'': libera um mutex. | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | <syntaxhighlight lang=c> | |
− | + | #ifndef __mutex_h | |
+ | #define __mutex_h | ||
− | + | #include <pthread.h> | |
− | + | class Mutex | |
− | } | + | { |
− | + | public: | |
+ | Mutex() {} | ||
+ | ~Mutex() {} | ||
+ | void lock() { pthread_mutex_lock(&mut); } | ||
+ | bool try_lock() { return (pthread_mutex_trylock(&mut) == 0); } // true when succeeds. | ||
+ | void unlock() { pthread_mutex_unlock(&mut); } | ||
− | + | private: | |
− | + | pthread_mutex_t mut; | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | }; | |
− | |||
− | |||
− | |||
− | + | #endif | |
− | # | ||
− | |||
− | / | + | </syntaxhighlight> |
− | |||
− | |||
− | |||
− | |||
− | |||
+ | ;POSIX Semaphores | ||
− | + | Nos sistemas POSIX, semáforos são implementados pelo tipo ''sem_t'' e manipulado através das funções (acesse as man-pages das chamadas para maiores detalhes): | |
− | + | *''sem_init'': inicializa um semáforo; | |
− | + | *''sem_destroy'': destroy um semáforo; | |
− | + | *''sem_wait'': implementa a operação ''p''; | |
− | + | *''sem_post'': implementa a operação ''v''. | |
− | + | Para utilizar estas funções é necessário linkar o programa à librt ou à libpthread (''-lrt'' ou ''-lpthread''). A classe C++ abaixo abstrai estas operações: | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | <syntaxhighlight lang=cpp> | |
− | + | #ifndef __semaphore_h | |
− | + | #define __semaphore_h | |
− | |||
− | + | #include <semaphore.h> | |
− | |||
− | + | class Semaphore | |
− | + | { | |
+ | public: | ||
+ | Semaphore(int i = 1) { sem_init(&sem, 0, i); } | ||
+ | ~Semaphore() { sem_destroy(&sem); } | ||
− | + | void p() { sem_wait(&sem); } | |
+ | void v() { sem_post(&sem); } | ||
− | + | operator int() | |
− | } | + | { |
− | </syntaxhighlight> | + | int ret; |
+ | sem_getvalue(&sem, &ret); | ||
+ | return ret; | ||
+ | } | ||
+ | |||
+ | private: | ||
+ | sem_t sem; | ||
+ | }; | ||
+ | |||
+ | #endif | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Exemplo de uso do operator: | ||
+ | <syntaxhighlight lang=cpp> | ||
+ | Semaphore sem; | ||
+ | cout << (int)sem << endl; | ||
+ | |||
+ | |||
+ | </syntaxhighlight> | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | ;Exercício 1 | |
− | + | O programa abaixo cria 5 threads, e cada uma destas threads atualiza uma variável global (memória compartilhada). | |
<syntaxhighlight lang=cpp> | <syntaxhighlight lang=cpp> | ||
#include <iostream> | #include <iostream> | ||
#include "thread.h" | #include "thread.h" | ||
− | # | + | |
− | + | #define NUM_THREADS 5 | |
+ | |||
using namespace std; | using namespace std; | ||
− | + | ||
− | + | int saldo = 1000; | |
− | + | ||
− | + | int AtualizaSaldo(int n) | |
− | |||
− | |||
− | |||
− | int | ||
{ | { | ||
− | + | int meu_saldo = saldo; | |
− | + | int novo_saldo = meu_saldo + n*100; | |
− | + | cout << "Novo saldo = " << novo_saldo << endl; | |
− | + | saldo = novo_saldo; | |
− | + | } | |
− | |||
− | |||
− | + | int main() | |
− | + | { | |
+ | Thread * threads[NUM_THREADS]; | ||
− | + | for(int t = 0; t < NUM_THREADS; t++) | |
− | + | threads[t] = new Thread(&AtualizaSaldo, t+1); | |
− | + | ||
− | + | cout << "Saldo final é " << saldo << "." << endl; | |
} | } | ||
− | + | </syntaxhighlight> | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | # Compile este programa. Você precisará da classe Thread. | |
+ | # Execute este programa várias vezes. Ele funciona? Será que ele gera as saídas esperadas? | ||
+ | # Identifique as '''seções críticas''' do programa. | ||
+ | # Corrija o programa utilizando '''mutex'''. Utilize a classe Mutex implementada na aula passada. | ||
+ | # Analise a função ''AtualizaSaldo()'' com a sua solução. Lembre-se que o uso do mutex implica em apenas uma thread acessar a seção crítica por vez, enquanto outras threads ficam bloqueadas, esperando. Disso vem que, quanto menor o trecho de código entre um ''lock'' e um ''unlock'', menos tempo uma thread necessita ficar esperando. | ||
+ | # Modifique o programa para usar um semáforo binário ao invés de um mutex em sua solução. Utilize a classe Semaphore. | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | ;Exercício 2 | |
− | <syntaxhighlight lang=cpp> | + | O programa abaixo manipula uma matriz de tamanho MxN (veja os defines para o tamanho da matriz). A função ''SumValues'' soma todos os valores em uma linha da matriz. A linha a ser somada é identificada pela variável ''i''. Modifique o programa principal (''main'') nos locais indicados para: |
+ | # Criar N threads, uma para somar os valores de cada linha. | ||
+ | # Receber o resultado do somatório de cada linha e gerar o somatório total da matriz. | ||
+ | # Analise o programa: há problemas de sincronização que precisam ser resolvidos? Se sim, resolva-os. | ||
+ | |||
+ | <syntaxhighlight lang=cpp> | ||
#include <iostream> | #include <iostream> | ||
#include "thread.h" | #include "thread.h" | ||
− | # | + | |
+ | /* number of matrix columns and rows */ | ||
+ | #define M 5 | ||
+ | #define N 10 | ||
using namespace std; | using namespace std; | ||
− | + | int matrix[N][M]; | |
− | + | Thread *threads[N]; | |
− | |||
− | int | + | /* thread function; it sums the values of the matrix in the row */ |
+ | int SumValues(int i) | ||
{ | { | ||
− | + | int n = i; /* number of row */ | |
− | + | int total = 0; /* the total of the values in the row */ | |
− | + | int j; | |
− | + | for (j = 0; j < M; j++) /* sum values in the "n" row */ | |
− | + | total += matrix[n][j]; | |
− | + | cout << "The total in row" << n << " is " << total << "." << endl; | |
− | + | /* terminate a thread and return a total in the row */ | |
− | + | exit(total); | |
− | |||
− | |||
− | |||
− | cout << " | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
} | } | ||
− | int main() | + | int main(int argc, char *argv[]) |
{ | { | ||
− | + | int i, j; | |
+ | int total = 0; /* the total of the values in the matrix */ | ||
− | + | /* initialize the matrix */ | |
− | + | for (i = 0; i < N; i++) | |
− | + | for (j = 0; j < M; j++) | |
+ | matrix[i][j] = i * M + j; | ||
+ | |||
+ | /* create threads */ | ||
+ | /* COLOQUE SEU CÓDIGO PARA CRIAR AS THREADS AQUI! */ | ||
+ | |||
+ | /* wait for terminate a threads */ | ||
+ | /* COLOQUE SEU CÓDIGO PARA PEGAR O SOMATÓRIO DE LINHAS E TOTALIZAR A SOMA DA MATRIZ AQUI! */ | ||
+ | |||
+ | cout << "The total values in the matrix is " << total << endl; | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | |||
+ | |||
+ | {{collapse bottom}} | ||
+ | {{collapse top| bg=lightyellow | expandir=true | Lista de exercícios "Revisão para Prova" }} | ||
+ | |||
+ | == Lista de exercícios/Revisão para Prova == | ||
+ | |||
+ | A lista de exercícios referente a primeira prova (Parte introdutória + Processos) segue neste LINK [http://docente.ifsc.edu.br/andre.damato/sop2018/lista_rev1.pdf | Lista de exercícios]. | ||
+ | |||
+ | {{collapse bottom}} | ||
+ | {{collapse top| bg=lightyellow | expandir=true | Primeiro trabalho}} | ||
+ | == JOGO DA VIDA DE CONWAY e JANTAR DOS FILÓSOFOS== | ||
− | int status; | + | ;Referências |
+ | |||
+ | * [https://www.open-mpi.org/doc/current/ MPI] | ||
+ | |||
+ | * [https://quark.phy.bnl.gov/~creutz/qcdoc/mpi/mpi.h Tipos de dados MPI] | ||
+ | |||
+ | * [https://pt.wikipedia.org/wiki/Jogo_da_vida Jogo da Vida] | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | A partir da implementação sequencial do Game of Life apresentada a seguir, estude o código e transforme esta implementação em uma implementação paralela utilizando o protocolo de passagem de mensagens MPI. | ||
+ | Perceba que a impĺementação mencionada utiliza como parâmetros de entrada um aquivo contendo o estado inicial do jogo, e o número de gerações que o usuário deseja rodar. A evolução do estado inicial | ||
+ | é impressa na tela passo a passo. Sendo assim, a partir disso: | ||
+ | |||
+ | *'''1: Elabore uma implementação com 2 processos para o jogo da vida utilizando o protocolo MPI, de maneira que um processo execute uma geração i qualquer e o outro execute a geração seguinte i+1''' | ||
+ | |||
+ | *'''2: (0,7) Os processos deverão se comunicar utilizando as funções do protocolo MPI.''' | ||
+ | |||
+ | *'''3: (0,3) Relatório simplificado explicando a sua solução. Utilize diagramas para representar a comunicação entre os processos, explicando como a matriz do jogo é transferida entre os processos. ''' | ||
+ | |||
+ | *'''4: Entregue o Relatório e o código fonte do trabalho em um pacote compactado via e-mail (PRAZO 19/10). | ||
+ | |||
+ | |||
+ | |||
+ | * ''' Compilar o código com MPI ''' | ||
+ | <code> | ||
+ | mpicc -o nomeApp arquivo.c | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | * ''' Rodar rodar programa utilizando MPI ''' | ||
+ | <code> | ||
+ | mpirun -np numeroDeProcessos ./nomeApp entrada.txt gerações | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | * (int) -np: Quantidade de processos que você deseja executar | ||
+ | |||
+ | * entrada.txt: Arquivo de entrada com estado inicial do jogo | ||
+ | |||
+ | * (int) gerações: número de gerações que você deseja rodar | ||
+ | |||
+ | * [http://docente.ifsc.edu.br/andre.damato/sop2018/entrada.txt Arquivo de entrada para Testes] | ||
+ | |||
+ | |||
+ | <syntaxhighlight lang=cpp> | ||
+ | |||
+ | |||
+ | |||
+ | /* | ||
+ | |||
+ | Conway's Game of Life | ||
+ | |||
+ | Compilar código com MPI | ||
+ | mpicc -o nomeApp arquivo.c | ||
+ | |||
+ | |||
+ | Rodar rodar programa utilizando MPI | ||
+ | |||
+ | mpirun -np numeroDeProcessos ./nomeApp entrada.txt gerações | ||
+ | |||
+ | (int) -np: Quantidade de processos que você deseja executar | ||
+ | entrada.txt: Arquivo de entrada com estado inicial do jogo | ||
+ | (int) gerações: número de gerações que você deseja rodar | ||
+ | |||
+ | |||
+ | */ | ||
+ | |||
+ | #include <stdio.h> | ||
+ | #include <string.h> | ||
+ | #include <stdlib.h> | ||
+ | #include "mpi.h" | ||
+ | |||
+ | |||
+ | //Numero de linhas e colunas | ||
+ | #define nLinhas 32 | ||
+ | #define nColunas 82 | ||
+ | |||
+ | char *matriz[nLinhas]; | ||
+ | char *entrada[nLinhas]; | ||
+ | |||
+ | |||
+ | void iniciar() { | ||
+ | |||
+ | int i; | ||
+ | |||
+ | //Alocar espaço em memória para as matrizes | ||
+ | for (i = 0; i < nLinhas; i++ ) | ||
+ | matriz[i] = (char *) malloc((nColunas) * sizeof( char )); | ||
+ | |||
+ | |||
+ | } | ||
+ | |||
+ | int adjacente(char **matriz, int i, int j){ | ||
+ | |||
+ | int x,y, initX, initY, limitX, limitY; | ||
+ | int vizinhos = 0; | ||
+ | |||
+ | if(i == 0) | ||
+ | initX = 0; | ||
+ | else | ||
+ | initX = -1; | ||
+ | |||
+ | if(i == (nLinhas-1)) | ||
+ | limitX = 1; | ||
+ | else | ||
+ | limitX = 2; | ||
+ | |||
+ | |||
+ | if(y == 0) | ||
+ | initY = 0; | ||
+ | else | ||
+ | initY = -1; | ||
+ | |||
+ | if(y == (nColunas-3)) | ||
+ | limitY = 1; | ||
+ | else | ||
+ | limitY = 2; | ||
+ | |||
+ | for(x = initX ; x < limitX; x++){ | ||
+ | for(y = initY; y < limitY; y++){ | ||
+ | if(matriz[i+x][j+y] == '#') | ||
+ | vizinhos++; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | |||
+ | if(matriz[i][j] == '#') | ||
+ | return (vizinhos-1); | ||
+ | else | ||
+ | return vizinhos; | ||
+ | |||
+ | } | ||
+ | |||
+ | |||
+ | void calculoGeracao(char **matriz, int ger) { | ||
+ | |||
+ | int i, j, a; | ||
+ | char novaGeracao[nLinhas][nColunas]; | ||
+ | |||
+ | /* Aplicando as regras do jogo da vida */ | ||
+ | for (i=0; i < nLinhas; i++){ | ||
+ | for (j=0; j < nColunas; j++) { | ||
+ | |||
+ | a = adjacente(matriz, i, j); | ||
+ | |||
+ | if (a == 2) novaGeracao[i][j] = matriz[i][j]; | ||
+ | if (a == 3) novaGeracao[i][j] = '#'; | ||
+ | if (a < 2) novaGeracao[i][j] = ' '; | ||
+ | if (a > 3) novaGeracao[i][j] = ' '; | ||
+ | |||
+ | if (j == 0) | ||
+ | novaGeracao[i][j] = '"'; | ||
+ | } | ||
+ | |||
+ | novaGeracao[i][79] = '"'; | ||
+ | novaGeracao[i][80] = '\n'; | ||
+ | |||
+ | |||
+ | } | ||
+ | |||
+ | /* Passando o resultado da nova geração para a matriz de entrada */ | ||
+ | for (i=0; i < nLinhas; i++){ | ||
+ | for (j=0; j < nColunas; j++) | ||
+ | matriz[i][j] = novaGeracao[i][j]; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | main(int argc, char *argv[2]){ | ||
+ | |||
+ | /* Para uso com MPI | ||
+ | |||
+ | |||
+ | //Variáveis para uso com MPI | ||
+ | int numeroDeProcessos = 0; | ||
+ | int rank = 0; | ||
+ | MPI_Status status; | ||
+ | |||
+ | //Iniciar MPI--- | ||
+ | MPI_Init( &argc, &argv ); | ||
+ | |||
+ | //Atribui a variável numeroDeProcessos o número de processos passado como parâmetro em -np | ||
+ | MPI_Comm_size( MPI_COMM_WORLD, &numeroDeProcessos ); | ||
+ | |||
+ | //Pega o valor do rank (processo) | ||
+ | MPI_Comm_rank( MPI_COMM_WORLD, &rank ); | ||
+ | */ | ||
+ | |||
+ | |||
+ | FILE *matrizEntrada; | ||
+ | matrizEntrada = fopen(argv[1], "r"); | ||
+ | iniciar(); | ||
+ | |||
+ | |||
+ | if (matrizEntrada == NULL) | ||
+ | printf ("Não posso abrir o arquivo \"%s\"\n", argv[1]); | ||
+ | |||
+ | |||
+ | char str[nColunas]; | ||
+ | int linha = 0; | ||
+ | |||
+ | |||
+ | |||
+ | //Lendo o estado inicial do jogo a partir do arquivo | ||
+ | while((fgets(str, nColunas, matrizEntrada) != NULL)&&(linha < nLinhas)){ | ||
+ | strcat(matriz[linha], str); | ||
+ | linha++; | ||
+ | } | ||
+ | |||
+ | int i,gens; | ||
+ | |||
+ | |||
+ | for(gens = 0; gens < atoi(argv[2]); gens++){ //Gens é o número de gerações especificado no segundo parâmetro | ||
+ | |||
+ | calculoGeracao(matriz, gens); | ||
+ | printf("%c[2J",27); // Esta linha serve para limpar a tela antes de imprimir o resultado de uma geração | ||
+ | |||
+ | //Lendo o estado do jogo e imprime na tela | ||
+ | for(i = 0; i < nLinhas; i++) | ||
+ | printf("%s", matriz[i]); | ||
+ | |||
+ | sleep(1); | ||
+ | |||
+ | } | ||
+ | |||
+ | |||
+ | for(i = 0; i < nLinhas; i++) | ||
+ | free(matriz[i]); | ||
+ | |||
+ | |||
+ | |||
+ | /* Finaliza o MPI */ | ||
+ | //MPI_Finalize(); | ||
+ | |||
+ | exit(0); | ||
+ | } | ||
+ | |||
+ | |||
+ | </syntaxhighlight> | ||
+ | |||
+ | |||
+ | ;Jantar dos Filósofos | ||
+ | O problema clássico Jantar dos Filósofos consiste em que n fluxos (n filósofos) disputam n recursos (n talheres). No problema, para conseguir "jantar" (ou executar), cada filósofo precisa pegar dois talheres adjascentes a ele. Cada recurso é compartilhado por dois filósofos. | ||
+ | *[http://www.doc.ic.ac.uk/~jnm/concurrency/classes/Diners/Diners.html Veja esta simulação] | ||
+ | *[http://en.wikipedia.org/wiki/Dining_philosophers_problem Veja esta descrição do problema] | ||
+ | |||
+ | |||
+ | * ''' (0,7) programa abaixo implementa um Jantar dos Filósofos utilizando semáforos para sincronização. Contudo, as chamadas para as operações ''v'' e ''p'' foram removidas, conforme comentários no código. Re-insira as operações no código e analise a solução. Esta modificação é suficiente para garantir que não haverá deadlock? Se sim, mostre o porque. Se não, proponha uma solução completa. ''' | ||
+ | |||
+ | |||
+ | *''' (0,3) Relatório simplificado explicando a sua solução. ''' | ||
+ | |||
+ | *''' Entregue o Relatório e o código fonte do trabalho em um pacote compactado via e-mail (PRAZO 19/10). | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | <syntaxhighlight lang=cpp> | ||
+ | #include <iostream> | ||
+ | #include "thread.h" | ||
+ | #include "semaphore.h" | ||
+ | |||
+ | using namespace std; | ||
+ | |||
+ | const int DELAY = 10000000; | ||
+ | const int ITERATIONS = 5; | ||
+ | |||
+ | Semaphore chopstick[5]; | ||
+ | |||
+ | int philosopher(int n) | ||
+ | { | ||
+ | cout << "Philosopher " << n << " was born!\n"; | ||
+ | |||
+ | int first = (n < 4)? n : 0; // left for phil 0 .. 3, right for phil 4 | ||
+ | int second = (n < 4)? n + 1 : 4; // right for phil 0 .. 3, left for phil 4 | ||
+ | |||
+ | // Foram removidos do laço abaixo: | ||
+ | // - uma chamada para chopstick[first].p() | ||
+ | // - uma chamada para chopstick[second].p() | ||
+ | // - uma chamada para chopstick[first].v() | ||
+ | // - uma chamada para chopstick[second].v() | ||
+ | for(int i = 0; i < ITERATIONS; i++) { | ||
+ | cout << "Philosopher " << n << " thinking ...\n"; | ||
+ | for(int i = 0; i < DELAY * 10; i++); | ||
+ | |||
+ | cout << "Philosopher " << n << " eating ...\n"; | ||
+ | for(int i = 0; i < DELAY; i++); | ||
+ | } | ||
+ | |||
+ | return n; | ||
+ | } | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | cout << "The Dining-Philosophers Problem\n"; | ||
+ | |||
+ | Thread * phil[5]; | ||
+ | for(int i = 0; i < 5; i++) | ||
+ | phil[i] = new Thread(&philosopher, i); | ||
+ | |||
+ | int status; | ||
for(int i = 0; i < 5; i++) { | for(int i = 0; i < 5; i++) { | ||
− | phil[i]->join(&status); | + | phil[i]->join(&status); |
− | if(status == i) | + | if(status == i) |
− | cout << "Philosopher " << i << " went to heaven!\n"; | + | cout << "Philosopher " << i << " went to heaven!\n"; |
− | else | + | else |
− | cout << "Philosopher " << i << " went to hell!\n"; | + | cout << "Philosopher " << i << " went to hell!\n"; |
− | } | + | } |
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | |||
+ | {{collapse bottom}} | ||
− | + | {{collapse top| bg=lightyellow | expandir=true | Softwares básicos, caso Hello Word! (Trabalho 2) Entrega dia 09/11}} | |
− | |||
− | |||
− | |||
− | |||
− | {{collapse top| bg=lightyellow | expandir=true | Softwares básicos, caso Hello Word! ( | ||
== Softwares básicos, caso Hello Word! == | == Softwares básicos, caso Hello Word! == | ||
Linha 1 404: | Linha 1 804: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
* Identifique quais são as seções de código obtidas a partir do '''hello.c'''. | * Identifique quais são as seções de código obtidas a partir do '''hello.c'''. | ||
− | * Pesquise e entenda o significado | + | * Pesquise e entenda o significado das seções de código: .bss, .txt, .data, .init. |
* Faça uma análise e identifique o endereço de memória que o programa ''hello world'' vai ser carregado. | * Faça uma análise e identifique o endereço de memória que o programa ''hello world'' vai ser carregado. | ||
* Este código objeto é relocável? Justifique sua resposta. | * Este código objeto é relocável? Justifique sua resposta. | ||
Linha 1 445: | Linha 1 845: | ||
* Explique as principais diferenças entre o arquivo objeto .o e o executável final. (dica utilize o objdump para fazer essa análise) | * Explique as principais diferenças entre o arquivo objeto .o e o executável final. (dica utilize o objdump para fazer essa análise) | ||
{{collapse bottom}} | {{collapse bottom}} | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− |
Edição atual tal como às 20h40min de 19 de dezembro de 2018
Sistemas Operacionais
- Professor: André D'Amato
- Encontros: Segundas às 15:40 no LabCad e quintas às 13:30 no LabSid.
- Atendimento paralelo: Quarta de 11:30 ás 12:30 horas.
Nome | Prova 1 | Prova 2 | Prova 3 | Atividade 1 | Atividade 2 | Média Trabalhos * 0,3 | Média Provas * 0,7 | Final |
---|---|---|---|---|---|---|---|---|
Alisson | 4,75(8,0) | 6,5 | 1,5 | 10 | 10 | 3,73 | 3 | 6,7 |
André | 6,0 | 6,25 | 5,0 | 10 | 10 | 4,25 | 3 | 7 |
Angelo | 5,1(6,3) | 1,25(2,3) | 6,4(4,0) | 10 | 0 | 3,5 | 1,5 | 5 |
Felipe | 8,25 | 9,5 | 8,0 | 10 | 0 | 6,0 | 1,5 | 7,51 |
Francisco | 7,5 | 0 | 0 | 10 | 0 | - | - | - |
Gabriel | 0(7,3) | 0(4,0) | 4,0 | 10 | 10 | 3,57 | 3 | 6,6 |
Guilherme | 8,25 | 5,3(7,0) | 4,7 | 10 | 10 | 4,65 | 3 | 7,7 |
Luiza | 4,4(7,5) | 6,0 | 4,2 | 10 | 5 | 4,13 | 2,25 | 6,0 |
Rafael | 6,75 | 6,0 | 6,4 | 10 | 0 | 4,5 | 1,5 | 6,0 |
Suyan | 4,4(6,0) | 5,0(4,5) | 3,7 | 10 | 10 | 3,4 | 3 | 6,4 |
Yara | 4,9(7,0) | 6,0 | 4,0 | 10 | 10 | 4 | 3 | 7,0 |
- Recuperação Data: 17/12/2018 no horário de aula!!!!
Conteúdo
Unidade 01: Introdução |
---|
Unidade 01: IntroduçãoVisão geral de funções, responsabilidades e estruturas de um SO
Arquitetura de sistemas operacionais e modelos de programação
|
Unidade 02: Processos |
---|
Unidade 02: ProcessosGerência de tarefas; contextos, processos e threads
Escalonamento de tarefas
Comunicação entre Processos
Coordenação de processos
|
Unidade 03: Memória |
---|
Unidade 03: MemóriaIntrodução ao Gerenciamento de Memória
Memória Principal
Memória Virtual
Exercícios |
Unidade 04: Armazenamento |
---|
Unidade 04: ArmazenamentoInterface do Sistema de Arquivos
Permissões de sistema de arquivos no LinuxNeste estudo de caso são realizados alguns exercícios práticos que permitem verificar como o sistema de arquivos é organizado no Linux. Acesse o estudo de caso através deste roteiro do Prof. Maziero da UTFPR.
Implementação do Sistema de Arquivos
Exercícios1. Qual tipo de organização de diretórios que o ubuntu utiliza, grafo cíclico, grafo acíclico, flat ou árvore, comprove seu raciocínio por meio de testes. 2. No ubuntu o que acontece quando deletamos um hard link, e em seguida acessamos o link como um arquivo comum e alteramos seu conteúdo? * É possível tomar tal ação? Se sim Qual o efeito? explique. * Faça o mesmo teste, porém desta vez utilize um soft link. Estrutura de Armazenamento em Massa
Gerenciamento de Entrada e Saída
Exercícios |
Laboratórios
Um Exemplo de Uso "API Padrão POSIX" |
---|
Um Exemplo de Uso "API Padrão POSIX"
Qual o tamanho limite da memória que você conseguiu alocar?
Em sua opinião NMAP trata-se de uma syscall ou de uma API? Afinal API e syscall são a mesma coisa? Explique. void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *addr, size_t length);
addr = Valor do início do mapeamento.
length = valor do tamanho da região a ser alocada.
prot = especificações de proteção da região alocada (consultar http://man7.org/linux/man-pages/man2/mmap.2.html).
flags = especificação do escopo e do tipo da região criada (exemplo publica ou privada, se é anônima ou não).
void* meu_malloc(size_t tamanho) {
void* addr = mmap(0, // addr
tamanho, // len
PROT_READ | PROT_WRITE, // prot
MAP_ANON | MAP_PRIVATE, // flags
-1, // filedes
0); // off
*(size_t*)addr = tamanho;
return addr;
}
int meu_free(void* addr) {
return munmap(addr - sizeof(size_t), (size_t) addr);
}
int soma(int *N1, int *N2){
return (*N1+*N2);
}
int main(int argc, char* argv[]) {
int* numero1 = meu_malloc(sizeof(int));
int* numero2 = meu_malloc(sizeof(int));
*numero1 = 10;
*numero2 = 20;
int resultado = soma(numero1, numero2);
printf("\n\n O resultado da soma é %d \n\n",resultado);
meu_free(numero1);
meu_free(numero2);
return 0;
}
|
Processos no Linux |
---|
Processos no Linux
// ex1: fork/wait básico
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.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$
// ex2: fork/wait "compartilhando" dados
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.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\n", getpid(), k);
exit(0);
}
else // Este é o processo pai
{
wait(&status);
k += 10;
printf("processo pai\t pid: %d\t K: %d\n", getpid(), k);
exit(0);
}
k += 10;
printf("processo %d\t K: %d\n", getpid(), k);
exit(0);
}
arliones@socrates:~/tmp$ gcc ex2.c -o ex2
arliones@socrates:~/tmp$ ./ex2
processo 18425 antes do fork
processo 18425 depois do fork
processo 18426 depois do fork
processo filho pid: 18426 K: 1000
processo pai pid: 18425 K: 10
arliones@socrates:~/tmp$
arliones@socrates:~/tmp$ gcc ex2.c -o ex2
arliones@socrates:~/tmp$ ./ex2
processo 32342 antes do fork
processo 32342 depois do fork
processo 32343 depois do fork
processo filho pid: 32343 K: 1000
processo 32343 K: 1010
processo pai pid: 32342 K: 10
processo 32342 K: 20
arliones@socrates:~/tmp$
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.
O status passado como parâmetro à função wait(&status) é, na verdade, o mecanismo de retorno de resultado do wait/waitpid. Ao retornar, esta variável contém informações sobre o resultado da execução do processo filho. Por exemplo, se um processo terminou normalmente (i.e., chamou exit), o comando WIFEXITED(status) retorna true. Este comando retorna false se o processo foi abortado (e.g., segmentation fault) ou morto (e.g., kill). Investigue no manual do wait no Linux (man wait) o funcionamento do comando WEXITSTATUS(status), e use-o para modificar o exercício anterior para calcular o 5!, sendo que cada processo pode executar apenas uma multiplicação.
|
Threads de aplicação |
---|
Threads de aplicaçãoEntregar um relatório impresso sobre a sua solução para o problema descrito. O relatório deve conter as seguintes seções:
Busque mais informações sobre estas funções utilizando o programa manpage do Linux (ex.: man getcontext). Estude o código no arquivo pingpong.c abaixo e explique seu funcionamento. #include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>
#define STACKSIZE 32768 /* tamanho de pilha das threads */
/* VARIÁVEIS GLOBAIS */
ucontext_t cPing, cPong, cMain;
/* Funções-comportamento das Tarefas */
void f_ping(void * arg) {
int i;
printf("%s iniciada\n", (char *) arg);
for (i=0; i<4; i++) {
printf("%s %d\n", (char *) arg, i);
swapcontext(&cPing, &cPong);
}
printf("%s FIM\n", (char *) arg);
swapcontext(&cPing, &cMain);
}
void f_pong(void * arg) {
int i;
printf("%s iniciada\n", (char *) arg);
for (i=0; i<4; i++) {
printf("%s %d\n", (char *) arg, i);
swapcontext(&cPong, &cPing);
}
printf("%s FIM\n", (char *) arg);
swapcontext(&cPong, &cMain);
}
/* MAIN */
int main(int argc, char *argv[]) {
char *stack;
printf ("Main INICIO\n");
getcontext(&cPing);
stack = malloc(STACKSIZE);
if(stack) {
cPing.uc_stack.ss_sp = stack ;
cPing.uc_stack.ss_size = STACKSIZE;
cPing.uc_stack.ss_flags = 0;
cPing.uc_link = 0;
}
else {
perror("Erro na criação da pilha: ");
exit(1);
}
makecontext(&cPing, (void*)(*f_ping), 1, "\tPing");
getcontext(&cPong);
stack = malloc(STACKSIZE);
if(stack) {
cPong.uc_stack.ss_sp = stack ;
cPong.uc_stack.ss_size = STACKSIZE;
cPong.uc_stack.ss_flags = 0;
cPong.uc_link = 0;
}
else {
perror("Erro na criação da pilha: ");
exit(1);
}
makecontext (&cPong, (void*)(*f_pong), 1, "\tPong");
swapcontext(&cMain, &cPing);
swapcontext(&cMain, &cPong);
printf("Main FIM\n");
exit(0);
}
|
Exercícios sobre pipe e memória compartilhada | ||||
---|---|---|---|---|
PIPE e SH_MEMORY
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
int fd[2], pipe_ret, filho;
char string[] = "Hello, pipe!\n";
char buffer[20];
pipe(fd);
if((filho = fork()) == -1)
{
perror("fork");
exit(1);
}
if(filho == 0)
{
/*Mandar string para a extremidade do pipe*/
exit(0);
}
else
{
/*Ler a mensagem no pipe*/
printf("Recebi este texto %s", buffer);
}
return(0);
}
|
Exercício (Algoritmo de Peterson) |
---|
Exercício (Algoritmo de Peterson)Exercício 1: Sincronize o código a seguir, de maneira que o processo pai imprima apenas os números impares e o processo filho os números pares. Para isso utilize o algoritmo de Peterson visto em aula. Utilize memória compartilhada para comunicação entre os processos. #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
main()
{
if (fork() != 0) { /* I am the parent */
int i;
for(i = 0;i < 10;i=i+2){
printf("Processo pai %d \n", i);
}
}
else { /*Child code */
int i;
for(i = 1;i < 10;i=i+2){
printf("Processo filho %d \n", i);
}
}
exit(0);
}
Explique seu raciocínio. |
Exercício (Semáforos) |
---|
Exercício (Semáforos)Exercício 1: Sincronize o código a seguir, de maneira que o processo pai imprima apenas os números impares e o processo filho os números pares. Para isso utilize Semáforos de acordo com a implementação em semaforo.h. Utilize memória compartilhada para comunicação entre os processos. #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
main()
{
if (fork() != 0) { /* I am the parent */
int i;
for(i = 0;i < 10;i=i+2){
printf("Processo pai %d \n", i);
sleep(1);
}
}
else { /*Child code */
int i;
for(i = 1;i < 10;i=i+2){
printf("Processo filho %d \n", i);
}
}
exit(0);
}
SEMAFORO.H
#include <sys/sem.h>
int criar_semaforo(int val, int chave)
{
int semid ;
union semun {
int val;
struct semid_ds *buf ;
ushort array[1];
} arg_ctl ;
key_t ft = ftok("/tmp", chave);
semid = semget(ft,1,IPC_CREAT|IPC_EXCL|0666);
if (semid == -1) {
semid = semget(ft,1,0666);
if (semid == -1) {
perror("Erro semget()");
exit(1) ;
}
}
arg_ctl.val = val; //valor de início
if (semctl(semid,0,SETVAL,arg_ctl) == -1) {
perror("Erro inicializacao semaforo");
exit(1);
}
return(semid) ;
}
void P(int semid){
struct sembuf *sops = malloc(10*sizeof(int));
sops->sem_num = 0;
sops->sem_op = -1;
sops->sem_flg = 0;
semop(semid, sops, 1);
free(sops);
}
void V(int semid){
struct sembuf *sops = malloc(10*sizeof(int));
sops->sem_num = 0;
sops->sem_op = 1;
sops->sem_flg = 0;
semop(semid, sops, 1);
free(sops);
}
void sem_delete(int semid)
{
if (semctl(semid,0,IPC_RMID,0) == -1)
perror("Erro na destruicao do semaforo");
}
|
Programação concorrente |
---|
Programação concorrente
A API POSIX disponibiliza uma biblioteca de threads chamada pthread. As threads são implementadas pela estrutura pthread_t, e manipuladas pelas funções (acesse as man-pages das chamadas para maiores detalhes):
Para utilizar estas funções é necessário linkar o programa à libpthread (-lpthread). A classe C++ abaixo abstrai estas operações: #ifndef __thread_h
#define __thread_h
#include <pthread.h>
#include <signal.h>
class Thread
{
public:
Thread(int ( * const entry)(int), int arg) {
if(pthread_create(&thread, 0, (void*(*)(void*))entry, (void *)arg))
thread = 0;
}
~Thread() {}
int join(int * status) { return pthread_join(thread, (void **)status); }
friend void exit(int status = 0) { pthread_exit((void *) status); }
private:
pthread_t thread;
};
#endif
A biblioteca pthread implementa um tipo pthread_mutex_t, que garante a exclusão mútua entre threads. Estes mutex são manipulados através das funções (acesse as man-pages das chamadas para maiores detalhes):
#ifndef __mutex_h
#define __mutex_h
#include <pthread.h>
class Mutex
{
public:
Mutex() {}
~Mutex() {}
void lock() { pthread_mutex_lock(&mut); }
bool try_lock() { return (pthread_mutex_trylock(&mut) == 0); } // true when succeeds.
void unlock() { pthread_mutex_unlock(&mut); }
private:
pthread_mutex_t mut;
};
#endif
Nos sistemas POSIX, semáforos são implementados pelo tipo sem_t e manipulado através das funções (acesse as man-pages das chamadas para maiores detalhes):
Para utilizar estas funções é necessário linkar o programa à librt ou à libpthread (-lrt ou -lpthread). A classe C++ abaixo abstrai estas operações:
#ifndef __semaphore_h
#define __semaphore_h
#include <semaphore.h>
class Semaphore
{
public:
Semaphore(int i = 1) { sem_init(&sem, 0, i); }
~Semaphore() { sem_destroy(&sem); }
void p() { sem_wait(&sem); }
void v() { sem_post(&sem); }
operator int()
{
int ret;
sem_getvalue(&sem, &ret);
return ret;
}
private:
sem_t sem;
};
#endif
Exemplo de uso do operator: Semaphore sem;
cout << (int)sem << endl;
O programa abaixo cria 5 threads, e cada uma destas threads atualiza uma variável global (memória compartilhada). #include <iostream>
#include "thread.h"
#define NUM_THREADS 5
using namespace std;
int saldo = 1000;
int AtualizaSaldo(int n)
{
int meu_saldo = saldo;
int novo_saldo = meu_saldo + n*100;
cout << "Novo saldo = " << novo_saldo << endl;
saldo = novo_saldo;
}
int main()
{
Thread * threads[NUM_THREADS];
for(int t = 0; t < NUM_THREADS; t++)
threads[t] = new Thread(&AtualizaSaldo, t+1);
cout << "Saldo final é " << saldo << "." << endl;
}
O programa abaixo manipula uma matriz de tamanho MxN (veja os defines para o tamanho da matriz). A função SumValues soma todos os valores em uma linha da matriz. A linha a ser somada é identificada pela variável i. Modifique o programa principal (main) nos locais indicados para:
#include <iostream>
#include "thread.h"
/* number of matrix columns and rows */
#define M 5
#define N 10
using namespace std;
int matrix[N][M];
Thread *threads[N];
/* thread function; it sums the values of the matrix in the row */
int SumValues(int i)
{
int n = i; /* number of row */
int total = 0; /* the total of the values in the row */
int j;
for (j = 0; j < M; j++) /* sum values in the "n" row */
total += matrix[n][j];
cout << "The total in row" << n << " is " << total << "." << endl;
/* terminate a thread and return a total in the row */
exit(total);
}
int main(int argc, char *argv[])
{
int i, j;
int total = 0; /* the total of the values in the matrix */
/* initialize the matrix */
for (i = 0; i < N; i++)
for (j = 0; j < M; j++)
matrix[i][j] = i * M + j;
/* create threads */
/* COLOQUE SEU CÓDIGO PARA CRIAR AS THREADS AQUI! */
/* wait for terminate a threads */
/* COLOQUE SEU CÓDIGO PARA PEGAR O SOMATÓRIO DE LINHAS E TOTALIZAR A SOMA DA MATRIZ AQUI! */
cout << "The total values in the matrix is " << total << endl;
return 0;
}
|
Lista de exercícios "Revisão para Prova" |
---|
Lista de exercícios/Revisão para ProvaA lista de exercícios referente a primeira prova (Parte introdutória + Processos) segue neste LINK | Lista de exercícios. |
Primeiro trabalho |
---|
JOGO DA VIDA DE CONWAY e JANTAR DOS FILÓSOFOS
|
Softwares básicos, caso Hello Word! (Trabalho 2) Entrega dia 09/11 |
---|
Softwares básicos, caso Hello Word!O objetivo do experimento de hoje é pesquisar e entender os processos de atribuição de endereços de programas realizados em tempo de compilação pelos softwares básicos como: compilador, linker, e assembler. Sendo assim, neste experimento vamos utilizar os seguintes softwares para criação e análise de código:
#include <stdio.h>
int main()
{
printf("Hello, World!");
return 0;
}
Trata-se do programa hello word, este programa apenas exibe uma mensagem na tela. No entanto, vamos analisar como são as etapas confecção do executável a partir deste código simples. Exercício 1:
|