Transcript lec9

Operating Systems
CMPSC 473
Signals, Introduction to mutual exclusion
September 28, 2010 - Lecture 9
Instructor: Bhuvan Urgaonkar
Today
• Inter-process communication
– Signals
• Start discussion on process synchronization
– Mutual exclusion problem
• Project 2: Multi-threaded programming using
pthreads out
– Deadline: Oct 19
Inter-process Communication
• Two forms of communication
– Message passing
• E.g., signals, interrupts, traps, emails, HTTP requests, …
• We will study signals
– Shared memory
• E.g., pipes, more general shared buffers, distributed shared
memory
System calls related to
signals
• kill(signal_num, pid) - to send a signal
• signal(signal_num, handler) - to handle it
Signal Handling (more)
• When does a process handle a signal?
– Whenever it gets scheduled next after the generation of the signal
• We said the OS marks some members of the PCB to indicate
that a signal is due
– And we said the process will execute the signal handler when it gets
scheduled
– But its PC had some other address!
• The address of the instruction the process was executing when it was
scheduled last
– Complex task due to the need to juggle stacks carefully while switching
between user and kernel mode
Signal Handling (more)
• Remember that signal handlers are functions defined by
processes and included in the user mode code segment
– Executed in user mode in the process’s context
• The OS forces the handler’s starting address into the
program counter
– The user mode stack is modified by the OS so that the
process execution starts at the signal handler
User mode
Normal program flow
Kernel mode
. . . (e.g., interrupt handling)
do_signal( )
handle_signal( )
setup_frame( )
Signal handler
Return code
on the stack
•
system_call( )
sys_sigreturn( )
restore_sigcontext( )
setup_frame: sets up the user-mode stack
– Forces Signal handler’s address into PC and some “return code”
– After the handler is done, this return code gets executed
• It makes a system call such as sigreturn (in Linux) that does the following:
–
–
1. Copies the hardware context of the normal program to KM Stack
2. Restores the User mode stack to its original state
• When the system call terminates, the normal program execution can continue
Signal Handling with
Threads
•
•
Recall: Signals are used in UNIX systems to notify a process that
a particular event has occurred
Recall: A signal handler is used to process signals
- Signal is generated by particular event
- Signal is delivered to a process
- Signal is handled
•
Options:
–
–
–
–
Deliver the signal to the thread to which the signal applies
Deliver the signal to every thread in the process
Deliver the signal to certain threads in the process
Assign a specific thread to receive all signals for the process
Signal Handling (Visual)
Signal due indicators
Signal is not due
PCB of P
Signal is due
Kernel
P
Parent of P
P’s signal
ISR run; assume Sys call done; assume handler runs
P scheduled again Par scheduled
Timer interrupt
System call
(trap)
Runs SIGSEGV
handler; dumps core
and exits
Sched choses P
Timer interrupt
Calls kill() to
Accesses illegal
send a signal to P memory location
(trap)
(trap)
time
These are the events that P sees (its view of what is going on)
The mutual exclusion problem
• We will study it in the context of the shared
memory model
Example: Producer and Consumer
Producer
while (true) {
/* produce an item and put in nextProduced */
while (count == BUFFER_SIZE); // do nothing
buffer [in] = nextProduced;
in = (in + 1) % BUFFER_SIZE;
count++;
}
Consumer
while (true) {
while (count == 0); // do nothing
nextConsumed = buffer[out];
out = (out + 1) % BUFFER_SIZE;
count--;
/* consume the item in nextConsumed
}
• Shared variables: buffer and count
• Local variables: in, out, nextProduced, nextConsumed
Race Condition
• count++ could be implemented as
register1 = count
register1 = register1 + 1
count = register1
• count-- could be implemented as
register2 = count
register2 = register2 - 1
count = register2
• Consider this execution interleaving with “count = 5” initially:
S0: producer executes register1 = count {register1 = 5}
S1: producer executes register1 = register1 + 1 {register1 = 6}
S2: consumer executes register2 = count {register2 = 5}
S3: consumer executes register2 = register2 - 1 {register2 = 4}
S4: producer executes count = register1 {count = 6 }
S5: consumer executes count = register2 {count = 4}
Example: Producer and Consumer
Producer
while (true) {
/* produce an item and put in nextProduced */
while (count == BUFFER_SIZE); // do nothing
buffer [in] = nextProduced;
in = (in + 1) % BUFFER_SIZE;
count++;
}
•
•
•
Consumer
while (true) {
while (count == 0); // do nothing
nextConsumed = buffer[out];
out = (out + 1) % BUFFER_SIZE;
count--;
/* consume the item in nextConsumed
}
Shared variables: buffer and count
Local variables: in, out, nextProduced, nextConsumed
Important: Think how producer-consumer applies to P2
– Hint: Customer = Producer and Worker = Consumer
We would like these operations
to be mutually exclusive
What about buffer manipulation?
•
Mutual
Exclusion
We want to use mutual exclusion to synchronize access to shared data
– Meaning: Only one thread can access a shared resource at a time
• Code that uses mutual exclusion to synchronize its execution is called a critical
section
– Only one thread at a time can execute code in the critical section
– All other threads are forced to wait on entry
– When one thread leaves the critical section, another can enter
do {
entry section
CRITICAL SECTION
exit section
REMAINDER SECTION
} while (TRUE);
Example to Show Mut. Excl. is Non-trivial
•
•
•
•
•
•
•
How about constructing a “lock” s.t. before getting into critical section, we acquire it and after leaving
critical section, we release it?
Is there any trouble if we use a boolean flag as a lock? Consider P1 and P2
while (lock == 1) ; // Do nothing, just wait
Doesn’t work!
//position 1
lock = 1;
.....
.....
// Critical Section
.....
lock = 0;
If P1 interrupted at position 1, race condition might occur
P1 was waiting until lock == 0. Before it announces its turn (i.e., sets lock = 1), suppose P2 gets
scheduled and sees that lock is 0
Before P2 leaves its critical section (i.e., sets lock = 0), suppose it gets descheduled
Since P1 was at position 1, it will set lock = 1 and also get into its critical section
Now, two processes are in the critical section at the same time!
Peterson’s Solution
• Two process solution
• Assume that the LOAD and STORE instructions are atomic; that is,
cannot be interrupted.
• The two processes share two variables:
– int turn;
– Boolean flag[2]
• The variable turn indicates whose turn it is to enter the critical section.
• The flag array is used to indicate if a process is ready to enter the critical
section. flag[i] = true implies that process Pi is ready
Algorithm for Process Pi
while (true) {
flag[i] = TRUE;
turn = j;
while ( flag[j] && turn == j);
CRITICAL SECTION
flag[i] = FALSE;
REMAINDER SECTION
}