Title: Announcements
1Announcements
2Cooperating 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?
3Problems 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?
4The Critical-Section Problem
- Consider a system
- n processes P0, P1, , Pn-1
- Each process has a critical section
- changing common values
- updating tables
- etc.
- Access to those variables must be safe
- mutually exclusive
5The 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.
6Critical 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
7Critical Section Properties
- Bounded Waiting
- After a process has made a request to enter its
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
8pthread_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)
9include ltpthread.hgt void find_min(void
list_ptr) pthread_mutex_t minimum_value_lock int
minimum_value, partial_list_size main() minim
um_value MIN_INT pthread_init() pthread_mute
x_init(minimum_value_lock, NULL) /inititaliz
e 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 lt partial_list_size i) if
(partial_list_ptri lt my_min) my_min
partial_list_ptri pthread_mutex_lock(minimum_v
alue_lock) if (my_min lt minimum_value) minimum
_value my_min pthread_mutex_unlock(minimum_val
ue_lock) pthread_exit(0)
10Locking 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
11pthread_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 lt
requested_number_of_records) int
output_record(struct database_record record_ptr)
int count pthread_mutex_lock(output_count_lo
ck) output_count count output_count
pthread_mutex_unlock(output_count_lock) if
(count lt requested_number_of_records)
print_record(record_ptr) return (count)
12pthread_mutex_trylock
/ rewritten output_record function / int
output_record(struct database_record record_ptr)
int count int lock_status
lock_statuspthread_mutex_trylock(output_count_l
ock) 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)
13Cooperation through shared mem.
x
x1 x2 x3 xn
P0
P1
1. Wait until x has a value
1. Wait until x is able to be set
2. Use the value
2. Produce a value
3. Change the value to indicate used
3. Set x to the value
Can we increase the parallelism?
What problems does this entail?
14The Producer-Consumer Problem
repeat produce an item in nextp
while counter n do no-op bufferin
nextp in (in 1) mod n counter
counter 1 until false
repeat while counter 0 do no-op
nextc bufferout out (out 1) mod n
counter counter -1 consume the item
in nextc until false
Are these correct?
Always?
15Semaphores
- Synchronization tool provided by the OS
- Integer variable that gives us two operations
- wait(s) while s lt 0 do nothing s s
- 1 - signal(s) s s 1
- Modifications to s are atomic.
16Semaphores for Critical Sections
Shared semaphore mysem 1
repeat wait(mysem) critical
section signal(mysem) remainder
section until false
Will this work for n processes?
17Semaphore 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 Queue
processes
18Wait
wait(Semaphore s) s.value s.value - 1 if
(s.value lt 0) add this process to
s.L block
19Signal
signal(Semaphore s) s.value s.value 1 if
(s.value lt 0) remove a process P from
s.L wakeup(P)
20Details
- Critical
- Semaphore operations must be atomic
- Uniprocessor
- simply inhibit interrupts (normal user cant)
- Use TestAndSet instruction
- Multiprocessor
- hardware must provide special support or
- use software solutions
21Using semaphores
- Two processes P1 and P2
- Statements S1 and S2
- S2 must execute only after S1
22Consider
P0 wait(S) wait(Q) . .
. signal(S) signal(Q)
P1 wait(Q) wait(S) . .
. signal(Q) signal(S)
Is there anything wrong with this?
23Semaphores
- 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
24Bounded Buffer Problem
x1 x2 x3 xn
P0
P1
25Bounded Buffer Solution
Shared semaphore empty n, full 0, mutex 1
repeat produce an item in nextp wait(empty) w
ait(mutex) add nextp to the buffer signal(mut
ex) 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
26Posix 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)
27include ltstdio.hgt include ltstdlib.hgt include
ltpthread.hgt include ltunistd.hgt include
ltsemaphore.hgt 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(
(rc1pthread_create( thread1, NULL, functionC,
NULL)) ) printf("Thread creation
failed d\n", rc1) if(
(rc2pthread_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)
This works for Linux
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)
28ifdef __APPLE__ include ltmach/semaphore.hgt incl
ude ltmach/task.hgt include ltmach/mach.hgt else in
clude ltsemaphore.hgt 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)) e
lse 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
29Unix 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)
30Unix 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)
include ltsys/types.hgt include
ltsys/ipc.hgt include ltsys/sem.hgt 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) /
31Unix Semaphores
- Each operation to be done is specified by a value
sem_op. - Let S be the semaphore value
- if sem_op gt 0 (signal operation)
- S is incremented and process awaiting for S to
increase are awaken - if sem_op 0
- If S0 do nothing
- if S!0, block the current process on the event
that S0 - if sem_op lt 0 (wait operation)
- if S gt sem_op then S S - sem_op then
if S lt0 wait
32Wait and Signal
- Set up the operations array for 1 semaphore
- struct sembuf operations1
- operations0.sem_num 0
- operations0.sem_flg SEM_UNDO
- Wait
- operations0.sem_op -1
- Signal
- operations0.sem_op 1
- Execute the operation on 1 semaphore
- semop(semid, operations, 1)
33Semaphores Interrupts
- Semaphore operations may be interrupted
- Will not be restarted
- Semop will return -1
int sem_wait(int semid) struct sembuf
operations1 int retval
operations0.sem_num 0 operations0.sem_o
p -1 operations0.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
34Unix 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
35Classical 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
36Binary 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?
37Binary to Counting Semaphores
Typedef struct qsem pthread_mutex_t s int
value void wait(qsem_t q) pthread_mutex_l
ock(q-gts) q-gtvalue-- if (value lt 0)
// I need to block..... how? pthread_mutex_unl
ock(q-gts)
void signal(qsem_t q) pthread_mutex_loc
k(q-gts) q-gtvalue if (value lt 0) //
I need to wake someone // .....
how? pthread_mutex_unlock(q-gts)
38Binary to Counting Semaphores
void qsem_create(qsem_t q, int initialvalue)
pthread_mutex_init((q-gts1), NULL)
pthread_mutex_init((q-gts2), NULL) // We
must initialize s1 to 1(default) // and s2
to 0 (we must lock it) pthread_mutex_lock((q
-gts2)) q-gtvalue initialvalue void
signal(qsem_t q) pthread_mutex_lock((q-gts1))
q-gtvalue if (value lt 0)
pthread_mutex_unlock((q-gts2)) else
pthread_mutex_unlock((q-gts1))
typedef struct qsem pthread_mutex_t
s1 pthread_mutex_t s2 int value void
wait(qsem_t q) pthread_mutex_lock((q-gts1))
q-gtvalue-- if (value lt 0)
pthread_mutex_unlock((q-gts1))
pthread_mutex_lock((q-gts2)) pthread_mutex_u
nlock((q-gts1))
39Bounded Buffer Problem
x1 x2 x3 xn
P0
P1
40Bounded Buffer Solution?
repeat produce an item in nextp
while counter n do no-op bufferin
nextp in (in 1) mod n counter
counter 1 until false
repeat while counter 0 do no-op
nextc bufferout out (out 1) mod n
counter counter -1 consume the item
in nextc until false
41Bounded Buffer Solution
Shared semaphore empty n, full 0,
producer_mutex 1, consumer_mutex 1
repeat produce an item in nextp wait(empty) w
ait(producer_mutex) add nextp to the
buffer only modify in signal(producer_mutex)
signal(full) until false
repeat wait(full) wait(consumer_mutex) remov
e an item from buffer place it in nextc only
modify out signal(consumer_mutex) signal(empty
) consume the item in nextc until false
Note We dont need counter any more. Why?
42The 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
43The Dining Philosophers Problem
- Each philosopher is a process
- One semaphore per fork
- fork array0..4 of semaphores
- Initialization forki.count1 for i0..4
- A first attempt
Process Pi repeat think wait(forki)
wait(forki1 mod 5) eat signal(forki1 mod
5) signal(forki) forever
- Deadlock if each philosopher starts by picking
left fork!
44The 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.count4
Process Pi repeat think wait(T)
wait(forki) wait(forki1 mod 5) eat
signal(forki1 mod 5) signal(forki)
signal(T) forever
45Other 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
46Readers 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
47Readers - Writers (priority?)
Shared Semaphore mutex1, wrt 1 Shared
integer readcount 0
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(wrt) write to the data object signal(wrt)
48Readers Writers (priority?)
outerQ, rsem, rmutex, wmutex, wsem 1
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)
49The Barbershop
Barber chairs
Cashier
Entrance
Standing room area
Exit
Sofa