Linux-0.11 kernel目录fork.c详解

news2024/9/30 23:28:13

Linux-0.11 kernel目录fork.c详解

fork.c中主要实现内核对于创建新的进程的行为。其中copy_process是其最核心的函数。

copy_process

int copy_process(int nr,long ebp,long edi,long esi,long gs,long none,
		long ebx,long ecx,long edx,
		long fs,long es,long ds,
		long eip,long cs,long eflags,long esp,long ss)

该函数的作用是从old进程中复制出一个new进程。 该函数在system_call.s中的sys_fork函数中执行。

copy_process从INT 0x80中断触发system_call系统调用,进而调用sys_fork。此时内核栈的状态如下所示:

在这里插入图片描述

这与copy_process的参数是一致的。

该函数首先在内存中分配了一个空闲页用于存储进程的PCB,即task_struct结构。并将该PCB放入了PCB的数组中。

最后将old进程的PCB内容先直接拷贝给new进程。

p = (struct task_struct *) get_free_page();
if (!p)
    return -EAGAIN;
task[nr] = p;	
*p = *current;	

下面这段就是将继承来的PCB结构进行适当的修改,详细解释见注释。

p->state = TASK_UNINTERRUPTIBLE;//设置进程状态为不可被中断
p->pid = last_pid;//last_pid为find_empty_process找到的没有被使用的pid值, 将其设置给新的进程
p->father = current->pid;//设置该进程的父进程
p->counter = p->priority;//设置该进程的时间片, 值等于其优先级的值。
p->alarm = 0; //alarm定时的时间
p->leader = 0;	//是否是进程组的leader
p->utime = p->stime = 0; //用户态运行时间和和核心态运行时间
p->cutime = p->cstime = 0;//子进程用户态运行时间和核心态运行时间。
p->start_time = jiffies;//进程的开始时间设置为系统的滴答数。

下面一段是设置PCB中有关TSS寄存器的值。下面也通过注释进行详解。
在这里插入图片描述
首先设置了内核栈的栈

p->tss.back_link = 0;
p->tss.esp0 = PAGE_SIZE + (long) p;//进程的内核栈栈顶指针
p->tss.ss0 = 0x10;//内核栈的段选择符

接下来是设置tss寄存器关于其他cpu寄存器的值。

p->tss.eip = eip;
p->tss.eflags = eflags;
p->tss.eax = 0;
p->tss.ecx = ecx;
p->tss.edx = edx;
p->tss.ebx = ebx;
p->tss.esp = esp;
p->tss.ebp = ebp;
p->tss.esi = esi;
p->tss.edi = edi;
p->tss.es = es & 0xffff;     //段寄存器取16位
p->tss.cs = cs & 0xffff;
p->tss.ss = ss & 0xffff;
p->tss.ds = ds & 0xffff;
p->tss.fs = fs & 0xffff;
p->tss.gs = gs & 0xffff;

下面这里,设置tss中ldt的值。

p->tss.ldt = _LDT(nr);
p->tss.trace_bitmap = 0x80000000;

GDT表中每一项是8个字节,每个进程拥有一个TSS和LDT,因此每个进程占用字节是16字节, 因此序号为n的进程的LDT在GDT表中的偏移量就是n*16 + 5*8

#define _LDT(n) ((((unsigned long) n)<<4)+(FIRST_LDT_ENTRY<<3))

如对上述知识遗忘,可以通过下面这张图进行温故。

在这里插入图片描述

下面这里进程内存的拷贝, 实际上确定进行进程新的线性地址, 并进行页表的拷贝。详见本文中copy_mem的讲解。

if (copy_mem(nr,p)) {
    task[nr] = NULL;
    free_page((long) p);
    return -EAGAIN;

下面主要处理对进程打开的文件的引用计数增加1。

for (i=0; i<NR_OPEN;i++)
    if ((f=p->filp[i]))
        f->f_count++;
if (current->pwd)
    current->pwd->i_count++;
if (current->root)
    current->root->i_count++;
if (current->executable)
    current->executable->i_count++;

这里设置GDT表中tss和ldt描述符的内容。

set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));
set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));
p->state = TASK_RUNNING;	/* do this last, just in case */

copy_mem

int copy_mem(int nr,struct task_struct * p)

该函数的作用是复制进程的页表。

code_limit=get_limit(0x0f);//根据代码段选择符获取代码段的长度
data_limit=get_limit(0x17);//根据数据段选择符获取数据段的长度
old_code_base = get_base(current->ldt[1]);//获取代码段的起始位置
old_data_base = get_base(current->ldt[2]);//获取数据段的起始位置
if (old_data_base != old_code_base)  //两个段起始位置相等
    panic("We don't support separate I&D");
if (data_limit < code_limit)
    panic("Bad data_limit");
//确立新进程的代码段地址, Linux-0.11的线性地址是按照64M划分的,所以进程号nr的线性地址的起始位置是nr* 0x4000000
new_data_base = new_code_base = nr * 0x4000000;
p->start_code = new_code_base;  // 设置该位置到PCB中
set_base(p->ldt[1],new_code_base); //设置代码段的地址
set_base(p->ldt[2],new_data_base); //设置数据段的地址

下面这段代码是将数据段所属的页表的进行。

if (copy_page_tables(old_data_base,new_data_base,data_limit)) {
    printk("free_page_tables: from copy_mem\n");
    free_page_tables(new_data_base,data_limit);
    return -ENOMEM;
}

copy_page_tables在memory.c中定义。

verify_area

void verify_area(void * addr,int size)

该函数用于在进程空间进行写操作时进行地址验证的函数。

addr是指在进程线性地址中相对于起始位置的偏移量, size指的是大小。

由于检测判断是以4K页为单位进行操作的,因此程序需要找出addr所在页的起始地址,如下图所示。

在这里插入图片描述

下面这段代码就是去寻找addr所在的内存页的起始地址, 即start。

unsigned long start;

start = (unsigned long) addr;
size += start & 0xfff; //size 加上页内偏移
start &= 0xfffff000; //start为逻辑地址的以4K为划分的起始地址
start += get_base(current->ldt[2]);//获取当前进程在线性地址中数据段的起始地址, 加起来就是该逻辑地址转化到了线性地址

下面进行写保护验证, 如果页面不可以写,则进行页面复制。

while (size>0) {
    size -= 4096;
    write_verify(start);
    start += 4096;
}

write_verify函数详解可以参考memory.c文件的讲解。

find_empty_process

int find_empty_process(void)

该函数的作用是在全局的task数组中找到一个空闲的项,并返回其下标。其在system_call.s中的sys_fork函数中被调用。

首先是寻找一个pid值

repeat:
    if ((++last_pid)<0) last_pid=1;
    for(i=0 ; i<NR_TASKS ; i++)
        if (task[i] && task[i]->pid == last_pid) goto repeat;

接着是去task数组中寻找可用的位置

for(i=1 ; i<NR_TASKS ; i++)
    if (!task[i])
        return i;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/380819.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何使用Coercer强制Windows Server认证任意主机

关于Coercer Coercer是一款功能强大的Python脚本&#xff0c;该工具可以通过九种不同的方法来强制让一台Windows Server认证任意主机。 功能介绍 1、自动检测远程设备的开放SMP管道&#xff1b; 2、一一调用存在安全漏洞的RPC功能来强制一台Windows Server认证任意主机&#…

字节二面,原来是我对自动化测试的理解太浅薄了..

如何使用Python实现自动化测试 如果你入职一家新的公司&#xff0c;领导让你开展自动化测试&#xff0c;作为一个新人&#xff0c;你肯定会手忙脚乱&#xff0c;你会如何落地自动化测试呢&#xff1f;资深测试架构师沉醉将告诉你如何落地自动kan化测试&#xff0c;本次话题主要…

什么是链表,如何实现?(单链表篇)

欢迎来到 Claffic 的博客 &#x1f49e;&#x1f49e;&#x1f49e; “仅仅活着是不够的&#xff0c;还需要有阳光&#xff0c;自由和花的芬芳。” 前言&#xff1a; 在日常使用的网站和软件中&#xff0c;列表属于最常见的一种东西了&#xff0c;其实现形式有顺序表&#xff0…

工具篇4.5数据可视化工具大全

1.1 Flourish 数据可视化不仅是一项技术&#xff0c;也是一门艺术。当然&#xff0c;数据可视化的工具也非常多&#xff0c;仅 Python 就有 matplotlib、plotly、seaborn、bokeh 等多种可视化库&#xff0c;我们可以根据自己的需要进行选择。但不是所有的人都擅长写代码完成数…

“一网统管”视频融合平台EasyCVR页面tab切换细节优化

EasyCVR视频融合平台基于云边端协同架构&#xff0c;能支持海量视频的轻量化接入与汇聚管理&#xff0c;借助大数据分析的决策判断&#xff0c;为网络摄像头、网络存储设备、智能终端、无人机、车载设备、移动执法仪、视频监控平台等提供一体化的视频接入、分发、存储、处理等能…

魔兽世界全版本GM命令全集

命令:.levelup 79 (升级数1-79) .modify money 999999999 增加金币.modify hp 9999999 9999999 增加被选择人物的血量 .modify mana 9999999 9999999 增加被选择人物的蓝量 .modify speed 30 加速人物跑步.modify speed 1 还原人物跑步.modify aspeed 1 还原人物游泳.gm fly on…

RuoYi-Bootstrap多模块版本使用过程中遇到的问题

1、设置复杂表头 最终效果见下图&#xff1a; 实现过程中错误&#xff1a; 页面报错&#xff0c;visible未定义&#xff0c;最开始以为是columns内的visible属性的问题&#xff0c;但是注释掉之后还是报同样的错误&#xff08;见下图&#xff09; 最开始的时候所有的列没有增加…

【架构师】零基础到精通——路由发现体系

博客昵称&#xff1a;架构师Cool 最喜欢的座右铭&#xff1a;一以贯之的努力&#xff0c;不得懈怠的人生。 作者简介&#xff1a;一名Coder&#xff0c;软件设计师/鸿蒙高级工程师认证&#xff0c;在备战高级架构师/系统分析师&#xff0c;欢迎关注小弟&#xff01; 博主小留言…

