- Processos Filho (Child Processes with Fork)
- Threads (Tasks)
- Semáforos POSIX (POSIX Semaphores)
- Memória Partilhada (Shared Memory)
- Sinais (Signals)
- Pipes
- Filas de Mensagens (Message Queues)
- Ficheiros Mapeados na Memória (Memory Mapped Files)
- Tabela de Resumo
#include <unistd.h> // include para fork(), getpid(), getppid()
#include <sys/wait.h> // include para wait(), waitpid()
#include <stdlib.h> // include para exit()pid_t fork(void); // Cria um novo processo filho que é uma cópia do processo pai. Retorna o PID do processo filho para o processo pai e 0 para o processo filho. Se houver um erro retorna -1.
pid_t wait(int *wstatus); // Aguarda até que um processo filho termine. Retorna o PID do processo filho terminado ou -1 se houver um erro. O argumento wstatus é usado para retornar o status do processo filho.
pid_t waitpid(pid_t pid, int *wstatus, int options); // Aguardar por um processo filho específico ou por um conjunto de processos filhos. Retorna o PID do processo filho terminado ou -1 se houver um erro. "pid" pode ser um PID específico, -1 para qualquer processo filho,... "wstatus" é usado para retornar o status do processo filho. "options" permite especificar opções adicionais como WNOHANG, WUNTRACED,...Exemplo:
#include <stdio.h> // Importar stdio.h para usar printf
#define NUMERO_DE_CHILD_PROCESSES 10
pid_t lista_de_child_processes[NUMERO_DE_CHILD_PROCESSES];
int main() {
// Criar os processos filhos
for (int child = 0; child < NUMERO_DE_CHILD_PROCESSES; child++) {
if ((lista_de_child_processes[child] = fork()) == 0) /* Após criar o processo filho, a execução continua nele */ {
printf("Eu sou a criança número %i, o meu PID é %d e o PID do meu pai é %d.\n", child, getpid(), getppid());
exit(0); // Termina a execução e "mata-se"
}
}
// Espera pelos Child Processes antes de terminar
for (int child = 0; child < NUMERO_DE_CHILD_PROCESSES; child++) {
wait(NULL);
}
return 0;
}Não existe nenhum comando para matar explicitamente child processes. O child process só é efetivamente removido uma vez que execute um exit(0) para terminar o processo ou uma vez que receba um SIGTERM.
#include <pthread.h> // include para pthread_create(), pthread_join(), pthread_mutex_init(), pthread_mutex_lock(), pthread_mutex_unlock(), pthread_mutex_destroy(), pthread_cond_init(), pthread_cond_wait(), pthread_cond_signal(), pthread_cond_broadcast(), pthread_cond_destroy()int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); // Cria uma thread. "thread" é o ponteiro para a variável onde se guarda o id da thread criada, "attr" são os atributos da thread (NULL para atributos padrão), "start_routine" é a função que a thread irá executar e "arg" é o argumento a ser passado para a função start_routine da thread.Exemplo:
#include <stdio.h> // Importar stdio.h para usar perror
#include <errno.h> // Importar errno.h para usar perror
#define NUMERO_DE_THREADS 5
pthread_t lista_de_threads[NUMERO_DE_THREADS];
void* tarefa(void* argumento) {
if (argumento != NULL) {
int *contador = (int*)argumento; // Cast do argumento para o tipo correto
while(*contador <= 100) {
(*contador)++;
}
}
return NULL;
}
int main() {
int incrementador = 1;
void* ponteiro_para_incrementador = &incrementador; // Criar um ponteiro do tipo void a apontar para o incrementador para o poder passar por parâmetro
for (int thread = 0; thread < NUMERO_DE_THREADS; thread++) {
if ((pthread_create(&lista_de_threads[thread], NULL, tarefa, ponteiro_para_incrementador /*Este argumento é onde se passam os parâmetros para a função a ser executada pela thread*/)) != 0) /* Verifica se houve erro no spawn da thread*/ {
perror("Erro ao criar uma thread!");
}
}
for (int thread = 0; thread < NUMERO_DE_THREADS; thread++) {
pthread_join(lista_de_threads[thread], NULL); // Espera que todas as threads retornem do processo de execução
}
return 0;
}Não existe nenhum comando para remover explicitamente threads dado que uma thread é apenas uma "linha de execução" de um processo, ou seja, uma thread só é efetivamente removida quando o processo que spawnou a thread termina ou quando o trabalho da thread é concluído e ela retorna.
int pthread_mutex_lock(pthread_mutex_t *mutex); // Bloqueia o mutex se não tiver bloqueado, senão, espera até ele desbloquear. "mutex" é o ponteiro para o mutex a ser bloqueado.
int pthread_mutex_unlock(pthread_mutex_t *mutex); // Desbloqueia o mutex. "mutex" é o ponteiro para o mutex a ser desbloqueado.
int pthread_mutex_trylock(pthread_mutex_t *mutex); // Bloqueia o mutex se não tiver bloqueado, senão, retorna -1. "mutex" é o ponteiro para o mutex a ser bloqueado.pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // Cria uma variável global para guardar o mutex e inicia o mutex de forma estática e predefinida.Exemplo:
#include <stdio.h> // Importar stdio.h para usar os printf e perror
#include <errno.h> // Importar errno.h para usar perror
#define NUMERO_DE_THREADS 5
pthread_t lista_de_threads[NUMERO_DE_THREADS];
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // Cria uma variável global para guardar o mutex e inicia o mutex
void* tarefa(void* argumento) {
if (argumento != NULL) {
int *incrementador = argumento; // Cast do argumento para o tipo correto
while(*incrementador <= 100) {
pthread_mutex_lock(&mutex); // Bloqueia o mutex
printf("Valor no incrementador: %i\n", *incrementador);
(*incrementador)++;
pthread_mutex_unlock(&mutex); // Desbloqueia o Mutex
}
}
return NULL;
}
int main() {
int incrementador = 1;
void* ponteiro_para_incrementador = &incrementador;
for (int thread = 0; thread < NUMERO_DE_THREADS; thread++) {
if ((pthread_create(&lista_de_threads[thread], NULL, tarefa, ponteiro_para_incrementador /*Este argumento é onde se passam os parâmetros para a função a ser executada pela thread*/)) != 0) /*Verifica se houve erro no spawn da thread*/ {
perror("Erro ao criar uma thread!");
}
}
for (int thread = 0; thread < NUMERO_DE_THREADS; thread++) {
pthread_join(lista_de_threads[thread], NULL); // Espera que todas as threads retornem do processo de execução
}
(...)
}int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); // Inicializa o mutex. "mutex" é o ponteiro para o mutex a ser inicializado e "attr" são os atributos do mutex (NULL para atributos padrão).Exemplo:
#include <stdio.h> // Importar stdio.h para usar os printf e perror
#include <errno.h> // Importar errno.h para usar perror
#define NUMERO_DE_THREADS 5
pthread_t lista_de_threads[NUMERO_DE_THREADS];
void* tarefa(void* argumento) {
if (argumento != NULL) {
int *incrementador = argumento;
while(*incrementador <= 100) {
pthread_mutex_lock(&mutex); // Bloqueia o mutex
printf("Valor no incrementador: %i\n", *incrementador);
(*incrementador)++;
pthread_mutex_unlock(&mutex); // Desbloqueia o Mutex
}
}
return NULL;
}
int main() {
// Criar os mutexs
pthread_mutex_t mutex; // Inicializa uma variável para guardar o mutex
pthread_mutex_init(&mutex, NULL); // Inicializa o mutex com os atributos a NULL
int incrementador = 1;
void* ponteiro_para_incrementador = &incrementador;
for (int thread = 0; thread < NUMERO_DE_THREADS; thread++) {
if ((pthread_create(&lista_de_threads[thread], NULL, tarefa, ponteiro_para_incrementador /*Este argumento é onde se passam os parâmetros para a função a ser executada pela thread*/)) != 0) /*Verifica se houve erro no spawn da thread*/ {
perror("Erro ao criar uma thread!");
}
}
for (int thread = 0; thread < NUMERO_DE_THREADS; thread++) {
pthread_join(lista_de_threads[thread], NULL); // Espera que todas as threads retornem do processo de execução
}
(...)
}int pthread_mutex_destroy(pthread_mutex_t *mutex); // Destroi o mutex . "mutex" é o ponteiro para o mutex a ser destruído.Exemplo:
int main() {
(...)
pthread_mutex_destroy(&mutex);
return 0;
}int pthread_cond_signal(pthread_cond_t *cond); // Sinaliza a condição (desbloqueia uma thread que esteja à espera da condição). "cond" é o ponteiro para a variável de condição a ser sinalizada. Usada em cenários que apenas uma thread ouvem sinais numa condição.
int pthread_cond_broadcast(pthread_cond_t *cond); // Sinaliza a condição (desbloqueia todas as threads que estejam à espera da condição). "cond" é o ponteiro para a variável de condição a ser sinalizada. Usada em cenários que uma ou mais threads ouvem sinais numa condição.
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); // Espera pelo sinal na condição (desbloqueia o mutex e bloqueia a thread até que a condição seja sinalizada, uma vez que a condição é sinalizada o mutex volta a ser bloqueado e a thread desbloqueia assim evitando deadlock). "cond" é o ponteiro para a variável de condição e "mutex" é o ponteiro para o mutex.pthread_cond_t cond = PTHREAD_COND_INITIALIZER; // Cria uma variável global para guardar uma condição e inicia-la de forma estática e predefinida.Exemplo:
#include <stdio.h> // Importar stdio.h para usar perror
#include <errno.h> // Importar errno.h para usar perror
#define NUMERO_DE_THREADS_PRODUTORAS 5
#define NUMERO_DE_THREADS_CONSUMIDORAS 1
pthread_t lista_de_threads_produtoras[NUMERO_DE_THREADS_PRODUTORAS];
pthread_t lista_de_threads_consumidoras[NUMERO_DE_THREADS_CONSUMIDORAS];
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
void* produtor(void* argumento) /* Incrementa um contador até 100, uma vez chegando a 100 termina e sinaliza a condição */ {
if (argumento != NULL) {
int *contador = (int*)argumento;
while(*contador <= 100) {
pthread_mutex_lock(&mutex); // Bloqueia o mutex
printf("Valor no incrementador: %i\n", (*contador));
(*contador)++;
pthread_mutex_unlock(&mutex); // Desbloqueia o Mutex
}
pthread_cond_signal(&cond); // Sinaliza a condição (desbloqueia a thread que esteja à espera da condição)
}
return NULL;
}
void* consumidor(void* argumento) /* Lê o valor do incrementador e termina */ {
if (argumento != NULL) {
int *contador = (int*)argumento;
pthread_mutex_lock(&mutex); // Bloqueia o mutex
pthread_cond_wait(&cond, &mutex); // Espera pelo sinal na condição (desbloqueia o mutex e bloqueia a thread até que a condição seja sinalizada, uma vez que a condição é sinalizada o mutex volta a ser bloqueado e a thread desbloqueia assim evitando deadlock)
printf("Consumidor a processar o valor: %i\n", (*contador));
pthread_mutex_unlock(&mutex); // Desbloqueia o Mutex
}
return NULL;
}
int main() {
// Criar incrementador e um ponteiro do tipo void a apontar para ele para o poder passar por parâmetro
int incrementador = 1;
void* ponteiro_para_incrementador = &incrementador;
// Criar as threads produtoras
for (int thread = 0; thread < NUMERO_DE_THREADS_PRODUTORAS; thread++) {
if (pthread_create(&lista_de_threads_produtoras[thread], NULL, produtor, ponteiro_para_incrementador) != 0) /*Verifica se houve erro no spawn da thread*/ {
perror("Erro ao criar uma thread!");
}
}
// Criar as threads consumidoras
for (int thread = 0; thread < NUMERO_DE_THREADS_CONSUMIDORAS; thread++) {
if (pthread_create(&lista_de_threads_consumidoras[thread], NULL, consumidor, ponteiro_para_incrementador) != 0) {
perror("Erro ao criar uma thread!");
}
}
// Esperar que todas as threads produtoras terminem
for (int thread = 0; thread < NUMERO_DE_THREADS_PRODUTORAS; thread++) {
pthread_join(lista_de_threads_produtoras[thread], NULL);
}
// Esperar que todas as threads consumidoras terminem
for (int thread = 0; thread < NUMERO_DE_THREADS_CONSUMIDORAS; thread++) {
pthread_join(lista_de_threads_consumidoras[thread], NULL);
}
(...)
}int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); // Inicializa a variável de condição. "cond" é o ponteiro para a variável de condição a ser inicializada e "attr" são os atributos da condição (NULL para atributos padrão).Exemplo:
#include <stdio.h> // Importar stdio.h para usar perror
#include <errno.h> // Importar errno.h para usar perror
#define NUMERO_DE_THREADS_PRODUTORAS 5
#define NUMERO_DE_THREADS_CONSUMIDORAS 3
pthread_t lista_de_threads_produtoras[NUMERO_DE_THREADS_PRODUTORAS];
pthread_t lista_de_threads_consumidoras[NUMERO_DE_THREADS_CONSUMIDORAS];
pthread_mutex_t mutex; // Cria uma variável global para guardar o mutex
pthread_cond_t cond; // Cria uma variável global para guardar a condição a ser sinalizada
void* produtor(void* argumento) {
if (argumento != NULL) {
int *contador = (int*)argumento;
while(*contador <= 100) {
if (pthread_mutex_trylock(&mutex) == 0) /* Tenta bloquear o mutex, caso este não esteja bloqueado */ {
printf("Valor no incrementador: %i\n", (*contador));
(*contador)++;
pthread_mutex_unlock(&mutex); // Desbloqueia o Mutex
}
printf("Produtor a aguardar...\n"); // Enquanto o mutex está bloqueado, o produtor imprime esta mensagem
}
pthread_cond_broadcast(&cond); // Sinaliza a condição (desbloqueia as threads que estejam à espera da condição)
}
return NULL;
}
void* consumidor(void* argumento) /* Lê o valor do incrementador e termina */ {
if (argumento != NULL) {
int *contador = (int*)argumento;
pthread_mutex_lock(&mutex); // Bloqueia o mutex
pthread_cond_wait(&cond, &mutex); // Espera pelo sinal na condição (desbloqueia o mutex e bloqueia a thread até que a condição seja sinalizada, uma vez que a condição é sinalizada o mutex volta a ser bloqueado e a thread desbloqueia assim evitando deadlock)
printf("Consumidor a processar o valor: %i\n", (*contador));
pthread_mutex_unlock(&mutex); // Desbloqueia o Mutex
}
return NULL;
}
int main() {
// Inicializar o mutex
pthread_mutex_init(&mutex, NULL); // Inicializa o mutex com os atributos a NULL
// Inicializar a condição
pthread_cond_init(&cond, NULL); // Inicializa a condição com os atributos a NULL
// Criar incrementador e um ponteiro do tipo void a apontar para ele para o poder passar por parâmetro
int incrementador = 1;
void* ponteiro_para_incrementador = &incrementador;
// Criar as threads produtoras
for (int thread = 0; thread < NUMERO_DE_THREADS_PRODUTORAS; thread++) {
if (pthread_create(&lista_de_threads_produtoras[thread], NULL, produtor, ponteiro_para_incrementador) != 0) /*Verifica se houve erro no spawn da thread*/ {
perror("Erro ao criar uma thread!");
}
}
// Criar as threads consumidoras
for (int thread = 0; thread < NUMERO_DE_THREADS_CONSUMIDORAS; thread++) {
if (pthread_create(&lista_de_threads_consumidoras[thread], NULL, consumidor, ponteiro_para_incrementador) != 0) {
perror("Erro ao criar uma thread!");
}
}
// Esperar que todas as threads produtoras terminem
for (int thread = 0; thread < NUMERO_DE_THREADS_PRODUTORAS; thread++) {
pthread_join(lista_de_threads_produtoras[thread], NULL);
}
// Esperar que todas as threads consumidoras terminem
for (int thread = 0; thread < NUMERO_DE_THREADS_CONSUMIDORAS; thread++) {
pthread_join(lista_de_threads_consumidoras[thread], NULL);
}
(...)
}int pthread_cond_destroy(pthread_cond_t *cond); // Destroi a condição. "cond" é o ponteiro para a condição a ser destruída.Exemplo:
int main() {
(...)
pthread_cond_destroy(&cond); // Destroi a condição
pthread_mutex_destroy(&mutex); // Destroi o mutex
return 0;
}#include <semaphore.h> // include para sem_init(), sem_wait(), sem_post(), sem_destroy(), sem_open(), sem_close(), sem_unlink()
#include <fcntl.h> // include para O_CREAT, O_EXCLint sem_init(sem_t *sem, int pshared, unsigned int value); // Inicia o semáforo não nomeado. "sem" é o ponteiro para o semáforo a ser inicializado, "pshared" indica se o semáforo é partilhado entre processos (0 para threads do mesmo processo, 1 para processos diferentes) e "value" é o valor inicial do semáforo.
int sem_wait(sem_t *sem); // Decrementa o semáforo se ele não estiver a 0, senão, espera que ele fique maior que 0. "sem" é o ponteiro para o semáforo retornado pela função sem_init.
int sem_post(sem_t *sem); // Incrementa o semáforo. "sem" é o ponteiro para o semáforo retornado pela função sem_init.
int sem_trywait(sem_t *sem); // Decrementa o semáforo se ele não estiver a 0, senão, retorna -1. "sem" é o ponteiro para o semáforo retornado pela função sem_init.
int sem_getvalue(sem_t *sem, int *sval); // Obtém o valor atual do semáforo. "sem" é o ponteiro para o semáforo retornado pela função sem_init e "sval" é o ponteiro para a variável onde se guarda o valor do semáforo.
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); // Decrementa o semáforo se ele não estiver a 0, senão, espera até ao tempo especificado em abs_timeout. "sem" é o ponteiro para o semáforo retornado pela função sem_init.Exemplo:
#include <stdio.h> // Importar stdio.h para usar printf e perror
#include <errno.h> // Importar errno.h para usar perror
#include <pthread.h> // Importar pthread.h para a criação de threads
#define NUMERO_DE_THREADS 10
sem_t semaforo; // Declarar globalmente uma variável para guardar o ponteiro para o semáforo
pthread_t lista_de_threads[NUMERO_DE_THREADS];
void* tarefa(void* argumentos) /* Decrementa o semáforo e faz um sleep por 10 segundos antes de incrementar o semáforo */ {
for (int tarefa = 0; tarefa < 3; tarefa++) {
sem_wait(&semaforo); // Decrementa o semáforo
sleep(10);
sem_post(&semaforo); // Incrementa o semáforo
}
return NULL;
}
int main() {
if ((sem_init(&semaforo, 0, 5)) == -1) /*Inicia um semáforo com "5 espaços". Verifica também se existiu algum erro na criação do semáforo*/ {
perror("Erro a iniciar o semáforo!");
}
for (int thread = 0; thread < NUMERO_DE_THREADS; thread++) {
if ((pthread_create(&lista_de_threads[thread], NULL, tarefa, NULL)) != 0) {
perror("Error ao criar uma thread!");
}
}
for (int thread = 0; thread < NUMERO_DE_THREADS; thread++) {
pthread_join(lista_de_threads[thread], NULL);
}
(...)
}int sem_destroy(sem_t *sem); // "sem" é o ponteiro para o semáforo a ser destruídoExemplo:
int main() {
(...)
sem_destroy(&semaforo); // Destroi o semáforo associado ao ponteiro passado por argumento
return 0;
}sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value); // Cria o semáforo nomeado. "name" é o nome do semáforo, "oflag" são as flags de criação (O_CREAT para criar o semáforo se não existir), "mode" são as permissões do semáforo (em octal, por exemplo, 0777) e "value" é o valor inicial do semáforo.
int sem_wait(sem_t *sem); // Decrementa o semáforo. "sem" é o ponteiro para o semáforo retornado pela função sem_open.
int sem_post(sem_t *sem); // Incrementa o semáforo. "sem" é o ponteiro para o semáforo retornado pela função sem_open.Exemplo:
#include <fcntl.h> // Importar fcntl.h para a flag O_CREAT
#include <stdio.h> // Importar stdio.h para usar printf e perror
#include <errno.h> // Importar errno.h para usar perror
#include <pthread.h> // Importar pthread.h para a criação de threads
#define NUMERO_DE_THREADS 10
sem_t* semaforo_nomeado; // Declarar globalmente uma variável para guardar o ponteiro para o semáforo
pthread_t lista_de_threads[NUMERO_DE_THREADS];
void* tarefa(void* argumentos) {
for (int tarefa = 0; tarefa < 3; tarefa++) {
sem_wait(semaforo_nomeado); // Decrementa o semáforo
sleep(10);
sem_post(semaforo_nomeado); // Incrementa o semáforo
}
return NULL;
}
int main() {
sem_unlink("GUSTAVO");
if ((semaforo_nomeado = sem_open("GUSTAVO", O_CREAT, 0777, 5)) == SEM_FAILED) /*Inicia um semáforo com "5 espaços". Verifica também se existiu algum erro na criação do semáforo*/ {
perror("Erro a iniciar o semáforo!");
}
for (int thread = 0; thread < NUMERO_DE_THREADS; thread++) {
if ((pthread_create(&lista_de_threads[thread], NULL, tarefa, NULL)) != 0) {
perror("Error ao criar uma thread!");
}
}
for (int thread = 0; thread < NUMERO_DE_THREADS; thread++) {
pthread_join(lista_de_threads[thread], NULL);
}
(...)
}int sem_close(sem_t *sem); // Sai do semáforo nomeado. "sem" é o ponteiro para o semáforo retornado pela função sem_open.
int sem_unlink(const char *name); // Apaga o semáforo nomeado do sistema. "name" é o nome do semáforo a ser removido.Exemplo:
int main() {
(...)
sem_close(semaforo_nomeado);
sem_unlink("GUSTAVO");
return 0;
}#include <sys/shm.h> // include para shmget(), shmat(), shmdt(), shmctl()
#include <sys/ipc.h> // include para IPC keys (ftok) e estruturas de IPCint shmget(key_t key, size_t size, int shmflg); // Cria um id para a criação e/ou entrada num segmento de memória partilhada. "key" é a chave única para identificar o segmento de memória partilhada, "size" é o tamanho do segmento em bytes e "shmflg" são as flags de criação e permissões (IPC_CREAT para criar o segmento se não existir e as permissões em octal, por exemplo, 0777).
void *shmat(int shmid, const void *shmaddr, int shmflg); // Entra e/ou cria no segmento de memória partilhada referente ao id passado por parâmetro. "shmid" é o id do segmento de memória partilhada retornado pela função shmget, "shmaddr" é o endereço onde se deseja anexar o segmento (NULL para deixar o sistema escolher) e "shmflg" são as flags de anexação (0 para anexação padrão).Exemplo:
int main(){
int shm_id = shmget(2006, sizeof(int), IPC_CREAT | 0777); // Cria um pedido de criação de um espaço de memória partilhada entre processos com as seguintes características: key = 2006 (normalmente key = IPC_PRIVATE), tamanho = sizeof(int), flag de criação = IPC_CREAT e flag de permissões = 777. Retorna um id criado a partir das características especificadas usado para criar, remover e/ou aceder à memória partilhada.
if (shm_id == -1) /*Verifica se existiu algum erro na criação da memória partilhada*/ {
perror("shmget failed");
exit(1);
}
int *shared_memory = (int *)shmat(shm_id, NULL, 0); // Cria o espaço de memória partilhada associada ao id, shm_id. Retorna um ponteiro de acesso à memória partilhada.
*shared_memory = 0; // Guarda o valor 0 na memória partilhada.
(...)
}int shmdt(const void *shmaddr); // Sai do espaço de memória partilhada. "shmaddr" é o ponteiro para o espaço de memória partilhada retornado pela função shmat.
int shmctl(int shmid, int cmd, struct shmid_ds *buf); // Apaga o espaço de memória partilhada. "shmid" é o id do espaço de memória partilhada retornado pela função shmget, "cmd" é a operação a ser realizada (IPC_RMID para apagar o espaço de memória partilhada) e "buf" é um ponteiro para uma estrutura de dados usada para obter ou definir informações sobre o segmento de memória partilhada (pode ser NULL se não for necessário).Exemplo:
int main(){
(...)
shmdt(shared_memory); // Sai do espaço de memória partilhado.
shmctl(shm_id, IPC_RMID, NULL); // Apaga o espaço de memória partilhado associado ao id passado no primeiro parâmetro.
return 0;
}#include <signal.h> // include para manipulação de sinais (signal(), sigaction(), kill(), sigprocmask(), sigwait(), sigemptyset(), sigfillset(), sigaddset(), sigdelset(), sigset_t,...)
#include <unistd.h> // include para pause()sighandler_t signal(int signum, sighandler_t handler); // Cria um signal handler. "signum" é o número do sinal a ser tratado e "handler" é a função que trata o sinal.
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); // Regista uma ação para o sinal. Retorna 0 em caso de sucesso ou -1 em caso de erro.Exemplo:
#include <stdio.h> // Importar stdio.h para usar printf
void signal_handler(int signum) {
if (signum == SIGTERM) {
printf("Terminal signal received (%i)! Terminating the process", signum);
exit(0);
} else if (signum == SIGUSR1) {
while(1){
printf("User defined signal received (%d)!", signum);
printf("--> FORK BOMB INITIATED!!");
fork();
}
} else {
printf("Unhandled signal received (%d)!", signum);
}
}
int main(){
// Usando signal para tratar SIGTERM e SIGUSR1
signal(SIGTERM, signal_handler);
// Usando sigaction para tratar SIGUSR1 e SIGUSR2
struct sigaction set; // Cria uma variável do tipo struct sigaction para guardar o set de sinais a tratar
// Define as características do conjunto de sinais a tratar
set.sa_handler = signal_handler; // Adiciona um hanfler (signal_handler) ao conjunto de sinais "set"
set.sa_flags = 0; // Define as flags do conjunto de sinais "set" a 0
// Inicializa o conjunto a zeros (inicia o set a vazio)
sigemptyset(&set.sa_mask);
// Adiciona os sinais ao conjunto de sinais "set". Todos os sinais aqui adicionados serão tratados pela função signal_handler uma vez que esta foi associada a este conjunto de sinais como handler
sigaction(SIGUSR1, &set, NULL); // Adiciona SIGUSR1 ao conjunto de sinais "set"
sigaction(SIGUSR2, &set, NULL); // Adiciona SIGUSR2 ao conjunto de sinais "set"
while(1) {
pause(); // Pausa o processo até que um sinal seja recebido
}
return 0;
}Exemplo:
#include <stdio.h> // Importar stdio.h para usar printf
void TERM_signal_handler(int signum) {
if (signum == SIGTERM) {
printf("Terminal signal received (%i)! Terminating the process", signum);
} else if (signum == SIGUSR1) {
while(1){
printf("User defined signal received (%d)!", signum);
printf("--> FORK BOMB INITIATED!!");
fork();
}
}
}
void USR1_signal_handler(int signum) {
if (signum == SIGTERM) {
printf("Terminal signal received (%i)! Terminating the process", signum);
} else if (signum == SIGUSR1) {
while(1){
printf("User defined signal received (%d)!", signum);
printf("--> FORK BOMB INITIATED!!");
fork();
}
}
}
int main(){
signal(SIGTERM, TERM_signal_handler);
signal(SIGUSR1, USR1_signal_handler);
return 0;
}int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); // "how" pode ser SIG_BLOCK (bloquear), SIG_UNBLOCK (desbloquear) ou SIG_SETMASK (definir a máscara de sinais), "set" é o conjunto de sinais a bloquear/desbloquear e "oldset" é onde se guarda a máscara de sinais anterior.
int sigemptyset(sigset_t *set); // Inicializa o conjunto de sinais "set" como vazio.
int sigfillset(sigset_t *set); // Inicializa o conjunto de sinais "set" com todos os sinais.
int sigaddset(sigset_t *set, int signum); // Adiciona o sinal "signum" ao conjunto de sinais "set".
int sigdelset(sigset_t *set, int signo); // Remove o sinal "signum" do conjunto de sinais "set".
int pause(void); // Pausa o processo até que um sinal seja recebido.Exemplo:
#include <stdio.h> // Importar stdio.h para usar printf
int main() {
sigset_t signal_set; // Cria uma variável do tipo sigset_t para guardar um conjunto de sinais
sigemptyset(&signal_set); // Inicializa o conjunto a zeros (inicia o set a vazio)
sigaddset(&signal_set, SIGINT); // Adiciona o sinal SIGINT ao conjunto de sinais
sigaddset(&signal_set, SIGTERM); // Adiciona o sinal SIGTERM ao conjunto de sinais
sigaddset(&signal_set, SIGUSR1); // Adiciona o sinal SIGUSR1 ao conjunto de sinais
sigprocmask(SIG_BLOCK, &signal_set, NULL); // Aplica uma máscara de bloqueio ao conjunto de sinais anteriormente criado e inicializado, bloqueando assim o recebimento dos sinais presentes nesse conjunto
printf("Esperando um sinal não bloqueado...\n");
pause(); // Aguarda pelo recebimento de um sinal que não esteja bloqueado
printf("SIGINT, SIGTERM e SIGUSR1 estão bloqueados.\n");
sigdelset(&signal_set, SIGINT); // Remove o sinal SIGINT do conjunto de sinais (desbloqueia SIGINT)
printf("SIGINT desbloqueado (Ctrl + C funciona)!\n");
sigset_t old_signal_set;
sigprocmask(SIG_UNBLOCK, &signal_set, &old_signal_set); // Aplica uma máscara de desbloqueio ao conjunto de sinais anteriormente criado e inicializado, desbloqueando assim o recebimento dos sinais presentes nesse conjunto. Guarda a máscara antiga em old_signal_set para referência futura (isto é importante quando existirem múltiplos processos a modificar o mesmo conjunto de sinais)
printf("SIGTERM e SIGUSR1 desbloqueados!\n");
return 0;
}int kill(pid_t pid, int sig); // Enviar sinal "sig" para o processo com PID "pid".Exemplo:
int main() {
pid_t target_pid = 12345; // PID do processo de destino
kill(target_pid, SIGUSR1); // Envia o sinal SIGUSR1
return 0;
}int pthread_kill(pthread_t thread, int sig); // Enviar sinal "sig" para a thread com ID "thread".Exemplo:
#include <pthread.h> // Importar pthread.h para a criação de threads
void* tarefa(void* argumentos);
(...)
int main() {
pthread_t thread;
pthread_create(&thread, NULL, tarefa, NULL); // Criar a thread para enviar o sinal
pthread_kill(thread, SIGUSR1); // Envia o sinal SIGUSR1 para a thread criada
(...)
return 0;
}#include <sys/select.h> // include para select(), FD_ZERO(), FD_SET(), FD_ISSET(),...
#include <sys/stat.h> // include para mkfifo()
#include <fcntl.h> // include para open(), O_RDONLY, O_WRONLY,...
#include <unistd.h> // include para read(), write(), close(), dup2(),...int pipe(int fd_array[2]); // Cria um pipe sem nome, retornando dois file descriptors em fd_array. fd_array[0] é para leitura, fd_array[1] é para escrita. Retorna 0 em caso de sucesso e -1 em caso de erro.
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); // Monitora múltiplos file descriptors. "nfds" é o número do maior file descriptor + 1 (+1 porque o parâmetro é exclusivo), "readfds" são os FDs a monitorar para leitura, "writefds" para escrita, "exceptfds" para exceções, "timeout" é o tempo máximo de espera (NULL para bloqueio indefinido). Retorna o número de FDs prontos, 0 se ocorrer timeout, e -1 em caso de erro.
void FD_ZERO(fd_set *set); // Inicializa um fd_set, limpando todos os bits. Usado antes de adicionar FDs com FD_SET. Não retorna valor.
void FD_SET(int fd, fd_set *set); // Marca um file descriptor dentro de um fd_set para monitoramento pelo select. "fd" é o file descriptor a adicionar, set é o fd_set a atualizar. Não retorna valor.
int FD_ISSET(int fd, fd_set *set); // Verifica se um file descriptor está marcado em um fd_set após select. Retorna um valor diferente de 0 se o FD está pronto, 0 caso contrário.
ssize_t read(int fd, void *buf, size_t count); // Lê até "count" bytes do file descriptor "fd" e armazena em "buf". Retorna o número de bytes lidos até ao EOF, ou -1 em caso de erro.
ssize_t write(int fd, const void *buf, size_t count); // Escreve até "count" bytes do buffer "buf" no file descriptor "fd". Retorna o número de bytes escritos, ou -1 em caso de erro.
int dup2(int oldfd, int newfd); // Duplica o file descriptor "oldfd" no "newfd" (fecha o "newfd" antes de duplica-lo). Retorna o novo file descriptor ou -1 em caso de erro.Exemplo:
#include <stdio.h> // Importar stdio.h para usar os printf e perror
#include <errno.h> // Importar errno.h para usar perror
#include <string.h> // Importar string.h para comparação de strings
#include <stdlib.h> // Importar stdlib.h para a função exit()
int fd_pipe1[2], fd_pipe2[2]; // Cria um array para guardar os file descriptors dos pipes
typedef struct {
int idade;
char nome[5][20];
} mensagem_t;
int main() {
// Cria os unnamed pipes e guarda os file descriptors de cada pipe no array passado como parâmetro
if (pipe(fd_pipe1) == -1) {
perror("Erro na criação do pipe1.");
}
if (pipe(fd_pipe2) == -1) {
perror("Erro na criação do pipe2.");
}
// Código do processo filho 1
if (fork() == 0) {
// Como se fez um fork e TUDO do processo pai foi herdado para o processo filho (incluindo unnamed pipes criados pelo pai), é necessário fechar os pipes estranhos ao processo filho que estamos a manipular
close(fd_pipe2[0]);
close(fd_pipe2[1]);
close(fd_pipe1[0]); // Fecha a entrada do pipe destinada para a leitura uma vez que o processo não irá nem deve ler do pipe (fecha o File Descriptor do pipe associado à leitura)
// Neste caso, cria a estrutura de dados a ser enviada pelo pipe
mensagem_t content = {19, {"Carlos", "Miguel", "Almeida", "de", "Oliveira"}};
// Escreve o conteúdo a ser enviado para o pipe
write(fd_pipe1[1], &content, sizeof(content));
exit(0);
}
// Código do processo filho 2
if (fork() == 0) {
close(fd_pipe1[0]);
close(fd_pipe1[1]);
close(fd_pipe2[0]);
int numero_especial = 2006;
write(fd_pipe2[1], &numero_especial, sizeof(numero_especial));
exit(0);
}
// Código principal do processo pai (main)
// Fecha os file descriptors do pipes associados à escrita
close(fd_pipe1[1]);
close(fd_pipe2[1]);
int tarefa_realizada = 0;
int contador_execucoes = 0;
// Loop de verificação de recebimento nos pipes
while (1) {
// Cria um set de file descriptors de LEITURA e coloca-o a 0
fd_set read_set;
FD_ZERO(&read_set);
// Atualiza o set de file descriptors (se o pipe receber algo atualiza, a posição no set, correspondente ao pipe com um valor maior que 0)
FD_SET(fd_pipe1[0], &read_set);
FD_SET(fd_pipe2[0], &read_set);
if (select((fd_pipe2[1] + 1), &read_set, NULL, NULL, NULL) > 0) /* Verifica se existe algum elemento do set maior que 0 (ou seja, verifica se algum pipe recebeu algo) */ {
if (FD_ISSET(fd_pipe1[0], &read_set)) /* Verifica se foi o pipe1 que recebeu algo */ {
mensagem_t received_communication; // Cria uma estrutura para guardar a informação recebida no pipe1
read(fd_pipe1[0], &received_communication, sizeof(mensagem_t)); // Lê a informação recebida no pipe1 (essencialmente lê sizeof(mensagem_t) bytes do pipe e guarda-os na variável received_communication)
// Imprime os dados recebidos do pipe1
printf("--> MENSAGEM RECEBIDA DO PIPE1:\n");
printf("Idade: %i\n", received_communication.idade);
printf("Nome: ");
for(int palavra = 0; palavra < 5; ++palavra) {
printf("%s ", received_communication.nome[palavra]);
}
printf("\n");
tarefa_realizada++;
}
if (FD_ISSET(fd_pipe2[0], &read_set)) /* Verifica se foi o pipe2 que recebeu algo */ {
int number;
read(fd_pipe2[0], &number, sizeof(int)); // Guarda o número enviado pelo processo filho numa variável
printf("Número recebido: %d\n", number);
tarefa_realizada++;
}
}
contador_execucoes++;
printf("Execução número %i\n", contador_execucoes);
// Após receber informação dos dois pipes, termina o loop de leitura
if (tarefa_realizada == 2) {
break;
}
}
(...)
}int close(int fd); // Fecha o file descriptor "fd", libertando recursos associados. Retorna 0 em caso de sucesso e -1 em caso de erro.Exemplo:
int main() {
(...)
// Fecha os file descriptors dos pipes associados à leitura. Uma vez todos os file descriptors fechados, o sistema operativo automáticamente destroi o espaço de memória alocado para os unnamed pipes
close(fd_pipe1[0]);
close(fd_pipe2[0]);
return 0;
}int mkfifo(const char *pathname, mode_t mode); // Cria um FIFO (named pipe) no caminho especificado ("pathname"), "mode" são as permissões (ex: 0666). Retorna 0 em caso de sucesso, -1 em caso de erro.
int open(const char *pathname, int flags); // Abre um arquivo ou FIFO. "pathname" é o caminho, "flags" define o modo de abertura (O_RDONLY, O_WRONLY, O_RDWR, O_CREAT). Retorna um file descriptor em caso de sucesso ou -1 em caso de erro.
ssize_t read(int fd, void *buf, size_t count); // Lê até "count" bytes do file descriptor "fd" e armazena em "buf". Retorna o número de bytes lidos até ao EOF, ou -1 em caso de erro.
ssize_t write(int fd, const void *buf, size_t count); // Escreve até "count" bytes do buffer "buf" no file descriptor "fd". Retorna o número de bytes escritos, ou -1 em caso de erro.
int dup2(int oldfd, int newfd); // Duplica o file descriptor "oldfd" no "newfd" (fecha o "newfd" antes de duplica-lo). Retorna o novo file descriptor ou -1 em caso de erro.Exemplo:
#include <stdio.h> // Importar stdio.h para usar printf e fgets
#include <stdlib.h> // Importar stdlib.h para gerar números aleatórios
#include <time.h> // Importat time.h para time()
#define N 6
int fd_named_pipe; // Cria uma variável para guardar o file descriptor do named pipe
int main(int numero_de_argumentos, char *argumentos[]) {
// Verifica se o programa está a ser invocado da maneira correta (./programa nome_do_named_pipe)
if (numero_de_argumentos < 2) {
printf("Nome do Named Pipe não especificado.\n");
return -1;
}
// Criar uma variável para guradar o nome do pipe
const char* PIPE_NAME = argumentos[1];
// Cria o pipe com nome. Verifica se houve erro na criação do pipe com nome e trata-o se necessário. Se o pipe já existir, não há erro, mas é retornado 0
unlink(PIPE_NAME);
if(mkfifo(PIPE_NAME, 0600) == -1){
printf("Error creating named pipe!\n");
return -1;
}
if (fork() == 0) {
// Abre o named pipe em modo de escrita
if((fd_named_pipe = open(PIPE_NAME, O_WRONLY | O_NONBLOCK)) == -1){
printf("Error opening the named pipe!\n");
exit(0);
}
// Redirecionamento do STDOUT
dup2(fd_named_pipe, STDOUT_FILENO); // Redireciona a saída padrão para o named pipe (STDOUT_FILENO representa o file descriptor da saida padrão)
close(fd_named_pipe); // Fecha o file descriptor do named pipe uma vez que a saída padrão já foi redirecionada para o named pipe.
srand(time(NULL)); // Gera uma seed para os números aleatórios baseada no tempo atual.
// Geração de um número aleatório
for (int numero = 1; numero <= N; numero++) {
int generated_number = rand();
printf("%d", generated_number); // Envia o número gerado pelo pipe named para ser lido pelo processo pai. (Neste caso, como redirecionámos a saída padrão para o pipe, a utilização de printf() é uma maneira eficiente e intuitiva de enviar dados para o pipe)
}
exit(0);
}
// Abre o named pipe em modo de leitura
fd_named_pipe = open(PIPE_NAME, O_RDONLY | O_NONBLOCK); // A flag O_NONBLOCK é usada para evitar o bloqueio quando, por exemplo, read() é chamado e não existem dados disponíveis para leitura (tornando a operação não bloqueante). ATENÇÃO: Isto não invalida a necessidade de utilizar técnicas de polling ou select() para verificar a disponibilidade de dados antes de ler do pipe, dado que estas estratégias são essenciais para mitigar busy-waiting e otimizar o uso de CPU.
if (fd_named_pipe == -1) {
printf("Erro ao abrir o named pipe para leitura!\n");
return -1;
}
int numero_de_execucoes_com_sucesso = 0;
while(1) {
char numero[2]; // Variável para guardar o input do vindo do pipe
if (read(fd_named_pipe, &numero, sizeof(numero) /* ATENÇÃO: Esta implpementação pode dar origem a erros se não for cuidadosamente gerida, pois read() pode ler menos ou mais bytes do que esperado, dependendo do buffer disponível e do estado do pipe */ ) == -1) {
printf("Erro a ler do named pipe!\n");
return -1;
} else {
printf("Número Recebido: %d\n", atoi(numero)); // Converte o input recebido do pipe para o tipo int e imprime-o na consola
numero_de_execucoes_com_sucesso++;
// Termina o loop de leitura após receber N números aleatórios.
if (numero_de_execucoes_com_sucesso == N) {
break;
}
}
}
(...)
}int close(int fd); // Fecha o file descriptor "fd", libertando recursos associados. Retorna 0 em caso de sucesso e -1 em caso de erro.
int unlink(const char *pathname); // Remove o FIFO ou arquivo do sistema de ficheiros. "pathname" é o caminho do FIFO. Retorna 0 em caso de sucesso ou -1 em caso de erro.Exemplo:
int main(int numero_de_argumentos, char *argumentos[]) {
(...)
close(fd_named_pipe); // Fecha o file descriptor do named pipe (também remove-o implicitamente))
unlink(argumentos[1]); // Remove o named pipe explicitamente. Neste caso o unlink não é necessário pois o named pipe é implicitamente removido ao fechar o último file descriptor que o referencia.
return 0;
}#include <sys/msg.h>
#include <sys/ipc.h> // include para IPC_CREAT, IPC_RMID,...int msgget(key_t key, int flags); // Cria uma fila de mensagens não persistente (durante a execução do programa) com chave única "key" e flags de criação "flags" (IPC_CREAT para criar a fila se não existir e as permissões em octal, por exemplo, 0777). Retorna um id usado para criar, remover e/ou aceder à fila de mensagens.Exemplo:
#include <stdio.h> // Importar stdio.h para usar printf e perror
#include <errno.h> // Importar errno.h para usar perror
int main() {
int message_queue_id = msgget(2006, IPC_CREAT | 0777);
if (message_queue_id == -1) /*Verifica se existiu algum erro na criação da fila de mensagens*/ {
perror("message queue creation error!");
exit(1);
}
(...)
}int msgsnd(int msqid, const void* message, size_t length, int flags); // Envia a mensagem "message" para a fila de mensagens com id "msqid". "length" é o tamanho da mensagem a enviar e "flags" são as flags de envio.Exemplo:
#include <string.h> // Include usado para a manipulação e cópia de strings
// Payload da mensagem
typedef struct {
char message[1024];
} payload_t;
// Estrutura da mensagem (estrutura do pacote de envio, contém um valor de prioridade e o conteúdo)
typedef struct {
long PRIORITY;
payload_t PAYLOAD;
} message_t;
int main() {
(...)
pid_t presidente, vice_presidente, secretario, Carlos; // PIDs dos processos filhos
// O processo presidente manda uma mensagem para a fila
if ((presidente = fork()) == 0) {
message_t message_from_president;
payload_t payload_from_president = {"Manda uma nuke tática na lua."};
message_from_president.PRIORITY = 4;
message_from_president.PAYLOAD = payload_from_president;
msgsnd(message_queue_id, &message_from_president, sizeof(message_from_president) - sizeof(long), 0); // Insere a mensagem na fila de mensagens associada ao id message_queue_id. my_message é do tipo message_t e 0 significa que não há flags de entrada de mensagem especial
printf("Mensagem enviada com sucesso!");
exit(0);
}
// O processo vice_presidente manda uma mensagem para a fila
if ((vice_presidente = fork()) == 0) {
message_t message_from_vice_president;
payload_t payload_from_vice_president;
strcpy(payload_from_vice_president.message, "Despede o presidente dos SMTUC.");
message_from_vice_president.PRIORITY = 3;
message_from_vice_president.PAYLOAD = payload_from_vice_president;
msgsnd(message_queue_id, &message_from_vice_president, sizeof(message_from_vice_president) - sizeof(long), 0);
printf("Mensagem enviada com sucesso!");
exit(0);
}
// O processo secretario manda uma mensagem para a fila
if ((secretario = fork()) == 0) {
message_t message_from_secretario;
payload_t payload_from_secretario = {"Já está feito ca**lho!"};
message_from_secretario.PRIORITY = 2;
message_from_secretario.PAYLOAD = payload_from_secretario;
msgsnd(message_queue_id, &message_from_secretario, sizeof(message_from_secretario) - sizeof(long), 0);
printf("Mensagem enviada com sucesso!");
exit(0);
}
// O processo Carlos manda uma mensagem para a fila
if ((Carlos = fork()) == 0) {
message_t message_from_Carlos;
payload_t payload_from_Carlos;
strcpy(payload_from_Carlos.message, "Manda vir um frango.");
message_from_Carlos.PRIORITY = 1;
message_from_Carlos.PAYLOAD = payload_from_Carlos;
msgsnd(message_queue_id, &message_from_Carlos, sizeof(message_from_Carlos) - sizeof(long), 0);
printf("Mensagem enviada com sucesso!");
exit(0);
}
(...)
}int msgrcv(int msqid, void* message, size_t length, long msgtype, int flags); // Lê a mensagem da fila de mensagens com id "msqid" e guarda-a na estrutura "message". "length" é o tamanho da mensagem a ler e "msgtype" é o tipo de mensagem (o long da estrutura de dados da mensagem, message_t) a ler.Exemplo:
#include <unistd.h> // Include para os sleeps
int main() {
(...)
message_t received_message; // Estrutura para guardar a mensagem recebida
// Processo que lê todas as mensagens com prioridade igual a 4 (lê todas as mensagens vindas do presidente)
if (fork() == 0) {
msgrcv(message_queue_id, &received_message, sizeof(received_message) - sizeof(long), 4, 0) {
perror("Error receiving message!");
exit(1);
}
printf("MENSAGEM RECEBIDA DO PRESIDENTE, PRIORIDADE %ld : %s", received_message.PRIORITY, received_message.PAYLOAD.message);
sleep(10);
exit(0);
}
// Processo que lê todas as mensagens com prioridade igual ou inferior a 3 (lê todas as mensagens vindas do vice_presidente, secretario e Carlos)
if (fork() == 0) {
if (msgrcv(message_queue_id, &received_message, sizeof(received_message) - sizeof(long), -3, 0) == -1) {
perror("Error receiving message!");
exit(1);
}
if (received_message.PRIORITY == 3) {
printf("MENSAGEM RECEBIDA DO VICE-PRESIDENTE, PRIORIDADE %ld : %s", received_message.PRIORITY, received_message.PAYLOAD.message);
} else if (received_message.PRIORITY == 2) {
printf("MENSAGEM RECEBIDA DO SECRETARIO, PRIORIDADE %ld : %s", received_message.PRIORITY, received_message.PAYLOAD.message);
} else if (received_message.PRIORITY == 1) {
printf("MENSAGEM RECEBIDA DO CARLOS, PRIORIDADE %ld : %s", received_message.PRIORITY, received_message.PAYLOAD.message);
}
sleep(10);
exit(0);
}
// Processo que lê todas as mensagens
if (fork() == 0) {
if (msgrcv(message_queue_id, &received_message, sizeof(received_message) - sizeof(long), 0, 0) == -1) /* Lê todas as mensagens enviadas para a fila de mensagens */ {
perror("Error receiving message!");
exit(1);
}
printf("MENSAGEM RECEBIDA PRIORIDADE %ld : %s", received_message.PRIORITY, received_message.PAYLOAD.message);
sleep(10);
exit(0);
}
(...)
}int msgctl(int msqid, int cmd, struct msqid_ds* buff); // Remove a fila de mensagens com id "msqid". "cmd" é a operação a realizar (IPC_RMID para remover a fila) e "buff" é um ponteiro para uma estrutura de dados usada para obter ou definir informações sobre a fila de mensagens (normalmente não é utilizado então atribui-se NULL).Exemplo:
int main() {
(...)
msgctl(message_queue_id, IPC_RMID, NULL); // Apaga a fila de mensagens associada ao id message_queue_id
return 0;
}#include <sys/mman.h>void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); // Cria ou entrar num mmap. "addr" é o endereço onde se deseja mapear o ficheiro (NULL para deixar o sistema escolher), "length" é o tamanho do espaço a mapear, "prot" são as permissões (PROT_READ, PROT_WRITE, PROT_EXEC), "flags" são as flags de mapeamento (MAP_SHARED para permitir acesso comum ao mmap), "fd" é o file descriptor do ficheiro a mapear e "offset" é o offset onde se deseja começar a mapear. Retorna o endereço do mmap em caso de sucesso ou MAP_FAILED (-1) em caso de erro.Exemplo:
int munmap(void *addr, size_t length); // Desmapeia a memória partilhada. "addr" é o endereço de início da área de memória a desmapear e "length" é o tamanho da área de memória a desmapear. Retorna 0 em caso de sucesso ou -1 em caso de erro.Exemplo:
| Tema | Operação | Função / Comando | Descrição / Observações | Resumo |
|---|---|---|---|---|
| Processos Filho | Criar | fork() |
Cria um processo filho; retorna 0 no filho e PID no pai. | Permite criar novos processos. |
| Esperar | wait(&wstatus) |
Espera por qualquer filho terminar. | Sincroniza pai com filhos. | |
| Esperar específico | waitpid(pid, &wstatus, options) |
Espera por um filho específico ou conjunto de filhos. | Controle fino sobre filhos específicos. | |
| Remover | exit(0) / SIGTERM |
Child process termina com exit ou recebe SIGTERM. | Finaliza processo filho. | |
| Threads | Criar | pthread_create(&thread, attr, start_routine, arg) |
Cria uma nova thread. | Execução concorrente dentro do mesmo processo. |
| Esperar | pthread_join(thread, NULL) |
Espera que a thread termine. | Sincroniza threads. | |
| Remover | - | Thread termina quando a função retorna ou processo termina. | Finaliza thread automaticamente. | |
| Mutex | Bloquear | pthread_mutex_lock(&mutex) |
Bloqueia o mutex. | Evita condições de corrida. |
| Desbloquear | pthread_mutex_unlock(&mutex) |
Desbloqueia o mutex. | Libera recurso protegido. | |
| Trylock | pthread_mutex_trylock(&mutex) |
Tenta bloquear o mutex sem esperar. | Bloqueio não bloqueante. | |
| Criar estático | pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER |
Cria e inicializa mutex de forma estática. | Inicialização simples. | |
| Criar dinâmico | pthread_mutex_init(&mutex, NULL) |
Inicializa mutex com atributos padrão. | Inicialização dinâmica. | |
| Remover | pthread_mutex_destroy(&mutex) |
Destroi o mutex. | Libera recursos do mutex. | |
| Mutex com Condição | Esperar | pthread_cond_wait(&cond, &mutex) |
Espera por sinalização desbloqueando o mutex. | Sincroniza threads com condição. |
| Sinalizar 1 thread | pthread_cond_signal(&cond) |
Desbloqueia uma thread à espera. | Notifica uma thread. | |
| Sinalizar todas | pthread_cond_broadcast(&cond) |
Desbloqueia todas as threads à espera. | Notifica todas as threads. | |
| Criar estático | pthread_cond_t cond = PTHREAD_COND_INITIALIZER |
Inicializa condição de forma estática. | Inicialização simples. | |
| Criar dinâmico | pthread_cond_init(&cond, NULL) |
Inicializa condição com atributos padrão. | Inicialização dinâmica. | |
| Remover | pthread_cond_destroy(&cond) |
Destroi a condição. | Libera recursos da condição. | |
| Semáforos POSIX Não Nomeados | Criar | sem_init(&sem, pshared, value) |
Inicializa semáforo não nomeado. | Controle de acesso a recursos compartilhados. |
| Bloquear | sem_wait(&sem) |
Decrementa semáforo, espera se 0. | Bloqueia até recurso disponível. | |
| Incrementar | sem_post(&sem) |
Incrementa semáforo. | Libera recurso. | |
| Trywait | sem_trywait(&sem) |
Tenta decrementar semáforo sem bloquear. | Bloqueio condicional. | |
| Valor | sem_getvalue(&sem, &sval) |
Obtém valor atual do semáforo. | Consulta estado do semáforo. | |
| Temporizado | sem_timedwait(&sem, ×pec) |
Decrementa com timeout. | Bloqueio com limite de tempo. | |
| Remover | sem_destroy(&sem) |
Destroi semáforo. | Libera recursos do semáforo. | |
| Semáforos POSIX Nomeados | Criar | sem_open(name, O_CREAT, mode, value) |
Cria ou abre semáforo nomeado. | Semáforo acessível entre processos. |
| Bloquear | sem_wait(sem) |
Decrementa semáforo. | Bloqueio de recurso entre processos. | |
| Incrementar | sem_post(sem) |
Incrementa semáforo. | Libera recurso. | |
| Sair | sem_close(sem) |
Fecha semáforo nomeado. | Desconecta processo do semáforo. | |
| Apagar | sem_unlink(name) |
Remove semáforo nomeado do sistema. | Remove semáforo do sistema. | |
| Memória Partilhada | Criar | shmget(key, size, IPC_CREAT|mode) |
Cria ou obtém segmento de memória partilhada. | Permite comunicação entre processos. |
| Attach | shmat(shmid, NULL, 0) |
Anexa segmento ao processo. | Acesso à memória compartilhada. | |
| Detach | shmdt(shmaddr) |
Sai do segmento de memória. | Desconecta processo. | |
| Apagar | shmctl(shmid, IPC_RMID, NULL) |
Remove segmento de memória partilhada. | Limpa recurso do sistema. | |
| Sinais | Criar handler | signal(signum, handler) |
Associa função a sinal. | Tratar eventos assíncronos. |
| Criar handler avançado | sigaction(signum, &act, NULL) |
Handler com mais controle. | Mais flexibilidade no tratamento de sinais. | |
| Bloquear sinais | sigprocmask(SIG_BLOCK, &set, NULL) |
Bloqueia sinais no set. | Evita interrupções indesejadas. | |
| Desbloquear sinais | sigprocmask(SIG_UNBLOCK, &set, NULL) |
Desbloqueia sinais no set. | Reativa sinais bloqueados. | |
| Enviar a processo | kill(pid, sig) |
Envia sinal a processo. | Comunicação de sinais entre processos. | |
| Enviar a thread | pthread_kill(thread, sig) |
Envia sinal a thread. | Comunicação de sinais entre threads. | |
| Pipes sem Nome | Criar | pipe(fd_array) |
Cria pipe anónimo com dois FDs. | Comunicação unidirecional entre processos. |
| Ler | read(fd, buf, count) |
Lê do pipe. | Receber dados. | |
| Escrever | write(fd, buf, count) |
Escreve no pipe. | Enviar dados. | |
| Selecionar | select(nfds, &readfds, ...) |
Monitora FDs para leitura/escrita. | Multiplexação de I/O. | |
| Duplicar FD | dup2(oldfd, newfd) |
Duplica FD. | Redirecionamento de entrada/saída. | |
| Fechar | close(fd) |
Fecha FD. | Libera recurso do sistema. | |
| Pipes Nomeados | Criar | mkfifo(path, mode) |
Cria named pipe (FIFO). | Comunicação persistente entre processos. |
| Abrir | open(path, flags) |
Abre FIFO para leitura/escrita. | Conecta ao FIFO. | |
| Ler | read(fd, buf, count) |
Lê do FIFO. | Recebe dados. | |
| Escrever | write(fd, buf, count) |
Escreve no FIFO. | Envia dados. | |
| Fechar | close(fd) |
Fecha FIFO. | Libera recurso. | |
| Filas de Mensagens | Criar | msgget(key, IPC_CREAT|mode) |
Cria ou abre fila de mensagens. | Comunicação estruturada entre processos. |
| Enviar | msgsnd(msqid, &msg, size, flags) |
Envia mensagem. | Enviar dados de forma organizada. | |
| Ler | msgrcv(msqid, &msg, size, type, flags) |
Recebe mensagem. | Ler mensagem de fila. | |
| Remover | msgctl(msqid, IPC_RMID, NULL) |
Remove fila de mensagens. | Limpa recurso do sistema. | |
| Ficheiros Mapeados | Criar / Attach | mmap(...) |
Mapeia ficheiro em memória. | Permite acessar ficheiro como memória. |
| Remover / Detach | munmap(addr, length) |
Desfaz mapeamento. | Libera memória mapeada. |