我们都知道进程是linux内核中最为重要的一个抽象概念,那么我们平时在fork一个进程时,该进程究竟是咋么产生的呢?
本篇博文会浅谈一下在进程创建过程中扮演着重要角色的do_fork函数
1.内核如何来抽象一个进程
内核通过一个叫做task_struct的结构体来抽象一个进程
该结构体的定义(以内核2.6为例)在include/linux.sched.h中
截取部分task_struct如下
task_struct{
volatile long state;
void *stack;
atomic_t usage;
unsigned int flags;
unsigned int ptrace;
int lock_depth;
int prio, static_prio, normal_prio;
unsigned int rt_priority;
const struct sched_class *sched_class;
struct sched_entity se;
struct sched_rt_entity rt;
unsigned char fpu_counter;
struct list_head tasks;
struct plist_node pushable_tasks;
struct mm_struct *mm, *active_mm;
pid_t pid;
struct task_struct *real_parent;
struct task_struct *parent;
struct list_head children;
struct fs_struct *fs;
struct files_struct *files;
struct signal_struct *signal;
}
上述task_struct属性是我节选出的部分其结构体中的属性,我们从中可以大致了解到标识一个进程的属性大致会有该用以表示该进程所处的状态,进程的标志,以及进程是否被其他进程跟踪,进程锁的深度,进程的优先级,进程的pid,进程的父母,进程的孩子链表,进程所打开的文件描述符表,进程所处的文件系统,进程的信号。。。。等等一堆我们平时可能遇到的和进程相关的东西
2.do_fork简单分析
接触linuxC编程的人都知道,创建一个进程我们需要调用fork函数,fork其实又是调用了clone函数来实现的,而clone函数中最关键的函数就是do_fork函数。
在分析do_fork前我们脑海中可以大致想象一下,进程究竟是如何被创建出来的,假如让你来创建一个进程你会咋么做?
我们可以这样去分析,既然原来的进程被抽象成一个task_struct,那么新进程也是一个task_struct只不过它里面的一些属性会不同与原来的task_struct,那么创建一个新进程所要做的工作就是赋值一个与原来进程一样都的task_struct结构,然后然后将新进程的task_struct不同于原来task_struct的属性进行修改即可
do_fork定义在kernel/fork.c文件中
1.在分析该函数之前我们先来分析一下它的函数的各个参数
参数如下
long do_fork(unsigned long clone_flags,
unsigned long stack_start,
struct pt_regs *regs,
unsigned long stack_size,
int __user *parent_tidptr,
int __user *child_tidptr)
个参数具体含义如下
1.clone_flags:该参数是此函数中最重要的一个参数,该值中的每个位都代表对子进程task_struct中的每种属性的设置
2.stack_start:子进程用户态堆栈的开始地址
3.regs:当系统发生系统调用时,需从用户态切换到内核态,此结构体用来保存此时用户态进程中的通用寄存器中的值,并被存放在内核态堆栈中
4.stack_size:目前未被使用,通常设为0
5.parent_tidptr:父进程在用户态下pid的地址
6.child_tidptr:子进程在用户态下pid的地址
其中clone_flags的标志位宏定义如下:
#define CSIGNAL 0x000000ff /* signal mask to be sent at exit */
#define CLONE_VM 0x00000100 /* set if VM shared between processes */
#define CLONE_FS 0x00000200 /* set if fs info shared between processes */
#define CLONE_FILES 0x00000400 /* set if open files shared between processes */
#define CLONE_SIGHAND 0x00000800 /* set if signal handlers and blocked signals shared */
#define CLONE_PTRACE 0x00002000 /* set if we want to let tracing continue on the child too */
#define CLONE_VFORK 0x00004000 /* set if the parent wants the child to wake it up on mm_release */
#define CLONE_PARENT 0x00008000 /* set if we want to have the same parent as the cloner */
#define CLONE_THREAD 0x00010000 /* Same thread group? */
#define CLONE_NEWNS 0x00020000 /* New namespace group? */