Semaphores and typical applications What are semaphores and messages How to control a critical section with semaphores Typical process coordination problems Semaphore code from Stevens (QEPD) #include <sys/types.h> // The header file info The header #include <sys/ipc.h> #include <sys/sem.h> #define KEY ((key_t) 54321) // an unlikely key #define COUNT 20000 struct sembuf op_up[1] = { 0, 1, 0 }; // A useful trick for semops struct sembuf op_down[1] = { 0, -1, 0 }; // use -1 for down – use this in wrappers main() { register int i, semid; // Always protect system code if ( (semid = semget(KEY, 1, 0666 | IPC_CREAT)) < 0) err_sys("semget error"); for (i = 0; i < COUNT; i++) { if (semop(semid, &op_up[0], 1) < 0) err_sys("semop up error"); if (semop(semid, &op_down[0], 1) < 0) err_sys("semop down error"); } if (semctl(semid, 0, IPC_RMID, (struct semid_ds *) 0) < 0) err_sys("IPC_RMID error"); exit(0); } Concurrency - From last time The theoretical core of OS An OS creates a set of processes that run concurrently A process is like a person - it has a stream of consciousness possessions - memory, files, data is created, reproduces, dies interacts with other processes In actuality, it is a program in execution Concurrent process misbehaviors Race conditions - unpredictable results because processes simultaneously modify data or devices Deadlocks - Each has what the other wants Starvation - some processes go hungry - others eat well Some operating system ideas - also review Everything is a file - even devices This allows a program to work with human input, or a device, or a temporary or permanent file Interrupt-driven behavior Example - windows - process is simultaneously sensitive to events from mouse, keyboard, process subsystem, and window manager Caching - Foreground and background copies Used in Cache memory Swapping The disk block cache - recent transactions are in memory Networking utilities Processes and their states The process state includes the following Running/ready/blocked (waiting on an event/events) Priority Kernel/User mode In-memory/swapped Process ID Priority The process owns U-area (but user can’t examine/modify it) Entries in the system open-file table Its own file control blocks (linked to the system table) Its own memory space The system process table Used for scheduling and control of processes Scheduler acts on timer interrupt or a process state change It schedules the highest-priority process that is ready It causes a context switch (gives the process control) This mechanism Gives system and I/O processes high priority Keeps the CPU puercos from monopolizing Interrupts are outside process country This avoids make a context switch for each interrupt It means the interrupt service routines use only the kernels memory Mutual Exclusion - software solutions Unsuccessful solutions (on separate slides) Strict alternation Algorithms where mutual exclusion fails Peterson’s algorithm Description of Bakery Algorithm Just like Baskin-Robbins - incoming process takes a number Since two users can grab same number, lowest-number process gets priority Avoiding busy waiting - Semaphores and messages Classical semaphores The semaphore is an ADT with a counter, a process queue, and two operations The down operation sleeps enqueued if the counter is zero, and then decrements the counter The up operation increments the counter and awakens a sleeper if the counter was 0. The down operation enters a critical section, the up leaves it. Messages Reliable messages to an administrator work like a down or up Messages and semaphores are equivalent That’s why Semaphores and messages continued Mistakes are easy Deadlock Gateway-controlling and resource-controlling semaphores invoked in wrong order - deadlocks and slow performance Example of a deadlock creator //process 0 Wait(a) Wait(b) //critical section Signal(a) Signal(b) //process 1 Wait(b) Wait(a) //critical section Signal(b) Signal(a) a Process 0 Process 1 b If process 1 is like process 0 (semaphores a and b requested In same order, this deadlock can’t occur Producer-consumer problem again Definition Producers They produce items and place in a buffer as long as one is available. They wait (and stop producing) when no buffer is available Consumers They wait until a buffer contains an item and then take it They then consume the item What needs protection The buffer place and take operations, not the producing or consuming Possible deadlock Producer is waiting for a consumer to take, but consumer can’t take because producer has buffering operation locked This problem can’t be solved with only one semaphore Producer-consumer with general semaphores Global variables Item buffers[Nbuffers]; Int last_filled=N_buffers, oldest_item=0, number_filled=0; // underrated programming technique, give descriptive and nonambiguous names, not i, j, in, out Semaphores Buffer_available=n,items_available=0;pool_available=1; Technique Circular buffering step operation Next = (next+1)%N_buffers // steps pointer from end to start The code itself Producer() { While(1) { New_item=produce(); Wait (buffer_available); Wait (pool_available); Buffers[next_available]=new_item; Count++;step(last_filled) Signal(items_available); Signal(pool_available); } } consumer() { While(1) { Wait (items_available); Wait (pool_available); Old_item=Buffers[oldest_item]; Count--;step(oldest_item) Signal(buffer_available); Signal(pool_available); New_item=produce(); } } Language Mechanisms - Monitors Monitor is a class with mutual exclusion: it has Member functions Data members Condition variables (user processes wait on these) Two ways to enter Normal Returning from wait on a condition variable Two ways to leave Normal (return from a member function) Waiting on a condition variable Monitors can be implemented by: Preprocessor that generates semaphore code Compiler modification Classical IPC problems Producer-consumer Scenario Producer produces information Consumer consumes it Both must wait on buffer availability Problem resembler disk I/O and print servers Code is in book - both C and monitor Readers-Writers Scenario Readers can write whenever no writer is active Writers must have solitude Problem resembles database Code in book - both C and monitor Dining Philosophers Scenario N philosophers think, wait for chopsticks, and eat Problem is useless but illustrative Code - both C and monitor System V IPC Resource type Get call Op call Control call Semaphore set semget semop semctl Message queue msgget msgsnd msgrcv msgctl Shared memory region xxxget call shmget shmat shmdt shmctl int xxxget(key, type-dependent, flags) returns Positive number, which is the instance ID -For error -xxxop call -The basic operation -Works on regions, groups of semops, or message queues xxxctl call Does specialized operations – removes instance, sets values and permissions Stevens – semaphore get #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #define KEY ((key_t) 98765L) #define PERMS 0666 main() { int i, semid; for (i = 0 ; i < 1000; i++) { if ( (semid = semget(KEY, 1, PERMS | IPC_CREAT)) < 0) err_sys("can't create semaphore"); printf("semid = %d\n", semid); if (semctl(semid, 0, IPC_RMID, (struct semid_ds *) 0) < 0) err_sys("can't remove semaphore"); } } #include "msgq.h" main() { int readid, writeid; /* * Open the message queues. The server must have * already created them. */ if ( (writeid = msgget(MKEY1, 0)) < 0) err_sys("client: can't msgget message queue 1"); if ( (readid = msgget(MKEY2, 0)) < 0) err_sys("client: can't msgget message queue 2"); client(readid, writeid); /* * Now we can delete the message queues. */ if (msgctl(readid, IPC_RMID, (struct msqid_ds *) 0) < 0) err_sys("client: can't RMID message queue 1"); if (msgctl(writeid, IPC_RMID, (struct msqid_ds *) 0) < 0) err_sys("client: can't RMID message queue 2"); exit(0); } Wrapper function for msgsnd mesg_send(id, mesgptr) int id; /* really an msqid from msgget() */ Mesg *mesgptr; { /* * Send the message - the type followed by the optional data. */ if (msgsnd(id, (char *) &(mesgptr->mesg_type), mesgptr->mesg_len, 0) != 0) err_sys("msgsnd error"); } /* * Receive a message from a System V message queue. * The caller must fill in the mesg_type field with the desired type. * Return the number of bytes in the data portion of the message. * A 0-length data message implies end-of-file. */ Wrapper for msgrcv int mesg_recv(id, mesgptr) int id; Mesg *mesgptr; { int n; /* really an msqid from msgget() */ /* * Read the first message on the queue of the specified type. */ n = msgrcv(id, (char *) &(mesgptr->mesg_type), MAXMESGDATA, mesgptr->mesg_type, 0); if ( (mesgptr->mesg_len = n) < 0) err_dump("msgrcv error"); return(n); } /* n will be 0 at end of file */ Header for both wrapper functions /* * Definition of "our" message. * * You may have to change the 4096 to a smaller value, if message queues * on your system were configured with "msgmax" less than 4096. */ #define MAXMESGDATA (4096-16) /* we don't want sizeof(Mesg) > 4096 */ #define MESGHDRSIZE (sizeof(Mesg) - MAXMESGDATA) /* length of mesg_len and mesg_type */ typedef struct { int mesg_len; /* #bytes in mesg_data, can be 0 or > 0 */ long mesg_type; /* message type, must be > 0 */ char mesg_data[MAXMESGDATA]; } Mesg; General pattern for an IPC example Main program Create the IPC (xxxget) Initialize the IPC (xxxctl) Set up the cleanup() function Create children to be the actors using a loop containing fork() Does monitoring function or creates a monitor note: Global variables are usually needed Child actors Can be persistent (barbers, philosophers) Can be temporary (readers, customers, Loreina says writers are immortal) Either live forever (with while loop) if immortal Or exit(0) if mortal Some specifics Definition Decide what IPC is needed and give valid names Decide what other global variables are needed and what protection is needed Write pseudocode, then fill in to readable code Program operation Examples can be done as real-time (uses sleep()) or discrete-time simulation DTS is faster, gives better statistics, but takes some thought Terminal output can be a problem – output from child needs a flush, especially if control characters are involved Think about the statistics, there is usually a dimensionless utilization parameter or ratio Example of this is ratio of haircutting capacity to offered business in the barbershop Example of a cleanup() function void cleanup() { if (0>(msgctl(semid, IPC_RMID, (struct msqid_ds *) 0) ) error_exit(“couldn’t remove Message queue”); exit(0); } /* code segment to set up signals for cleanup extern void cleanup; ………… for (int signal=0;signal<32;signal++) signal(signal,cleanup); Comments: Cleanup must be declared extern or the signal mechanism won’t find it This won’t work for all signals, notably 9 Response to 9 (SIGKILL) isn’t alterable because it is important to be able to really control the system Methodology for the sleepy barber Semaphores awaitingBarber – awaitingCustomer – mutex – controls waiting-room variables, used whenever a customer arrives or a haircut starts Global variables emptyChairs -