Inter-Process Communication: Intro + Pipes

Slides:



Advertisements
Similar presentations
More on Processes Chapter 3. Process image _the physical representation of a process in the OS _an address space consisting of code, data and stack segments.
Advertisements

©2009 Operačné systémy Procesy. 3.2 ©2009 Operačné systémy Process in Memory.
Introduction to Operating Systems Inter-process Communication
1 CMSC421: Principles of Operating Systems Nilanjan Banerjee Principles of Operating Systems Assistant Professor, University of Maryland Baltimore County.
UNIX Process Control Bach 7 Operating Systems Course Hebrew University Spring 2007.
Inter Process Communication:  It is an essential aspect of process management. By allowing processes to communicate with each other: 1.We can synchronize.
CS Lecture 16 Outline Inter-process Communication (IPC) – Pipes – Signals Lecture 161CS Operating Systems 1.
Inter Process Communication. Introduction Traditionally describe mechanism for message passing between different processes that are running on some operating.
Inter-Process Communication Mechanisms CSE331 Operating Systems Design.
Operating Systems Yasir Kiani. 22-Sep Agenda for Today Review of previous lecture Process management commands: bg, fg, ^Z, jobs, ^C, kill Thread.
Server Sockets: A server socket listens on a given port Many different clients may be connecting to that port Ideally, you would like a separate file descriptor.
Lecture 3 Process Concepts. What is a Process? A process is the dynamic execution context of an executing program. Several processes may run concurrently,
Shell (Part 2). Example r What if we want to support something like this: m ps –le | sort r One process should execute ps –le and another should execute.
CS252: Systems Programming Ninghui Li Based on Slides by Prof. Gustavo Rodriguez-Rivera Topic 8: Opening Files and Starting Processes.
The Socket Interface Chapter 21. Application Program Interface (API) Interface used between application programs and TCP/IP protocols Interface used between.
CS162B: Pipes Jacob T. Chan. Pipes  These allow output of one process to be the input of another process  One of the oldest and most basic forms of.
Florida State UniversityCOP5570 – Advanced Unix Programming Today’s topics System V Interprocess communication (IPC) mechanisms –Message Queues –Semaphores.
Slide 10-1 Copyright © 2004 Pearson Education, Inc. Operating Systems: A Modern Perspective, Chapter 10.
1 Shared Memory. 2  Introduction  Creating a Shared Memory Segment  Shared Memory Control  Shared Memory Operations  Using a File as Shared Memory.
System calls for Process management
CE Operating Systems Lecture 13 Linux/Unix interprocess communication.
IPC Programming. Process Model Processes can be organized into a parent-child hierarchy. Consider the following example code: /* */
Washington WASHINGTON UNIVERSITY IN ST LOUIS Core Inter-Process Communication Mechanisms (Historically Important) Fred Kuhns
1 Signals (continued) CS 241 April 9, 2012 University of Illinois.
UNIX Signals * POSIX-Defined Signals * Signaling Processes * Signal Mask * sigaction * kill and sigaction * alarm * Interval Timers * POSIX.1b Timers *
Signals & Message queue Inter process mechanism in Linux system 3/24/
S -1 Processes. S -2 wait and waitpid (11.2) Recall from a previous slide: pid_t wait( int *status ) wait() can: (a) block; (b) return with status; (c)
System calls for Process management Process creation, termination, waiting.
Dsh: A Devil Shell COMPSCI210 Recitation 14 Sep 2012 Vamsi Thummala.
Copyright ©: Nahrstedt, Angrave, Abdelzaher, Caccamo1 Signals.
Introduction to Operating Systems Inter-process Communication Most of the following slides are adapted from slides that were downloaded from the Web.
G.Jyostna.
Module 12: I/O Systems I/O hardware Application I/O Interface
Processes and threads.
Chapter 3: Process Concept
Prof. Leonardo Mostarda University of Camerino
Chapter 3 – Process Concepts
Unix Process Management
Chapter 4: Processes Process Concept Process Scheduling
Processes Overview: Process Concept Process Scheduling
Chapter 3: Process Concept
Exceptional Control Flow
CS 3733 Operating Systems Topics: IPC and Unix Special Files
Exceptional Control Flow
Threads and Cooperation
Exceptional Control Flow Part II
Structure of Processes
Exceptional Control Flow
Chapter 4: Processes Process Concept Process Scheduling
תרגול 8 – ק/פ ותקשורת תהליכים ב-Linux
Exceptional Control Flow
Operating System Concepts
13: I/O Systems I/O hardwared Application I/O Interface
CS703 - Advanced Operating Systems
Operating Systems Lecture 12.
IPC Prof. Ikjun Yeom TA – Hoyoun
Processes Prof. Ikjun Yeom TA – Mugyo
Exceptional Control Flow
Inter-Process Communication ENCE 360
Chapter 13: I/O Systems I/O Hardware Application I/O Interface
CSE 451: Operating Systems Winter 2003 Lecture 4 Processes
Chapter 2 Processes and Threads 2.1 Processes 2.2 Threads
Process Description and Control in Unix
Signals.
Chapter 13: I/O Systems I/O Hardware Application I/O Interface
Exceptional Control Flow
Exceptions and networking
Module 12: I/O Systems I/O hardwared Application I/O Interface
Exceptional Control Flow
Presentation transcript:

