Lecture 5 Code examples on csserver in directory /home/hwang/cs470/lecture05 Questions? Friday, January 21 CS 470 Operating Systems - Lecture 5 1 Outline Interprocess communication (IPC) Signals Shared memory Friday, January 21 CS 470 Operating Systems - Lecture 5 2 Interprocess Communication (IPC) Processes executing concurrently may be either independent or cooperating. Most are independent. They cannot affect or be affected by other processes executing in the system. Processes that do not share data are independent by definition. Any processes that share data are cooperating by definition. Friday, January 21 CS 470 Operating Systems - Lecture 5 3 Interprocess Communication (IPC) Why would we want processes to cooperate? Information sharing Computational speedup Modularity Convenience Cooperating processes require an interprocess communication (IPC) mechanism that allows the exchange of data and information. Friday, January 21 CS 470 Operating Systems - Lecture 5 4 Signals Signals are built-in, software interrupts that can be used as a communication technique. Unix uses signals to notify a process that an event has occured. Signals may be synchronous or asynchronous. Signals can be generated by the kernel in response to a hardware exception (segment violation, floating point error) or a software exception (illegal instruction). Friday, January 21 CS 470 Operating Systems - Lecture 5 5 Signals Signals can be sent by one process to another process via the kill( ) system call. Users can send signals to the foreground process with key presses (CTRL-C, CTRL-Z) or the kill command. Once a signal is generated, it is delivered to a process to be handled, in one of three ways: Ignore the signal Use the default handler Use a provided signal-handling function Friday, January 21 CS 470 Operating Systems - Lecture 5 6 Signals Each signal has a default action associated with it. E.g., SIGINT (CTRL-C) default handler terminates the process. A few signals have permanent actions. E.g., SIGKILL always terminates the process. Details of the signal system can be found on most Unix systems using "man 7 signal". The symbolic names and numbers are defined in the <signal.h> library. Friday, January 21 CS 470 Operating Systems - Lecture 5 7 Signal Handlers Program in file signal.c demonstrates how to install a signal handler. A signal handler simply is a void function with an int parameter. I.e. all handers have prototype: void hander_name (int signum); where signum is the signal number being handled. Friday, January 21 CS 470 Operating Systems - Lecture 5 8 Signal Handlers A signal interrupts the current instruction and control transfers to the handler. When the handler returns, the interrupted instructions is continued. The handler has access to global data and may execute most any code. The main restriction is that I/O should be done using the read and write system calls directly as higher level I/O routines (printf, scanf, <<, >>) are not signal safe. Friday, January 21 CS 470 Operating Systems - Lecture 5 9 Signal Handlers For portability, sigaction( ) should be used to define a custom handler. Its prototype is: int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); The sigaction struct is defined as: struct sigaction { void (*sa_handler)(int); // handler func sigset_t sa_mask; // initialized int sa_flags; // but not used } Friday, January 21 CS 470 Operating Systems - Lecture 5 10 Signal Handlers The sa_handler field is set to the handler function and the struct is passed to sigaction( ) along with the signal number (e.g., SIGQUIT). The handler in signal.c simply outputs the message in buffer and sets a flag. When the handler returns, the main program sees the flag and terminates. Friday, January 21 CS 470 Operating Systems - Lecture 5 11 Shared Memory Signals are very low-level and cannot pass much data between processes. Two main higher-level mechanisms: shared memory and message queues. Look at shared memory today. Friday, January 21 CS 470 Operating Systems - Lecture 5 12 Shared Memory A common paradigm in cooperating processes is the producer-consumer problem. Producer - creates information Consumer - reads and processes information Generally, do not want to copy data from producer's process space to consumer's process space. Can solve this by using shared memory. Share variables and definitions. E.g., a buffer Friday, January 21 CS 470 Operating Systems - Lecture 5 13 Producer/Consumer Data This might look like: An unbounded buffer would be impractical. One possible solution: // Shared definitions const int BUFSIZE = 10; struct Item {...}; Process A Shared Process B OS Kernel // Shared data Item buffer[BUFSIZE]; int in = 0, out = 0, numItems = 0; Friday, January 21 CS 470 Operating Systems - Lecture 5 14 Producer/Consumer Data The buffer is used as a circular queue with two logical "pointers". in points to the next free space (at the back); out points to the first filled space (at the front). The buffer is empty when numItems is 0, and it is full when numItems is BUFSIZE. Must synchronize access so that the consumer does not try to remove an item that does not exist, yet, and the producer does not overwrite an item before it has been consumed. Friday, January 21 CS 470 Operating Systems - Lecture 5 15 Producer/Consumer Processes Producer Consumer Item nextP; Item nextC; while (true) { nextP = MakeItem(); while(numItems == BUFSIZE) ; // do nothing buffer[in] = nextP; in = (in+1) % BUFSIZE; numItems++; } while (true) { while(numItems == 0) ; // do nothing nextC = buffer[out]; out = (out+1) % BUFSIZE; numItems­­; UseItem(nextC); } Friday, January 21 CS 470 Operating Systems - Lecture 5 16 Producer/Consumer Processes This solution does not address what happens if both processes want to access the shared data at "exactly" the same time. In particular, there is a potential problem if both processes access the numItems counter simultaneously. We will look at synchronization techniques in Chapter 6. Friday, January 21 CS 470 Operating Systems - Lecture 5 17 System V Shared Memory An example implementation using System V (SysV) shared memory is shown in file shmprod-cons.cpp System V IPC and shared memory routines are defined in libraries <sys/ipc.h> and <sys/shm.h>. Both the producer (parent process) and consumer (child process) are in this file. Uses signal handlers to clean up before exit. Friday, January 21 CS 470 Operating Systems - Lecture 5 18 System V Shared Memory The logical structure of the shared memory is a shared definition. It can be anything, but typically is either a buffer of bytes or a struct of plain data items. In the example, the SharedMemory struct defines the logical structure for this program. The producer process sets up the shared memory first using shmget, then forks the consumer process. Both processes map the shared memory into their address spaces using shmat. Friday, January 21 CS 470 Operating Systems - Lecture 5 19 System V Shared Memory The shmget( ) routine has prototype: int shmget(key_t key, size_t size, int flags); An id is returned that is used in other calls: // Create named shm block id=shmget(123,2048,IPC_CREAT|0660); // Access existing block by name id=shmget(123,0,0); // Create unnamed shm block id=shmget(IPC_PRIVATE,2048,0660); Friday, January 21 CS 470 Operating Systems - Lecture 5 20 System V Shared Memory The shmat( ) routine has prototype: void *shmat(id, addr, flags); The addr argument allows us to specify a local address at which the shared memory can be accessed, it should normally be NULL to allow the system to choose an address. flags is usually 0. shmat( ) returns a pointer to the first byte of shared memory. This pointer needs to be cast to the actual struct type being used. Access is like any other struct pointer. Friday, January 21 CS 470 Operating Systems - Lecture 5 21 System V Shared Memory The shmdt( ) routine detaches the shared memory segment from the current process. Detaching the segment does not delete the segment or clear the segment contents. The segment can be reattached later: // Attach shared memory segment shmaddr = shmat(shmid, (void *)0, 0); // Detach shared memory segment ret = shmdt(shmaddr); Friday, January 21 CS 470 Operating Systems - Lecture 5 22 System V Shared Memory The shmctl( ) routine is used to change perms on a shared memory segment, get info about a segment and delete a segment: // Remove the segment from the system ret = shmctl(shmid, IPC_RMID, 0); The segment may not be deleted until sometime later, after all processes using the segment have detached it. Friday, January 21 CS 470 Operating Systems - Lecture 5 23 System V Shared Memory Example uses signal handlers to make sure clean up happens. Otherwise, the shared memory segment stays allocated in the system. The program is stopped by typing CTRL-C which is sends SIGINT to the producer (as the foreground process). The producer sends SIGINT to the consumer and waits for it to terminate. The consumer detaches the shared memory and exits. The producer then detaches and deletes the shared memory before it exits. Friday, January 21 CS 470 Operating Systems - Lecture 5 24