multi-process systems: synchronizationhomes.di.unimi.it/~boccignone/giuseppeboccignone... · }...

41
Operating Systems and Distributed Systems Multi-Process Systems: Synchronization Operating Systems and Distributed Systems Semaphores

Upload: others

Post on 18-Jul-2020

1 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

Operating Systems and Distributed Systems

Multi-Process Systems:Synchronization

Operating Systems and Distributed Systems

Semaphores

Page 2: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

Operating Systems and Distributed Systems

Dijkstra Semaphore

• Invented in the 1960s

• Conceptual OS mechanism, with no specific implementation defined (could be enter()/exit())

• Basis of all contemporary OS synchronization mechanisms

• A nonnegative integer variable that can only be changed or tested by these two indivisible (atomic) functions:

- verhoog (increment)

= up = signal =post

- prolaag (probeer te verlagen = try to decrement) = down = wait

Operating Systems and Distributed Systems

Dijkstra Semaphore Definition

V(s): [s = s + 1]

P(s): [while(s == 0) {wait}; s = s - 1]

wake up (signal to get ready) a process blocked on s queue

wait (block) on the s queue

Page 3: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

Operating Systems and Distributed Systems

Types of Semaphores

• Binary Semaphore:– Takes values 0 and 1

– Can be used for mutual exclusion (mutex)

– Can be used for signaling

• Counting Semaphores:– Takes any nonnegative value

– Typically used to count resources and block resource consumers when all resources are busy

class semaphore {

! int counter;

public:

! semaphore(int v = 1) { counter = v;};

! void P(){

! ! disableInterrupts();

! ! while(counter == 0) {

! ! ! enableInterrupts();

! ! ! disableInterrupts();

! ! }

! ! counter--;

! ! enableInterrupts();

! };

! void V(){

! ! disableInterrupts();

! ! counter++;

! ! enableInterrupts();

! };

};Operating Systems and Distributed Systems

Implementing Semaphores (1):interrupts

Page 4: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

void mos_sem_wait(mos_sem_t *s)

{

handle_t int_handle;

int_handle = mos_disable_ints();

s->val--;

// If no resources are available then we

wait in the queue

if(s->val < 0) {

mos_thread_t *id;

id = mos_thread_current();

mos_tlist_add(&s->q, id);

mos_thread_suspend_noints(int_handle);

} else

mos_enable_ints(int_handle);

}

void mos_sem_post(mos_sem_t *s)

{

handle_t int_handle;

mos_thread_t *thread;

int_handle = mos_disable_ints();

// Post a unit and wake-up the next waiting thread

s->val++;

if((thread = mos_tlist_remove(&s->q)) != NULL) {

mos_thread_resume_noints_nodispatch(thread, int_handle);

mos_enable_ints(int_handle);

} else {

mos_enable_ints(int_handle);

}

}

Operating Systems and Distributed Systems

Real stuff: Semaphoreswith interrupts in MANTIS OS

P(s) V(s)

typedef struct {

! pthread_mutex_t mutex;! pthread_cond_t cond;

! int counter;} mysem_t;

/*_____Function prototypes of a semaphore_*/

void unlock_mutex(void *);void mysem_init(mysem_t *, int);

void mysem_wait(mysem_t *);void mysem_post(mysem_t *);

/*____________________________________*/

Operating Systems and Distributed Systems

Implementing Semaphores (2):mutexes and condition variables

Page 5: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

void mysem_post(mysem_t *s)

{

! pthread_mutex_lock(&s->mutex);

!

! if (!(s->counter++))

! ! pthread_cond_signal(&s->cond);

!

! pthread_mutex_unlock(&s->mutex);

}

Operating Systems and Distributed Systems

Implementing Semaphores (2):mutexes and condition variables

V(s): [s = s + 1]

Analyze mysemaphore.h and mysemaphore.c!

void unlock_mutex(void *m)

{

! //cleanup handler to unlock the mutex

! pthread_mutex_unlock((pthread_mutex_t *)m);

}

void mysem_wait(mysem_t *s)

{

! pthread_mutex_lock(&s->mutex);

! while (!s->counter) {

! ! //free the resources that a thread may hold at the time it

//terminates

! ! //PUSH: just before locking the mutex,

//install a cleanup handler to unlock the mutex

! ! pthread_cleanup_push(unlock_mutex,(void *)&s->mutex);

! ! pthread_cond_wait(&s->cond, &s->mutex);

! ! //POP: removes the most recently installed cleanup handler,

//0 does not execute the handler

! ! pthread_cleanup_pop(0);

! }

!

! s->counter--;

!

! pthread_mutex_unlock(&s->mutex);

}Operating Systems and Distributed Systems

Implementing Semaphores (2):mutexes and condition variables

P(s): [while(s == 0) {wait}; s = s - 1]

Page 6: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

void mysem_init(mysem_t *s, int num)

