Semaphores

advertisement
Lab 8
CIS 370
UMass Dartmouth


Threads allow parallel execution of processes
or distinct parts of a single process.
All threads within a process share:
⋄The same address space
⋄Process instructions
⋄Most data
⋄Open files (descriptors)
⋄Signals and signal handlers
⋄Current working directory
⋄User and group id

Each thread has a unique:
⋄Thread ID (tid)
⋄Set of registers, stack pointer
⋄Stack for local variables, return addresses
⋄Signal mask
⋄Priority
⋄Return value: errno

Basic thread operations:
⋄Creation
⋄Termination
⋄Joining
⋄Synchronization

In Linux, threads are handled with the Pthreads API.
#include <pthread.h>
pthread_t tid;
pthread_attr_t attr;


//Thread Identifier
//Set of attributes for a thread
A thread can get its own thread identifier using:
pthread_t pthread_self();
Attributes can be initialized using:
int pthread_attr_init(&attr);
int pthread_create(pthread_t *thread, const pthread_attr_t
*attr, void *(*thread_func)(void*), void *arg);
⋄Returns 0 on success, or error on failure.
⋄ thread is a pointer to the created thread.
⋄ attr is a pointer to a set of thread
attributes.
- If attr is NULL, default attributes are used.
⋄ thread_func is the first function a newly
created thread will execute.
⋄ arg is the only parameter to thread_func.
int pthread_join(pthread_t thread, void **value_ptr);
⋄Returns 0 on success, or error on failure.
⋄ thread is an executing thread.
⋄ value_ptr contains the value of the target
thread’s exit parameter.
 A call to join() will suspend the calling thread until
the target thread terminates.
void pthread_exit(void *value_ptr);
⋄Never returns.
⋄ value_ptr is the thread’s exit parameter.
 A call to pthread_exit() will terminate the calling
process and send a reference to value_ptr to any
joining thread.
Output:
Child Thread: Value = 5
Parent Thread: Value = 15
Actual Output:
Expected Output:
Value = 2,000,000
Run1: Value = 1,090,299
Run2: Value = 1,002,569
Run3: Value = 1,003,650
Run4: Value = 1,806,077
Run5: Value = 1,010,439
Run6: Value = 1,243,521
Run7: Value = 1,455,218


If this code is executed serially (Thread 1,
followed by Thread 2) there are no
problems. However threads execute in an
arbitrary order.
Consider the following execution scenario:
Thread 1
Thread 2
Value
tmp = value
---
0
tmp = tmp + 1
---
0
---
tmp = value
0
---
tmp = tmp + 1
0
value = tmp
---
1
value = tmp
1



The problem is caused by allowing
multiple threads to operate on the same
data simultaneously.
Solution: provide functions that will block
one thread if another thread is trying to
access data that it is currently using.
Pthreads may use semaphores to achieve
this.


Concept introduced by E.W. Dijkstra as a
solution to process synchronization.
A Semaphore can be viewed as an integer
variable on which the following operations are
allowed:
⋄Wait()
⋄Signal()

In order to provide mutual exclusion:
wait(semaphore)
// Critical section
signal(semaphore)

Given a Semaphore sem, we’ll define the wait()
and signal() operations as follows.
wait(sem)
if(sem != 0)
decrement sem by one
else
wait until sem becomes non-zero, then decrement
signal(sem)
increment sem by one
if (queue of waiting processes/threads is not empty)
restart the first process/thread in wait queue


To use Semaphores in UNIX:
#include <sys/sem.h>
UNIX semaphore operations are geared to
work on sets of semaphores, rather than
single objects.
⋄ As a result, most semaphore routines are fairly
complicated

Associated with each semaphore in the set:
⋄ semval – The semaphore value 0 or a positive integer
⋄ sempid – The pid of the process that last acted on
the semaphore
⋄ semcnt – Number of processes waiting for the
semaphore to reach a value greater than its current
value
⋄ semzcnt – Number of processes waiting for the
semaphore to reach the value zero.
int semget(key_t key, int nsems, int permflags);
◊
returns the semaphore set ID on success, or -1 on failure.
◊
key is a system-wide unique identifier describing the
semaphore you want to connect to (or create).
◊
nsems gives the number of semaphores required in the
semaphore set.
◊
permflags tells semget() what to do with the semaphore in
question.
-
IPC_CREAT: Create the semaphore if it doesn't already
exist in the kernel.
-
IPC_EXCL: When used with IPC_CREAT, fail if semaphore
already exists.
int semctl(int semid, int sem_num, int command, union semun
ctl_arg);
◊
semid is a valid semaphore identifier, returned by a
previous call to semget().
◊
sem_num identifies a particular semaphore from the
set. The first semaphore in a set is numbered 0.
◊
command controls the behavior of semctl().
◊
ctl_arg is a union defined as:
union semun{
};
int val;
struct semid_ds *buf;
unsigned short *array;
More about the command parameter:
Standard IPC functions
IPC_STAT
IPC_SET
IPC_RMID
Place status info into ctl_arg.buf
Set ownerships/permissions from ctl_arg.buf
Remove semaphore set from system
Single semaphore operations
GETVAL
SETVAL
GETPID
GETNCNT
GETZCNT
Return value of semaphore (semval)
Set value of semaphore to ctl_arg.val
Return value of sempid
Return semcnt
Return semzcnt
All semaphore (set) operations
GETALL
SETALL
Place all semvals into ctl_arg.array
Set all semvals according to ctl_arg.array
‣ One important use of semctl() is to set the initial
values of semaphores, since semget() does not
allow a process to do this.
int semop(int semid, struct sembuf *op_array, size_t num_ops);
‣ This call actually performs fundamental semaphore operations. If
unsuccessful, -1 is returned.
◊
semid is a valid semaphore identifier, returned by a previous
call to semget().
◊
op_array is an array of sembuf structures, defined in
<sys/sem.h>.
◊
num_ops is the number of sembuf structures in the array.
struct sembuf{
};
unsigned short sem_num;
short sem_op;
short sem_flg;
//index in semaphore set
//tells semop() what to do
//options
More about the sem_op parameter:
Case 1: sem_op value negative
This is basically the semaphore wait() operation. If the
semaphore’s semval is ≥ |sem_op|, decrement semval by
|sem_op|, otherwise wait until semval ≥ |sem_op|.
*If sem_flg is set to IPC_NOWAIT, semop() will return an error.
Case 2: sem_op value positive
This is basically the semaphore signal() operation. The value of
sem_op is simply added to the corresponding semval.
Processes waiting on the new value of the semaphore will be
woken up.
Case 3: sem_op value zero
In this case semop() will wait until the semaphore value is
zero, but doesn’t alter semval.
*If sem_flg is set to IPC_NOWAIT, and semval is not already
zero, semop() will return an error immediately.



In this lab, you will experiment with Linux Semaphores to provide
mutual exclusion between threads trying to access a critical section
of code.
You will first have to implement the Semaphore wait() and signal()
functions using the UNIX Semaphore procedures described in these
slides.
Download the template code and modify it to include various
Semaphore operations which will ensure that the threads will
always increment the critical_value variable to 2,000,000.
www.cis.umassd.edu/~jplante/cis370/lab08/lab8_template.c
To compile C code implementing Threads, you will need to append
“-lpthread” in the terminal.
Ex: gcc –o lab8 lab8_template.c -lpthread
Download