260 likes | 419 Views
Thread Synchronization with Semaphores. Lab 8 CIS 370 UMass Dartmouth. Threads. 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)
E N D
Thread Synchronization with Semaphores Lab 8 CIS 370 UMass Dartmouth
Threads • 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
Threads • 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
POSIX Threads (Pthreads) • In Linux, threads are handled with the Pthreads API. #include <pthread.h> pthread_ttid;//Thread Identifier pthread_attr_tattr;//Set of attributes for a thread • A thread can get its own thread identifier using: pthread_tpthread_self(); • Attributes can be initialized using: intpthread_attr_init(&attr);
Pthreads - Creation intpthread_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.
Pthreads - Joining intpthread_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.
Pthreads - Termination 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.
Pthreads - Example Output: Child Thread: Value = 5 Parent Thread: Value = 15
Pthreads – Another Example Actual Output: 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 Expected Output: Value = 2,000,000
Pthreads – Another Example • 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:
Pthreads – Another Example • 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.
Semaphores • 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)
Semaphores • 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
Semaphores • 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
Semaphores • 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.
Get/create semaphore set – semget() intsemget(key_t key, intnsems, intpermflags); • 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.
Control semaphore set – semctl() intsemctl(intsemid, intsem_num, int command, union semunctl_arg); • semidis 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{ intval; structsemid_ds *buf; unsigned short *array; };
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 More about the command parameter: 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
Semaphore Initialization Example • One important use of semctl() is to set the initial values of semaphores, since semget() does not allow a process to do this.
Semaphores – semop() intsemop(intsemid, structsembuf *op_array, size_tnum_ops); • This call actually performs fundamental semaphore operations. If unsuccessful, -1 is returned. • semidis 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. structsembuf{ unsigned short sem_num; //index in semaphore set short sem_op; //tells semop() what to do short sem_flg; //options };
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. More about the sem_op parameter: 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.
Assignment • 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