{

! pthread_mutexattr_t m;

! pthread_condattr_t c;

!

! //initializes the semaphore count

! s->counter = num;

!

! //initializes the mutex attribute object m and

//fills it with default values for the attributes

! pthread_mutexattr_init(&m);

! pthread_mutex_init(&s->mutex, &m);

! //destroys a mutex attribute object, which must not be reused

//until it is reinitialized.

! pthread_mutexattr_destroy(&m);

!

! pthread_condattr_init(&c);

! pthread_cond_init(&s->cond, &c);

! pthread_condattr_destroy(&c);

}

Operating Systems and Distributed Systems

Implementing Semaphores (2):mutexes and condition variables

Analyze mysemaphore.h and mysemaphore.c!

Operating Systems and Distributed Systems

Critical Section with Semaphores

• Doing a critical section with a semaphore is as simple as with a lock

semaphore_t mutex = 1;

int shared_variable;

void worker() {

while(1) {

P(mutex);

shared_variable++;

V(mutex);

}

}

Page 7: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

int count = 0;

mysem_t mymutex; //Declare the semaphore global (outside of any function)

void * ThreadAdd(void *); //function executed by threads

int main(int argc, char * argv[]){

pthread_t tid1, tid2;

! //Initialize the unnamed semaphore in the main function: initial value is set to 1.

! mysem_init(&mymutex, 1);

!

pthread_create(&tid1, NULL, ThreadAdd, NULL);!

pthread_create(&tid2, NULL, ThreadAdd, NULL);!

pthread_join(tid1, NULL);! /* wait for the thread 1 to finish */

pthread_join(tid2, NULL) ; /* wait for the thread 2 to finish */

if (count < 2 * NITER) printf("\n BOOM! count is [%d], should be %d\n", count, 2*NITER);

else printf("\n OK! count is [%d]\n", count);

! pthread_exit(NULL);

}

void * ThreadAdd(void * a)

{

int i, tmp;

! int value;

for(i = 0; i < NITER; i++){! ! ! !

//entering the critical section: wait on semaphore

! mysem_wait(&mymutex);!

tmp = count; /* copy the global count locally */

tmp = tmp+1; /* increment the local copy */

count = tmp; /* store the local value into the global count */

! //exiting the critical section: increments the value of the semaphore and

//wakes up a blocked process waiting on the semaphore, if any.

! mysem_post(&mymutex);! !

! }

}Operating Systems and Distributed Systems

Critical Section with Semaphores

Com

pile a

nd r

un p_thraddmysem.c!

Operating Systems and Distributed Systems14

Signaling Semaphores: Barriers

• Used for synchronizing multiple processes

• Processes wait at a “barrier” until all in the group arrive

• After all have arrived, all processes can proceed

• May be implemented using locks and condition variables or...

B and D atbarrier

A

B

C

D

All atbarrier

A

B

C

D

Barrier releasesall processes

A

B

C

D

Processes approachingbarrier

A

B

C

D

Page 8: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

Operating Systems and Distributed Systems

Signaling Semaphores

• Using a binary semaphore is to signal some event

– A thread waits for an event by calling P

– A thread signals the event by calling V

• This creates a barrier between two threads

. . .

. . .

V(ready1);

P(ready2);

. . .

. . .

Thread #1

. . .

. . .

V(ready2);

P(ready1);

. . .

. . .

Thread #2

semaphore ready1 = 0;

semaphore ready2 = 0;

Global Variables

#define NUM_THREADS 2

mysem_t ready1, ready2;

void *simple1(void *);

void *simple2(void *);

pthread_t tid[NUM_THREADS]; /* array of thread IDs */

int main( int argc, char *argv[] ) {

! int i, ret;!

! mysem_init(&ready1, 0);

! mysem_init(&ready2, 0);!

! pthread_create(&tid[0], NULL, simple1, NULL);

! pthread_create(&tid[1], NULL, simple2, NULL);

! for ( i = 0; i < NUM_THREADS; i++)

! ! pthread_join(tid[i], NULL);!

! printf("\nmain() reporting that all %d threads have terminated\n", i);!

! pthread_exit(NULL);!

} /* main */

void *simple1(void * parm){

! printf("Thread 1 here, before the barrier.\n");

! mysem_post(&ready1);

! mysem_wait(&ready2);

! printf("Thread 1 after the barrier.\n");

}

void *simple2(void * parm){

! printf("Thread 2 here, before the barrier.\n");

! mysem_post(&ready2);

! mysem_wait(&ready1);

! printf("Thread 2 after the barrier.\n");

}

Operating Systems and Distributed Systems

Signaling Semaphores

Page 9: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

host:pthreadsync Bebo$ ./p_mysembarr

Thread 2 here, before the barrier.Thread 1 here, before the barrier.Thread 1 after the barrier.Thread 2 after the barrier.

main() reporting that all 2 threads have terminated

Operating Systems and Distributed Systems

Signaling Semaphores

Com

pile a

nd r

un p_mysembarr.c!

Operating Systems and Distributed Systems

Signaling Semaphores: Driver/Controller synchronization

• The semaphore principle is logically used with the busy and done flags in a controller

• Driver signals controller with a V(busy), then waits for completion with P(done)

• Controller waits for work with P(busy), then announces completion with V(done)

. . .

. . .

V(busy);

P(done);

. . .

. . .

Device driver

. . .

. . .

P(busy);

