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
  • 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 | B pipes stdout of A to stdin of B. 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;
}