// lecture notes: 15th Feb 2016 Topics: -- Thread(fs/gs registers) -- locks -- semaphores -- condition variables the keyword : __thread __thread int x = 0; (makes private to each and every thread. every thread has its own copy of x) how to increment x ? We have access to the following things : threadID, kernalID, PthreadID, $SP void fn(){ x = x + 1; } TCB and TLB are constant size, keep track of TLS addresss is the key in doing so. We can keep track of offset from a table of TID vs. TLS Address. But Intel we have fs and gs registers which do thsi for us which allows the following execution like "mov 600 ($fs), $eax" "man" these things: pthread_kill(pthread_t, int) tkill(281442, 11) <- given tid tgkill(28140, 28142, 11) <- gives pid, tid <- explicit thread group. Allows one level of security. pthread_cancel(pthread_id, ...) two levels of threading: thread level ->> kernel level threading pthread clone sys call pthread_tryjoin_np() <- doesnt wait for the thread to exit - unix specific pthread_detach() <- called by child thread, no join allowed then zoombie : parent didnt wait for child to exit eg: pid = fork(); if(pid == 0){ <- child processs gpid = fork(); <- grand child if(gpid >0){ <- parent death exit(0); } getppid(); <- returns 1 which is pid of INIT } kill -9 -1 : signals all process of INIT to death, instant death. lookup : why we need to detach? lookup SIGCHLD (signal generated for parent when child dies) ________________________________________________ LOCKS: -- mutual exclusion -- compare & swap -- spin lock -- reader writer -- mutual exclusion: struct is: lock_t llock; lock_init(&llock); ------------------ how it happens: lock (&llock); x = x + 1 unlock(&llock); -- compare and swap wastes cpu cycles void lock(lock_t *l){ while(cmp&swap(&p->owner, 0, tid) != 0); <- spin here(spin lock) return; } void unlock(lock_t *l){ l->owner = 0 } --------------- futex paper quiz monday --------------- FUTEX -> fast user level mutex READ FUTEXES ARE TRICKY Paper two-level locks: struct lock_t{ pid_t owner; pid_t nwaiting; } int r = cmp&swp(&l->owner, 0, tid); if(r==0) return; make_syscall_to_acquire_lock(); ----------------------- SEMAPHORES struct BinSema{ int count; } sem_t s; sem_init(&s); . . . . . . sem_post(&s); sem_wait(&s); void sem_post(sem_t *s){ s -> count++ /// atomic } void sem_wait(sem_t *s){ while(sem -> coutner ==0); sem->counter--; } ------------------------ SEMAPHORES WITH QUEUES struct BinSema{ int count; queue_t waiters; int nwaiters; } void sem_wait(sem_t *s){ add_to_queue(tid); park(); sem->counter--; } void sem_post(sem_t *s){ s -> count++; tid = head_of_queue(); signal(tid); } you can have semaphores which allow multiple producers or consumers --------------------------- PRODUCER CONSUMER WITH SEMAPHORES consumer(){ sem_wait(&s); lock(queue->lock); if(queue->length >0){ item=dequeu; . . . unlock(queue->lock); } } man these things: pthread_mutex_t; pthread_mutex_lock pthread_mutex_unlock pthread_mutex_trylock pthread_mutex_init pthread_atfork() ---------------------------------- MALLOC Assignment LD_PRELOAD malloc.c -> libmalloc.so (shared object) write your own single threaded appln to test this object LD_PRELOAD = "../path/to/libmalloc.so" ./a.out