V(done);

. . .

. . .

Controller

semaphore busy = 0;

semaphore done = 0;

Global Variables

Page 10: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

Operating Systems and Distributed Systems

Signaling Semaphores

• We can use them for requesting a specific sequence of operation

Operating Systems and Distributed Systems

POSIX semaphores• POSIX semaphores come in two forms:

– named semaphores

– unnamed semaphores.

• A named semaphore is identified by a name of the form /somename.

– Two processes can operate on the same named semaphore by passing the same name to sem_open().

• creates a new named semaphore or opens an existing named semaphore.

– After the semaphore has been opened, it can be operated on using sem_post() and sem_wait().

– When a process has finished using the semaphore, it can use sem_close() to close the semaphore.

– When all processes have finished using the semaphore, it can be removed from the system using sem_unlink()

• POSIX named semaphores have kernel persistence: if not removed by sem_unlink(), a semaphore will exist until the system is shut down.

Page 11: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

Operating Systems and Distributed Systems

POSIX semaphores• POSIX semaphores come in two forms:

– named semaphores

– unnamed semaphores.

• An unnamed semaphore does not have a name. Instead the semaphore is placed in a region of memory that is shared between multiple threads (a thread-shared semaphore) or processes (a process-shared semaphore).

• A thread-shared semaphore is placed in an area of memory shared between by the threads of a process, for example, a global variable.

• A process-shared semaphore must be placed in a shared memory region (e.g., a System V shared memory segment created using semget(), or a POSIX shared memory object built created using shm_open()).

– Before being used, an unnamed semaphore must be initialised using sem_init().

– It can then be operated on using sem_post() and sem_wait().

– When the semaphore is no longer required, and before the memory in which it is located is deallocated, the semaphore should be destroyed using sem_destroy().

All POSIX semaphore functions and types are prototyped or defined in semaphore.h. To define a semaphore object, use

sem_t sem_name;

To initialize a semaphore, use sem_init():

int sem_init(sem_t *sem, int pshared, unsigned int value);

!•! sem points to a semaphore object to initialize!•! pshared is a flag indicating whether or not the semaphore should be shared

with fork()ed processes. LinuxThreads does not currently support shared semaphores

!•! value is an initial value to set the semaphore toExample of use: sem_init(&sem_name, 0, 10);

Operating Systems and Distributed Systems

POSIX semaphores

Page 12: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

To wait on a semaphore, use sem_wait: int sem_wait(sem_t *sem);

Example of use: sem_wait(&sem_name);

! •! If the value of the semaphore is negative, the calling process blocks; one of the blocked processes wakes up when another process calls sem_post.

To increment the value of a semaphore, use sem_post: int sem_post(sem_t *sem);

Example of use: sem_post(&sem_name);

! •! It increments the value of the semaphore and wakes up a blocked process waiting on the semaphore, if any.

Operating Systems and Distributed Systems

POSIX semaphores

To find out the value of a semaphore, use int sem_getvalue(sem_t *sem, int *valp);

! •! gets the current value of sem and places it in the location pointed to by valp

Example of use: int value;

sem_getvalue(&sem_name, &value);

printf("The value of the semaphors is %d\n", value);

To destroy a semaphore, use int sem_destroy(sem_t *sem);

! •! destroys the semaphore; no threads should be waiting on the semaphore if its destruction is to succeed.

Example of use: sem_destroy(&sem_name);

Operating Systems and Distributed Systems

POSIX semaphores

Page 13: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

#define NITER 1000000

int count = 0;

sem_t mymutex; //Declare the semaphore global (outside of any function)

void * ThreadAdd(void *); //function executed by threads

int main(int argc, char * argv[]){

pthread_t tid1, tid2;!

! //Initialize the unnamed semaphore in the main function: initial value is set to 1.

// Note the second argument: passing zero denotes that the semaphore is shared between threads (and

// not processes).

! sem_init(&mymutex, 0, 1);

!

pthread_create(&tid1, NULL, ThreadAdd, NULL);

pthread_create(&tid2, NULL, ThreadAdd, NULL);

pthread_join(tid1, NULL);! /* wait for the thread 1 to finish */

pthread_join(tid2, NULL); /* wait for the thread 2 to finish */

if (count < 2 * NITER) printf("\n BOOM! count is [%d], should be %d\n", count, 2*NITER);

else printf("\n OK! count is [%d]\n", count);

! // destroys the semaphore; no threads should be waiting on the semaphore if its destruction is to succeed

! sem_destroy(&mymutex);

pthread_exit(NULL);

}

void * ThreadAdd(void * a){

int i, tmp;

! int value;

for(i = 0; i < NITER; i++){! ! !

! //entering the critical section: wait on semaphore

! sem_wait(&mymutex);! !

tmp = count; /* copy the global count locally */

tmp = tmp+1; /* increment the local copy */

count = tmp; /* store the local value into the global count */

! //exiting the critical section:

sem_post(&mymutex);!

! }

} Operating Systems and Distributed Systems

POSIX semaphores: example

Operating Systems and Distributed Systems

POSIX semaphores: example

Compile and run p_thraddsem.c!

