目录
一、什么是线程
二、线程的创建
三、重新理解线程
四、进程和线程对比
一、什么是线程
- 在一个程序里的一个执行路线就叫做线程(thread)。更准确的定义是:线程是“一个进程内部的控制序列”
- 一切进程至少都有一个执行线程
- 线程在进程内部运行,本质是在进程地址空间内运行
- 在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更加轻量化
- 透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流
我们知道,操作系统是软硬件的管理者,他需要去管理系统中的各种资源,那么进程作为操作系统中重要的一环,也必须被管理,管理的本质是先描述在组织,因此操作系统用PCB来进行描述进程,并使用链表将进程们组织串联起来。
那么一个进程是有他自己的task_struct(linux下的PCB),task_struct里存在指向虚拟内存的指针,再通过页表进行虚拟到物理的转化。这一系列的操作都是操作系统帮我们完成的,但是似乎创建进程的代价比较大。
创建进程的目的,大部分都是让进程帮我们执行一个执行流解决不了的代码(比如当前进程需要下载,还需要一个进程帮我们播放),如果我将需要执行的代码写在一个程序中,在一个进程的条件下,让多个执行流分别执行不同的代码。这种就可以通过创建进程来实现。
进程可以创建线程,每一个线程也有自己的task_struct,但是指向的进程地址空间是当前进程的,同样可以访问里面的数据,那就可以让各个线程分别跑不同的代码,这样就在代价不大的情况下实现了目的。
因此,我们可以说线程是比进程更加轻量化的执行流或者说线程是在进程内部执行的一种执行流。
那么现在我们看待进程,不仅仅是只有这个进程了,还包括他创建的线程资源。创建进程,操作系统就会给他分配资源,进程是承担操作系统的基本实体。
我们要将PCB放到CPU上进行调度,CPU不会区分你是进程还是线程,他只管你是不是一个PCB,是不是合法的内容,由于线程是<=进程的,那么我们可以说线程才是CPU调度的基本单位。
讲个小故事:
有一个1家3口的家庭,他们每个人都有自己的事情要做,孩子好好学习,父母努力挣钱,目的就是让整个家庭过得更好。其中每个人相当于一个线程,整个家庭相当于进程,家里的资源共享,分工协作完成任务。
而之前我们学习的进程,相当于一个家庭只有一个人。独自完成任务,如果需要,可 以让其他家庭来帮助我完成,但是家庭与家庭之间是具有独立性的。
二、线程的创建
创建线程我们需要用到POSIX线程库
POSIX线程库
- 与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”打头的
- 要使用这些函数库,要通过引入头文件<pthread.h>
- 链接这些线程函数库时要使用编译器命令的“-lpthread”选项
我们可以使用pthread.h头文件下的pthread_create函数创建线程
功能:创建一个新的线程
- 参数 thread:返回线程ID
- attr:设置线程的属性,attr为NULL表示使用默认属性
- start_routine:是个函数地址,线程启动后要执行的函数
- arg:传给线程启动函数的参数
- 返回值:成功返回0;失败返回错误码
代码如下,我们使用创建一个线程,给他传递函数地址,让他去帮我们执行函数里面的内容。因为每个线程都是一个执行流,因此效果应该是两个while循环一起跑。
#include<iostream>
#include<unistd.h>
#include<pthread.h>
using namespace std;
void* TreadToutine(void *arg)
{
const char* threadname = (const char*) arg;
while(1)
{
cout<<"我是一个新线程"<<threadname<<endl;
sleep(1);
}
}
int main()
{
pthread_t tid;
pthread_create(&tid,NULL,TreadToutine, (void*)"thread 1");
//主线程
while(1)
{
cout<<"我是主线程"<<endl;
sleep(1);
}
return 0;
}
注意编译时需要链接pthread库,因此需要加上-lpthread 。
在命令行中输入ps -aL ,可以查看当前运行的轻量级进程,我们发现这两个线程PID是一样的,LWP(Light weight process)不一样。操作系统调度的时候其实看的也是LWP。从此,Linux下的所有执行流都可以把他叫做轻量级进程。
三、重新理解线程
- 现在,我们知道的进程可以创建线程,线程是更加轻量化的执行流,同时CPU调度运行是看PCB的,进程有PCB,线程也有PCB。因此进程和线程都可以放到CPU上运行。
- 操作系统还会根据你的PID来判断CPU调度运行时覆盖切换那些寄存器。因为一个进程,创建的线程他们的PCB是一样的,只是LWP不一样。
- 因此当CPU调度PCB的时候,如果发现你是从A进程到了B进程,那么会将你的进程地址空间、页表、catch缓存等等都要更新,而如果发现你是从A进程的某个线程,切换到B进程的某个线程,那么只需要往部分寄存器做写入就可以了。
由此线程切换比进程切换效率更高。
同时CPU会根据时钟中断来进行进程切换,比如时钟中断是1ms,A进程没有线程,那么他会独享这1ms,B进程加自己有5个线程,那么这5个线程会瓜分1ms。也是为了防止恶意进程创建很多线程独占CPU的资源。
四、进程和线程对比
进程是资源分配的基本单位
线程是调度的基本单位
线程共享进程数据,但也拥有自己的一部分数据:
- 线程ID
- 一组寄存器
- 栈
- errno
- 信号屏蔽字
- 调度优先级
总结就是线程有自己的硬件上下文和栈空间
进程的多个线程共享同一地址空间,因此Text Segment、Data Segment都是共享的,如果定义一个函数,在各线程 中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:
文件描述符表
每种信号的处理方式(SIG_ IGN、SIG_ DFL或者自定义的信号处理函数)
当前工作目录
用户id和组id
进程和线程的关系如下图: