2021-2022-1 20212806《Linux内核原理与分析》第七周作业

一、阅读理解task_struct数据结构

代码及解析:

struct task_struct {
	volatile long state;	
	void *stack;
	atomic_t usage;
	unsigned int flags;	 //进程状态的信息
	unsigned int ptrace;

#ifdef CONFIG_SMP
	struct llist_node wake_entry;
	int on_cpu;        //当前进程在哪个CPU上运行
	struct task_struct *last_wakee;
	unsigned long wakee_flips;
	unsigned long wakee_flip_decay_ts;

	int wake_cpu;
#endif
	int on_rq;

	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;            //实时任务调度实体
#ifdef CONFIG_CGROUP_SCHED
	struct task_group *sched_task_group;
#endif
	struct sched_dl_entity dl;

Linux进程的状态与操作系统原理中的描述的进程状态有所不同,比如就绪状态和运行状态都是TASK_TUNNING,一个正在运行的进程,我们调用do_exit(),就进入了TASK_ZOMBIE(进程被终止)。程序创建的进程具有父子关系,在编程时往往需要引用这样的父子关系。进程描述符中有几个域用来表示这样的关系。

二、分析fork函数对应的内核处理过程sys_clone,理解创建一个新进程如何创建和修改task_struct数据结构

(一) fork()函数的理解

       一个进程调用fork()函数创建子进程时,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。
        fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。
fork()是在用户态用于创建一个子系统的系统调用。Fork()系统调用在父进程和子进程各返回一次。即:fork可以被调用一次,却能够返回两次,它可能有三种不同的返回值:
1.在父进程中,fork返回新创建子进程的进程ID;
2.在子进程中,fork返回0;
3.如果出现错误,fork返回一个负值;

        在执行fork函数后,如果新进程创建成功,则返回两个进程,一个是父进程,一个是子进程。在父进程中,fork返回新创建子进程的进程ID;在子进程中,fork函数返回0。我们可以通过输出fork返回的值来判断当前进程是子进程还是父进程。

(二)fork()函数的底层实现

       Linux通过复制父进程来创建一个新进程,那么这就给我们理解这一个过程提供一个想象的框架:

 1.  复制一个PCB——task_struct

    err = arch_dup_task_struct(tsk, orig);

2.  要给新进程分配一个新的内核堆栈

    ti = alloc_thread_info_node(tsk, node);

    tsk->stack = ti;

    setup_thread_stack(tsk, orig); //这里只是复制thread_info,而非复制内核堆栈

3.  要修改复制过来的进程数据,比如pid、进程链表等等都要改改吧,见copy_process内部

       Linux通过clone()系统调用实现fork()。这个调用通过一系列的参数标志来指明父,子进程需要共享的资源。fork(),vfork()和__clone()库函数都根据各自需要的参数标志去调用clone()。然后由clone()去调用do_fork()。do_frok完成了创建中的大部分工作,它的定义在ker/frok.c文件中。该函数调用copy_process()的函数,然后让进程开始运行。copy_process()做扫尾工作并返回一个指向子进程的指针。再回到do_fork()函数,如果copy_process()函数返回成功,新创建的子进程被唤醒并让其投入运行。

三、使用gdb跟踪分析一个fork系统调用内核处理函数sys_clone

1.使用实验楼环境进行试验,首先进入“LinuxKernel”文件夹,删除“menu”文件,重新下载“menu”文件,如下图所示:
在这里插入图片描述
2.进入“menu”文件中,利用mv命令将test.c替换为test_fork.c文件,并执行qemu命令:
在这里插入图片描述3.进入gdb环境中,分别在sys_clone,do_fork,dup_task_struct,copy_process,copy_thread,ret_from_fork处设置断点,然后跟踪调试test_fork.c程序:
在这里插入图片描述4.单条指令开始执行,逐步跟踪do_fork的运行情况:
在这里插入图片描述

四、总结

       fork()函数创建一份子进程。首先是fork()函数执行系统调用,调用system_call(),system_call进而根据进程调用号找到sys_fork()函数所在的位置,进而执行sys_fork()函数;之后,sys_fork()函数开始调用do_fork()函数,do_frok()函数位于fork.c文件中,它完成了子进程创建的大部分工作;最后do_fork()函数调用copy_process()函数,copy_process()函数完成了很多工作,比如,为新进程分配内核堆栈,检查进程数目是否超限,区分父、子进程,获取子进程PID等操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值