Linux内核进程创建、销毁
进程创建
进程创建方式
传统UNIX内核
- 复制父进程所拥有的资源
- 建立新的内存映射关系
- 栈空间
- 创建:页目录、页表不一样
- 创建:页框内容一样
现代UNIX内核
- 写时复制技术
- 建立新的内存映射关系
- 页保护
- 写时创建页框,修改映射关系
- 创建:页目录不一样,页表一样
- 创建:页框内容一样
- 运行:页目录不一样,页表不一定一样
- 运行:页框内容不一定一样
- 轻量级进程
- 页表共享
- 文件表
- 信号处理
- vfork系统调用
- 内存地址共享
- 父进程阻塞
创建一个进程
用户进程创建接口
- clone() ---- 轻量级进程
- fork() ---- clone()
- 子进程是父进程的完整副本,复制了父亲进程的资源,包括内存的内容task_struct内容
- vfork() ---- clone()
- 子进程与父进程共享数据段,而且由vfork()创建的子进程将先于父进程运行
进程创建步骤
进程创建流程
- do_fork()功能:处理进程创建产生的系统调用
- 查找pidmap_array位图,分配新PID
- 父进程被跟踪current->ptrace检测
- 调用copy_process()复制进程描述符
- 启动标志CLONE_STOPPED检测
- vfork标志CLONE_VFORK检测
- 结束并返回PID
进程描述符创建
- copy_process()功能:创建进程描述符
- 入参clone_flags检测
- 调用dup_task_struct()获取进程描述符
- 初始化进程描述符中的list_head数据结构、自旋锁
- 复制信号量、文件表、信号、页表和命名空间
- 复制clone()前的CPU寄存器值
- 使子进程返回0
- 调用sched_fork()初始化新进程调度程序数据结构
- 切换到running状态
- 把新进程描述符插入进程链表
- 把新进程PID插入pidhash[PIDTYE_PID]散列表
- 终止并返回子进程描述符
进程创建标志
标志 | 含义 |
CLONE_PARENT | 创建的子进程的父进程是调用者的父进程,新进程与创建它的进程成了“兄弟”而不是“父子” |
CLONE_FS | 子进程与父进程共享相同的文件系统,包括root、当前目录、umask |
CLONE_FILES | 子进程与父进程共享相同的文件描述符(file descriptor)表 |
CLONE_NEWNS | 在新的namespace启动子进程,namespace描述了进程的文件hierarchy |
CLONE_SIGHAND | 子进程与父进程共享相同的信号处理(signal handler)表 |
CLONE_PTRACE | 若父进程被trace,子进程也被trace |
CLONE_VFORK | 父进程被挂起,直至子进程释放虚拟内存资源 |
CLONE_VM | 子进程与父进程运行于相同的内存空间 |
CLONE_PID | 子进程在创建时PID与父进程一致 |
CLONE_THREAD | Linux 2.4中增加以支持POSIX线程标准,子进程与父进程共享相同的线程群 |
进程销毁
销毁进程的理由
资源回收
- 内存
- 文件
- 信号量
终止一个进程
用户态
- exit()
- pthread_exit()
核心态
- exit_group() ---- 终止线程组
- do_group_exit()
- exit() ---- 终止某线程
- do_exit()
终止current线程组
- 检查SIGNAL_GROUP_EXIT标志
- 调用zap_other_threads()杀死current线程组中的其它进程
- 遍历线程组
- 发送SIGKILL信号
- 调用do_exit()终止自身
终止线程
- 设置删除标识PF_EXITING
- 释放内存页exit_mm()
- 释放信号量exit_sem()
- 释放文件描述符exit_fs()
- 更新父进程和子进程的亲属关系
- 重新调度schedule()
进程被终止之后
进程设计逻辑
- UNIX允许进程查询内核以获得其父进程的PID,或者其任何子进程的执行状态。
- UNIX内核在进程一终止后不会就丢弃包含在进程描述符字段中的数据。
僵死进程
- 技术上来说进程已死,但必须保存它的描述符,直到父进程得到通知。
- 如果父进程在子进程结束前结束如何处理?