PThreads Primer
This is from PThreads Primer book by Bil Lewis, Daniel J. Berg
Concurrency means that two or more threads can be in the middle of executing
code at the same time; it could be the same code, it could be different code.
They may or may not be actually executing at the same time, but they are in
the middle of it.
Parallelism means that two or more threads actually run at the same time on different CPUs.
A system call is basically a function that ends up trapping to routines in the kernel. we divide system calls into two categories, blocking and nonblocking calls (aka synchronous and asynchronous I/O).
Signals are the UNIX kernel's way of interrupting a running process and letting it know that something of interest has happened.
Synchronization is the method of ensuring that multiple threads coordinate their activities so that one thread doesn't accidently change data that another thread is working on.
Scheduling is the act of placing threads onto CPUs, so that they can execute, and of taking them off of those CPUs so that others can run instead.
Process structure used to contain the memory map, the signal dispatch table, signal mask, user ID, group ID, working directory, etc., along with runtime statistics, CPU state (registers, etc.), and a kernel stack (for executing system calls). A nonblocking system call may send the process a signal to tell it that the call is completed.
Threads are a method of allowing concurrent execution in a single address space. Threads allow parallel execution on multiple processor machines.
Threads own stack and stack pointer; a program counter; some thread information,
When a process makes a system call, the following events occur: 1. The process traps to the kernel. 2. The trap handler runs in kernel mode, and saves all of the registers. 3. It sets the stack pointer to the process structure's kernel stack. 4. The kernel runs the system call. 5. The kernel places any requested data into the user-space structure that the programmer provided. 6. The kernel changes any process structure values affected. 7. The process returns to user mode, replacing the registers and stack pointer, and returns the appropriate value from the system call.
such as scheduling priority, and signal mask, stored in the thread structure; and the CPU registers (the stack pointer and program counter are actually just registers).
Signals are the mechanism that UNIX uses in order to get asynchronous behavior in a program9. How this works in non-threaded programs is this: Your program is running along normally, minding its own business. Then something (another process or the kernel) sends a signal to your process. The kernel then stops your process in its tracks, and forces it to run some other function (it is probably one you have written). That function runs, doing whatever it wants, then it can either return (and your program will then resume right where it left off), or it can do a siglongjmp() (in which case your program resumes at the sigsetjmp() location), or it can just call exit() (causing the entire process to exit).
When a process makes a system call, the following events occur: 1. The program will call sigaction() to declare some function to be the handler for a given signal (say, function foo() will handle SIGUSR1). The kernel will put a pointer to that handler into the process structurefs signal dispatch table. 2. Next, your program will call sigprocmask() to tell the kernel which signals it is willing to accept (here, SIGUSR1, yes; SIGUSR2, no). 3. Finally your program takes off and starts doing what you wrote it to do. 4. Now, when some other process sends your process SIGUSR1, your program will stop what it's doing... 5. and run the handler code you wrote. You have no idea what your program might be doing when the signal arrives. That's the idea with signals, they can be completely asynchronous. 6. When the signal handler is done, it typically just does a return, and your program continues where it left off, as if nothing had happened.
detached thread : intend not to join a thread nondetached thread : intend to do a join
The main thread is always a nondetached thread, so it can be joined
detached thread will clean up after itself upon exit, returning its thread structure, TSD array, and stack to the heap for reuse. A nondetached thread will clean up after itself only after it has been joined1.
pthread_detach: dynamically change the detach status
When any thread falls off the bottom of its initial function, it implicitly calls pthread_exit(), exiting only that one thread. pthread_cancel :one thread to tell another thread to exit. pthread_self() : to return the thread ID thread_name(tid) : to produce a printable string for the TID2
Time Sliced Scheduling Strict Priority Scheduling
cause to context switch. Synchronization. got lock on resource. [devloper control] Preemption. some another higer priority thread runnable [devloper control] Yielding. sched_yield() [devloper control] Time-Slicing. system does time slicing.[system-vendor control]
Thread state Active, Runnable, Sleeping[lock], Zombie
realtime tricks?meaning no blocking system calls, probably no I/O8, no paging (you'll need to lock down all the memory that your thread will use: functions, stack, data.)
SCHED_FIFO : There is no time slicing and threads never change their priorities(unless the programmer makes a call to pthread_setschedparam()). SCHED_RR : This is much the same as SCHED_FIFO, save that there is time slicing and threads rotate in their queues. SCHED_OTHER : POSIX puts no limits on the behavior of this option
four aspects of scheduling attributes Scope: pthread_attr_setscope() allows you to select either PTHREAD_SCOPE_PROCESS (local scheduling, unbound threads) or PTHREAD_SCOPE_SYSTEM (global scheduling, bound threads). Policy : pthread_attr_setschedpolicy() allows you to select SCHED_RR, SCHED_FIFO, or SCHED_OTHER, or other implementation-defined policies. Priority : pthread_attr_setschedparam() allows you to set the priority level of a thread by setting the sched_param struct element param.sched_priority. Inheritance : pthread_setinheritsched() allows you to specify if the scheduling policy and parameters will be inherited from the creating thread (PTHREAD_INHERIT_SCHED), or will be set directly by the other functions (PTHREAD_EXPLICIT_SCHED).
Implementation of synchronization requires the existence of an atomic test and set instruction in hardware.
A critical section is a section of code that must be allowed to complete atomically with no interruption that affects its completion.
There are two basic things you want to do. Thing one is that you want to protect shared data. This is what locks do. Thing two is that you want to prevent threads from running when there's nothing for them to do. You don't want them spinning, wasting time. This is what semaphores, condition variables, join, barriers, etc. are for.
Mutexes The mutual exclusion lock.pthread_mutex_lock() and pthread_mutex_unlock().
pthread_mutex_trylock() returns 0 if you get the lock, and EBUSY if you don't. If you get EBUSY, you'll have to figure out something else to do, as entering the critical section anyway would be highly antisocial.
counting semaphore is a variable that you can increment[sem_post] arbitrarily high, but decrement[sem_wait] only to zero.
POSIX semaphores are unique among synchronization variables in one particular fashion: They are async safe, meaning that it is legal to call sem_post() from a signal handler. No other synchronization variable is async safe. So, if you want to write a signal handler that causes some other thread to wake up, this is the way to do it.
Parallelism means that two or more threads actually run at the same time on different CPUs.
A system call is basically a function that ends up trapping to routines in the kernel. we divide system calls into two categories, blocking and nonblocking calls (aka synchronous and asynchronous I/O).
Signals are the UNIX kernel's way of interrupting a running process and letting it know that something of interest has happened.
Synchronization is the method of ensuring that multiple threads coordinate their activities so that one thread doesn't accidently change data that another thread is working on.
Scheduling is the act of placing threads onto CPUs, so that they can execute, and of taking them off of those CPUs so that others can run instead.
Process structure used to contain the memory map, the signal dispatch table, signal mask, user ID, group ID, working directory, etc., along with runtime statistics, CPU state (registers, etc.), and a kernel stack (for executing system calls). A nonblocking system call may send the process a signal to tell it that the call is completed.
Threads are a method of allowing concurrent execution in a single address space. Threads allow parallel execution on multiple processor machines.
Threads own stack and stack pointer; a program counter; some thread information,
When a process makes a system call, the following events occur: 1. The process traps to the kernel. 2. The trap handler runs in kernel mode, and saves all of the registers. 3. It sets the stack pointer to the process structure's kernel stack. 4. The kernel runs the system call. 5. The kernel places any requested data into the user-space structure that the programmer provided. 6. The kernel changes any process structure values affected. 7. The process returns to user mode, replacing the registers and stack pointer, and returns the appropriate value from the system call.
such as scheduling priority, and signal mask, stored in the thread structure; and the CPU registers (the stack pointer and program counter are actually just registers).
Signals are the mechanism that UNIX uses in order to get asynchronous behavior in a program9. How this works in non-threaded programs is this: Your program is running along normally, minding its own business. Then something (another process or the kernel) sends a signal to your process. The kernel then stops your process in its tracks, and forces it to run some other function (it is probably one you have written). That function runs, doing whatever it wants, then it can either return (and your program will then resume right where it left off), or it can do a siglongjmp() (in which case your program resumes at the sigsetjmp() location), or it can just call exit() (causing the entire process to exit).
When a process makes a system call, the following events occur: 1. The program will call sigaction() to declare some function to be the handler for a given signal (say, function foo() will handle SIGUSR1). The kernel will put a pointer to that handler into the process structurefs signal dispatch table. 2. Next, your program will call sigprocmask() to tell the kernel which signals it is willing to accept (here, SIGUSR1, yes; SIGUSR2, no). 3. Finally your program takes off and starts doing what you wrote it to do. 4. Now, when some other process sends your process SIGUSR1, your program will stop what it's doing... 5. and run the handler code you wrote. You have no idea what your program might be doing when the signal arrives. That's the idea with signals, they can be completely asynchronous. 6. When the signal handler is done, it typically just does a return, and your program continues where it left off, as if nothing had happened.
detached thread : intend not to join a thread nondetached thread : intend to do a join
The main thread is always a nondetached thread, so it can be joined
detached thread will clean up after itself upon exit, returning its thread structure, TSD array, and stack to the heap for reuse. A nondetached thread will clean up after itself only after it has been joined1.
pthread_detach: dynamically change the detach status
When any thread falls off the bottom of its initial function, it implicitly calls pthread_exit(), exiting only that one thread. pthread_cancel :one thread to tell another thread to exit. pthread_self() : to return the thread ID thread_name(tid) : to produce a printable string for the TID2
Time Sliced Scheduling Strict Priority Scheduling
cause to context switch. Synchronization. got lock on resource. [devloper control] Preemption. some another higer priority thread runnable [devloper control] Yielding. sched_yield() [devloper control] Time-Slicing. system does time slicing.[system-vendor control]
Thread state Active, Runnable, Sleeping[lock], Zombie
realtime tricks?meaning no blocking system calls, probably no I/O8, no paging (you'll need to lock down all the memory that your thread will use: functions, stack, data.)
SCHED_FIFO : There is no time slicing and threads never change their priorities(unless the programmer makes a call to pthread_setschedparam()). SCHED_RR : This is much the same as SCHED_FIFO, save that there is time slicing and threads rotate in their queues. SCHED_OTHER : POSIX puts no limits on the behavior of this option
four aspects of scheduling attributes Scope: pthread_attr_setscope() allows you to select either PTHREAD_SCOPE_PROCESS (local scheduling, unbound threads) or PTHREAD_SCOPE_SYSTEM (global scheduling, bound threads). Policy : pthread_attr_setschedpolicy() allows you to select SCHED_RR, SCHED_FIFO, or SCHED_OTHER, or other implementation-defined policies. Priority : pthread_attr_setschedparam() allows you to set the priority level of a thread by setting the sched_param struct element param.sched_priority. Inheritance : pthread_setinheritsched() allows you to specify if the scheduling policy and parameters will be inherited from the creating thread (PTHREAD_INHERIT_SCHED), or will be set directly by the other functions (PTHREAD_EXPLICIT_SCHED).
Implementation of synchronization requires the existence of an atomic test and set instruction in hardware.
A critical section is a section of code that must be allowed to complete atomically with no interruption that affects its completion.
There are two basic things you want to do. Thing one is that you want to protect shared data. This is what locks do. Thing two is that you want to prevent threads from running when there's nothing for them to do. You don't want them spinning, wasting time. This is what semaphores, condition variables, join, barriers, etc. are for.
Mutexes The mutual exclusion lock.pthread_mutex_lock() and pthread_mutex_unlock().
pthread_mutex_trylock() returns 0 if you get the lock, and EBUSY if you don't. If you get EBUSY, you'll have to figure out something else to do, as entering the critical section anyway would be highly antisocial.
counting semaphore is a variable that you can increment[sem_post] arbitrarily high, but decrement[sem_wait] only to zero.
POSIX semaphores are unique among synchronization variables in one particular fashion: They are async safe, meaning that it is legal to call sem_post() from a signal handler. No other synchronization variable is async safe. So, if you want to write a signal handler that causes some other thread to wake up, this is the way to do it.
No comments:
Post a Comment
would you like it. :)