Shared Memory
- Process P1 creates a shared memory region M:
shmget(key, size, flags); shmget(IPC_PRIVATE, 50, IPC_CREAT | 0600); - P1 and P2 attach M to their own memory space:
shmat(shmid, shmaddr, flags); shm = (int*) shmat(shmid, NULL, 0); - They can now communicate using M, reading and writing to M
- After we are done, both detach M
shmdt( (char*) shm); - P1 delets M
shmctl(shmid, IPC_RMID, 0); - Advantages: Efficient, easy to use
- Disadvantages: Synchronisation, difficult implementation
Message Passing
- P1 prepares message M and sends it to P2
- Send syscall copies message from P1 to kernel memory
- When P2 is ready to receive, OS copies message from kernel memory to P2
- Message sending and receiving are usually syscalls
- Naming Schemes
- Direct Communication: sender/receiver explicitly names the other party
- E.g. unix domain socket
- One link per pair of communicating processes, identity of other party is neended
- Indirect Communication: messages are sent/received from message storage (mailbox or port)
- E.g. unix message queue
- One mailbox can be shared among multiple processes
- Direct Communication: sender/receiver explicitly names the other party
- Synchronisation Behaviours
- Blocking Primitives (synchronous): Receiver is blocked until message arrives
- Non-blocking Primitives (asynchronous): Receiver receives message if available, or finds out that message is not ready yet
- Advantages
- Portable, can be easily implemented on different processing environments, such as distributed system and wide area network
- Easier synchronisation, sync primitives implicitly sync sender and receiver
- Disadvantages
- Requires OS intervention
- Extra copying of data
Unix Pipes
- Shell pipe
A | Bpipes stdout ofAto stdin ofB. Underlying mechanism of this is unix pipe - Unix pipe has two “ends”, read and write end
#define READ_END 0
#define WRITE_END 1
int main()
{
int pipeFd[2], pid, len;
char buf[100], *str = "Hi!";
pipe( pipeFd );
if ((pid = fork()) > 0) { /* parent */
close(pipeFd[READ_END]);
write(pipeFd[WRITE_END], str, strlen(str)+1);
close(pipeFd[WRITE_END]);
} else { /* child */
close(pipeFd[WRITE_END]);
len = read(pipeFd[READ_END], buf, sizeof(buf));
printf("%d read: %s\n", pid, buf);
close(pipeFd[READ_END]);
}
}Can be used with dup to redirect stuff
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main() {
int pipefd[2];
pid_t pid;
if (pipe(pipefd) == -1) // 1. Create pipe
return 1;
pid = fork(); // 2. Fork process
if (pid == -1) return 1;
if (pid == 0) { // CHILD
close(pipefd[0]); // Close read end
// Redirect stdout (1) to pipe's write end
dup2(pipefd[1], STDOUT_FILENO);
close(pipefd[1]); // Close original write desc
// Execute 'ls' (output goes into the pipe)
execlp("ls", "ls", (char *)NULL);
} else { // PARENT
close(pipefd[1]); // Close write end
// Redirect stdin (0) to pipe's read end
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[0]); // Close original read desc
// Execute 'wc -l' (input comes from the pipe)
execlp("wc", "wc", "-l", (char *)NULL);
}
return 0;
}Unix Signals
- An asynchronous notification regarding an event sent to a process/thread
- Recipient must handle signal with: default set of handlers or user supplied signal (only for some signals)
- E.g. kill, interrupt, stop, continue, memory error, arithmetic error etc.
void handler(int signo) {
if (signo == SIGSEGV) // do something
}
int main() {
if (signal(SIGSEGV, handler) == SIG_ERR) return 0;
}