Inter-Process Communication: Intro + Pipes

Communication between processes Processes live in different “worlds”; memory address spaces due to virtual memory. Communication between processes is needed e.g. killing a process from a shell command e.g. sending data between processes Process A’s address space Process B’s address space

Inter-process Communication (IPC) Cooperating processes or threads need inter-process communication (IPC) Data transfer Sharing data Event notification Resource sharing Process control Processes may be running on one or more computers connected by a network. The method of IPC used may vary based on the bandwidth and latency of communication between the threads, and the type of data being communicated. IPC may also be referred to as inter-application communication.

IPC mechanisms IPC mechanisms Signals Pipes Unnamed Pipes Named Pipes of FIFOs Message Queues Shared Memory Memory Mapped Files Semaphores (later) Remote processes, over network: Remote Procedure Calls (RPC) Sockets (in network course)

IPC – (Unnamed) Pipes A byte-stream among processes. Bytes written by a process is readable by another process in the other end. Applications:  in shell passing output of one program to another program e.g. cat file1 file2 | sort Limitations: Processes need to be relatives (parent-child or siblings) cannot be used for broadcasting; Data in pipe is a byte stream – has no structure No way to distinguish between several readers or write Implementation: Internal kernel buffers, socket buffers or STREAM interface.

Pipes Pipe is a data structure in the kernel. 18/02/08 Pipes Pipe is a data structure in the kernel.  Data is stored in kernel buffers temporarily. No random access (seek) on pipes.  A pipe is created by using the pipe system call int pipe(int* fd); fd is an array of size 2  Two file descriptors are returned fd[0] is open for reading fd[1] is open for writing Some systems implement bidirectional pipes, both ends are readable/writable.  Typical buffer size is 512 bytes (Minimum limit defined by POSIX). Reads and writes may be blocked by the buffer. 

Pipes Typical use: A pipe provides a one-way flow of data 18/02/08 Pipes Typical use: Pipe created by a process Process calls fork() File descriptors are inherited by child, so pipe is shared with the child. Pipe used as a communication medium between parent and child A pipe provides a one-way flow of data example: who | sort| lpr output of who is input to sort output of sort is input to lpr

Pipe example #include <unistd.h> #include <stdio.h> 18/02/08 Pipe example #include <unistd.h> #include <stdio.h> int main(void){   int n;                // to keep track of num bytes read int fd[2]; // to hold fds of both ends of pipe pid_t pid; // pid of child process char line[80]; // buffer to hold text read/written if (pipe(fd) < 0)                    // create the pipe         perror("pipe error"); if ((pid = fork()) < 0) { // fork off a child         perror("fork error"); } else if (pid > 0) { // parent process close(fd[0]); // close read end         write(fd[1], "hello world\n", 12); // write to  it  }else { // child process close(fd[1]); // close write end         n = read(fd[0], line, 80);     // read from  pipe         write(1, line, n);              // echo  to  screen } exit(0);

as a result of pipe() call After the fork() call Descriptor table For parent stdin fd 0 stdout fd 1 stderr fd 2 fd 3 fd 4 filedes[2] gets {3, 4} as a result of pipe() call

After the fork() call Descriptor table For parent Descriptor table For child stdin fd 0 fd 0 stdin stdout fd 1 fd 1 stdout stderr fd 2 fd 2 stderr fd 3 fd 3 fd 4 fd 4

After the close() calls Descriptor table For parent Descriptor table For child stdin fd 0 fd 0 stdin stdout fd 1 fd 1 stdout stderr fd 2 fd 2 stderr fd 3 X fd 3 fd 4 X fd 4 This pipe allows parent to send data to the child. If two way communication is needed, then the parent needs to create two pipes before fork() and use the second pipe as a second channel. Systems with bidirectional pipes can do it in single pipe. The other end does not get EOF if at least one process is keeping one end open. Always close the end that process will not use!

IPC – Unnamed Pipes Unnamed pipes in shell are used with redirection cat myfile | grep key | sort | lpr Shell setups stdout of a child to pipe write, stdin of next one as pipe read   int fd[2], c;            // to hold fds of both ends of pipe   if (pipe(fd) < 0)  perror("pipe error"); // create the pipe   if (fork()) { if (fork()) {           // parent process          close(fd[0]); close(fd[1]);     // not going to use it         wait(&c); wait(&c);             // wait for both      } else {                       // pipe reader child         close(fd[1]);               // not using write end         dup2(fd[0],0);             // redirect stdin to read end         close(fd[0]);               // it is duplicated, close         execl("/usr/bin/wc","wc","-l",NULL); // run binary      }   } else {         close(fd[0]);           // not using read end          dup2(fd[1],1);         // redirect stdout to write end          close(fd[1]);           // it is duplicated, close          execl("/bin/cat","cat","/etc/passwd",NULL);// run binary    }

IPC - Named Pipes or FIFOs Pipes are restricted to processes of same family (parent/child, siblings). Relies on file descriptor inheritance. FIFOs are pipes named as a path on the file system. They can be accessed by any process that “knows the name” Pipes are temporary. They disappear when last process closes. FIFOs or named pipes, are special files that persist even after all processes have closed them A FIFO has a name and permissions just like an ordinary file and appears in a directory listing Any process with the appropriate permissions can access a FIFO A user creates a FIFO by executing the mkfifo command from a command shell or by calling the mkfifo() system call within a program

