Download presentation
Presentation is loading. Please wait.
Published byJason Chambers Modified over 9 years ago
1
Announcements
2
Cooperating Processes Operating systems allow for the creation and concurrent execution of multiple processes & threads eases program complexity increases efficiency Can they work together? How? Messages? What about shared memory? Operating systems allow for the creation and concurrent execution of multiple processes & threads eases program complexity increases efficiency Can they work together? How? Messages? What about shared memory?
3
Problems with concurrent execution Concurrent processes (or threads) often need to share data (maintained either in shared memory or files) and resources If there is no controlled access to shared data, some processes will obtain an inconsistent view of this data The action performed by concurrent processes will then depend on the order in which their execution is interleaved Consider two threads one doing x++ and the other doing x--. How many different values for x? Concurrent processes (or threads) often need to share data (maintained either in shared memory or files) and resources If there is no controlled access to shared data, some processes will obtain an inconsistent view of this data The action performed by concurrent processes will then depend on the order in which their execution is interleaved Consider two threads one doing x++ and the other doing x--. How many different values for x?
4
The Critical-Section Problem Consider a system: n processes {P 0, P 1, …, P n-1 } Each process has a critical section changing common values updating tables etc. Access to those variables must be safe mutually exclusive Consider a system: n processes {P 0, P 1, …, P n-1 } Each process has a critical section changing common values updating tables etc. Access to those variables must be safe mutually exclusive
5
The Critical-Section Problem A solution must satisfy 3 conditions: Mutual exclusion Progress Bounded waiting No assumptions can be made about speed Solutions execute some entry code and some exit code surrounding critical section. A solution must satisfy 3 conditions: Mutual exclusion Progress Bounded waiting No assumptions can be made about speed Solutions execute some entry code and some exit code surrounding critical section.
6
Critical Section Properties Mutual Exclusion At any time, at most one process can be in its critical section (CS) Progress Only processes that are not executing in their CS can participate in the decision of who will enter next in the CS. This selection cannot be postponed indefinitely Mutual Exclusion At any time, at most one process can be in its critical section (CS) Progress Only processes that are not executing in their CS can participate in the decision of who will enter next in the CS. This selection cannot be postponed indefinitely
7
Critical Section Properties Bounded Waiting After a process has made a request to enter it’s CS, there is a bound on the number of times that the other processes are allowed to enter their CS otherwise the process will suffer from starvation Of course there must also be no deadlock Bounded Waiting After a process has made a request to enter it’s CS, there is a bound on the number of times that the other processes are allowed to enter their CS otherwise the process will suffer from starvation Of course there must also be no deadlock
8
pthread_mutex int pthread_mutex_init( pthread_mutex_t *mutex_lock, const pthread_mutexattr_t *lock_attr); int pthread_mutex_lock( pthread_mutex_t *mutex_lock); int pthread_mutex_unlock( pthread_mutex_t *mutex_lock); int pthread_mutex_trylock( pthread_mutex_t *mutex_lock); int pthread_mutex_init( pthread_mutex_t *mutex_lock, const pthread_mutexattr_t *lock_attr); int pthread_mutex_lock( pthread_mutex_t *mutex_lock); int pthread_mutex_unlock( pthread_mutex_t *mutex_lock); int pthread_mutex_trylock( pthread_mutex_t *mutex_lock);
9
#include void *find_min(void *list_ptr) pthread_mutex_t minimum_value_lock; int minimum_value, partial_list_size; main(){ minimum_value = MIN_INT; pthread_init(); pthread_mutex_init(&minimum_value_lock, NULL); /*inititalize lists etc, create and join threads*/ } void *find_min(void *list_ptr){ int *partial_list_ptr, my_min = MIN_INT, i; partial_list_ptr = (int *)list_ptr; for (i = 0; i < partial_list_size; i++) if (partial_list_ptr[i] < my_min) my_min = partial_list_ptr[i]; pthread_mutex_lock(minimum_value_lock); if (my_min < minimum_value) minimum_value = my_min; pthread_mutex_unlock(minimum_value_lock); pthread_exit(0); }
10
Locking Overhead Serialization points Minimize the size of critical sections Be careful Rather than wait, check if lock is available pthread_mutex_trylock If already locked, will return EBUSY Will require restructuring of code Serialization points Minimize the size of critical sections Be careful Rather than wait, check if lock is available pthread_mutex_trylock If already locked, will return EBUSY Will require restructuring of code
11
pthread_mutex_trylock /* Finding k matches in a list */ void *find_entries(void *start_pointer) { /* This is the thread function */ struct database_record *next_record; int count; current_pointer = start_pointer; do { next_record = find_next_entry(current_pointer); count = output_record(next_record); } while (count < requested_number_of_records); } int output_record(struct database_record *record_ptr) { int count; pthread_mutex_lock(&output_count_lock); output_count ++; count = output_count; pthread_mutex_unlock(&output_count_lock); if (count <= requested_number_of_records) print_record(record_ptr); return (count); }
12
pthread_mutex_trylock /* rewritten output_record function */ int output_record(struct database_record *record_ptr) { int count; int lock_status; lock_status=pthread_mutex_trylock(&output_count_lock); if (lock_status == EBUSY) { insert_into_local_list(record_ptr); return(0); } else { count = output_count; output_count += number_on_local_list + 1; pthread_mutex_unlock(&output_count_lock); print_records(record_ptr, local_list, requested_number_of_records - count); return(count + number_on_local_list + 1); }
13
Cooperation through shared mem. x P0P1 1. Wait until x has a value 2. Use the value 3. Change the value to indicate used 1. Wait until x is able to be set 2. Produce a value 3. Set x to the value Can we increase the parallelism? What problems does this entail? x1 x2 x3 … xn
14
The Producer-Consumer Problem repeat … produce an item in nextp … while counter == n do no-op buffer[in] = nextp in = (in + 1) mod n counter = counter + 1 until false repeat … while counter == 0 do no-op nextc = buffer[out] out = (out + 1) mod n counter = counter -1 … consume the item in nextc … until false Are these correct? Always?
15
Semaphores Synchronization tool provided by the OS Integer variable that gives us two operations wait(s) while s <= 0 do nothing s = s - 1 signal(s) s = s + 1 Modifications to s are atomic. Synchronization tool provided by the OS Integer variable that gives us two operations wait(s) while s <= 0 do nothing s = s - 1 signal(s) s = s + 1 Modifications to s are atomic.
16
repeat wait(mysem) critical section signal(mysem) remainder section until false Shared semaphore: mysem= 1; Will this work for n processes? Semaphores for Critical Sections
17
Semaphore Implementation How do we wait? spin? sleep? – How long? How do we wake up? Solution: Let process block itself by placing in waiting queue wait call places the process on the queue When a process is blocked, it must be woken up signal process must wake up next process on queue Semaphore struct semaphore { int value; Queueprocesses; }; How do we wait? spin? sleep? – How long? How do we wake up? Solution: Let process block itself by placing in waiting queue wait call places the process on the queue When a process is blocked, it must be woken up signal process must wake up next process on queue Semaphore struct semaphore { int value; Queueprocesses; };
18
Wait wait(Semaphore s) { s.value = s.value - 1; if (s.value < 0) { add this process to s.L block; } wait(Semaphore s) { s.value = s.value - 1; if (s.value < 0) { add this process to s.L block; }
19
signal(Semaphore s) { s.value = s.value + 1; if (s.value <= 0) { remove a process P from s.L wakeup(P); } signal(Semaphore s) { s.value = s.value + 1; if (s.value <= 0) { remove a process P from s.L wakeup(P); } Signal
20
Details Critical Semaphore operations must be atomic Uniprocessor simply inhibit interrupts (normal user can’t) Use TestAndSet instruction Multiprocessor hardware must provide special support or use software solutions Critical Semaphore operations must be atomic Uniprocessor simply inhibit interrupts (normal user can’t) Use TestAndSet instruction Multiprocessor hardware must provide special support or use software solutions
21
Using semaphores Two processes P1 and P2 Statements S1 and S2 S2 must execute only after S1 Two processes P1 and P2 Statements S1 and S2 S2 must execute only after S1 P1: S1; signal(synch); P2: wait(synch); S2;
22
Consider P0: wait(S); wait(Q);. signal(S); signal(Q); P1: wait(Q); wait(S);. signal(Q); signal(S); Is there anything wrong with this?
23
Semaphores Semaphores can be: binary counting Binary integer variable is 0 or 1 strictly a mutual exclusion variable pthread_mutex Counting integer variable indicates quantity allows more than one process/thread in at a time Semaphores can be: binary counting Binary integer variable is 0 or 1 strictly a mutual exclusion variable pthread_mutex Counting integer variable indicates quantity allows more than one process/thread in at a time
24
Bounded Buffer Problem P0P1 x1 x2 x3 … xn
25
Bounded Buffer Solution repeat produce an item in nextp wait(empty); wait(mutex); add nextp to the buffer signal(mutex); signal(full); until false repeat produce an item in nextp wait(empty); wait(mutex); add nextp to the buffer signal(mutex); signal(full); until false repeat wait(full); wait(mutex); remove an item from buffer place it in nextc signal(mutex); signal(empty); consume the item in nextc until false repeat wait(full); wait(mutex); remove an item from buffer place it in nextc signal(mutex); signal(empty); consume the item in nextc until false Shared semaphore: empty = n, full = 0, mutex = 1;
26
Posix Semaphores Counting semaphores sem_init - creates a unnamed semaphore and initializes it int sem_init(sem_t *sem, int pshared, unsigned int value); sem_open - creates a named semaphore and initializes it sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value); sem_wait - performs a wait operation int sem_wait(sem_t *sem); int sem_trywait(sem_t *sem); int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); sem_post - performs a signal operation int sem_post(sem_t *sem); Counting semaphores sem_init - creates a unnamed semaphore and initializes it int sem_init(sem_t *sem, int pshared, unsigned int value); sem_open - creates a named semaphore and initializes it sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value); sem_wait - performs a wait operation int sem_wait(sem_t *sem); int sem_trywait(sem_t *sem); int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); sem_post - performs a signal operation int sem_post(sem_t *sem);
27
#include void *functionC(void *ptr); int counter = 0; sem_t sem; main() { int rc1, rc2; pthread_t thread1, thread2; sem_init(&sem, PTHREAD_PROCESS_PRIVATE, 1); // Now it is set to one, one person will be able to access at a time printf("Got semaphore %d\n",sem); /* Create independent threads each of which will execute functionC */ if( (rc1=pthread_create( &thread1, NULL, &functionC, NULL)) ) { printf("Thread creation failed: %d\n", rc1); } if( (rc2=pthread_create( &thread2, NULL, &functionC, NULL)) ) { printf("Thread creation failed: %d\n", rc2); } /* Wait till threads are complete before main continues. Unless we */ /* wait we run the risk of executing an exit which will terminate */ /* the process and all threads before the threads have completed. */ pthread_join( thread1, NULL); pthread_join( thread2, NULL); sem_close(&sem); exit(0); } void *functionC(void *ptr) { int tmp; sem_wait(&sem); tmp = counter; sleep(1); tmp++; counter = tmp; printf("Counter value: %d\n",counter); sem_post(&sem); } ~ This works for Linux
28
#ifdef __APPLE__ #include #else #include #endif void qsem_create(void * semStructure, int initialValue) { #ifdef __APPLE__ semaphore_create(mach_task_self(), (semaphore_t *)semStructure, SYNC_POLICY_FIFO, initialValue); #else int pshared = 0; sem_init((sem_t *)semStructure, pshared, initialValue); #endif } void qsem_signal(void * semStructure) { #ifdef __APPLE__ semaphore_signal(*((semaphore_t *)semStructure)); #else sem_post((sem_t *)semStructure); #endif } void qsem_wait(void * semStructure) { #ifdef __APPLE__ semaphore_wait(*((semaphore_t *)semStructure)); #else sem_wait((sem_t *)semStructure); #endif } void qsem_close(void * semStructure) { #ifdef __APPLE__ semaphore_destroy(mach_task_self(), *((semaphore_t *)semStructure)); #else sem_close((sem_t *)semStructure); #endif } #ifdef __APPLE__ semaphore_t sem; #else sem_t sem; #endif
29
Unix System V Semaphores Are a generalization of the counting semaphores (more operations are permitted). A semaphore includes: the current value S of the semaphore number of processes waiting for S to increase number of processes waiting for S to be 0 System calls semget creates an array of semaphores semctl allows for the initialization of semaphores semop performs a list of operations: one on each semaphore (atomically) Are a generalization of the counting semaphores (more operations are permitted). A semaphore includes: the current value S of the semaphore number of processes waiting for S to increase number of processes waiting for S to be 0 System calls semget creates an array of semaphores semctl allows for the initialization of semaphores semop performs a list of operations: one on each semaphore (atomically)
30
Unix Semaphore Code Creation union semun argument; key_t key = IPC_PRIVATE; int flags = 0777 | IPC_CREAT; int semid = semget(key, 1, flags); argument.val = initialvalue; semctl(semid, 0, SETVAL, argument); Destruction int ignored_int; union semun ignored; semctl(semid, ignored_int, IPC_RMID, ignored); Creation union semun argument; key_t key = IPC_PRIVATE; int flags = 0777 | IPC_CREAT; int semid = semget(key, 1, flags); argument.val = initialvalue; semctl(semid, 0, SETVAL, argument); Destruction int ignored_int; union semun ignored; semctl(semid, ignored_int, IPC_RMID, ignored); #include void *functionC(void *ptr); int counter = 0; int sem; #define NSEMS 1 #define SEMFLAG (IPC_CREAT| 0666) union semun { int val; /* Value for SETVAL */ struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ unsigned short *array; /* Array for GETALL, SETALL */ struct seminfo *__buf; /* Buffer for IPC_INFO (Linux specific) */ };
31
Unix Semaphores Each operation to be done is specified by a value sem_op. Let S be the semaphore value if sem_op > 0: (signal operation) S is incremented and process awaiting for S to increase are awaken if sem_op = 0: If S=0: do nothing if S!=0, block the current process on the event that S=0 if sem_op < 0: (wait operation) if S >= | sem_op | then S = S - | sem_op | then if S <=0 wait Each operation to be done is specified by a value sem_op. Let S be the semaphore value if sem_op > 0: (signal operation) S is incremented and process awaiting for S to increase are awaken if sem_op = 0: If S=0: do nothing if S!=0, block the current process on the event that S=0 if sem_op < 0: (wait operation) if S >= | sem_op | then S = S - | sem_op | then if S <=0 wait
32
Wait and Signal Set up the operations array for 1 semaphore struct sembuf operations[1]; operations[0].sem_num = 0; operations[0].sem_flg = SEM_UNDO; Wait operations[0].sem_op = -1; Signal operations[0].sem_op = 1; Execute the operation on 1 semaphore semop(semid, operations, 1); Set up the operations array for 1 semaphore struct sembuf operations[1]; operations[0].sem_num = 0; operations[0].sem_flg = SEM_UNDO; Wait operations[0].sem_op = -1; Signal operations[0].sem_op = 1; Execute the operation on 1 semaphore semop(semid, operations, 1);
33
Semaphores & Interrupts Semaphore operations may be interrupted Will not be restarted Semop will return -1 Semaphore operations may be interrupted Will not be restarted Semop will return -1 int sem_wait(int semid) { struct sembuf operations[1]; int retval; operations[0].sem_num = 0; operations[0].sem_op = -1; operations[0].sem_flg = SEM_UNDO; while ((retval = semop(semid, operations, 1)) == -1) { if (errno != EINTR) { fprintf(stderr,"sem_wait error %d\n", errno); exit(4); } return retval; }
34
Unix Semaphores Operating System level data structure Can be shared among processes Identified by a key and an ID List semaphores $ ipcs Remove zombie semaphores $ ipcrm -s semid Operating System level data structure Can be shared among processes Identified by a key and an ID List semaphores $ ipcs Remove zombie semaphores $ ipcrm -s semid
35
Classical Synchronization Problems Counting semaphores from binary semaphores Bounded Buffer Shared buffer between producer and consumer Readers and Writers data object shared between many some read only some write only Dining Philosophers n processes p resources Counting semaphores from binary semaphores Bounded Buffer Shared buffer between producer and consumer Readers and Writers data object shared between many some read only some write only Dining Philosophers n processes p resources
36
Binary to Counting Semaphores Can you create a counting semaphore from binary semaphores? Needed: Integer count Operations on the count must be atomic Must block processes appropriately How many binary semaphores are needed? Can you do it with just one? Can you create a counting semaphore from binary semaphores? Needed: Integer count Operations on the count must be atomic Must block processes appropriately How many binary semaphores are needed? Can you do it with just one?
37
Binary to Counting Semaphores Typedef struct qsem { pthread_mutex_t s; int value; } void wait(qsem_t* q) { pthread_mutex_lock(q->s); q->value--; if (value < 0) { // I need to block..... how? } pthread_mutex_unlock(q->s); } void signal(qsem_t* q) { pthread_mutex_lock(q->s); q->value++; if (value <= 0) { // I need to wake someone //..... how? } pthread_mutex_unlock(q->s); }
38
Binary to Counting Semaphores typedef struct qsem { pthread_mutex_t s1; pthread_mutex_t s2; int value; } void wait(qsem_t* q) { pthread_mutex_lock(&(q->s1)); q->value--; if (value < 0) { pthread_mutex_unlock(&(q->s1)); pthread_mutex_lock(&(q->s2)); } pthread_mutex_unlock(&(q->s1)); } void qsem_create(qsem_t *q, int initialvalue) { pthread_mutex_init(&(q->s1), NULL); pthread_mutex_init(&(q->s2), NULL); // We must initialize s1 to 1(default) // and s2 to 0 (we must lock it) pthread_mutex_lock(&(q->s2)); q->value = initialvalue; } void signal(qsem_t* q) { pthread_mutex_lock(&(q->s1)); q->value++; if (value <= 0) pthread_mutex_unlock(&(q->s2)); else pthread_mutex_unlock(&(q->s1)); }
39
Bounded Buffer Problem P0P1 x1 x2 x3 … xn
40
Bounded Buffer Solution? repeat … produce an item in nextp … while counter == n do no-op buffer[in] = nextp in = (in + 1) mod n counter = counter + 1 until false repeat … while counter == 0 do no-op nextc = buffer[out] out = (out + 1) mod n counter = counter -1 … consume the item in nextc … until false
41
Bounded Buffer Solution repeat produce an item in nextp wait(empty); wait(producer_mutex); add nextp to the buffer only modify in signal(producer_mutex); signal(full); until false repeat wait(full); wait(consumer_mutex); remove an item from buffer place it in nextc only modify out signal(consumer_mutex); signal(empty); consume the item in nextc until false Shared semaphore: empty = n, full = 0, producer_mutex = 1, consumer_mutex = 1; Note: We don’t need counter any more. Why?
42
The Dining Philosophers Problem 5 philosophers who only eat and think each need to use 2 forks for eating we have only 5 forks Illustrates the difficulty of allocating resources among processes/threads without deadlock and starvation 5 philosophers who only eat and think each need to use 2 forks for eating we have only 5 forks Illustrates the difficulty of allocating resources among processes/threads without deadlock and starvation
43
The Dining Philosophers Problem Each philosopher is a process One semaphore per fork: fork: array[0..4] of semaphores Initialization: fork[i].count:=1 for i:=0..4 A first attempt: Each philosopher is a process One semaphore per fork: fork: array[0..4] of semaphores Initialization: fork[i].count:=1 for i:=0..4 A first attempt: Process Pi: repeat think; wait(fork[i]); wait(fork[i+1 mod 5]); eat; signal(fork[i+1 mod 5]); signal(fork[i]); forever Deadlock if each philosopher starts by picking left fork!
44
The Dining Philosophers Problem A solution: admit only 4 philosophers at a time that tries to eat Then 1 philosopher can always eat when the other 3 are holding 1 fork Introduce semaphore T that limits at 4 the numb. of philosophers “sitting at the table” Initialize: T.count:=4 A solution: admit only 4 philosophers at a time that tries to eat Then 1 philosopher can always eat when the other 3 are holding 1 fork Introduce semaphore T that limits at 4 the numb. of philosophers “sitting at the table” Initialize: T.count:=4 Process Pi: repeat think; wait(T); wait(fork[i]); wait(fork[i+1 mod 5]); eat; signal(fork[i+1 mod 5]); signal(fork[i]); signal(T); forever
45
Other solutions A philosopher may only pick up forks in pairs must allocate all resources at once Asymmetric solution odd philosophers select left then right even philosophers select right then left All solutions must not starve a philosopher A philosopher may only pick up forks in pairs must allocate all resources at once Asymmetric solution odd philosophers select left then right even philosophers select right then left All solutions must not starve a philosopher
46
Readers and Writers Problem Data object is shared many readers many writers Many can read at the same time Only one writer at a time no reading while writing Many different varieties reader priority writer priority Data object is shared many readers many writers Many can read at the same time Only one writer at a time no reading while writing Many different varieties reader priority writer priority
47
Readers - Writers (priority?) Shared Semaphore mutex=1, wrt = 1; Shared integerreadcount = 0; Shared Semaphore mutex=1, wrt = 1; Shared integerreadcount = 0; wait(wrt); write to the data object signal(wrt); wait(wrt); write to the data object signal(wrt); wait(mutex); readcount = readcount + 1; if (readcount == 1) wait(wrt); signal(mutex); read the data wait(mutex); readcount = readcount - 1; if (readcount == 0) signal(wrt); signal(mutex); wait(mutex); readcount = readcount + 1; if (readcount == 1) wait(wrt); signal(mutex); read the data wait(mutex); readcount = readcount - 1; if (readcount == 0) signal(wrt); signal(mutex);
48
Readers – Writers (priority?) wait (outerQ) wait (rsem) wait (rmutex) readcnt++ if (readcnt == 1) wait (wsem) signal(rmutex) signal (rsem) signal (outerQ) READ wait (rmutex) readcnt--; if (readcnt == 0) signal (wsem) signal (rmutex) wait (outerQ) wait (rsem) wait (rmutex) readcnt++ if (readcnt == 1) wait (wsem) signal(rmutex) signal (rsem) signal (outerQ) READ wait (rmutex) readcnt--; if (readcnt == 0) signal (wsem) signal (rmutex) wait (wsem) writecnt++; if (writecnt == 1) wait (rsem) signal (wsem) wait (wmutex) WRITE signal (wmutex) wait (wsem) writecnt--; if (writecnt == 0) signal (rsem) signal (wsem) wait (wsem) writecnt++; if (writecnt == 1) wait (rsem) signal (wsem) wait (wmutex) WRITE signal (wmutex) wait (wsem) writecnt--; if (writecnt == 0) signal (rsem) signal (wsem) outerQ, rsem, rmutex, wmutex, wsem: = 1
49
The Barbershop Entrance Standing room area Sofa Barber chairs Cashier Exit
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.