Or try (for MacOS) the platform independent code in p_thraddsem2.c!

Page 14: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

Operating Systems and Distributed Systems

Critical Section with Semaphores

• General semaphores represent "available resources" that can be acquired by multiple threads at the same time until the resource pool is empty.

• Additional threads must then wait until the required number of resources are available again.

• Semaphores are very efficient, as they allow simultaneous access to resources.

• But improper use often leads to thread starvation, or to deadlock---where two threads block each other indefinitely, each one waiting for a resource the other thread has currently locked.

28

The producer-consumer problem using Semaphores

Page 15: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

#define BSIZE 4 //number of slots in the buffer

#define NUMITEMS 30 //max number of items

#define NUM_THREADS 2

pthread_t tid[NUM_THREADS]; // array of thread IDs

typedef struct {

! char buf[BSIZE];

! int occupied;

! int nextin, nextout;

#ifdef __APPLE__

! semaphore_t mutex ;//control access to critical region

! semaphore_t more ; //counts full buffer slots

! semaphore_t less ; //counts empty buffer slots

#else

! sem_t mutex;//control access to critical region

! sem_t more; //counts full buffer slots

! sem_t less; //counts empty buffer slots

#endif

!

} buffer_t;

buffer_t buffer;

29

The producer-consumer problem using Semaphores

int main( int argc, char *argv[] )

{

! int i;

!

! _sem_create(&(buffer.mutex), 1);

! _sem_create(&(buffer.more), 0);

! _sem_create(&(buffer.less), BSIZE);

!

! pthread_create(&tid[1], NULL, consumer, NULL);

! pthread_create(&tid[0], NULL, producer, NULL);

! for ( i = 0; i < NUM_THREADS; i++)

! ! pthread_join(tid[i], NULL);

!

! printf("\nmain() reporting that all %d threads have terminated\n", i);

!

! _sem_destroy(&(buffer.mutex));

! _sem_destroy(&(buffer.more));

! _sem_destroy(&(buffer.less));

! return 0;

!

} /* main */

30

The producer-consumer problem using Semaphores

Page 16: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

void *producer(void * parm)

{

! char item[NUMITEMS]="IT'S A SMALL WORLD, AFTER ALL."; // items to be put in buffer

! int i;

!

! printf("producer started.\n");

!

! for(i=0;i<NUMITEMS;i++) { // produce an item, one character from item[]

! !

! ! if (item[i] == '\0') break; // Quit if at end of string.

! !

! ! if (buffer.occupied >= BSIZE) printf("producer waiting.\n");

! !

_sem_wait(&(buffer.less));! //decrement empty count

! ! _sem_wait(&(buffer.mutex));! //enter critical region

! ! printf("producer executing.\n");

! !

! ! buffer.buf[buffer.nextin++] = item[i];//put new item in buffer

! ! buffer.nextin %= BSIZE;

! ! buffer.occupied++;! ! //items in buffer

! !

! ! _sem_signal(&(buffer.mutex));! //leave critical region

! ! _sem_signal(&(buffer.more));! //signals the consumer and increments full count

}

! printf("producer exiting.\n");

! pthread_exit(0);

}

31

The producer-consumer problem using Semaphores

void *consumer(void * parm)

{

! char item;

! int i;

!

! printf("consumer started.\n");

!

! for(i=0;i<NUMITEMS;i++){ // consume an item, one character from buffer

! ! if (buffer.occupied <= 0) printf("consumer waiting.\n");

! ! _sem_wait(&(buffer.more));! //decrement full count

! ! _sem_wait(&(buffer.mutex));! //enter critical region

! !

! ! printf("consumer executing.\n");

! !

! ! item = buffer.buf[buffer.nextout++];//take item from buffer! !

! ! buffer.nextout %= BSIZE;

! ! buffer.occupied--;

! !

! ! _sem_signal(&(buffer.mutex));! //leave critical region

! ! _sem_signal(&(buffer.less));! //signals the producer and increments empty count

! !

! ! printf("%c\n",item);! ! //prints the item

! !

! }

! printf("consumer exiting.\n");

! pthread_exit(0);

}

32

The producer-consumer problem using Semaphores

Page 17: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

Operating Systems and Distributed Systems

POSIX semaphores: example

Compile and run p_boundsem.c!

Operating Systems and Distributed Systems

Unix semaphores

• All the UNIX semaphore functions operate on arrays of general semaphores, rather than a single binary semaphore

• The semaphore function definitions are:

#include <sys/sem.h>

int semctl(int semid, int semnum, int cmd, ... /*arg*/);

int semget(key_t key, int nsems, int semflg);

int semop(int semid, struct sembuf *sops, unsigned int nsops);

Page 18: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

Operating Systems and Distributed Systems

Unix semaphores• The semget function creates a new semaphore or obtains the

semaphore key of an existing semaphore.

• The function semop is used for changing the value of the semaphore (atomic function)

– The first parameter, sem_id, is the semaphore identifier, as returned from semget

– The second parameter, sops, is a pointer to an array of structures, each of which will have at least the following members:

int semop(int semid, struct sembuf *sops, unsigned int nsops);

