返回介绍

7. 线程基础

发布于 2024-10-12 21:58:07 字数 2563 浏览 0 评论 0 收藏 0

线程(thread)有时被称做轻量级进程(lightweight process, LWP)。

线程是程序执行流的最小单元,由 PID、寄存器和堆栈组成。进程有一到多个线程,共享程序内存空间及进程级资源(open files、signal 等)。

私有空间

  • 执行栈(stack),存储函数调用过程信息(frame, call stack)。
  • 局部存储(thread local storage, TLS)。
  • 寄存器(register)。
$ ulimit -a

stack size              (kbytes, -s) 8192

注意:ulimit 分 soft 和 hard 两种。hard 是 soft 的上限,由 root 设定。而 soft 可以由用户更改。也可通过 getrlimitsetrlimit 函数获取或设置。

线程状态

  • 运行(running)。
  • 就绪(ready):可立即运行,但 CPU 被占用。
  • 等待(waitting):等待事件(如 I/O 或同步)发生,无法执行。
        +--------------- 本线程被选中 ----------------+
        |                                           |
        v                                           |
   +---------+                                  +-------+
   | running | --------- 时间片用尽 ------------> | ready |
   +---------+                                  +-------+
        |                                           ^
        |               +----------+                |
        +----- 开始 ---> | waitting | --- 结束 ------+
                        +----------+

执行调度

被选中的线程拥有一段可执行时间,该段时间被称做时间片(time slice)。一旦时间耗尽,则被操作系统暂停,重新进入就绪状态,等待下次再被选中。

内核按系统线程分配时间片。线程是调度单位,进程是资源单位。理论上多线程可以抢夺更多时间片,但过多线程会导致更多上下文切换,反而降低效率。同一进程内的线程之间,无法分享时间片。

$ cat /proc/sys/kernel/sched_rr_timeslice_ms
100

$ sysctl kernel.sched_rr_timeslice_ms
kernel.sched_rr_timeslice_ms = 100

常见线程调度有优先级(priority schedule)和轮转法(round robin)。高优先级的线程会更早执行,直到没有高优先级,低优先级才被调度。但操作系统会自动调整优先级,以使得调度更有效率。

根据执行方式,可分为 IO 密集型(IO bound)和 CPU 密集型(CPU bound)两类。IO 密集型更受欢迎,更容易被操作系统提升优先级,以便及时处理事件反馈。

使用 nice 可以影响进程优先级,进而影响到时间片大小。也就是 nice 值越高,抢占能力越弱,优先级越低。优先级 PRI += NI ,值越小优先级越高。 NI[-20, 19]

$ nice -n 19 command
$ renice -n 19 -p {pid}

过低的优先级,可能导致线程饿死(starvation)。也就是说,在它执行之前,总有高优先级线程试图执行。为避免这类情况,操作系统会提升长时间等待线程的优先级。

  • 用户指定优先级。
  • 根据等待状态频繁程度提升或降低优先级。
  • 长时间得不到执行被提升优先级。

上下文

将 CPU 从一个线程切换到另一个线程,需要保存其状态(register),并恢复另一线程状态,这就是上下文切换(context switch)。IP/PC 寄存器被修改后,切换完成,开始执行新线程代码。

进程和线程都有上下文切换。区别在于线程切换期间,虚拟内存状态(TLB)保持不变。

向操作系统移交控制权,保存状态(register, MMU/TLB, ...),可能导致高速缓存失效(L1/2/3 cache)。

$ watch grep "ctxt" /proc/{pid}/status  # 定时输出

voluntary_ctxt_switches:	3508          # 自愿(比如有较多 IO 等待操作)
nonvoluntary_ctxt_switches:	64          # 强制(时间片用完)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文