1 / 41

Operating Systems, 112

Operating Systems, 112. Practical Session 3 Threads. Threads. Executed within a process Allow multiple independent executions under the same process (container) Possible states: running, ready, blocked, terminated.

ernst
Download Presentation

Operating Systems, 112

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Operating Systems, 112 Practical Session 3 Threads

  2. Threads • Executed within a process • Allow multiple independentexecutions under the same process (container) • Possible states: running, ready, blocked, terminated. • In most of today’s operating systems, a process is created with at least one thread but may have more than one thread (multithreading).

  3. Threads - Advantages • Shareopen files, data structures, global variables, child processes, etc. • Peer threads can communicate without using System calls • Threads are faster to create/terminate/switch than processes (have no resources attached) • Parallelism which improve overall performance: • A single core CPU and a substantial amount of computing and I/O • Multiple cores

  4. Threads - Disadvantages • Share open files, data structures, global variables, child processes, etc. • No protection between threads – one can read/write/wipe out/corrupt the other’s data • Sending some signals (such as SIGSTOP) to a process affects all threads running within it.

  5. Threads vs. Processes(“classic” approach – Linux’s clone results in some ambiguity) Signal handlers must be shared among all threads of a multithreaded application; however, each thread must have its own mask of pending and blocked signals (POSIX 1003.1).

  6. Threads- motivation • Dispatcher thread: • while (TRUE) { • get_next_request(&buf); • handoff_work(&buf); • } Dispatcher • Worker thread: • while (TRUE) { • wait_for_work(&buf); • look_for_page_in_cache(&buf, &page); • if (page_not_in_cache(&page)) • read_page_from_disk(&buf, &page); • return page(&page); • } Workers Web page cache Webpage request Why are threads better in this case? Example from “Modern Operating Systems”, 2nd Edition, pg. 88

  7. Threads – some known Issues • Does the fork() command duplicate just the calling thread or all threads of the process? • OS dependent; many UNIX systems implement both types of fork() (e.g. Solaris 10). • Does the exec() command replace the entire process? • The entire process is replaced including all its threads.

  8. User-level and Kernel-level Threads User-level Threads Kernel-level Threads P1 P2 Scheduler User space Kernel space Kernel Scheduler Scheduler

  9. User-level threads • The kernel sees just the main thread of the process (all other threads that run within the process’ context are “invisible” to the OS) • The user application – not the kernel – is responsible for scheduling CPU time for its internal threads within the running time scheduled by the kernel to it.

  10. User-level threads (cont’d) • The kernel’s inability to distinguish between user level threads makes it difficult to design preemptive scheduling for such thread. • When context switching is made directly towards the entire process, clock interrupts are usually used for this purpose. User level thread will usually have to voluntarily give up the CPU. • If a thread makes a blocking system call, the entire process is blocked. • Will only utilize a single CPU.

  11. Kernel-level threads • All threads are visible to the kernel. • The kernel manages the threads. • The kernel schedules each thread within the time-slice of each process. • The user cannot define the scheduling policy. • Context switching is slower for kernel threads than for user-level threads. • Because the kernel knows about the threads, in multiple CPU machines, each CPU can run a different thread of the same process, at the same time.

  12. User-level vs. kernel-level threads

  13. A tech. note on POSIX threads • When the first Unix and POSIX functions were designed it was assumed that there will be a single thread of execution. • Consider a naïve implementation of errno in a multi threaded environment for example. • Hence, the need for reentrant functions. • While this is supported by many standard functions, the compiler must be aware of the need for re-entrant functions: • gcc –D_REENTRANT –lpthread …

  14. Threads in POSIX (pthreads)

  15. Threads in POSIX (pthreads) – cont.

  16. Hello World! When compiling a multi-threaded app: gcc–D_REENTRANT –o myprogmyprog.c –lpthread #include <pthread.h> #include <stdio.h> void *printme() { printf("Hello World!\n"); return NULL; } void main() { pthread_ttcb; void *status; if (pthread_create(&tcb, NULL, printme, NULL) != 0) { perror("pthread_create"); exit(1); } if (pthread_join(tcb, &status) != 0) { perror("pthread_join"); exit(1); } } What can happen if we remove the join part?

  17. Example A – Version 1 void *printme(void *id) { int *i; i = (int *)id; printf("Hi. I'm thread %d\n", *i); return NULL; } void main() { int i, vals[4]; pthread_t tids[4]; void *retval; for (i = 0; i < 4; i++) { vals[i] = i; pthread_create(tids+i, NULL, printme, vals+i); } for (i = 0; i < 4; i++) { printf("Trying to join with tid%d\n", i); pthread_join(tids[i], &retval); printf("Joined with tid%d\n", i); } }

  18. Example A – Version 1possible output Trying to join with tid0 Hi. I'm thread 0 Hi. I'm thread 1 Hi. I'm thread 2 Hi. I'm thread 3 Joined with tid0 Trying to join with tid1 Joined with tid1 Trying to join with tid2 Joined with tid2 Trying to join with tid3 Joined with tid3

  19. Example A – Version 2 void *printme(void *id) { int*i; i = (int*)id; printf("Hi. I'm thread %d\n", *i); pthread_exit(NULL); } void main() { inti, vals[4]; pthread_ttids[4]; void *retval; for (i = 0; i < 4; i++) { vals[i] = i; pthread_create(tids+i, NULL, printme, vals+i); } for (i = 0; i < 4; i++) { printf("Trying to join with tid%d\n", i); pthread_join(tids[i], &retval); printf("Joined with tid%d\n", i); } pthread_exit(NULL); }

  20. Example A – Version 2possible output Trying to join with tid0 Hi. I'm thread 0 Hi. I'm thread 1 Hi. I'm thread 2 Hi. I'm thread 3 Joined with tid0 Trying to join with tid1 Joined with tid1 Trying to join with tid2 Joined with tid2 Trying to join with tid3 Joined with tid3

  21. Example A – Version 3 void *printme(void *id) { int*i; i = (int*)id; printf("Hi. I'm thread %d\n", *i); pthread_exit(NULL); } void main() { inti, vals[4]; pthread_ttids[4]; void *retval; for (i = 0; i < 4; i++) { vals[i] = i; pthread_create(tids+i, NULL, printme, vals+i); } pthread_exit(NULL); for (i = 0; i < 4; i++) { printf("Trying to join with tid%d\n", i); pthread_join(tids[i], &retval); printf("Joined with tid%d\n", i); } }

  22. Example A – Version 3output Hi. I'm thread 0 Hi. I'm thread 1 Hi. I'm thread 2 Hi. I'm thread 3 If the main thread calls pthread_exit(), the process will continue executing until the last thread terminates or the process is terminated

  23. Example A – Version 4 void *printme(void *id) { int *i = (int *)id; sleep(5); printf("Hi. I'm thread %d\n", *i); pthread_exit(NULL); } int main() { int i, vals[4]; pthread_t tids[4]; void *retval; for (i = 0; i < 4; i++) { vals[i] = i; pthread_create(tids+i, NULL, printme, vals+i); } return 0; }

  24. Example A – Version 4possible output No Output!

  25. Example A – Version 5 void *printme(void *id) { int*i; i = (int*)id; printf("Hi. I'm thread %d\n", *i); exit(0); } main() { inti, vals[4]; pthread_ttids[4]; void *retval; for (i = 0; i < 4; i++) { vals[i] = i; pthread_create(tids+i, NULL, printme, vals+i); } for (i = 0; i < 4; i++) { printf("Trying to join with tid%d\n", i); pthread_join(tids[i], &retval); printf("Joined with tid%d\n", i); } pthread_exit(NULL); }

  26. Example A – Version 5possible output Trying to join with tid0 Hi. I'm thread 0

  27. Threads in XV6 (Assignment 2) • XV6 doesn’t support threads. • Only processes can be created using the fork() system call. • Thread support can be added to XV6: • Implementing the clone(…) system call. • Configuring clone(…) to create a process with the same memory space as its parent process. • Adding some basic threads functionality (create, terminate, join…)

  28. Thread-specific data Programs often need global or static variables that have different values in different threads:Thread-specific data (TSD). Each thread possesses a private memory block, the TSD area. This area is indexed by TSD keys (Map). TSD keys are common to all threads, but the value associated with a given TSD key can be different in each thread. Defined in POSIX.

  29. Thread-specific data – cont. • Question: Why can’t we achieve this by using regular variables? • Because threads share one memory space. • Example: separate log for each thread.

  30. Thread-specific data – cont.

  31. Thread-specific data – cont.

  32. Example B (1) #include <pthread.h> #include <stdio.h> #include <stdlib.h> typedefstructinfo { intposition; } info_t; static pthread_key_ttsdKey = 0; intg1; intgArr[3]; void globalDestructor(void *value) { printf("In the data destructor\n"); if (value != NULL) free(value); pthread_setspecific(tsdKey, NULL); }

  33. Example B (2) void increaseInArray(intval) { // Read position from TSD, and increase the array info_t *myData = pthread_getspecific(tsdKey); gArr[myData->position] += val; printf("Thread %d is increasing position %d by %d\n", pthread_self(), myData->position, val); } void *funcA(void *arg) { int* pPos = (int*)arg; intpos = *pPos; // Allocate memory for TSD info_t *myData = (info_t *)malloc(sizeof(info_t)); myData->position = pos; pthread_setspecific(tsdKey, myData); increaseInArray(pthread_self()); return NULL; }

  34. Example B (3) void *funcB(void *arg) { intlocal = 29; printf("Thread %d: local=%d, g1=%d\n", pthread_self(), local, g1); g1 = 90; pthread_exit(NULL); } int main() { pthread_t t1, t2, t3; intpos1 = 0, pos2 = 1; intlocal = 15; // ... g1 = 7; pthread_key_create(&tsdKey, globalDestructor); printf("Thread main id %d\n", pthread_self());

  35. Example B (4) printf("Creating two threads\n"); pthread_create(&t1, NULL, &funcA, &pos1); pthread_create(&t2, NULL, &funcA, &pos2); printf("Waiting for two threads\n"); pthread_join(t1, NULL); pthread_join(t2, NULL); printf("Creating another thread\n"); pthread_create(&t3, NULL, &funcB, NULL); pthread_join(t3, NULL); printf("The array is: %d, %d\n", gArr[0], gArr[1]); printf("Thread %d: local=%d, g1=%d\n", pthread_self(), local, g1); pthread_key_delete(tsdKey); return 0; }

  36. Example Bpossible output Thread main id 1024 Creating two threads Thread 1026 is increasing position 0 by 1026 In the data destructor Thread 2051 is increasing position 1 by 2051 In the data destructor Waiting for two threads Creating another thread Thread 3074: local=29, g1=7 The array is: 1026, 2051 Thread 1024: local=15, g1=90

  37. Midterm – 2006 בעץ תהליכים כל קודקוד מייצג תהליך.קודקודg מצביע על קודקודqאם"םg הוא אבא של q, כלומר אם g יצר את q. g q (א) שרטטו את עץ התהליכים הנוצר ע"י הרצת הקוד הבא בשפת C. (תנו שמות שרירותיים לתהליכים הנוצרים.) 1. int x; 2. fork(); 3. x = fork(); 4. if(x != 0) • fork(); • 7. printf(“pid= %d”,getpid());

  38. Midterm – 2006 (cont’d) פתרון (א): 1 5 4 2 3 6

  39. Midterm – 2006 (cont’d) ב. מהו הפלט של הרצת התוכנית מסעיף א'? האם זהו הפלט היחיד האפשרי? הסבירו. (עד 3 שורות). פתרון (ב):שישה מספרים גדולים מ 0. הפלט אינו יחיד, כל שישה מספרים נכונים ג. אם בין שורות 4 ו 6 נוסיף את השורה: 5. kill(x, SIGINT); מה ישתנה בעץ התהליכים ובפלט? פתרון (ג): התהליכים 3 ו 4 ימותו. הפלט עשוי להישאר זהה או שיודפסו רק 5 מספרים או רק 4 מספרים

  40. Midterm – 2006 (cont’d) ד. האם ייתכן תסריט שבו לאחר השינוי נקבל פלט זהה לפלט אותו קיבלנו לפני השינוי? אם כן, מהו תסריט זה? אם לא, נמקו מדוע לא יתכן כי נקבל פלט זהה. פתרון (ד): כן יתכן כזה תסריט. נניח שהמתזמן נותן לכל בן שנוצר ב- forkלרוץ עד אשר הוא מסיים הרי שכל אחד יספיק להגיע לשורת ההדפסה.

  41. Midterm – 2006 (cont’d) ה. נניח כי תידרשו לכתוב תוכנית מרובת threads , שתרוץ על מערכת הפעלה התומכת גם ב-user threads וגם ב-kernel threads. באיזו אפשרות תבחרו אם ה-threads מבצעים פעולות I/O רבות? הסבירו (עד 3 שורות). הסבירו באילו נסיבות (כלומר, עבור איזה סוג תוכנית) הייתם בוחרים באפשרות השנייה. פתרון (ה): פעולת I/O גורמת ל user threads כולם לעבור ל blocking שכן מערכת ההפעלה לא מודעת לקיומם ולכן לא סביר לבחור באופציה זו במקרה של ריבוי פעולות I/O. לעומת זאת, כדאי לבחור ב user threads במקרים בהם רוצים למשל שליטה מלאה על התזמון. בנוסף, אם מדובר במערכת עם יחסית מעט מעבדים נעדיף user threads שכן החלפה ביניהם היא מהירה יותר.

More Related