int semget(key_t key, int nsems, int semflg);

struct sembuf {

ushort sem_num; short sem_op;

short sem_flg;};

creating a key:see shared memory

Allocate resources. Block the calling process until the value of the semaphore is greater than or equal to the absolute value of sem_op. (That is, wait until enough resources have been freed by other processes for this one to allocate.) Then add (effectively subtract, since it's negative) the value of sem_op to the semaphore's value.

Release resources. The value of sem_op is added to the semaphore's value.

This process will wait until the semaphore in question reaches 0.

Operating Systems and Distributed Systems

Unix semaphores

– sem_num is the number of the semaphore in the set that you want to manipulate.

– sem_op is what you want to do with that semaphore. This takes on different meanings, depending on whether sem_op is positive, negative, or zero, as shown in the following table:

struct sembuf {

ushort sem_num; short sem_op;

short sem_flg;};

sem_op < 0

sem_op > 0

sem_op == 0

Note that when sem op = "1 the operation is equivalent to a conventional P. Whensem op = +1 it is equivalent to a conventional V . Hence the capabilities of UNIX semaphores

are a superset of those of Dijkstra!s semaphores

Page 19: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

Operating Systems and Distributed Systems

Unix semaphores

– sem_num is the number of the semaphore in the set that you want to manipulate.

– sem_op is what you want to do with that semaphore.

– sem_flg field which allows the program to specify flags the further modify the effects of the semop() call.

• One of these flags is IPC_NOWAIT which, as the name suggests, causes the call to semop() to return with error EAGAIN if it encounters a situation where it would normally block. This is good for situations where you might want to "poll" to see if you can allocate a resource.

• Another very useful flag is the SEM_UNDO flag. This causes semop() to record, in a way, the change made to the semaphore. When the program exits, the kernel will automatically undo all changes that were marked with the SEM_UNDO flag.

struct sembuf {

ushort sem_num; short sem_op;

short sem_flg;};

Operating Systems and Distributed Systems

Unix semaphores• The semctl function allows direct control of

semaphore information:

– The command parameter is the action to take and a fourth parameter, if present, is a union semun, which must have at least the following members:

int semctl(int semid, int semnum, int command, ... /*arg*/);

union semun {

int val; /* used for SETVAL only */

struct semid_ds *buf; /* used for IPC_STAT and IPC_SET */

ushort *array; /* used for GETALL and SETALL */

};

Page 20: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

SETVAL

Set the value of the specified semaphore to the value in the val member of the passed-in

union semun.

GETVAL

Return the value of the given semaphore.

SETALL

Set the values of all the semaphores in the set to the values in the array pointed to by the

array member of the passed-in union semun. The semnum parameter to semctl() isn't

used.

GETALL

Gets the values of all the semaphores in the set and stores them in the array pointed to

by the array member of the passed-in union semun. The semnum parameter to semctl()

isn't used.

IPC_RMID

Remove the specified semaphore set from the system. The semnum parameter is ignored.

IPC_STAT

Load status information about the semaphore set into the struct semid_ds structure

pointed to by the buf member of the union semun.

Operating Systems and Distributed Systems

Unix semaphores– The command parameter

Operating Systems and Distributed Systems

Unix semaphores• The semctl function is used to destroy a

sem

int semid;

.

.

semid = semget(...);

.

.

semctl(semid, 0, IPC_RMID);

Page 21: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

void V(int sid){

! struct sembuf sb;

! sb.sem_num=0;

!sb.sem_op=1; //Release resources. The value of sem_op is added to the

//semaphore's value.

! sb.sem_flag =SEM_UNDO; //record the change made to the semaphore.

! if((semop(sid, &sb,1)) == -1)

! ! puts("semop error");

}

void P(int sid){

! struct sembuf sb;

! sb.sem_num=0;

! sb.sem_op= -1;//wait until enough resources have been freed to allocate

! sb.sem_flag =SEM_UNDO; //record the change made to the semaphore.

! if((semop(sid, &sb,1)) == -1)

! ! puts("semop error");

}

Operating Systems and Distributed Systems41

Implementing Dijkstra semaphores in Unix

int main( )

{

! int i;

! pid_t pid;

! key_t ! semaphoreKey;

! int semaphoreCount;

!int semaphoreFlag;

! int semaphoreIdent;!

! struct! sembuf * semaphoreOpList;

!

! semaphoreKey = ftok("semsimple.c", 'X')!

! semaphoreCount = 1;

! semaphoreFlag = IPC_CREAT | 0666;

! semaphoreIdent = semget(semaphoreKey, semaphoreCount, semaphoreFlag);

!

pid=fork( );

!/***********************/

! P(semaphoreIdent);

! printf("\n");

! for(i=1;i<100; i++){

! ! printf(". ");

! }

! printf("\n\n");

! V(semaphoreIdent);

/***********************/

! if(pid){

! ! /* The father created the semaphore, the father destroys it! */! !

! ! wait(NULL);

! semctl(semaphoreIdent, 0, IPC_RMID,0);

! ! printf("Father process destroyed semaphore\n");

}

! return 0;

} Operating Systems and Distributed Systems

Example 1: father&son race

creating a key

get semaphore

remove semaphore

} father & child CS