FIFO Creation in shell FIFO are created using the mknod or the mkfifo commands mkfifo name mkfifo –m mode name mknod name p Make sure you remove (rm) your pipes after use! >man mknod mknod - make block or character special files mknod [OPTION]... NAME TYPE [MAJOR MINOR] …. Both MAJOR and MINOR must be specified when TYPE is b, c, or u, and they must be omitted when TYPE is p. …..... p create a FIFO >man mkfifo mkfifo -- make fifos mkfifo [-m mode] fifo_name ... mkfifo creates the fifos requested, in the order specified. By default, the resulting fifos have mode 0666 (rw-rw-rw-), limited by the current umask(2).

Using Named Pipes First, create your pipes mkfifo pipe1 mkfifo pipe2 mkfifo pipe3 Then, attach a data source to your pipes ls -l >> pipe1 & cat myfile >> pipe2 & who >> pipe3 & Then, read from the pipes with your reader process cat < pipe1 | lpr spell < pipe2 sort < pipe3 Finally, delete your pipes rm pipe[1-3] Note: >> to append to pipe & for background execution

IPC – FIFO – mkfifo() system call int mkfifo(const char *path, mode_t mode); The mkfifo() function creates a new FIFO special file corresponding to the path name specified in the path parameter The mode parameter specifies the permissions for the newly created FIFO If successful, the function returns zero; otherwise, it returns –1 and sets errno

Inter-Process Communication: Signals Slides adapted from: Randy Bryant of Carnegie Mellon University

Signals A signal is a small message that notifies a process that an event of some type has occurred in the system Akin to exceptions and interrupts.  Sometimes called as software interrupts.  Interrupts: Hardware to OS, Signals: OS to processes Signal type is identified by small integer ID’s (1-30) Only information in a signal is its ID and the fact that it arrived Most of them causes termination but process can block, define a "handler" or ignore them (except SIGKILL and SIGSTOP).   

Signals ID Name Default Action Corresponding Event Thumbnail. 1 SIGHUP Terminate Terminal line close 2 SIGINT User typed ctrl-c  3 SIGQUIT Ctrl+ \ 4 SIGILL Illegal instruction on CPU 8 SIGFPE Floating point exception 9 SIGKILL Kill program (cannot override or ignore) 11 SIGSEGV Terminate  Segmentation violation 13 SIGPIPE Write on a closed pipe 14 SIGALRM User timer 15 SIGTERM Terminate process (can be overwritten) 17 SIGCHLD Ignore Child stopped or terminated 19 SIGSTOP Suspend Suspend process execution 18 SIGCONT Continue Continue suspended proces 10 12 SIGUSR1 SIGUSR2 User defined  Thumbnail. Thumbnail.

Signal Concepts: Sending a Signal Kernel sends (delivers) a signal to a destination process by updating some state in the context of the destination process Signals can be initiated by: Hardware event, interrupt: SIGFPE, SIGSEGV, SIGILL.  OS event: SIGPIPE, SIGHUP, SIGCHLD, SIGALRM, User input Process request: kill() system call PCB stores signal delivery status and setup for a process. Each is a bitmap, a bit per signal: Pending: Signal is sent to the process, waiting to be delivered Block:  Delivery of signal is to be blocked by the process

Signal Concepts: Sending a Signal Process B User level Process A Process C kernel Pending for A Blocked for A Pending for B Blocked for B Pending for C Blocked for C

Signal Concepts: Sending a Signal Process B User level Process A Process C kill(C,signal) kernel Pending for A Blocked for A Pending for B Blocked for B Pending for C Blocked for C

Signal Concepts: Sending a Signal Process B User level Process A Process C kernel Pending for A Blocked for A Pending for B Blocked for B Pending for C 1 Blocked for C

Signal Concepts: Sending a Signal Process B User level Process A Process C kernel Received by C Pending for A Blocked for A Pending for B Blocked for B Pending for C 1 Blocked for C

Signal Concepts: Sending a Signal Process B User level Process A Process C kernel Pending for A Blocked for A Pending for B Blocked for B Pending for C Blocked for C

Signal Concepts: Receiving a Signal A destination process receives a signal when it is forced by the kernel to react in some way to the delivery of the signal Some possible ways to react: Ignore the signal (do nothing) Terminate the process (with optional core dump) Catch the signal by executing a user-level function called signal handler Akin to a hardware exception handler being called in response to an asynchronous interrupt: (1) Signal received by process (2) Control passes to signal handler Icurr Inext (3) Signal handler runs (4) Signal handler returns to next instruction

Signal Concepts: Pending and Blocked Signals A signal is pending if sent but not yet received There can be at most one pending signal of any particular type Important: Signals are not queued If a process has a pending signal of type k, then subsequent signals of type k that are sent to that process are discarded A process can block the receipt of certain signals Blocked signals can be delivered, but will not be received until the signal is unblocked A pending signal is received at most once

