进程终止
_exit是unix系统调用
exit是_exit的c语言封装,它进行
- 调用退出处理程序
- 刷新stdio缓冲区
- 调用
_exit从main函数返回一般即exit
解释器文件

Shell内建命令
将某一 shell 命令实现为内建命令,不外乎如下两个目的:效率以及会对 shell 产生副作用(side effect)。
一些频繁使用的命令(如pwd、echo和test)逻辑都很简单,放在shell内部实现效率会更高。
将其他命令内置于 shell实现,则是希望命令对shell本身能产生副作用:更改shell所存储的信息,修改shell进程的属性,亦或是影响shell进程的运行。例如,cd命令必须改变shell自身的工作目录,故而不应在一个独立进程中执行。产生副作用的内建命令还包括exec、exit、read、set、source、ulimit、umask、wait以及shell的作业控制(job-control)命令(jobs、fg和bg)。想了解shell支持的全套内建命令,可参考shell手册页(manual page)文档。
exec时fd会被保持
close-on-exec
内核调度实体
KSE
进程和线程都是KSE,它们的区别只在于共享资源的形式
多进程 vs 多线程
多线程无隔离性
- 注意线程安全,使用线程安全版本的函数或者以线程安全的方式调用函数
- 恶意线程可以占用大量资源,修改其他线程的内存空间
多线程易于数据共享,上下文切换开销小,创建快
pthread_join
线程之间平等peer,没有进程的父子关系
只能等待某一个特定的线程,不能像进程的wait一样模糊匹配
条件变量总是结合互斥量使用。条件变量就共享变量的状态改变发出通知,而互斥量则提供对该共享变量访问的互斥(mutual exclusion)。
为什么条件变量的惯常写法使用while而不是if
通知发送到唤醒并不是原子的。换言之,中间可能发生了改变条件状态的事- 设计时设置“宽松的”判断条件或许更为简单。有时,用条件变量来表征可能性而非确定性,在设计应用程序时会更为简单。换言之,就条件变量发送信号意味着“可能有些事情”需要接收信号的线程去响应,而不是“一定有一些事情”要做。
- 虚假唤醒
pthread_once
确保多线程中只进行一次初始化
库函数TSD改造
pthread_key_create为该库函数添加一个键,绑定该库函数的deconstructor。使用pthread_once保证这个注册过程只进行一次
每次库函数的调用中,首先pthread_getspecific查询是否已经为本线程创建缓冲空间,如果没有就创建并使用pthread_setspecific来记录
线程安全
可供多个线程安全调用
可重入(避免全局和静态)是线程安全的
线程特有数据TSD 静态局部
线程局部存储TLS 全局
在线程中fork exec是可以的,但需要注意fork到exec期间的异步安全(信号安全)
非异步安全函数依赖全局状态,如malloc是线程安全的,但它不是可重入的
异步安全
event driven
比如使用信号算是一种
more than non-blocking, non-blocking还是需要spin wait的
Problems with threads
-
Synchronization bugs (data races, deadlock, etc.)
-
Limits concurrency to number of threads
-
Stacks are expensive
-
Possible interleaving of threads is hard to reason about Problems with events
-
State across events can’t be stored on stack (“stack ripping”) coroutine
-
Possible interleaving of events is hard to reason about
-
Sequential execution is lost, has to be manually traced across code
-
Blocking I/O with threads is simple to write but doesn’t scale well
- Number of outstanding operations ⇐ number of threads
-
Asynchronous I/O allows a thread to have many I/O operations in parallel
- Asynchronous because code handles completion at some later time, such that it is not synchronized with the start of the request
-
Asynchronous I/O leads to event-driven programming
-
Event-driven programming complicates sequential code
- A linear series of I/O calls is spread across multiple functions, which a programmer must manually string together in their head
-
Coroutines are the standard C++ middle ground: looks like threads, but many coroutines can execute on a single thread
-
Other languages have different approaches
-
The tension between simplicity and performance has been an open challenge in systems code for the past 25 years
Scale out, not up