Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Sysprog 14

1,896 views

Published on

Published in: Technology
  • Be the first to comment

Sysprog 14

  1. 1. C/C++ Linux System Programming <ul><ul><li>Session 14 </li></ul></ul><ul><ul><li>User-space System Programming </li></ul></ul><ul><ul><li> – session 4 </li></ul></ul>
  2. 2. Outline <ul><li>Threads: Concepts and Linux Implementation </li></ul><ul><li>Creation and Termination </li></ul><ul><li>Cancellation </li></ul><ul><li>Mutual Exclusion and Threads </li></ul><ul><li>Process vs. Threads </li></ul>
  3. 3. Threads <ul><li>A lightweight process: </li></ul><ul><ul><li>Share address space (unlike process) </li></ul></ul><ul><ul><li>Another execution context (like a process) </li></ul></ul><ul><li>Why? </li></ul><ul><ul><li>IPC </li></ul></ul><ul><ul><li>Dedicated (blocking) listener: Consider IPC or Device case </li></ul></ul>
  4. 4. Linux Implementation of Threads <ul><li>Clone system call: </li></ul><ul><ul><li>Part of a thread group (2.6): </li></ul></ul><ul><ul><ul><li>all have same TGID (PID) </li></ul></ul></ul><ul><ul><ul><li>Have separate TID (gettid) </li></ul></ul></ul><ul><ul><ul><li>Same signal dispositions (separate masks) </li></ul></ul></ul><ul><ul><ul><li>Any thread can wait for the child of any other thread </li></ul></ul></ul><ul><ul><ul><li>A common VM </li></ul></ul></ul><ul><ul><li>Ability to pass start of exec stack </li></ul></ul><ul><li>Scheduled and context-switched as processes </li></ul><ul><li>Rest is in User-space (libpthread.so) </li></ul>
  5. 5. Pthread Object “Template”: <ul><li>pthread_xxx_t </li></ul><ul><li>int pthread_xxx_init (pthread_xxx_t *obj, pthread_xxxattr_t *attr); </li></ul><ul><li>int pthread_xxx_destroy (pthread_xxx_t *obj); </li></ul>
  6. 6. Attribute Objects “Template”: <ul><li>Initialization/Deletion of type xxx: </li></ul><ul><ul><li>int pthread_xxxattr_destroy(pthread_xxxattr_t *attr); </li></ul></ul><ul><ul><li>int pthread_xxxattr_init(pthread_xxxattr_t *attr); </li></ul></ul><ul><li>get/set attribute type xxx, attribute aaa: </li></ul><ul><ul><li>int pthread_xxxattr_getaaa(pthread_xxxattr_t *attr, int *val); </li></ul></ul><ul><ul><li>int pthread_xxxattr_setaaa(pthread_xxxattr_t *attr, int val); </li></ul></ul>
  7. 7. Thread creation / termination <ul><li>int pthread_create(pthread_t *restrict thread, const pthread_attr_t *restrict attr, void *(*start_routine)(void*), void *restrict arg); </li></ul><ul><li>void pthread_exit(void *value_ptr); </li></ul><ul><li>Thread attributes (pthread_attr_t) </li></ul><ul><li>pthread_t – opaque identifier: </li></ul><ul><ul><li>pthread_t pthread_self(void); </li></ul></ul><ul><ul><li>int pthread_equal(pthread_t t1, pthread_t t2); </li></ul></ul>
  8. 8. Reaping <ul><li>Wait equivalent (from any thread not just creator): </li></ul><ul><ul><li>int pthread_join(pthread_t thread, void **value_ptr); </li></ul></ul><ul><li>To dodge reaping: </li></ul><ul><ul><li>int pthread_detach(pthread_t thread); </li></ul></ul><ul><li>PTHREAD_CREATE_DETACHED: </li></ul><ul><ul><li>No need to reap (reaping would fail) </li></ul></ul>
  9. 9. Thread Specific Data <ul><li>Need some memory to appear differently to different threads </li></ul><ul><li>Memory is keyed, key created/deleted once: </li></ul><ul><ul><li>int pthread_key_create(pthread_key_t *key, void (*destructor)(void*)); // before any thread uses it </li></ul></ul><ul><ul><li>int pthread_key_delete(pthread_key_t key); // after all threads are done with it </li></ul></ul><ul><ul><li>Destructor is thread specific </li></ul></ul><ul><li>To retrieve/modify memory: </li></ul><ul><ul><li>void *pthread_getspecific(pthread_key_t key); </li></ul></ul><ul><ul><li>int pthread_setspecific(pthread_key_t key, const void *value); </li></ul></ul>
  10. 10. Cancelling Threads <ul><li>int pthread_cancel(pthread_t thread); </li></ul><ul><li>Asynchronous </li></ul><ul><ul><li>not a signal </li></ul></ul><ul><ul><li>Cleanup handling only (unlike signals) </li></ul></ul><ul><li>Order: </li></ul><ul><ul><li>Cancel call </li></ul></ul><ul><ul><li>Cancellation point </li></ul></ul><ul><ul><li>Cleanup handlers </li></ul></ul><ul><ul><li>Thread-specific data destructors </li></ul></ul>
  11. 11. Cancellability: <ul><li>For critical sections, can't cancel </li></ul><ul><ul><li>int pthread_setcancelstate(int state, int *oldstate); // PTHREAD_CANCEL_ENABLE/DISABLE </li></ul></ul><ul><li>For control over cancel-safe points </li></ul><ul><ul><li>int pthread_setcanceltype(int type, int *oldtype); // PTHREAD_CANCEL_ASYNCHRONOUS/DEFERRED </li></ul></ul><ul><ul><li>To place a cancellation point: </li></ul></ul><ul><ul><ul><li>void pthread_testcancel(void); </li></ul></ul></ul><ul><li>Push/pop mindset, call sets on “module” entry, and restore on “module” exit </li></ul>
  12. 12. Cleanup Stack <ul><li>void pthread_cleanup_pop(int execute); </li></ul><ul><li>void pthread_cleanup_push(void (*routine)(void*), void *arg); </li></ul><ul><li>Execution upon: </li></ul><ul><ul><li>Exit </li></ul></ul><ul><ul><li>Cancel </li></ul></ul><ul><ul><li>Pop with non-zero execute </li></ul></ul><ul><li>Both in same lexical scope </li></ul>
  13. 13. static RETSIGTYPE sigexit_handler(int signum) { int i; nslcd_exitsignal=signum; /* cancel all running threads */ for (i=0;i<nslcd_cfg->ldc_threads;i++) if (pthread_cancel(nslcd_threads[i])) { } } static void worker_cleanup(void *arg) { MYLDAP_SESSION *session=(MYLDAP_SESSION *)arg; myldap_session_close(session); } static void *worker(void UNUSED(*arg)) { MYLDAP_SESSION *session; /* create a new LDAP session */ session=myldap_create_session(); /* clean up the session if we're done */ pthread_cleanup_push(worker_cleanup,session); /* start waiting for incoming connections */ while (1) { /* wait for a new connection */ acceptconnection(session); } pthread_cleanup_pop(1); return NULL; } int main(int argc,char *argv[]) { ... install_sighandler(SIGTERM,sigexit_handler); install_sighandler(SIGUSR1,sigexit_handler); install_sighandler(SIGUSR2,sigexit_handler); nslcd_threads= (pthread_t *)malloc(nslcd_cfg->ldc_threads*sizeof(pthread_t)); for (i=0;i<nslcd_cfg->ldc_threads;i++) { if (pthread_create(&nslcd_threads[i],NULL,worker,NULL)) { ... exit(EXIT_FAILURE); } } for (i=0;i<nslcd_cfg->ldc_threads;i++) { if (pthread_join(nslcd_threads[i],NULL)) { ... exit(EXIT_FAILURE); } } }
  14. 14. Mutual Exclusion <ul><li>Threads are much more prone to race conditions – why? </li></ul><ul><li>pthread_mutex_t </li></ul><ul><ul><li>int pthread_mutex_lock(pthread_mutex_t *mutex); </li></ul></ul><ul><ul><li>int pthread_mutex_trylock(pthread_mutex_t *mutex); </li></ul></ul><ul><ul><li>int pthread_mutex_unlock(pthread_mutex_t *mutex); </li></ul></ul><ul><li>pthread_mutex_t is strictly for mutual exclusion, not synchronization (unlike...?) </li></ul>
  15. 15. Mutex Object: pthread_mutex <ul><li>Initialization: </li></ul><ul><ul><li>Static: </li></ul></ul><ul><ul><ul><li>pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; </li></ul></ul></ul><ul><ul><li>Non-static: </li></ul></ul><ul><ul><ul><li>int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); </li></ul></ul></ul><ul><ul><ul><li>int pthread_mutex_destroy(pthread_mutex_t *mutex); </li></ul></ul></ul>
  16. 16. Mutex Types <ul><li>Type Attribute Values </li></ul><ul><ul><li>PTHREAD_MUTEX_DEFAULT: deadlock on double lock by same thread (fast) </li></ul></ul><ul><ul><li>PTHREAD_MUTEX_RECURSIVE: no deadlock, but same # of unlocks before release </li></ul></ul><ul><ul><li>PTHREAD_MUTEX_ERRORCHECK: error on double lock by same thread </li></ul></ul>
  17. 17. RT Attributes: <ul><li>Protocol: </li></ul><ul><ul><li>PTHREAD_PRIO_NONE </li></ul></ul><ul><ul><li>PTHREAD_PRIO_INHERIT: If a higher priority thread is blocked on this, execute at that its prio </li></ul></ul><ul><ul><li>PTHREAD_PRIO_PROTECT: Execute at max (mine, ceiling {all my other mutexes}) </li></ul></ul><ul><li>Prioceiling: </li></ul><ul><ul><li>Highest possible priority to run at: should be > highest priority that locks this mutex </li></ul></ul>
  18. 18. Futex <ul><li>An area in memory used for mutexing threads, atomic operations </li></ul><ul><li>Futex system call in contended cases </li></ul><ul><li>By mmapping this (MMAP_SHARED), we have full kernel support, thus </li></ul><ul><ul><li>Sharing Attribute </li></ul></ul>
  19. 19. Another Mutex Object: Read/Write Locks <ul><li>Multiple concurrent readers </li></ul><ul><li>only one writer allowed (Lock state) </li></ul><ul><li>Attributes: sharing </li></ul><ul><li>Read: </li></ul><ul><ul><li>int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); </li></ul></ul><ul><ul><li>int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); </li></ul></ul><ul><li>Write: </li></ul><ul><ul><li>int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); </li></ul></ul><ul><ul><li>int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); </li></ul></ul><ul><li>Unlock: </li></ul><ul><ul><li>int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); </li></ul></ul>
  20. 20. Condition Variables <ul><li>Synchronization </li></ul><ul><li>pthread_cond_t cond = PTHREAD_COND_INITIALIZER; </li></ul><ul><li>Attributes: sharing </li></ul><ul><li>Always mutexed (why?) </li></ul><ul><li>Implicit cancellation point – upon cancel, do not consume </li></ul><ul><li>int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t mutex); </li></ul><ul><ul><li>Atomically: release mutex and wait on condition, acquired on wakeup </li></ul></ul><ul><li>int pthread_cond_broadcast(pthread_cond_t *cond); // all </li></ul><ul><li>Int pthread_cond_signal(pthread_cond_t *cond); // at least one </li></ul>
  21. 21. static void PushMessage (WMMsgQueuePtr pQueue, WMMsgNodePtr pNode) { /* Lock the queue mutex */ pthread_mutex_lock (&pQueue->pmMutex); pNode->pNext = NULL; if (pQueue->pTail != NULL) { pQueue->pTail->pNext = pNode; } pQueue->pTail = pNode; if (pQueue->pHead == NULL) { pQueue->pHead = pNode; } /* Increase the count of elements in the queue by one */ ++g_nQueueSize; /* Release the queue mutex */ pthread_mutex_unlock (&pQueue->pmMutex); /* Signal that the queue is not empty */ pthread_cond_signal (&pQueue->pcNotEmpty); } static WMMsgNodePtr PopMessage (WMMsgQueuePtr pQueue, WMInfoPtr pWMInfo) { /* Lock the queue mutex */ pthread_mutex_lock (&pQueue->pmMutex); /* Wait for --- */ while (pQueue->pHead == NULL) { pthread_cond_wait (&pQueue->pcNotEmpty, &pQueue->pmMutex); } pNode = pQueue->pHead; if (pQueue->pHead != NULL) { pQueue->pHead = pQueue->pHead->pNext; } if (pQueue->pTail == pNode) { pQueue->pTail = NULL; } /* Drop the number of elements in the queue by one */ --g_nQueueSize; /* Release the queue mutex */ pthread_mutex_unlock (&pQueue->pmMutex); return pNode; }
  22. 22. Process vs Thread <ul><li>Ease of communication </li></ul><ul><li>Creation overhead </li></ul><ul><li>Potential for race conditions </li></ul><ul><li>Vulnerability coupling </li></ul><ul><li>SMP systems </li></ul>

×