啃下这350+软件测试工程师面试题,4面阿里测试岗,总算顺利拿到 offer

下边是我根据工作这几年来的面试经验&#xff0c;加上之前收集的资料&#xff0c;整理出来350道软件测试工程师常考的面试题。字节跳动、阿里、腾讯、百度、快手、美团等大厂常考的面试题&#xff0c;在文章里面都有提到。 虽然这篇文章很长&#xff0c;但是绝对值得你点击一下…

JavaScript高级程序设计读书分享之8章——8.1理解对象

JavaScript高级程序设计(第4版)读书分享笔记记录 适用于刚入门前端的同志 创建自定义对象的通常方式是创建 Object 的一个新实例&#xff0c;然后再给它添加属性和方法。 let person new Object() person.name Tom person.age 18 person.sayName function(){//示 this.name…

MySQL运维篇之主从复制

02、主从复制 2.1、概述 主从复制是指将主数据库的DDL和DML操作通过二进制日志传到从库服务器中&#xff0c;然后在从库上对这些日志重新执行&#xff08;也叫重做&#xff09;&#xff0c;从而使得从库和主库的数据保持同步。 MySQL支持一台主库同时向多台从库进行复制&…

Vue+ElementUI+SpringBoot项目配合分页插件快速实现分页(简单暴力)

首先需要在项目中引入Element-UI的组件库&#xff0c;使用以下命令&#xff0c;不会引入的请自行百度。 npm i element-ui -S Element官网地址&#xff1a;https://element.eleme.cn/#/zh-CN/component/changelog 去Element-UI官网组件库找到合适的分页插件&#xff0c;并把他引…

HashMap的7种遍历方式

目录1.JDK 8 之前的遍历1.1 EntrySet 遍历1.2 KeySet 遍历1.3 EntrySet 迭代器遍历1.4 KeySet 迭代器遍历2.JDK 8 之后的遍历2.1 Lambda 遍历2.2 Stream 单线程遍历2.3 Stream 多线程遍历3.总结1.JDK 8 之前的遍历 1.1 EntrySet 遍历 public static void main(String[] args) …

高压功率放大器在径向驻波型超声波电机研究中的应用

实验名称&#xff1a;大力矩径向驻波型超声波电机有限元分析与实验研究研究方向&#xff1a;超声电机测试目的&#xff1a;提出了一种大力矩径向驻波型超声波电机&#xff0c;在实现电机大力矩输出的同时保持结构紧凑的特点。首先设计并分析了电机的结构和工作原理&#xff0c;…

Biomod2 (上):物种分布模型预备知识总结

Biomod11.栅格数据处理1.1 读取一个栅格图片1.2 计算数据间的相关系数1.3 生成多波段的栅格图像1.4 修改变量名称1.4.1 计算多个变量之间的相关性2. 矢量数据处理2.1 提取矢量数据2.2 数据掩膜2.2 栅格计算2.3 拓展插件的使用3. 图表绘制3.1 遥感影像绘制3.2 柱状图分析图绘制3…

C语言循环控制语句Break,goto,continue语句讲解

循环控制语句改变你代码的执行顺序。通过它你可以实现代码的跳转。 C 语言中 break 语句有以下两种用法&#xff1a; 当 break 语句出现在一个循环内时&#xff0c;循环会立即终止&#xff0c;且程序流将继续执行紧接着循环的下一条语句。 它可用于终止 switch 语句中的一个 …

【日志框架-笔记】深入浅出 Log4j,理论-源码-配置

log4j一、log4j 的概述及其入门程序入门程序二、日志输出的需要及PatternLayout类源码分析日志输出的需要PatternLayout类的源码分析三、Log4j 占位符的具体含义四、Log4j 配置文件实操如何对配置文件进行解析的&#xff1f;(LogManager的静态代码块&#xff09;实操五、自定义…

ClickHouse的架构与基本概念

一、ClickHouse的定义 ClickHouse是一个完全的列式分布式数据库管理系统(DBMS)&#xff0c;允许在运行时创建表和数据库&#xff0c;加载数据和运行查询&#xff0c;而无需重新配置和重新启动服务器&#xff0c;支持线性扩展&#xff0c;简单方便&#xff0c;高可靠性&#xf…

spring spring-boot @valid @NotNull @NotEmpty 基本校验使用以及 全局异常优化集成

valid NotNull NotEmpty 一套标准的基础校验&#xff0c;可以将校验注解和附带错误信息添加到请求入参上即可完成校验&#xff0c;可以去除简单的校验代码&#xff0c;节省一定的时间和代码量 Maven 依赖 spring-boot <dependency><groupId>org.springframewor…

【已解决】VM中安装的Ubuntu窗口太小、无法和Windows复制粘贴

按理说窗口都是铺满VM的&#xff0c;可是有时候安装Ubuntu之后发现小太了&#xff0c;就800*600&#xff08;4:3&#xff09; 1 窗口太小方法一 在桌面右键&#xff0c;打开display settings 调整resolution&#xff0c;选择你需要的即可&#xff0c;但是这样的调整不是很完…