child creation

Page 22: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

host:Lez20_ex Bebo$ ./semsimple

Successful semaphore creation

Semaphore identity: 458758

Process 939 gained lock on semaphore

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

Process 939 released lock on semaphore

Semaphore identity: 458758

Process 940 gained lock on semaphore

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

Process 940 released lock on semaphore

Father process destroyed semaphore

Operating Systems and Distributed Systems

Example 1: father&son raceCom

pile a

nd r

un semsimple.c!

void main(int argc, char *argv[]){

! int i, j, status, num_comm;

! key_t key;

!

! if(argc<2) perror("Error"); exit(1);!

! num_comm= atoi(argv[1]);

! if ((key = ftok("semdemo.c", 'J')) == -1) {

perror("ftok"); exit(1);

}!

!

! sid = semget(key, 1, 0660 | IPC_CREAT);

! V(sid);

! if(fork()==0){

! ! for(i=0; i<num_comm;i++){

! ! ! /* race */

! ! ! P(sid);

! ! ! printf("Child_Communication %d\n",i);

! ! ! for(j=0; j<20; j++) printf("in child critical session %d\n",j);

! ! ! V(sid);! ! !

! ! }

! ! exit(0);

! }!

! for(i=0; i<num_comm;i++){

! ! P(sid);

! ! printf("Father_Communication %d\n",i);

! ! for(j=0; j<20; j++) printf("in father critical session %d\n",j);

! ! V(sid);

! }

! wait(&status);

! semctl(sid, 0, IPC_RMID,0);

}

Operating Systems and Distributed Systems

Example 2: father&son race

creating a key

get semaphore

remove semaphore

