一、一个系统中线程ID是否唯一?
就像每个进程有一个进程ID一样,每个线程也有一个线程ID。进程ID在整个系统中是唯一的,但线程ID不同,线程ID只在它所属的进程环境中有效。
这段话选自《UNIX环境高级编程第二版》第11.3节。我第一次看到这段话时,自然而然的认为线程ID既然只在其所述的进程有效,那自然在不同的进程中可以重复才对,不然多浪费资源对吧。但是工作之后我发现事实并不是如此。比如我们在程序设计时经常需要在一定的条件发生时终止某个特定线程,而这时我们的做法是直接给这个线程发终止信号。试想,如果不同的进程拥有相同线程ID的线程,那是不是会出现错杀的情况呢?所以线程的ID在系统中绝对是唯一的。
我们也可以从另外一方面去理解这个问题。我们都知道Linux的线程叫轻量级进程(LWP,Light Weight Process),每个进程有一个或者多个线程(LWPs)。系统运行时以线程为执行单元,那如果一个进程没有起其他线程会怎样呢?那这个进程其实就是自己的主线程,所以单线程进程的线程ID就等于该进程ID。因为进程ID系统唯一,所以线程ID也应该系统唯一。
二、进程优先级是数值越大优先级越高吗?还是数值越低优先级越高?
这个问题我开始也搞糊涂了,因为我发现用ps
和top
两个命令看到同一个进程的优先级不一样,用ps
看时,数值越大进程优先级越高,用top
看时则刚好相反。那到底是怎么回事呢?
首先根据调度策略的不同,我们将进程分为两类:
1. 实时进程
实时进程有SCHE_FIFO
和SCHE_RR
两种调度策略。SCHE_FIFO
的进程只有等当前进程执行完才能调度下一个,而SCHE_RR
采用时间片轮转的方式进行调度。实时进程的优先级范围为[0,MAX_RT_PRIO-1](MAX_RT_PRIO=100),称为实时优先级,并且只有实时进程有实时优先级,这也是实时进程的静态优先级。Linux会根据实时进程的静态优先级计算优先级权重从而实现调度。
2. 普通进程
普通进程的调度策略为SCHE_OTHER
。普通进程没有实时优先级,仅根据nice值获得它的静态优先级。并且nice只对普通进程有效,实时进程没有nice值。
static_prio=MAX_RT_PRIO + 20 + nice
nice的范围为[-20,19],所以普通进程静态优先级的范围为[100,139]。
由上可知进程进程优先级(静态优先级)是数值越大优先级越低,之所以ps
和top
看到的优先级不一样是因为显示的算法不一样。
- 如下图所示,使用
ps -lA
看到的进程优先级ps_prio = static_prio - 40。所以图片中看到的watchdog进程实际的(静态)优先级应为0,而khelper进程优先级应为 100(已减去nice值20);
- 使用
ps -eo pid,cmd,class,pri,rtprio,ni
看到的进程优先级ps_prio = 139-static_prio。此时列出的优先级符合一般的思维,就是显示的数值越大,优先级越高。
- 使用
top
看到的进程优先级top_prio = static_prio-MAX_RT_PRIO。
三、一个线程所在的进程被杀后,该线程会被终止吗?
当然会终止。线程并不能独立运行,线程只是进程的一个执行实体。所以当进程结束时,该进程下的所有线程也就都结束了。但是此时线程退出有正常退出和非正常退出两种情况。Linux创建一个线程的默认状态是joinable,那么这些线程必须由其创建者主动调用pthread_join
来回收资源和获取线程退出状态,如果没有调用pthread_join
的,就会类似于僵尸进程一样,造成资源浪费。如果不想主动回收,可以在线程中调用pthread_detach
来分离线程。这样线程终止时资源就会被系统自动回收。
四、向进程中的一个线程发送终止信号,该进程会退出吗?
如果线程中的任一线程调用了
exit
,_Exit
或者_exit
,那么整个进程机会终止。与此类似,如果信号的默认动作是终止进程,那么,把该信号发给线程会终止整个进程。
单个线程可以通过下列三种方式退出,在不终止整个进程的情况下停止它的控制流。
(1)线程只是从启动例程中返回,返回值是线程退出码;
(2)线程被同一进程中其他线程取消;
(3)线程调用pthread_exit
;
五、什么叫优先级反转和优先级继承?
大家都知道多进程使用共享资源时都需要用到锁,以此达到互斥效果,保证共享数据的安全。我们假设此时系统中有A,B,C三个进程,其中A进程和C进程在运行过程中都需要访问同一块共享内存,用一个信号量控制。三个进程的优先级关系为Prio(A) > Prio(B) > Prio(C)。如下图所示,刚开始C进程在运行时获得了信号量,可是还没有执行完时(没有释放信号量),进程A在T1时刻抢占了Cpu并运行。进程A运行时也需要访问共享内存,可是信号量此时在进程C手里,在T2时刻进程A调用sem_wait()
让出Cpu。可是这个时候进程C并没有马上得到调度,优先级比C高的进程B获得调度并开始执行。T3时刻,进程B运行完之后才轮到进程C运行。T4时刻,进程C使用完共享资源,用sem_post()
让出Cpu,进程A最终得到信号量才继续运行。由此发现,高优先级的进程A的调度与否反而被掌握在了控制了信号量而优先级低它很多的进程C手里,这种情况就叫优先级反转。
优先级继承就是为了解决优先级反转的问题。如下图所示,在T2时刻,Linux使进程C临时获得进程A的高优先级以保证得到调度,等进程C使用完共享数据并释放信号量,恢复进程C的优先级。