Signal Concepts: Pending/Blocked Bits Kernel maintains pending and blocked bit vectors in the context of each process pending: represents the set of pending signals Kernel sets bit k in pending when a signal of type k is delivered Kernel clears bit k in pending when a signal of type k is received  blocked: represents the set of blocked signals Can be set and cleared by using the sigprocmask function Also referred to as the signal mask.

Signal Concepts: Sending a Signal Process B User level Process A Process C Sends to C kernel Pending for A Blocked for A Pending for B Blocked for B Pending for C 1 Blocked for C

Sending Signals: Process Groups Every process belongs to exactly one process group Shell pid=10 pgid=10 Fore- ground job Back- ground job #1 Back- ground job #2 pid=20 pgid=20 pid=32 pgid=32 pid=40 pgid=40 Background process group 32 Background process group 40 Child Child getpgrp() Return process group of current process setpgid() Change process group of a process (see text for details) pid=21 pgid=20 pid=22 pgid=20 Foreground process group 20

Sending Signals with /bin/kill Program /bin/kill program sends arbitrary signal to a process or process group Examples /bin/kill –9 24818 Send SIGKILL to process 24818 /bin/kill –9 –24817 Send SIGKILL to every process in process group 24817 linux> ./forks 16 Child1: pid=24818 pgrp=24817 Child2: pid=24819 pgrp=24817 linux> ps PID TTY TIME CMD 24788 pts/2 00:00:00 tcsh 24818 pts/2 00:00:02 forks 24819 pts/2 00:00:02 forks 24820 pts/2 00:00:00 ps linux> /bin/kill -9 -24817 24823 pts/2 00:00:00 ps linux> ./delay 100 & ps kill -9 XXX

Sending Signals from the Keyboard Typing ctrl-c (ctrl-z) causes the kernel to send a SIGINT (SIGTSTP) to every job in the foreground process group. SIGINT – default action is to terminate each process SIGTSTP – default action is to stop (suspend) each process Shell pid=10 pgid=10 Fore- ground job Back- ground job #1 Back- ground job #2 pid=20 pgid=20 pid=32 pgid=32 pid=40 pgid=40 Background process group 32 Background process group 40 Child Child pid=21 pgid=20 pid=22 pgid=20 Foreground process group 20

Example of ctrl-c and ctrl-z STAT (process state) Legend: First letter: S: sleeping T: stopped R: running Second letter: s: session leader +: foreground proc group See “man ps” for more details bluefish> ./forks 17 Child: pid=28108 pgrp=28107 Parent: pid=28107 pgrp=28107 <types ctrl-z> Suspended bluefish> ps w PID TTY STAT TIME COMMAND 27699 pts/8 Ss 0:00 -tcsh 28107 pts/8 T 0:01 ./forks 17 28108 pts/8 T 0:01 ./forks 17 28109 pts/8 R+ 0:00 ps w bluefish> fg ./forks 17 <types ctrl-c> 28110 pts/8 R+ 0:00 ps w Can also use kill command: ./forks 17 & kill (parent) (Only kills parent) kill (child) (Child becomes a zombie)