{child CS

father CS {

Page 23: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

host:Lez20_MP_IPC Bebo$ ./semnodemo 30

....

Father_Communication 3

in father critical session 0

in father critical session 1

in father critical session 2

Child_Communication 0

in father critical session 3

in child critical session 0

in father critical session 4

in child critical session 1

in father critical session 5

in child critical session 2

in father critical session 6

.....

Operating Systems and Distributed Systems

Example: father&son race

Execution without semaphores:

Compile and run semdemo.c and semnodemo.c!

Operating Systems and Distributed Systems

Difference between the System V and POSIX semaphore implementations

• One marked difference is that in System V you can control how much the semaphore count can be increased or decreased; whereas in POSIX, the semaphore count is increased and decreased by 1.

• POSIX semaphores do not allow manipulation of semaphore permissions, whereas System V semaphores allow you to change the permissions of semaphores to a subset of the original permission.

• Initialization and creation of semaphores is atomic (from the user's perspective) in POSIX semaphores.

• From a usage perspective, System V semaphores are clumsy, while POSIX semaphores are straight-forward

• The scalability of POSIX semaphores (using unnamed semaphores) is much higher than System V semaphores. In a user/client scenario, where each user creates her own instances of a server, it would be better to use POSIX semaphores.

• System V semaphores, when creating a semaphore object, creates an array of semaphores whereas POSIX semaphores create just one. Because of this feature, semaphore creation (memory footprint-wise) is costlier in System V semaphores when compared to POSIX semaphores.

• POSIX semaphores provide a mechanism for process-wide semaphores rather than system-wide semaphores. So, if a developer forgets to close the semaphore, on process exit the semaphore is cleaned up. In simple terms, POSIX semaphores provide a mechanism for non-persistent semaphores.

Page 24: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

Operating Systems and Distributed Systems 47

• Synchronize by exchanging messages

• Two primitives:– Send: send a message

– Receive: receive a message

– Both may specify a “channel” to use

• Issue: how does the sender know the receiver got the message?

• Issue: authentication

Message-Based Communication and Synchronisation

Operating Systems and Distributed Systems

Message-Based Communication and Synchronisation

• Use of a single construct for both synchronisation and communication

• Three issues:– the model of synchronisation

– the method of process naming

– the message structure

Process P1 Process P2

send message

receive message

time time

Page 25: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

Operating Systems and Distributed Systems

Process Synchronisation• Variations in the process synchronisation model

arise from the semantics of the send operation

• Asynchronous (or no-wait) (e.g. POSIX)

– Requires buffer space. What happens when the buffer is full?

Process P1 Process P2

send message

receive message

message

time time

Operating Systems and Distributed Systems

Process Synchronisation

• Synchronous (e.g. CSP, occam2)– No buffer space required

– Known as a rendezvous

Process P1 Process P2

send message

receive message

time time

blocked M

Page 26: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

Operating Systems and Distributed Systems

Process Synchronisation• Remote invocation (e.g. Ada)

– Known as an extended rendezvous

• Analogy:

– The posting of a letter is an asynchronous send

– A telephone is a better analogy for synchronous communication

Process P1 Process P2

send message

receive message

time time

blocked

M

reply

Operating Systems and Distributed Systems

Asynchronous and Synchronous Sends

• Asynchronous communication can implement synchronous communication:

P1! ! ! ! P2

asyn_send (M)! ! ! wait (M)

wait (ack)! ! ! ! asyn_send (ack)

• Two synchronous communications can be used to construct a remote invocation:

P1 ! ! ! ! P2

syn_send (message) wait (message)

wait (reply)! ! ! ...

! ! ! ! ! ! construct reply

! ! ! ! ! ! ...

Page 27: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

Operating Systems and Distributed Systems

Disadvantages of Asynchronous Send

• Potentially infinite buffers are needed to store unread messages

• More communications are needed with the asynchronous model, hence programs are more complex

• It is more difficult to prove the correctness of the complete system

• Where asynchronous communication is desired with synchronised message passing then buffer processes can easily be constructed; however, this is not without cost

Operating Systems and Distributed Systems

Process Naming

• Two distinct sub-issues

– direction versus indirection

– symmetry

• With direct naming, the sender explicitly names the receiver:

! ! send <message> to <process-name>

• With indirect naming, the sender names an intermediate entity (e.g. a channel, mailbox, link or pipe):

! ! send <message> to <mailbox>

• With a mailbox, message passing can still be synchronous

• Direct naming has the advantage of simplicity, whilst indirect naming aids the decomposition of the software; a mailbox can be seen as an interface between parts of the program

Page 28: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

Operating Systems and Distributed Systems

Process Naming• A naming scheme is symmetric if both sender and

receiver name each other (directly or indirectly)

send <message> to <process-name>

wait <message> from <process-name>

send <message> to <mailbox>

wait <message> from <mailbox>

• It is asymmetric if the receiver names no specific source but accepts messages from any process (or mailbox)

wait <message>

• Asymmetric naming fits the client-server paradigm

• With indirect the intermediary could have:

– a many-to-one structure – a many-to-many structure

– a one-to-one structure – a one-to-many

Operating Systems and Distributed Systems

Message Structure

• A language usually allows any data object of any defined type (predefined or user) to be transmitted in a message

• Need to convert to a standard format for transmission across a network in a heterogeneous environment

• OS allow only arrays of bytes to be sent

Page 29: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

Operating Systems and Distributed Systems

POSIX/Unix Message Queues

• POSIX/Unix supports asynchronous, indirect message passing through the notion of message queues

• A message queue can have many readers and many writers

• Priority may be associated with the queue

• Intended for communication between processes (not threads)

• Message queues have attributes which indicate their maximum size, the size of each message, the number of messages currently queued etc.

• An attribute object is used to set the queue attributes when the queue is created

Operating Systems and Distributed Systems

POSIX Message Queues

• Message queues are given a name when they are created

• To gain access to the queue, requires an mq_open name

• mq_open is used to both create and open an already existing queue (also mq_close and mq_unlink)

• Sending and receiving messages is done via mq_send and mq_receive

• Data is read/written from/to a character buffer.

• If the buffer is full or empty, the sending/receiving process is blocked unless the attribute O_NONBLOCK has been set for the queue (in which case an error return is given)

• If senders and receivers are waiting when a message queue becomes unblocked, it is not specified which one is woken up unless the priority scheduling option is specified

Page 30: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

Operating Systems and Distributed Systems

POSIX Message Queues

• A process can also indicate that a signal should be sent to it when an empty queue receives a message and there are no waiting receivers

• In this way, a process can continue executing whilst waiting for messages to arrive or one or more message queues

• It is also possible for a process to wait for a signal to arrive; this allows the equivalent of selective waiting to be implemented

• If the process is multi-threaded, each thread is considered to be a potential sender/receiver in its own right

Operating Systems and Distributed Systems60

Unix Message passing

Page 31: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

Operating Systems and Distributed Systems 61

Unix Message passing

int msgsnd(int msqid, struct msgbuf *msgp,

size_t msgsz, int msgflg);

ssize_t msgrcv(int msqid, struct msgbuf *msgp,

size_t msgsz, long msgtyp, int msgflg);

appends a copy of the message pointed to by

msgp to the message queue whose identifier is

specified by msqid.

reads a message from the message queue specified by

msqid into the msgbuf pointed to by the msgp

argument, removing the read message from the queue.

int msqid = msgget(key_t key, int msgflg);

returns the message queue identifier associated to the value of the key argument.

int msgctl(int msqid, int cmd, struct msqid_ds *buf);

performs the control operation specified by cmd on the message queue with identifier msqid

struct msgbuf {

long mtype; /* msg type > 0*/

char mtext[1];/* msg data */!

};

struct msgbuf {

long mtype; /* msg type > 0*/

char mtext[1];/* msg data */!

};

62

The producer-consumer problem with N messages

Producer Consumer

Empty Pool

Full Pool

Page 32: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

msgid2

msgid1

63

The producer-consumer problem with N messages

Producer Consumer

struct msgformat {

long pid;

char buf[BSIZE];

! int occupied;

! int nextin, nextout;

} msg;

struct msgformat {

long pid;

char buf[BSIZE];

! int occupied;

! int nextin, nextout;

} msg;

64

The producer-consumer problem with N messages

Page 33: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

int main() {

! char item[NUMITEMS]="IT'S A SMALL WORLD, AFTER ALL."; // items to be put in buffer

! int i;

pid_t pid;

struct msqid_ds *buf;

! int msgid1, msgid2;

!

if((msgid1=msgget(MSGKEY, 0777|IPC_CREAT))==-1) perror("Error queue1");

! if((msgid2=msgget(MSGKEY2, 0777|IPC_CREAT))==-1) perror("Error queue2");

!

! pid=getpid(); !

! for(i=0;i<NUMITEMS;i++) { // produce an item, one character from item[]

! ! if (item[i] == '\0') break; /* Quit if at end of string. */

! !

! ! //wait for an empty to arrive

! ! msgrcv(msgid1,&msg,sizeof(msg),0,0);

! ! printf("PRODUCER %d: message received from process %ld\n", pid, msg.pid);

! !

! ! //construct a message to send

! ! msg.buf[msg.nextin++] = item[i];! //put new item in buffer

! ! msg.nextin %= BSIZE;

! ! msg.occupied++;

! ! msg.pid=pid;

! ! //send message

! ! msgsnd(msgid2,&msg,sizeof(msg),0);

! ! printf("PRODUCER %d: message sent \n", pid);

}

! msgctl(msgid1,IPC_RMID,buf); //remove queue1

! msgctl(msgid2,IPC_RMID,buf); //remove queue2!

}

65

The producer-consumer problem with N messages

int main() {

! char item;

! int i;

int msgid1, msgid2;

pid_t pid;

if((msgid1=msgget(MSGKEY, 0777))==-1) perror("Errore coda1");

! if((msgid2 = msgget(MSGKEY2, 0777))==-1) perror("Errore coda2");

! pid=getpid();

! !

! //construct an empty message to send

! msg.nextout = 0; msg.occupied = 0; msg.pid=pid;!

! msgsnd(msgid1,&msg,sizeof(msg),0);

! printf("CONSUMER %d: empty message sent \n",pid);

! for(i=0;i<NUMITEMS;i++){ //

! ! //get message containing item

! ! msgrcv(msgid2,&msg,sizeof(msg),0,0);

! ! printf("CONSUMER %d: message received from process %ld\n",pid, msg.pid);

! !

! ! //extract item from message! !

! ! item = msg.buf[msg.nextout++];! //take item from buffer! !

! ! msg.nextout %= BSIZE;

! ! msg.occupied--;

! !

! ! //send message

! ! msg.pid=pid;

! ! msgsnd(msgid1,&msg,sizeof(msg),0);

! ! printf("CONSUMER %d: message sent \n",pid);

! !

! ! printf("%c\n",item);! //prints the item

}

}66

The producer-consumer problem with N messages

Page 34: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

67

The producer-consumer problem with N messages

Compile and run msgproducer.c

and msgconsumer.c!

Operating Systems and Distributed Systems

Some classical problems in process synchronization

• Dinining philosophers

• Readers and writers

• Sleepy barber

Page 35: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

Operating Systems and Distributed Systems69

Dining Philosophers

• N philosophers around a table– All are hungry

– All like to think

• N chopsticks available– 1 between each pair of

philosophers

• Philosophers need two chopsticks to eat

• Philosophers alternate between eating and thinking

• Goal: coordinate use of chopsticks

Operating Systems and Distributed Systems70

Dining Philosophers: solution

• Use a semaphore for each chopstick

• A hungry philosopher

– Gets lower, then higher numbered chopstick

– Eats

– Puts down the chopsticks

• Potential problems?

– Deadlock

– Fairness

Page 36: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

71

A nonsolution to the dining philosophers problem

Dining Philosophers: solution

72Solution to dining philosophers problem (part 1)

Dining Philosophers: solution

Page 37: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

73

Dining Philosophers: solution

Operating Systems and Distributed Systems

Readers-Writers Problem

Readers

Writers

Page 38: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

Operating Systems and Distributed Systems

Readers-Writers Problem (2)

Reader

Shared Resource

ReaderReaderReaderReaderReaderReaderReader

WriterWriterWriterWriterWriterWriterWriter

Operating Systems and Distributed Systems

Readers-Writers Problem (3)

Reader

Shared Resource

ReaderReaderReaderReaderReaderReaderReader

WriterWriterWriterWriterWriterWriterWriter

Page 39: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

Operating Systems and Distributed Systems

Readers-Writers Problem (4)

Reader

Shared Resource

ReaderReaderReaderReaderReaderReaderReader

WriterWriterWriterWriterWriterWriter

Writer

78

The readers and writers problem

Page 40: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

Operating Systems and Distributed Systems79

The Sleepy Barber Problem

Operating Systems and Distributed Systems

The Sleepy Barber

Waiting Room

Entrance to Waiting

Room (sliding door)

Entrance to Barber’s

Room (sliding door)

Shop Exit

• Barber can cut one person’s hair at a time

• Other customers wait in a waiting room

Page 41: Multi-Process Systems: Synchronizationhomes.di.unimi.it/~boccignone/GiuseppeBoccignone... · } Operating Systems and Distributed Systems Critical Section with Semaphores Compile and

Operating Systems and Distributed Systems81

The Sleepy Barber Problem