Overview: threads vs. processes (created with fork)
Property | Processes created with fork | Threads of a process | Ordinary function calls |
variables | get copies of all variables | share global variables | share global variables |
IDs | get new process IDs | share the same process ID but have unique thread ID | share the same process ID (and thread ID) |
Communication | Must explicitly communicate, e.g.pipes or use small integer return value |
May communicate with return value or shared variables if done carefully |
May communicate with return value or shared variables (don't have to be careful) |
Parallelism (one CPU) | Concurrent | Concurrent | Sequential |
Parallelism (multiple CPUs) | May be executed simultaneously | Kernel threads may be executed simultaneously | Sequential |
Look at the programs getsizeschild, getsizesthread and getsizescall.
USP Chapter 12: POSIX Threads:
Motivating problem: monitor multiple file descriptors:
Methods you should know about:
Consider a function processfd called as an ordinary function and as a created thread.
Figure 12.1 (page 412): Program that makes an ordinary call to processfd has a single thread of execution.
Figure 12.2 (page 412): Program that creates a new thread to execute processfd has two threads of execution.
A function that is used as a thread must have a special format.
It takes a single parameter of type pointer to void and returns a pointer to
void.
The parameter type allows any pointer to be passed.
This can point to a structure, so in effect, the function can use any
number of parameters.
We will discuss the meaning of the return value later.
The processfd function used above might have prototype:
void *processfd(void *arg);
Instead of passing the file descriptor to be monitored directly, we pass
a pointer to it.
The function my access the parameter as follows:
       int fd;
       fd = *((int *)arg);
Creating a thread:
void *processfd(void *arg); int error; int fd; pthread_t tid; if (error = pthread_create(&tid, NULL, processfd, &fd)) fprintf(stderr, "Failed to create thread: %s\n", strerror(error));Look at Program 12.1, processfd.
Program 12.2, monitorfd.c is a function that creates threads to
monitor an array of open file descriptors.
The function is passed an array of file descriptors and the size of the array.
For each file descriptor it creates a thread and then waits for all of the
threads to complete.
12.3 Thread Management
POSIX function | description |
pthread_cancel | terminate another thread |
pthread_create | create a thread |
pthread_detach | set thread to release resources |
pthread_equal | test two thread IDs for equality |
pthread_exit | exit a thread without exiting process |
pthread_kill | send a signal to a thread |
pthread_join | wait for a thread |
pthread_self | find out own thread ID |
Most POSIX functions return 0 on success and a nonzero error code on failure.
They do not set errno but the value returned when an error occurs
has the value that errno would have.
None of the POSIX thread functions ever return the error EINTR.
Most of the pthread functions are declared in pthread.h.
12.3.1 The Thread ID
Each thread has an id of type pthread_t.
On most systems this is just an integer (like a process ID) but it does not
have to be.
A thread can get its ID with pthread_self and IDs can be compared with pthread_equal
pthread_t pthread_self(void); int pthread_equal(thread_t t1, pthread_t t2);
12.3.2 Creating a thread
A thread is created with pthread_create
int pthread_create(pthread_t *restrict thread, const pthread_attr_t *restrict attr, void *(*start_routine)(void *), void *restrict arg);
12.3.3 Detaching and joining
int pthread_detach(pthread_t thread);Example 12.5 shows how to make a thread detached:
void *processfd(void *arg); int error; int fd pthread_t tid; if (error = pthread_create(&tid, NULL, processfd, &fd)) fprintf(stderr, "Failed to create thread: %s\n", strerror(error)); else if (error = pthread_detach(tid)) fprintf(stderr, "Failed to detach thread: %s\n", strerror(error));Example 12.6 shows how a thread can detach itself:
void *detachfun(void *arg) { int i = *((int *)(arg)); if (!pthread_detach(pthread_self())) return NULL; fprintf(stderr, "My argument is %d\n", i); return NULL; }
int pthread_join(pthread_t thread, void **value_ptr);Example 12.7 shows how you might do this:
int error; int *exitcodep; pthread_t tid; if (error = pthread_join(tid, &exitcodep)) fprintf(stderr, "Failed to join thread: %s\n", strerror(error)); else fprintf(stderr, "The exit code was %d\n", *exitcodep);
12.3.4 Exiting and Cancellation
A process can terminate when:
When a thread is done, it can call return from its main function (the one used by pthread_create) or it can call pthread_exit
void pthread_exit(void *value_ptr);One thread can request that another exit with pthread_cancel
int pthread_cancel(pthread_t thread);The pthread_cancel returns after making the request.
Stop here for now ...
The result of the request depends on the target thread's type and state.
The possible states are
A thread can change its state with:
int pthread_setcancelstate(int state, int *oldstate);
Program 12.3, processfdcancel, goes into the disable state while it is executing docommand.
The possible cancellation types are
int pthread_setcanceltype(int type, int *oldtype); void pthread_testcancel(void);
12.3.5 Passing parameters and returning values
You can pass multiple parameters to a thread by passing a pointer, such as
an array, to the thread when it is created.
Care must be taken in receiving return values.
Where is the number of bytes copied stored?
It cannot be in an automatic variable in copyfilemalloc.
copyfilemalloc calls malloc to create space for the value.
The calling program must free this space if it is going to run for a long time.
Look at Program 12.5, copyfilemalloc
An alternate is for the creating thread to allocate the space for the return value and tell the thread where this is (using the thread parameter).
Program 12.6, callcopypass passes an array of 3 integers to
the created thread.
The thread stores the return value in the last entry of the array.
Look at Program 12.6 and Program 12.7, copyfilepass
We could have used an array of size 2.
When creating multiple threads that are to be joined, you must
Look at program 12.9, badparameters.
This program creates a number of threads, passing
a pointer to the loop index as a parameter.
Exercise 12.14: What can you return from this thread:
void *whichexit(void *arg) { int n; int np1[1]; int *np2; char s1[10]; char s2[] = "I am done"; n = 3; np1[0] = n; np2 = (int *)malloc(sizeof(int)); *np2 = n; strcpy(s1, "Done"); return(NULL); }
Cancellation Points
accept | mq_timedsend | putpmsg | sigsuspend |
aio_suspend | msgrcv | pwrite | sigtimedwait |
clock_nanosleep | msgsnd | read | sigwait |
close | msync | readv | sigwaitinfo |
connect | nanosleep | recv | sleep |
creat | open | recvfrom | system |
fcntl* | pause | recvmsg | tcdrain |
fsync | poll | select | usleep |
getmsg | pread | sem_timedwait | wait |
getpmsg | pthread_cond_timedwait | sem_wait | waitid |
lockf | pthread_cond_wait | send | waitpid |
mq_receive | pthread_join | sendmsg | write |
mq_send | pthread_testcancel | sendto | writev |
mq_timedreceive | putmsg | sigpause |
catclose | ftell | getwc | pthread_rwlock_wrlock |
catgets | ftello | getwchar | putc |
catopen | ftw | getwd | putc_unlocked |
closedir | fwprintf | glob | putchar |
closelog | fwrite | iconv_close | putchar_unlocked |
ctermid | fwscanf | iconv_open | puts |
dbm_close | getc | ioctl | pututxline |
dbm_delete | getc_unlocked | lseek | putwc |
dbm_fetch | getchar | mkstemp | putwchar |
dbm_nextkey | getchar_unlocked | nftw | readdir |
dbm_open | getcwd | opendir | readdir_r |
dbm_store | getdate | openlog | remove |
dlclose | getgrent | pclose | rename |
dlopen | getgrgid | perror | rewind |
endgrent | getgrgid_r | popen | rewinddir |
endhostent | getgrnam | posix_fadvise | scanf |
endnetent | getgrnam_r | posix_fallocate | seekdir |
endprotoent | gethostbyaddr | posix_madvise | semop |
endpwent | gethostbyname | posix_spawn | setgrent |
endservent | gethostent | posix_spawnp | sethostent |
endutxent | gethostname | posix_trace_clear | setnetent |
fclose | getlogin | posix_trace_close | setprotoent |
fcntl* | getlogin_r | posix_trace_create | setpwent |
fflush | getnetbyaddr | posix_trace_create_withlog | setservent |
fgetc | getnetbyname | posix_trace_eventtypelist_getnext_id | setutxent |
fgetpos | getnetent | posix_trace_eventtypelist_rewind | strerror |
fgets | getprotobyname | posix_trace_flush | syslog |
fgetwc | getprotobynumber | posix_trace_get_attr | tmpfile |
fgetws | getprotoent | posix_trace_get_filter | tmpnam |
fopen | getpwent | posix_trace_get_status | ttyname |
fprintf | getpwnam | posix_trace_getnext_event | ttyname_r |
fputc | getpwnam_r | posix_trace_open | ungetc |
fputs | getpwuid | posix_trace_rewind | ungetwc |
fputwc | getpwuid_r | posix_trace_set_filter | unlink |
fputws | gets | posix_trace_shutdown | vfprintf |
fread | getservbyname | posix_trace_timedgetnext_event | vfwprintf |
freopen | getservbyport | posix_typed_mem_open | vprintf |
fscanf | getservent | printf | vwprintf |
fseek | getutxent | pthread_rwlock_rdlock | wprintf |
fseeko | getutxid | pthread_rwlock_timedrdlock | wscanf |
fsetpos | getutxline | pthread_rwlock_timedwrlock |
No other POSIX functions are allowed to introduce cancellation points
12.4 Thread Safety
Almost all system functions and library functions are safe to use with threads.
The exceptions are given in the following table.
asctime | fcvt | getpwnam | nl_langinfo |
basename | ftw | getpwuid | ptsname |
catgets | gcvt | getservbyname | putc_unlocked |
crypt | getc_unlocked | getservbyport | putchar_unlocked |
ctime | getchar_unlocked | getservent | putenv |
dbm_clearerr | getdate | getutxent | pututxline |
dbm_close | getenv | getutxid | rand |
dbm_delete | getgrent | getutxline | readdir |
dbm_error | getgrgid | gmtime | setenv |
dbm_fetch | getgrnam | hcreate | setgrent |
dbm_firstkey | gethostbyaddr | hdestroy | setkey |
dbm_nextkey | gethostbyname | hsearch | setpwent |
dbm_open | gethostent | inet_ntoa | setutxent |
dbm_store | getlogin | l64a | strerror |
dirname | getnetbyaddr | lgamma | strtok |
dlerror | getnetbyname | lgammaf | ttyname |
drand48 | getnetent | lgammal | unsetenv |
ecvt | getopt | localeconv | wcstombs |
encrypt | getprotobyname | localtime | wctomb |
endgrent | getprotobynumber | lrand48 | |
endpwent | getprotoent | mrand48 | |
endutxent | getpwent | nftw |
12.5 User Threads and Kernel Threads
User-level threads run on top of an operating system.
Figure 12.3 (page 433) User-level threads.
Figure 12.4 (page 434) Kernel-level threads.
With a hybrid model, the threads of a process can share several kernel entities.
Figure 12.5 (page 435) A hybrid model.
POSIX threads can support any of these models.
A thread has a contentionscope of PTHREAD_SCOPE_PROCESS or PTHREAD_SCOPE_SYSTEM.
One or both can be supported on a given system.
Thread Attributes
Attributes behave like objects that can be created or destroyed.
property | function | attribute objects | pthread_attr_destroy |
pthread_attr_init | |
state | pthread_attr_getdetachstate |
pthread_attr_setdetachstate | |
stack | pthread_attr_getguardsize |
pthread_attr_setguardsize | |
pthread_attr_getstack | |
pthread_attr_setstack | |
scheduling | pthread_attr_getinheritsched |
pthread_attr_setinheritsched | |
pthread_attr_getschedparam | |
pthread_attr_setschedparam | |
pthread_attr_getschedpolicy | |
pthread_attr_setschedpolicy | |
pthread_attr_getscope | |
pthread_attr_setscope |
int pthread_attr_destroy(pthread_attr_t *attr); int pthread_attr_init(pthread_attr_t *attr);
12.6.1 The thread state
The state is either PTHREAD_CREATE_JOINABLE (the default) or
PTHREAD_CREATE_DETACHED.
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate); int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);Example 12.15: create a detached thread:
int error, fd; pthread_attr_t tattr; pthread_t tid; if (error = pthread_attr_init(&tattr)) fprintf(stderr, "Failed to create attribute object: %s\n", strerror(error)); else if (error = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED)) fprintf(stderr, "Failed to set attribute state to detached: %s\n", strerror(error)); else if (error = pthread_create(&tid, &tattr, processfd, &fd)) fprintf(stderr, "Failed to create thread: %s\n", strerror(error));
The thread stack
You can set a location and size for the thread stack.
int pthread_attr_getstack(const pthread_attr_t *restrict attr, void **restrict stackaddr, size_t *restrict stacksize); int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);Some systems allow you to set a guard for the stack so that an overflow into the guard area can generate a SIGSEGV signal.
int pthread_attr_getguardsize(const pthread_attr_t *restrict attr, size_t *restrict guardsize); int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
12.6.3 Thread scheduling
The contentionscope can be PTHREAD_SCOPE_PROCESS or
PTHREAD_SCOPE_SYSTEM.
The scope determines whether the thread competes with other threads of the process or with other processes in the system.
int pthread_attr_getscope(const pthread_attr_t *restrict attr, int *restrict contentionscope); int pthread_attr_setscope(pthread_attr_t *attr, int contentionscope);
Example 12.16: create a thread that contends with other processes
int error; int fd; pthread_attr_t tattr; pthread_t tid; if (error = pthread_attr_init(&tattr)) fprintf(stderr, "Failed to create an attribute object:%s\n", strerror(error)); else if (error = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM)) fprintf(stderr, "Failed to set scope to system:%s\n", strerror(error)); else if (error = pthread_create(&tid, &tattr, processfd, &fd)) fprintf(stderr, "Failed to create a thread:%s\n", strerror(error));
Inheritance of scheduling policy:
With PTHREAD_INHERIT_SCHED the scheduling attributes of the
attribute object are ignored and the created thread has the same
scheduling attributes of the creating thread.
With PTHREAD_EXPLICIT_SCHED the scheduling is taken from the
attribute.
int pthread_attr_getinheritsched(const pthread_attr_t *restrict attr, int *restrict inheritsched); int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched);
Scheduling parameters and policy.
Typical scheduling policies are SCHED_FIFO, SCHED_RR>
and SCHED_OTHER.
What is supported is system dependent.
Each policy is modified by scheduling parameters stored in a struct sched_param
This may contain a priority or a quantum value.
int pthread_attr_getschedparam(const pthread_attr_t *restrict attr, struct sched_param *restrict param); int pthread_attr_setschedparam(pthread_attr_t *restrict attr, const struct sched_param *restrict param); int pthread_attr_getschedpolicy(const pthread_attr_t *restrict attr, int *restrict policy); int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);