Sending Signals with kill Function void fork12() { pid_t pid[N]; int i; int child_status; for (i = 0; i < N; i++) if ((pid[i] = fork()) == 0) { /* Child: Infinite Loop */ while(1) ; } for (i = 0; i < N; i++) { printf("Killing process %d\n", pid[i]); kill(pid[i], SIGINT); pid_t wpid = wait(&child_status); if (WIFEXITED(child_status)) printf("Child %d terminated with exit status %d\n", wpid, WEXITSTATUS(child_status)); else printf("Child %d terminated abnormally\n", wpid); Interesting to use interpositioning code from previous lecture setenv LD_PRELOAD ../13-ecf-procs/myfork.so setenv CHILD ./forks 12 forks.c

Receiving Signals Suppose kernel is returning from an exception handler and is ready to pass control to process p Process A Process B user code kernel code context switch Time user code kernel code context switch user code

Receiving Signals Suppose kernel is returning from an exception handler and is ready to pass control to process p Kernel computes pnb = pending & ~blocked The set of pending nonblocked signals for process p If (pnb == 0) Pass control to next instruction in the logical flow for p Else Choose least nonzero bit k in pnb and force process p to receive signal k The receipt of the signal triggers some action by p Repeat for all nonzero k in pnb Pass control to next instruction in logical flow for p

Default Actions Each signal type has a predefined default action, which is one of: The process terminates The process stops until restarted by a SIGCONT signal The process ignores the signal

Installing Signal Handlers The signal function modifies the default action associated with the receipt of signal signum: handler_t *signal(int signum, handler_t *handler) Different values for handler: SIG_IGN: ignore signals of type signum SIG_DFL: revert to the default action on receipt of signals of type signum Otherwise, handler is the address of a user-level signal handler Called when process receives signal of type signum Referred to as “installing” the handler Executing handler is called “catching” or “handling” the signal When the handler executes its return statement, control passes back to instruction in the control flow of the process that was interrupted by receipt of the signal

Signal Handling Example void sigint_handler(int sig) /* SIGINT handler */ { printf("So you think you can stop the bomb with ctrl-c, do you?\n"); sleep(2); printf("Well..."); fflush(stdout); sleep(1); printf("OK. :-)\n"); exit(0); } int main(int argc, char** argv) /* Install the SIGINT handler */ if (signal(SIGINT, sigint_handler) == SIG_ERR) unix_error("signal error"); /* Wait for the receipt of a signal */ pause(); return 0; Try running: ./sigint ctrl-C Code not entirely reliable, if there’s a delay in pause sigint.c

Signal Handler Usually works in same stack. Kernel pushes handler activation on stack. User mode Kernel mode User mode locals param IPcur Process stack is modified as a call to handler function SP and IP registers are modified SP Handler Returns SP SP IP: IPcur IP: handleraddr IP: IPcur Signal raised Return

Nested Signal Handlers Handlers can be interrupted by other handlers Main program Handler S Handler T (2) Control passes to handler S (1) Program catches signal s Icurr (4) Control passes to handler T (3) Program catches signal t Inext (7) Main program resumes (5) Handler T returns to handler S (6) Handler S returns to main program

Blocking and Unblocking Signals Implicit blocking mechanism Kernel blocks any pending signals of type currently being handled. E.g., A SIGINT handler can’t be interrupted by another SIGINT Explicit blocking and unblocking mechanism sigprocmask function Supporting functions sigemptyset – Create empty set sigfillset – Add every signal number to set sigaddset – Add signal number to set sigdelset – Delete signal number from set

Safe Signal Handling Handlers are tricky because they are concurrent with main program and share the same global data structures. Shared data structures can become corrupted. Pending signals are not queued For each signal type, one bit indicates whether or not signal is pending… …thus at most one pending signal of any particular type. You can’t use signals to count events, such as children terminating. Waiting for Signals int sigsuspend(const sigset_t *mask)

Inter-Process Communication: Shared memory

IPC- Shared Memory Allows multiple processes to share virtual memory space. Fastest but not necessarily the easiest (synchronization-wise) way for processes to communicate with one another. Process A Process B Shared memory region 0x30000 0x50000 0x50000 0x70000

IPC- Shared Memory One process creates or allocates the shared memory segment. size and access permissions set at creation. The process then attaches the shared segment, causing it to be mapped into its current data space. If needed, the creating process then initializes the shared memory. Once created, and if permissions permit, other processes can gain access to the shared memory segment and map it into their data space. Each process accesses the shared memory relative to its attachment address. For each process involved, the mapped memory appears to be no different from any other of its memory addresses.

POSIX Shared Memory Process A Process B Create shared memory segment segment id = shmget(key, size, IPC_CREAT); Attach shared memory to its address space addr= (char *) shmat(id, NULL, 0); write to the shared memory *addr = 1; Detach shared memory shmdt(addr); Process B Use existing segment (same key, no IPC_CREAT) segment id = shmget(key, size, 0666); addr = (char *) shmat(id, NULL, 0); c = *addr;

Example: Producer-Consumer Problem Producer process produces information that is consumed by a consumer process e.g. print utility places data and printer fetches data to print.

Server code for producer main() { char c; int shmid; key_t key=5678; char *shm, *s; /* Create the segment. */ if ((shmid = shmget(key, 27, IPC_CREAT | 0666)) < 0) { printf("server: shmget error\n"); exit(1); } /* Attach the segment to our data space. */ if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) { printf("server: shmat error\n"); /* Output data*/ s = shm; for (c = 'a'; c <= 'z'; c++) *s++ = c; /* Wait the client consumer to respond*/ while (*shm != '*') sleep(1); shmdt(shm); exit(0);

Client code for consumer main(){ int shmid; key_t key=5678; char *shm, *s; /* Locate the segment. */ if ((shmid = shmget(key, SHMSZ, 0666)) < 0) { printf("client: shmget error\n"); exit(1); } /* attach the segment to our data space.*/ if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) { printf("client: shmat error\n"); exit(1); /* Read what the server put in the memory, and display them*/ for (s = shm; *s != ‘z’; s++) putchar(*s); putchar('\n'); /* Finally, change the first character of the segment to '*‘ */ *shm = '*'; exit(0);

Shared memory example: Currency Exchange enum currency {DOLLAR , EURO, STERLIN, POUND}; struct Currency {double sell, buy; double stock;}; int buy(struct Currency *c, double amount, double *balance) { if (*balance < amount*c->buy) return -1; *balance -= amount*c->buy; c->stock += amount; return 0; } int sell(struct Currency *c, double amount, double *balance) { if (c->stock < amount) return -1; *balance += amount*c->sell; c->stock -= amount;

Shared memory example: Currency Exchange A shared memory segment keeps currency values sell and buy, and current stock. Processes attach it and make exchange operations based on user input Shared Mem Process DOLLAR Sell 3.72 Buy 3.71 Stock 10000 EURO ….. …. … Buy/Sell Buy/Sell Process Buy/Sell Process

Currency exchange - 1 #include "exchange.h” struct Currency init[4] ={ {3.73, 3.72, 10000}, {3.932, 3.944, 10000}, {4.551,4.552, 10000}, {3.24, 3.25, 5000}}; struct Currency *curshared; int main() { int key, i; // create a shared memory for 4 Currency structures key = shmget(EXCHKEY, sizeof(struct Currency)*4, IPC_CREAT|0600); if (key < 0) { perror("shmget") ; return 1;} // attach it and get result in curshared pointer curshared = (struct Currency *) shmat( key, NULL, 0); for (i = 0; i < 4; i++) curshared[i] = init[i]; shmdt((void *) curshared); return 0; }

Currency exchange - 2 #include<exchange.h> struct Currency *curshared; double balance = 1000; // initial balance int main() { // get key for already created shmem key = shmget(EXCHKEY, sizeof(struct Currency)*4, 0); if (key < 0) { perror("shmget") ; return 1; } // attach shared memory and get address in curshared curshared = (struct Currency *) shmat( key, NULL, 0); if (curshared == NULL) return -1; while (fgets(line, 80, stdin)) { // trade loop // assume input is parsed here if (... “buy” ) buy(curshared+c , amount, &balance); if (… “sell”) sell(curshared+c , amount, &balance); } shmdt((void *) curshared); return 0;

Generating a common key.. key_t ftok(const char *path, int id); The ftok() function shall return a key based on path and id that is usable in subsequent calls to msgget(), semget(), and shmget(). The ftok() function shall return the same key value for all paths that name the same file, when called with the same id value. Only the low-order 8-bits of id are significant. The behavior of ftok() is unspecified if these bits are 0.

IPC - Shared memory Advantages Limitation Alternative good for sharing large amount of data very fast, Limitation no synchronization provided. i.e. wait for data to be available from other process. Integrity of shared variables may be violated. i.e negative stock on a currency. (to be covered in Synchronization chapter) applications must use other synchronization mechanisms. Persistent until reboot. Needs cleanup. Alternative mmap() system call, which maps file into the address space of the caller,

IPC- mmap() using a File as Shared Memory mmap() is used to map a file to a process's virtual memory address space. More flexible than shared memory, file and memory based access work together. Once a mapping has been established, file can be manipulated by updating memory instead of file system calls like open()/read()/write()/lseek()… Unlike shared memory, the contents of a file are nonvolatile and will remain available even after a system has been shut down (and rebooted).

Using a File as Shared Memory void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); start is the address for attachment. mostly set to 0, which directs the system to choose a valid attachment address. length: The number of bytes to be attached. File size should be less than or equal to this. prot: used to set the type of access (protection) for the segment. Flags: MAP_SHARED for a shared mapping. Otherwise isolated. fd: open file descriptor. Once the mapping is established, the file can be closed. offset: set the starting position for the mapping. If successful, returns a reference to the mapped memory object. else returns MAP_FAILED which is actually the value -1 cast to a void *. int munmap(void *start, size_t length); Called automatically when the process quits.

Writing to a file through mmap() #include<exchange.h> struct Currency *curshared; double balance = 1000; // initial balance int main() { fd = open("exchange.dat", O_RDWR | O_CREAT, 0600); if (fd < 0) { perror("open") ; return 1; } // map file as part of memory as a shared segment curshared = (struct Currency *) mmap(NULL, 4*sizeof(struct Currency), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (curshared == 0) { perror("mmap"); return -1;} close(fd); // you can close the file afterwards if (curshared == NULL) return -1; while (fgets(line, 80, stdin)) { // trade loop // assume input is parsed here if (... “buy” ) buy(curshared+c , amount, &balance); if (… “sell”) sell(curshared+c , amount, &balance); } // delete the mapping munmap(curshared, 4*sizeof(struct Currency)); return 0;

Reading from a file through mmap() #define NUMINTS (1000) #define FILESIZE (NUMINTS * sizeof(int)) int main(int argc, char *argv[]) { int i,fd, result; int *map; /* mmapped array of int's */ fd = open("/tmp/j", O_RDONLY); map = mmap(0, FILESIZE, PROT_READ, MAP_SHARED, fd, 0); /* Read the file int-by-int from the mmap */ for (i = 1; i <=NUMINTS; ++i) printf("%d: %d\n", i, map[i]); result = munmap(map, FILESIZE) close(fd); return 0; }

Inter-Process Communication: Message queues, sockets, remote procedure calls

Message sending with named pipes #include <stdio.h> #include <unistd.h> #include <sys/stat.h> #include <fcntl.h>   void child(char *path) {     int fd;     char buf[] = "123456789";     fd = open(path, O_WRONLY);     write(fd, buf, sizeof(buf));     printf("Child send: %s\n", buf);     close(fd); } void parent(char *path)     char buf[512];     fd = open(path, O_RDONLY);     read(fd, buf, sizeof(buf));     printf("Parent receive: %s\n", buf); int main() {     char *path = "/tmp/fifo";     pid_t pid;       setlinebuf(stdout);     unlink(path);     mkfifo(path, 0600);     pid = fork();     if (pid == 0) {         child(path);     } else {         parent(path);     }     return 0; } Parent receive: 123456789 Child send: 123456789

IPC - Message Queues The sending process places via some (OS) message-passing module a message onto a queue which can be read by another process. Each message is given an identification or type so that processes can select the appropriate message. Process must share a common key in order to gain access to the queue in the first place Sender Process Receiver Process msgsnd(0xa01, *msgp, 0) msgrcv(0xa01, *buf, 0, 0) KERNEL Queue 0xa01 msgsnd msgrcv insert remove Messages: explicit length, different types.

IPC - Message Queues Before a process can send or receive a message, the queue must be initialized through the msgget(). Operations to send and receive messages are performed by the msgsnd() and msgrcv() functions, respectively. In non-blocking message passing allow for asynchronous message transfer the process is not suspended as a result of sending or receiving a message. In blocking or synchronous message passing the sending process blocks until the message has been transferred or has even been acknowledged by a receiver.

Message sending #define MSGSZ 128 typedef struct msgbuf { /*msg structure */ long mtype; char mtext[MSGSZ]; } message_buf; main(){ int result, msgid, msgflg = IPC_CREAT | 0666; key_t key; message_buf sbuf; size_t buf_length; key = 1234; msqid = msgget(key, msgflg); sbuf.mtype = 1; /*send a msg of type 1 */ strcpy(sbuf.mtext, "Did you get this?"); buf_length = strlen(sbuf.mtext) + 1 ; /* send msg */ result = msgsnd(msqid, &sbuf, buf_length, IPC_NOWAIT); printf("Message: \"%s\" Sent\n", sbuf.mtext); exit(0); }

Message receiving #define MSGSZ 128 typedef struct msgbuf { /* msg struct */ long mtype; char mtext[MSGSZ]; } message_buf; main(){ int msqid; key_t key; message_buf rbuf; key = 1234; msqid = msgget(key, 0666); /* receive msg */ result = msgrcv(msqid, &rbuf, MSGSZ, 1, 0); /* print msg */ printf("%s\n", rbuf.mtext); exit(0); } The Message queue is opened with msgget (message flag 0666) and the same key as message_send.c. A message of the same type 1 is received from the queue with the message ``Did you get this?'' stored in rbuf.mtext.

Discussion of message queues Msgq are more versatile than pipes and address some of their limitations, Can transmit msgs as structured entities You don’t have to worry about a partial message being sent/received. The transfer is atomic. 3 basic modes in msgrcv() defining type parameter: Any type (FIFO) if type parameter is 0 Specific message type, type parametter > 0 Minimum typed message (like priority), a negative value as absolute value as the upper limit Msg type can be used to specify e.g. priorities, urgency, designate recipient, Kernel does not help with recipient specification, Cannot broadcast msg’s,

IPC - Sockets Sockets provide point-to-point, two-way communication between two processes. A socket is an endpoint of communication to which a name can be bound. It has a type and one or more associated processes. Sockets exist in communication domains. A socket domain is an abstraction that provides an addressing structure and standard interface for communication Socket domains define and implement underlying complex protocol where socket interface is simple and uniform. Some common domains: UNIX, INET, INET6, IPX, NETLINK, X25, AX25, APPLETALK, ATMPVC The UNIX domain provides a socket address space on a single system. UNIX domain sockets are named with UNIX paths. can be used to communicate between processes on a single system. Sockets can also be used to communicate between processes on different systems. The socket address space between connected systems is called the Internet domain. Internet domain communication uses the TCP/IP internet protocol suite.

Socket types Define the communication properties visible to the application. A stream socket (similar to making a phone call) Phone. Delivery in a networked environment is guaranteed. If you send through the stream socket three items "A, B, C", they will arrive in the same order − "A, B, C". These sockets use TCP for data transmission. A datagram socket (similar to sending a mail) Delivery in a networked environment is not guaranteed. They're connectionless because you don't need to have an open connection as in Stream Sockets − you build a packet with the destination information and send it out. They use UDP. Delivery and order of delivery is not guaranteed. A sequential packet socket They are similar to a stream socket, with the exception that record boundaries are preserved. A raw socket provides access to the underlying communication protocols.

Some system calls in the socket interface: More to come in Ceng 445 Computer Networking System Call Description socket(family, type, flags) Create a socket with given family and type bind(socket, sockaddr, addrlen) Bind socket to an address listen(socket, queuelen) Listen requests coming from socket accept(socket, peeraddr, addrlen) Accept next connection on listened socket connect(socket, destaddr, addrlen) Connect to a socket given in address send(socket, buffer, size, flags) Send data to a socket recv(socket, buffer, bufsize, flags) Receive data from socket

Socket creation and naming int socket(int domain, int type, int protocol) Domain: AF_INET | AF_UNIX | … Type: SOCK_STREAM | SOCK_DGRA | … In the UNIX domain, a connection is usually composed of one or two path names. In the Internet domain, a connection is composed of local and remote addresses and local and remote ports.

Server steps Create a socket with the socket() system call. Bind the socket to an address using the bind() system call. For a server socket on the Internet, an address consists of a port number on the host machine. Listen for connections with the listen() system call, which specifies how many connection requests can be queued Accept a connection with the accept() system call. This call typically blocks until a client connects with the server. Send and receive data using the read() and write() system calls. snd() and rcv() system calls.

Client steps Create a socket with the socket() system call. Connect the socket to the address of the server using the connect() system call. Send and receive data. There are a number of ways to do this, but the simplest way is to use the read() and write() system calls.

Socket server - UNIX #define ADDRESS "mysocket" /* addr to connect */ char *strs = "This is the string from the server.\n"; main(){ char c; FILE *fp;int fromlen; register int i, s, ns, len; struct sockaddr_un saun, fsaun; /* create a UNIX domain stream socket */ s = socket(AF_UNIX, SOCK_STREAM, 0)) saun.sun_family = AF_UNIX; strcpy(saun.sun_path, ADDRESS); unlink(ADDRESS); /*unlink (rm) ADDRESS to bind won’t fail */ len = sizeof(saun.sun_family) + strlen(saun.sun_path); result = bind(s, &saun, len);/*bind the address to the socket */ result = listen(s, 5); /* listen on the socket */ ns = accept(s, &fsaun, &fromlen)) /* Accept a connection */ fp = fdopen(ns, "r"); /* open the connection */ /* send the string to the client */ send(ns, strs, strlen(strs), 0); /* read from the server */ while ((c = fgetc(fp)) != EOF) { putchar(c); if (c == '\n’) break; } close(s); exit(0);

Socket client - UNIX #define ADDRESS "mysocket" /* addr to connect */ char *strs = "This is the first string from the client.\n"; main(){ char c; FILE *fp; register int i, s, len; struct sockaddr_un saun; /* create a UNIX domain stream socket */ s = socket(AF_UNIX, SOCK_STREAM, 0); saun.sun_family = AF_UNIX; strcpy(saun.sun_path, ADDRESS); len = sizeof(saun.sun_family) + strlen(saun.sun_path); result= connect(s, &saun, len); fp = fdopen(s, "r"); /* read from the server */ while ((c = fgetc(fp)) != EOF) { putchar(c); if (c == '\n') break; } /* Now we send some strings to the server.*/ send(s, strs, strlen(strs), 0); close(s); exit(0);

IPC Summary Signal: Signal sends an integer to another process. Doesn't mix well with multi-threads. Pipe: Useful only among processes related as parent/child. Unidirectional. FIFO, or named pipe: Two unrelated processes can use FIFO unlike plain pipe. Unidirectional. Shared memory: Do your own concurrency control. Message Queue: OS maintains discrete message. Sockets: Bidirectional. Meant for network communication, but can be used locally too. Can be used for different protocol. There's no message boundary for TCP. Semaphore: A synchronization mechanism for multi processes or threads, similar to a queue of people waiting for bathroom.

Other issues.. Message boundary issue: Performance Byte streams (e.g. Pipes and TCP sockets) the client sends "Hello", "Hello", and "How about an answer?” The server can receive as "Hell", "oHelloHow", and " about an answer?"; or more realistically "HelloHelloHow about an answer?". No clue where the message boundary is. Fix: Limit the message length Performance Pipe I/O: fastest Msg queues: slower Sockets: slowest [at most half as slow in latency and bandwidth] Portability. Can also be used for communication with processes on the same machine.

IPC support provided by OS or other envs.

IPC - Remote Procedure Calls (RPCs) Host A Host B An RPC is analogous to a function call. Caller and process executing the instruction are separate processes, possibly on different computers. Various applications like: A desktop server providing interaction of various components of a desktop A file system server providing file system services over network. A cluster of computers working in parallel for a computation. When an RPC is made, the calling arguments are passed to the remote procedure and the caller waits for a response to be returned from the remote procedure. Mostly implemented by user space libraries and services. Examples: Sun RPC, DBUS, CORBA, DCOM, WCF, JRMI, MPI, XMLRPC, JSONRPC, SOAP,..... Client call int f(int a[]) RPC library packing, marshalling Send request over Network Accept requests RPC library unmarshall, unpack Server native f() call returns val packing, marshalling return value Send result Get result unmarshall, unpack f() returns to caller

Sequence of events during a RPC The client calls the client stub. The call is a local procedure call, with parameters pushed on to the stack in the normal way. The client stub packs the parameters into a message and makes a system call to send the message. Packing the parameters is called marshalling. The client's local operating system sends the message from the client machine to the server machine. The local operating system on the server machine passes the incoming packets to the server stub. The server stub unpacks the parameters from the message . Unpacking the parameters is called unmarshalling. Finally, the server stub calls the server procedure. The reply traces the same steps in the reverse direction.

Differences between using local and remote procedure calls Remote calls can fail because of unpredictable network problems. Also, callers generally must deal with such failures without knowing whether the remote procedure was actually invoked. Idempotent procedures (those that have no additional effects if called more than once) are easily handled, but enough difficulties remain that code to call remote procedures is often confined to carefully written low-level subsystems.

Interface Description Language To let different clients access servers, a number of standardized RPC systems have been created. Most of these use an interface description language (IDL) to let various platforms call the RPC. The IDL files can then be used to generate code to interface between the client and server. The most common tool used for this is RPCGEN.

More info and details available at Programming in C UNIX System Calls and Subroutines using C. A. D. Marshall 1994-2005 http://www.cs.cf.ac.uk/Dave/C/CE.html The Linux Programmer's Guide by Sven Goldt, Sven van der Meer, Scott Burkett, Matt Welsh http://www.tldp.org/LDP/lpg/

Pipes Process A’s address space Process B’s address User Space Kernel

Signals Process A’s address space Process B’s address User Space Kernel Space 1 2 3 4 5 6 7 8

Shared memory Process B’s address space Process A’s address space User Kernel Space