Mudanças entre as edições de "SOP-EngTel 2018 2"
(Criou página com '= Sistemas Operacionais = *'''Professor:''' André D'Amato *'''Encontros:''' Segundas às 15:40 e quintas às 13:30 no LabSid. *'''Atendimento paralelo:''' a definir. *[[S...') |
|||
Linha 10: | Linha 10: | ||
+ | <!-- | ||
== Listas de exercícios == | == Listas de exercícios == | ||
Linha 1 457: | Linha 1 458: | ||
{{collapse bottom}} | {{collapse bottom}} | ||
+ | |||
+ | --> |
Edição das 13h15min de 26 de julho de 2018
Sistemas Operacionais
- Professor: André D'Amato
- Encontros: Segundas às 15:40 e quintas às 13:30 no LabSid.
- Atendimento paralelo: a definir.
|}
Trocas de mensagens com pipes (Atividade 3) |
---|
Trocas de mensagens com pipes
Um mecanismo disponibilizado por sistemas UNIX para troca de mensagens entre processos é o PIPE. Pipes são mecanismos de comunicação indireta onde mensagens são trocadas através de mailboxes. Cada mailbox possui um identificador único, permitindo que processos identifiquem o canal de comunicação entre eles. O fluxo de mensagens em um Pipe é:
No UNIX, pipes são inicializados através da SystemCall pipe, que possui a seguinte sintaxe:
As primitivas send/receive para uso de um pipe no UNIX são implementadas por SystemCalls read/write, conforme segue:
Abaixo há um exemplo de programa criando um pipe e compartilhando os descritores entre dois processos (criados via fork()). #include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
char *message = "This is a message!!!" ;
main()
{
char buf[1024] ;
int fd[2];
pipe(fd); /*create pipe*/
if (fork() != 0) { /* I am the parent */
write(fd[1], message, strlen (message) + 1) ;
}
else { /*Child code */
read(fd[0], buf, 1024) ;
printf("Got this from MaMa!!: %s\n", buf) ;
}
}
|
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);
}
}
else { /*Child code */
int i;
for(i = 1;i < 10;i=i+2){
printf("Processo filho %d \n", i);
}
}
exit(0);
}
SEMAFORO.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 (Atividade 4) |
---|
Programação concorrente (Atividade 4)
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;
}
|
Problemas clássicos de coordenação de processos |
---|
Problemas clássicos de coordenação de processos
O problema clássico Produtor/Consumidor consiste em dois fluxos de execução (threads/processos), sendo que um dos fluxos (consumidor) só pode executar a partir do momento em que seus dados de entrada foram produzidos pelo outro fluxo (produtor).
#include <iostream>
#include "thread.h"
#include "semaphore.h"
using namespace std;
const int REP = 5;
char buffer;
Semaphore empty(1);
Semaphore full(0);
int producer(int n)
{
cout << "Producer was born!\n";
// Faltam, no laço abaixo:
// - uma chamada para empty.p()
// - uma chamada para full.v()
char data = -1;
for(int i = 0; i < REP; i++) {
cout << "Producing ...\n";
data = (char) i + 0x61;
buffer = data;
cout << "Stored... " << data << endl;
}
return n;
}
int consumer(int n)
{
cout << "Consumer was born!\n";
// Faltam, no laço abaixo:
// - uma chamada para full.p()
// - uma chamada para empty.v()
char data = -1;
for(int i = 0; i < REP; i++) {
cout << "Retrieving ...\n";
data = buffer;
cout << "Consumed... " << data << endl;
}
return n;
}
int main()
{
cout << "The Producer x Consumer Problem\n";
Thread prod(&producer, REP);
Thread cons(&consumer, REP);
int status;
prod.join(&status);
if(status == REP)
cout << "Producer went to heaven!\n";
else
cout << "Producer went to hell!\n";
cons.join(&status);
if(status == REP)
cout << "Consumer went to heaven!\n";
else
cout << "Consumer went to hell!\n";
return 0;
}
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.
#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++) {
phil[i]->join(&status);
if(status == i)
cout << "Philosopher " << i << " went to heaven!\n";
else
cout << "Philosopher " << i << " went to hell!\n";
}
return 0;
}
|
Softwares básicos, caso Hello Word! (Atividade 5) |
---|
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:
|
Permissões de sistema de arquivos no Linux |
---|
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. |
Lista de exercícios |
---|
Lista de exercíciosLista de exercícios referente ao dia 22/06 | Lista de exercícios. |
-->