- 操作系统就是一个中断处理程序
- 负责将第一个进程加载完后 第一个进程通过fork execve exit一系列系统调用创建其他的
- 创建状态机 : fork
- 替换 : execve
- 终止 : _exit
- 请问main退出后的detached thread的行为 ?
Linux 操作系统启动流程
- CPU Reset → Firmware → Loader → Kernel _start() → 第一个程序 /bin/init → 程序 (状态机) 执行 + 系统调用
操作系统为 (所有) 程序提供 API
- 进程 (状态机) 管理
- fork, execve, exit - 状态机的创建/改变/删除 ← 今天的主题
- 存储 (地址空间) 管理
- mmap - 虚拟地址空间管理
- 文件 (数据对象) 管理
- open, close, read, write - 文件访问管理
- mkdir, link, unlink - 目录管理
- 进程 (状态机) 管理
创建状态机
- 如果要创建状态机,我们应该提供什么样的 API?
- UNIX 的答案: fork
- 做一份状态机完整的复制 (内存、寄存器现场)
替换状态机
- UNIX 的答案: execve
- 将当前运行的状态机重置成成另一个程序的初始状态
- int execve(const char *filename, char * const argv, char * const envp);
- 执行名为 filename 的程序
- 允许对新状态机设置参数 argv (v) 和环境变量 envp (e)
- 刚好对应了 main() 的参数!
终止状态机
- 有了 fork, execve 我们就能自由执行任何程序了,最后只缺一个销毁状态机的函数!
- UNIX 的答案: _exit
- void _exit(int status)
- 立即摧毁状态机
- 销毁当前状态机,并允许有一个返回值
- 子进程终止会通知父进程 (后续课程解释)
- 这个简单……
- 但问题来了:多线程程序怎么办?
exit 的几种写法 (它们是不同)
exit(0) - stdlib.h 中声明的 libc 函数
- 因为是libc的库函数,所以会帮助我们做libc的clean up。比如将libc缓冲区中的数据写出去
- 会调用 atexit 注册的函数
- return 也会将没flush的缓冲区清空。main种调用return就是相当于调用了exit(0)
- 执行 “exit_group” 系统调用终止整个进程 (所有线程)
- 那么操作系统会把当前进程所有的资源,包括地址空间、io、线程、管道、fd等等等等全部终止使用并且回收
- 因为是libc的库函数,所以会帮助我们做libc的clean up。比如将libc缓冲区中的数据写出去
_exit(0) - glibc 的 syscall wrapper
- 是系统调用,不会帮助我们做libc的clean up(因为他根本就不知道libc)。直接就终止了整个进程。
- 不会调用 atexit注册的函数
- 执行 “exit_group” 系统调用终止整个进程 (所有线程)
- 是系统调用,不会帮助我们做libc的clean up(因为他根本就不知道libc)。直接就终止了整个进程。
syscall(SYS_exit, 0)
- 执行 “exit” 系统调用终止当前线程
- 不会调用 atexit注册的函数
pthread_exit(0)
- libc的函数 , 所以会帮助我们做libc的clean up。比如将libc缓冲区中的数据写出去
- 会调用atexit注册的函数
- 当有多个thread时,执行”exit“系统调用终止当前thread
- 当只有一个thread时,执行exit_group终止process
- libc的函数 , 所以会帮助我们做libc的clean up。比如将libc缓冲区中的数据写出去
1 |
|
问题
(1)请问main退出后的detached thread的行为 ?解答如下
关键在于 main 如何退出。
- exit(0) / return 0 : 整个process被终止
- mutli-thread程序中,任意thread调用exit(0)(最后会到系统调用exit_group),整个进程被终止,所有thread(包括detached thread)都被销毁。(进程的地址空间都没了,线程当然活不了)
- main中调用return 0就相当于调用了exit(0)。
- pthread_exit : 只是终止当前main thread。
- 只退出当前thread。(最后会调用到系统调用exit)
- detached thread 仍运行
- exit(0) / return 0 : 整个process被终止
(2)编程时是否应该保证在main线程结束前join thread?或者说保证一个detached的thread在main exit之前就结束?
- 解答:普通thread在main线程结束前,一定要join thread,保证业务执行的完整性(像C++,你没有设置子线程detach,也不join它,main函数运行完,还有子线程没执行完,它都给你直接报core dump了,你直接都能看出来); detach thread不需要,一般detach thread设计的目的,就是在后台做一些非关键性任务,是否正常结束并没有要求,也不会出什么错误,如果要求任务的执行必须是完整的,不能设计成detach thread
atexit科普 : atexit (func)用来设置一个程序正常结束前调用的函数. 当程序通过调用exit ()或从main中return 时, func 会先被调用, 然后再结束程序.