Announcements
Download
Report
Transcript Announcements
Announcements
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?
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?
The 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
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.
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
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
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);
#include <pthread.h>
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);
}
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
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);
}
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);
Cooperation through shared
mem.
x1
x
P0
1. Wait until x
has a value
x2
x3
…
xn
P1
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?
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?
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.
Semaphores for Critical
Sections
Shared semaphore: mysem= 1;
repeat
wait(mysem)
critical section
signal(mysem)
remainder section
until false
Will this
work for n
processes?
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
Queue
};
value;
processes;
Wait
wait(Semaphore s)
{
s.value = s.value - 1;
if (s.value < 0)
{
add this process to s.L
block;
}
}
Signal
signal(Semaphore s)
{
s.value = s.value + 1;
if (s.value <= 0)
{
remove a process P from s.L
wakeup(P);
}
}
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
Using semaphores
Two processes P1 and P2
Statements S1 and S2
S2 must execute only after S1
P1:
P2:
S1;
signal(synch);
wait(synch);
S2;
Consider
P0:
P1:
wait(S);
wait(Q);
.
.
.
signal(S);
signal(Q);
wait(Q);
wait(S);
.
.
.
signal(Q);
signal(S);
Is there anything wrong with this?
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
Bounded Buffer Problem
x1
x2
x3
…
xn
P0
P1
Bounded Buffer Solution
Shared semaphore: empty = n, full = 0, mutex = 1;
repeat
repeat
produce an item in nextp
wait(empty);
wait(mutex);
wait(full);
wait(mutex);
remove an item from buffer
place it in nextc
add nextp to the buffer
signal(mutex);
signal(full);
until false
signal(mutex);
signal(empty);
consume the item in nextc
until false
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);
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>
void *functionC(void *ptr);
int counter = 0;
sem_t sem;
This works for Linux
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
void *functionC(void *ptr)
{
int tmp;
printf("Got semaphore %d\n",sem);
sem_wait(&sem);
tmp = counter;
sleep(1);
tmp++;
counter = tmp;
printf("Counter value: %d\n",counter);
sem_post(&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);
}
}
~
#ifdef __APPLE__
#include <mach/semaphore.h>
#include <mach/task.h>
#include <mach/mach.h>
#else
#include <semaphore.h>
#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
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)
Unix Semaphore Code
Creation
union semun argument;
key_t key = IPC_PRIVATE;
int flags = 0777 | IPC_CREAT;
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
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) */
};
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);
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
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);
Semaphores & Interrupts
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;
}
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
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
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?
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);
}
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));
}
Bounded Buffer Problem
x1
x2
x3
…
xn
P0
P1
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
Bounded Buffer Solution
Shared semaphore: empty = n, full = 0, producer_mutex = 1, consumer_mutex = 1;
repeat
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
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
Note: We don’t need counter any more. Why?
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
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:
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!
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
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
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
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
Readers - Writers (priority?)
Shared Semaphore mutex=1, wrt = 1;
Shared integer readcount = 0;
wait(wrt);
wait(mutex);
readcount = readcount + 1;
if (readcount == 1)
wait(wrt);
signal(mutex);
write to the data object
read the data
signal(wrt);
wait(mutex);
readcount = readcount - 1;
if (readcount == 0)
signal(wrt);
signal(mutex);
outerQ, rsem, rmutex, wmutex, wsem: = 1
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 (wsem)
writecnt++;
if (writecnt == 1)
wait (rsem)
signal (wsem)
wait (wmutex)
WRITE
signal (wmutex)
wait (wsem)
writecnt--;
if (writecnt == 0)
signal (rsem)
signal (wsem)
The Barbershop
Barber chairs
Cashier
Entrance
Standing
room
area
Exit
Sofa