Linux — POSIX 线程基础

线程对于Linux后台程序员来说并不陌生,线程带给我们并发能力的提升,也提高了软件开发和问题定位的难度,本文

尝试结合GlibC 代码, 对POSIX的线程做一个简单说明,重点介绍线程的创建,释放和连接上需要注意的问题。

多进程和多线程的都只有一个目的,并行处理,提高CPU利用率。相比进程,线程的优势体现在以下几个方面:

1.线程创建销毁开销小于进程

2.线程上下文切换开销小于进程

3.线程间通信优于进程

针对第三点,这针对第三点,这是一个见仁见智的问题,一个进程下的所有线程共享内存地址,所以线程间的通信可

以很随意,但是为了维护对公共资源的有序读写,又引入了锁,信号量等机制,这些工具一旦使用不当会出现死锁,

临界区竞争等问题,所以多线程的模式也提高了编程难度和定位问题的复杂度.

Linux的线程也叫 轻量级线程(LWP, light weight process),来自 Native POSIX Thread Library (NPTL)库的实现

,该库在1995年被POSIX.1c定为标准API。每个线程拥有自己的task_struct,所以用独立的堆栈空间,能独立的够被

CPU调度。

 #include 

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);

参数性质:

分别为,线程ID,线程属性,执行函数入口,线程执行函数参数.

POSIX 线程的第一个参数 thread 类型为 pthread_t,本质是long int 型。用于返回进程中唯一标识ID。因为该ID只在

进程中唯一识别线程,所以可称为局部线程ID(相对后面用于CPU调度,pid_t类型的全局线程ID而言)。在NPTL

中,thread 存储的是线程描述结构(struct pthread)的地址。

由于栈地址是复用的,所以thread_t* thread的值也会重复,如果主线程创建了一个线程后退出,那么再创建的线程

其 thread 值会和前面的线程一样的

POSIX 线程是CPU的调度实体,所以CPU需要一个唯一ID标示不同的线程,这就是pid_t类型的全局线程ID。Linux

的进程由一个或多个线程组成,所以进程也叫线程组,线程组内的第一个线程称为主线程,该线程ID就是线程组

ID,也是进程ID。而之后创建的线程,其线程组ID(进程ID)不变,每个线程有独立的,pid_t类型的LWP ID,CPU

就是根据该ID获得线程上下文信息(struct_task)实现线程调度.

 

线程的栈:

进程的内存分布布局中,用户空间分为我们熟知的代码段,已初始化数据段,未初始化数据段,堆,栈等等.  随着

进程启动,用户内存空间单独划分一段区域为主线程栈,主线程栈的大小可以动态变化,从高地址向下扩展,随后

创建的线程栈大小则是固定的,线程栈通过mmap方式在内存映射区划分内存,线程栈大小可以通过ulimit -s

查看,默认为8192kb大小,线程栈之间有一个保护区,该区域被访问触发会发信号告警,保护区默认大小是一个

内存页(4096字节).

 

线程退出:

 

#include 

 void pthread_exit(void *retval);

其中参数retval是要退出后要上报的结果,其他线程可以通过pthread_join得到该值. 

需要注意的是,retval要求指向的地址不会因为线程退出而失效,这说明用线程变量存储返回值是不靠谱的.

 

线程的连接:

 

#include 

int pthread_join(pthread_t thread, void **retval);

pthread_join函数用于取出其他线程退出后上报的结果,只能针对可连接的线程(joinable)线程使用,而且每个线程

只能被;连接一次。 参数thread是上下文提到的局部线程ID,线程不能自己连接自己,既不能在thread填写自己的

局部线程ID.  

pthread_join 的作用和进程中的waitpid非常像,waitpid是父进程为子进程“收尸”,pthread_join也是一个线程为另

一个线程“收尸”。父进程如果不进行wait操作,子进程退出后就会成为僵尸进程,线程也一样,可连接(joinable

)的线程在退出后如果没有其他线程调用pthread_join 接住它的退出状态,那它也同样不会释放线程的资源。调

用pthread_join的线程在接收到返回状态前会陷入阻塞。

和进程的wait不同,父进程可以等待任何一个子进程退出,但是线程必须明确指定要等待的线程id。这是因为线程

之间没有父线程和子线程层级关系。一个进程也就是一个线程组,里面只有一个主线程,其他的都是衍生出来的

线程,人人平等,即便主线程调用pthread_exit退出了(不建议这么做),进程还是能继续运行。

 

线程分离:

 

如果线程觉得退出也没什么遗言,可以把线程属性设置为分离状态,线程的分离状态可以在创建线程时配置属性

来调整,pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED)也可以在任何线程中调用

pthread_detach函数来调整该属性

 

#include 

int pthread_deatch(pthread_t thread);

较为普遍的做法是线程内部自己修改分离状态。

pthread_detach(pthread_self())

线程处于分离状态表示退出后没有任何遗言,随着线程结束马上交出栈资源,所以也无需其他线程连接它。

pthread_join 分离状态的线程,会返回错误 EINVAL。

你可能感兴趣的:(操作系统,Linux)