进程管理
文章目录
1. 进程如何创建
1.1 linux 0.11内核fork进程实现要点
- 每个进程需要有一个内核栈。不管是4KB还是8KB。这个内核栈需要承载:task_struct结构体本身和内核栈
- 继承父进程的task_struct数据结构,然后进行微调
- 设置进程的栈。
- 拷贝父进程的进程地址空间给子进程。
1.2 fork相关函数实现
fork实现
do_fork(SIGCHLD, 0, 0, NULL, NULL);
vfork实现
do_fork(CLONE_VFORK | CLONE_VM | SIGCHID, 0, 0 ,NULL,NULL);
clone实现
do_fork(clone_flags, newsp, 0, parent_tidptr, child_tidptr);
内核线程
do_fork(flags|CLONE_VM|CLONE_UNTRACED,(unsigned long)fn, (unsigned long)arg, NULL, NULL);
-
fork函数
-
子函数会从复函数继承进程地址空间,包括进程上下文、进程堆栈、内存信息、打开的文件描述符、进程优先级、根目录、资源限制、控制终端等。
- 返回值:父进程会返回子进程的PID,子进程会返回0。
NAME fork - create a child process SYNOPSIS #include <sys/types.h> #include <unistd.h> pid_t fork(void); DESCRIPTION fork() creates a new process by duplicating the calling process. The new process is referred to as the child process. The calling process is referred to as the parent process.
-
vfork函数
-
vfork父进程会一直阻塞,知道子进程调用exit()或exec()为止。
#include <sys/types.h> #include <unistd.h> pid_t vfork(void);
-
vfork()函数通过系统调用进入到linux内核,然后通过do_fork()函数来实现。
-
-
clone函数
-
clone()函数用来创建用户线程。fn就是线程的回调函数。
/* Prototype for the glibc wrapper function */ #define _GNU_SOURCE #include <sched.h> int clone(int (*fn)(void *), void *stack, int flags, void *arg, ... /* pid_t *parent_tid, void *tls, pid_t *child_tid */ );
-
clone()函数通过系统调用进入到Linux内核,然后通过do_fork()函数来实现。
-
-
内核线程创建API
kthread_create(threadfn, data, namefmt, arg...) kthread_run(threadfn, data, namefmt, ...)
-
2. do_fork函数
2.1 do_fork函数原型
[kernel/fork.c]
long do_fork(unsigned long clone_flags, unsinged long stack_start, unsigned stack_size, int __user *parent_tidptr, int __user *child_tidptr)
-
do_fork()函数有5个参数。
- clone_flags: 创建进程的标志位集合。
- stack_start: 用户态栈的起始地址。
- stack_size:用户态栈的大小,通常设置为0。
- parent_tidptr和child_tidptr:指向用户空间中地址的两个指针,分别指向父子进程的PID
-
常用的clone标志位
参数标志 含义 CLONE_VM 父子进程共享进程地址空间 CLONE_FS 父子进程共享文件系统信息 CLONE_FILES 父子进程共享打开的文件 父进程被跟踪,子进程也会被跟踪 CLONE_VFORK 在创建子进程时启动linux内核的完成机制(completion),wait_for_completion()会使父进程进入睡眠等待,指到子进程调用execve()或exit()释放虚拟内存资源。 指定子进程拥有同一个父进程。 CLONE_THREAD 父子进程在同一个线程组里。 CLONE_NEWS 为子进程创建新的命名空间 CLONE_SYSVSEM 父子进程共享Sytem V等语义 CLONE_SETTLS 为子进程创建新的TLS(thread local storage) CLONE_PARENT_SETTID 设置父进程的TID
3. 进程终止
-
进程主动终止主要有如下两个途径:
- 从main()函数返回,链接程序会自动添加对exit()系统调用。
- 主动调用exit()系统调用。
-
进程被动终止主要有如下三个途径:
- 进程收到一个自己不能处理的信号。
- 进程在内核态执行的时候产生了一个异常。
- 进程收到SIGKILL等终止信号。
-
exit()系统调用吧退出码转换成内核要求的格式并且调用do_exit()函数来处理。
SYSCALL_DEFINE1(exit, int, error_code) { do_exit((error_code & 0xff) << 8); }
4. Linux内核中线程的实现
- 进程是资源管理的最小单位,线程是程序执行的最小单位。
- Linux线程的实现:在内核里和普通进程是一样的,都是task_struct数据结构俩描述。成为轻量级进程。
- pthread库:应用程序实现线程的库。
5. 0号进程和1号进程
- 进程0是指的Linux内核初始化从无到有创建的一个内核线程。
- 进程0
- idle进程
- swapper进程
- Linux内核初始化函数start_kernel()在初始化完内核所需要的所有数据结构之后会创建里另外一个内核线程,这个内核线程就是进程1或叫init进程。
6. 僵尸进程和托孤进程
- 当一个进程通过exit()系统调用已经终止之后,进入处理与僵尸状态(ZOMBIE)。
- 当父进程通过调用wait()系统调用来获取已终结的子进程的信息之后,内核才会去释放子进程的task_struct数据结构。
- 托孤进程
- 如果父进程先于子进程消亡,那么子进程就变成托孤进程。