2019-2020-1 20199313《Linux内核原理与分析》第七周作业
第七周学习——“进程的描述与进程的创建”
-
问题描述:
- 经过上一周的学习,我们进一步学习了计算机操作系统的核心工作机制,构造了一个简单的Linux系统MenuOS,利用了GDB简单分析Start_kernel,我们还学习了使用系统的库函数,并在此基础上,学会了使用系统调用,使用gdb调试系统调用,并观察system_call函数是如何工作的
-
本周学习:
- 本周在上周学习的基础上继续使用使用gdb调试系统调用,调试断点,分析系统调用运行过程。
- 并观察fork函数是如何工作的
一、首先,本周继续补充解决上周的疑难杂症
对于最新版本的uabntu的gcc编译版本问题的研究
解决方法
经各方学习,发现,问题最终还是出在gcc编译器上,我们所使用的gcc编译器的版本为gcc-7.4,对于我们想要编译的内核版本而言,实在是太高了,所以我们并不能成功的编译内核文件,利用上周的方法我们成功改变了编译器版本,在此进行一定的补充:
···
apt-get install gcc gdb
apt-get install make
apt-get install bison flex libssl-dev libncurses5-dev
apt-get install libncurses5-dev
sudo apt-get install libncurses5-dev make openssl libssl-dev bison flex
···
需要补充安装许多周边工具包,而且不仅需要更改gcc版本,g++的版本也需要改变,否则会出现库查询不到的情况,在此我已经能成功编译linux-3.16.6版本内核了
二、进程的描述
OS的三大管理功能:(1)进程管理(进程)(2)内存管理(虚拟内存)(3)文件系统(文件)
关于进程的创建,Linux提供了几个系统调用来创建和终止进程,以及执行新程序,(fork,vfork,clone和exec,exit);其中clone用来创建轻量级进程,必须制定要共享的资源,exec系统调用执行一个新程序,exit系统调用终止进程。不论是fork,vfork还是clone,在内核中最终都是调用了do_fork来实现进程的创建。
以及fork()函数的源码:
long do_fork(unsigned long clone_flags,
unsigned long stack_start,
unsigned long stack_size,
int __user *parent_tidptr,
int __user *child_tidptr)
{
struct task_struct *p; //创建进程描述符指针
int trace = 0;
long nr; //子进程pid
//创建子进程的描述符和执行时所需的其他数据结构
p = copy_process(clone_flags, stack_start, stack_size,
child_tidptr, NULL, trace);
if (!IS_ERR(p)) { //copy_process执行成功
struct completion vfork; //定义完成量(一个执行单元等待另一个执行单元完成)
trace_sched_process_fork(current, p);
nr = task_pid_vnr(p); //获取pid
//如果clone_flags包含CLONE_VFORK标识,将vfork完成量赋给进程描述符
if (clone_flags & CLONE_VFORK) {
p->vfork_done = &vfork;
init_completion(&vfork);
get_task_struct(p);
}
wake_up_new_task(p); //将子进程添加到调度器的队列
//这个函数的作用是在进程创建的最后阶段,父进程会将自己设置为不可中断状态,然后睡眠在 等待队列上(init_waitqueue_head()函数 就是将父进程加入到子进程的等待队列),等待子进程的唤醒。
if (clone_flags & CLONE_VFORK) {
if (!wait_for_vfork_done(p, &vfork))
ptrace_event(PTRACE_EVENT_VFORK_DONE, nr);
}
} else {
nr = PTR_ERR(p); //错误处理
}
return nr; //返回子进程pid(此处的pid为子进程的pid)
}
可以看出fork()函数对与do_fork() 在生成进程时很关键。不仅调用copy_process()为子进程复制出一份进程信息,如果是vfork()则初始化完成处理信息。
然后调用wake_up_new_task将子进程加入调度器,为之分配CPU,如果是vfork(),则父进程等待子进程完成exec替换自己的地址空间。
三、实验的过程
大致的思路:
更为精细的过程需要进一步分析内核源码,需要继续努力!