33.1 线程栈
创建线程时,每个线程都有一个属于自己的线程栈,且大小固定。
使用pthread_attr_setstacksize()可以设置线程栈的大小。
使用pthread_attr_setstack()可以同时控制线程栈的大小和位置。
33.2 线程和信号
UNIX信号模型如何映射到线程中:
操作线程信号掩码:
刚创建的新进程会从其创建者处继承信号掩码的一份拷贝。线程可以使用pthread_sigmask()来改变或并获取当前的信号掩码。
向线程发送信号:
函数pthread_kill()向同一进程下的另一线程发送信号sig.目标线程由参数thread标识。
因为仅在同一线程中可保持线程ID的唯一性,所以无法调用pthread_kill()向其他进程中的线程发送信号。
pthread_sigqueue()将pthread_kill()和sigqueue()的功能合二为一:向同一进程中的另一线程发送携带数据的信号。
妥善处理异步信号
没有任何pthread API属于异步信号安全函数,均无法在信号处理函数中安全加以调用。
所以当多线程应用程序必须处理异步产生的信号时,通常不应该将信号处理函数作为接收信号到达的通知机制。
推荐的方法如下:
33.3 线程和进程控制
线程和 exec()
线程和fork()
当多线程进程进行fork()时,仅会将发起调用的线程复制到子进程中,其他线程尽在子进程中消失,也不会为这些线程调用清理函数以及针对线程特有数据的解构函数。
这将导致以下问题:
- 互斥量问题:
- 内存泄露问题:
解决方法:
线程和exit()
如果任何线程调用了exit,或者主线程执行了return.那么所有线程都会消失,也不会执行线程特有数据的解构函数以及清理函数。
33.4 线程实现模型
多对一 (M:1)实现 (用户级线程)
在M:1线程实现中,关乎线程创建,调度以及同步的所有细节全部都由进程内用户空间的线程库来处理。对于进程中存在的多个线程,内核一无所知。
- 优点:许多线程操作速度都很快,无需切换到内核模式。此外,由于线程库无需内核支持,所以M:1实现在系统间的移植更方便一些。
- 缺陷:
一对一(M:N)实现(内核级线程)
每一个线程映射一个单独的KSE,内核分别对每个线程做调度处理。线程同步操作通过内核系统调用实现。
- 优点:
- 缺点:
多对多(M:N)实现(两级模型)
M:N实现旨在结合1:1和M:1模型的优点,避免两者的缺点。
问题:过于复杂。
最终,NPTL线程使用的是1:1线程模型。
33.5 Linux POSIX线程的实现
针对Pthread API : Linux 下有两种实现。
- LinuxThreads: 这是最初的Linux线程实现。
- NPTL: 这是Linux线程实现的现代版,性能优于LinuxThreads.
LinuxThreads:
LinuxThreads曾是LINUX上的主流线程实现。
设计要点:
但是有很多与SUSv3 Pthread标准的背离之处。
太多了,就不写了。。。
NPTL:
设计NPTL是为了弥补LinuxThreads的大部分缺陷。
特别是:
其他调整:(这些调整出现在LINUX2.6内核)
不一致的地方:
特定的Linux发布版本中,哪一种线程实现是有效的:
可以通过一些技术找出某个特定系统的线程实现,也可以发现在提供两种线程实现的系统上运行的程序默认使用的实现版本。
$ getconf GNU_LIBPTHREAD_VERSION
选择程序使用的线程实现