1. task_struct 数据结构
task_struct 是 Linux 内核中表示进程的重要数据结构。它包含了有关进程状态、调度信息、文件系统信息等的详细信息。以下是对 task_struct 数据结构的一些主要字段:
struct task_struct {
volatile long state; // 进程状态
void *stack; // 进程堆栈指针
unsigned int flags; // 进程标志
struct list_head tasks; // 进程链表
pid_t pid; // 进程ID
pid_t tgid; // 进程组ID
struct mm_struct *mm; // 内存管理结构
struct files_struct *files; // 文件描述符表
// ... 其他字段和信息 ...
};
state(进程状态):表示进程的当前状态,可能是运行、睡眠、停止等。状态的改变反映了进程的活动。示例:long state;
pid(进程标识符):进程的唯一标识符。通过这个字段,内核可以追踪和管理进程。示例:pid_t pid;
parent(父进程指针):parent 字段指向父进程的 task_struct 结构。这是用于构建进程树的关键字段。示例:struct task_struct __rcu *parent;
children(子进程链表):children 是一个链表,包含指向当前进程的所有子进程的指针。这有助于实现进程之间的父子关系。示例:struct list_head children;
sibling(兄弟进程链表):sibling 是一个链表,包含指向与当前进程具有相同父进程的其他进程的指针。这是同一进程组的进程之间的链接。示例:struct list_head sibling;
comm(进程名):comm 字段包含了进程的名称。它是一个简短的字符串,用于标识进程。示例:char comm[TASK_COMM_LEN];
mm(内存管理):mm 字段指向描述进程内存布局和管理的结构。这包括进程的地址空间布局、页表等信息。示例:struct mm_struct *mm;
active_mm(当前地址空间):active_mm 字段指向当前进程正在使用的地址空间。在进程切换时,这个字段可能会改变。示例:struct mm_struct *active_mm;
group_leader(线程组领导):group_leader 字段指向线程组的领导进程。在多线程环境中,这有助于管理线程组。示例:struct task_struct __rcu *group_leader;
fs(文件系统信息):fs 字段包含有关进程打开的文件的信息。它指向 fs_struct 结构,其中包含有关文件系统的信息。示例:struct fs_struct *fs;
files(文件表):files 字段指向文件描述符表,它包含了进程打开的文件和其他 I/O 相关信息。示例:struct files_struct *files;
signal(信号处理):signal 字段包含有关进程信号处理的信息。信号是用于与进程通信和控制其行为的机制。示例:struct signal_struct *signal;
cred(安全凭证):cred 字段包含有关进程权限和身份的信息。这是与安全相关的关键信息。示例:struct cred __rcu *cred;
thread_info(线程信息):thread_info 字段包含了有关线程的低级信息,如堆栈指针和处理器状态。这对于线程管理是必要的。示例:struct thread_info thread_info;
2、分析 fork 函数对应的内核处理过程 sys_clone
fork 函数在Linux内核中的实现通常是通过 sys_clone 系统调用来完成的,sys_clone 是创建新进程的底层系统调用,该过程分析如下:
1.首先是fork触发系统调用sys_clone,然后最终执行的是do_fork。
2.fork创建了两个进程,一个父进程,一个子进程,其中子进程是对父进程的拷贝,它从父进程处复制了整个进程的地址空间,只有进程号和一些计时器等等是自己独有的,由于要复制很多资源,所以fork创建进程是很慢的。
3.fork执行一次有两次返回值,在父进程里返回新建的子进程编号,在子进程里返回0.由于创建进程的三种方式最终都是调用do_fork。
4.创建子进程的一个重要的过程是复制父进程里面的资源,有看到下面的copy_process函数,所以基本判断是复制资源的函数.这段代码是创建子进程的精华部分,所有的创建进程上下文,为新进程设置新的pid,还有复制代码,都在这一部分了。
3、使用gdb跟踪分析一个fork系统调用内核处理函数sys_clone ,验证对Linux系统创建一个新进程的理解
结果如图所示:
4. 总结
创建一个新进程在内核中的执行过程大致如下:
使用系统调用Sys_clone(或fork,vfork)系统调用创建一个新进程。这些系统调用会通过调用do_fork函数来实现进程的创建。Linux通过复制父进程的进程控制块(PCB)中的task_struct结构来创建一个新进程,并为新进程分配一个新的内核堆栈。需要修改复制过来的进程数据,例如进程ID(pid)、进程链表等。这一步通过执行copy_process和copy_thread函数来完成。设置新进程的内核栈顶,即p->thread.sp = (unsigned long) childregs。这个值表示当调度到子进程时,使用的内核栈的顶部位置。设置子进程的第一条指令地址,即p->thread.ip = (unsigned long) ret_from_fork。这个地址表示当调度到子进程时,执行的第一条指令。