线程
线程(Thread)
早期OS中,拥有资源和独立运行的基本单位是进程。然而随着计算机技术的发展,进程出现了很多弊端。由于进程是资源拥有者,创建、撤销与切换存在较大的时空开销,因此后来引入了能独立运行的基本单位线程。
线程是操作系统进行运算调度的基本单位,它被包含在进程中,是进程中的实际运作单位。一个进程中可以并发多个线程,在硬件和运行时允许时,各线程可以并行执行不同的任务。
互动实验
共享数据与私有执行状态
访问权限PRO
PRO EXCLUSIVE探索不止于此
尊享 Pro 权益解锁完整互动实验、进阶解析与精选参考答案。
由于线程通常共享进程资源,创建、结束与切换的开销往往比独立进程小,有助于提高系统内程序的并发程度、资源利用率与吞吐量。因此使用线程可以带来很多好处:
- 创建一个线程通常比创建一个进程花费更少的时间。
- 结束一个线程通常比结束一个进程花费更少的时间。
- 在同一个进程中切换不同线程通常花费更少的时间。
- 线程属于某一个进程,与进程内其他线程共享进程资源,但仍有自己的栈和执行状态。
知识测验
题目
同一进程的每个线程通常独有哪种状态?
子进程(Child Process)
fork()是UNIX或类UNIX中的分叉函数,它创建一个初始内容与父进程相近的子进程。父子进程从fork()返回后分别继续执行,就像两个用户启动了同一应用程序的两个副本。
创建子进程
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void) {
fflush(NULL);
pid_t child = fork();
if (child < 0) {
perror("fork");
return 1;
}
if (child == 0) {
execlp("/bin/ls", "ls", (char *)NULL);
_exit(127);
}
int status;
if (waitpid(child, &status, 0) < 0) {
perror("waitpid");
return 1;
}
return 0;
}互动实验
单步运行fork与exec
访问权限PRO
PRO EXCLUSIVE探索不止于此
尊享 Pro 权益解锁完整互动实验、进阶解析与精选参考答案。
正常情况下,子进程由父进程创建,子进程可以再创建新的进程。父子进程异步执行,父进程不能仅凭自己的执行进度预测子进程何时结束。父进程可以调用wait()或waitpid()等待子进程结束,取得其终止状态并回收剩余的进程记录。
一些情况下会产生僵尸进程(zombie process)和孤儿进程(orphan process):
- 僵尸进程:子进程退出了,但是父进程没有用wait()或waitpid()获取其状态信息,子进程的进程记录仍保存在系统中。
- 孤儿进程:父进程结束了,而一个或多个子进程还在运行。它们由init或其他指定的接管进程接管,退出后的状态由接管进程回收。
动手练习
C编程:一次fork,两个value
在Linux、macOS或其他POSIX系统中编写fork_values.c,令int value = 10,并且只调用一次fork。
- 子进程将value加5,打印它,并以退出码3结束。
- 父进程用waitpid等待这个子进程,然后打印父进程的value和子进程退出码。
- 处理fork、waitpid失败情况,并在运行前预测三行输出。