根据一下问题来看笔记

  • 进程占多大的线形地址空间
  • 进程实际分配多少物理内存
  • 创建进程的开销在哪里

一. 从fork系统调用开始

kernel/sys_call.s第222行

_sys_fork:
    call _find_empty_process        #为新进程分配id
    testl %eax,%eax                 #若为负数则返回
    js 1f
    push %gs                        #压入copy_process需要的参数
    pushl %esi
    pushl %edi
    pushl %ebp
    pushl %eax
    call _copy_process              #调用copy_process函数
    addl $20,%esp                   #丢弃压栈内容
1:  ret

二. copy_process函数分析

作用: 复制当前进程的代码段和数据段以及环境

这里需要说明的是每个任务的线性地址为64M,每个任务的线性地址不重叠。

1. copy_mem函数重点

  • 上面代码重点分析copy_mem函数中get_base函数的意思,首先来看段描述符的格式如下图:

#define _get_base(addr) ({\
unsigned long __base; \
#段描述符有两个32位,%3代表 addr+7的内容,也就是上图第一行的基地址31~24的内容放到dh寄存器
__asm__("movb %3,%%dh\n\t" \
    #%2代表addr+4的内容,上图第一行基地址的内容放到dl寄存器
    "movb %2,%%dl\n\t" \
    #上面两行构成dx的内容,右移16位空出低16位
    "shll $16,%%edx\n\t" \
    #将addr+2的内容,上图第二行31~16的内容放到%dx,edx中构成了完整的段基址
    "movw %1,%%dx" \
    :"=d" (__base) \
    :"m" (*((addr)+2)), \
     "m" (*((addr)+4)), \
     "m" (*((addr)+7))); \
__base;})
  • copy_mem函数的get_limit(0x0f)中0x0f的意思,段选择子的格式如下:

get_limit(0x0f)中的0x0f是段选择子,0x0f为 0000 0000 0000 1111,指定了LDT表中具有RPL=3,索引值为1,T1位为1,指定LDT表

  • copy_mem函数代码分析:
  • 拷贝当前进程的页目录和页表给新进程
  • 设置好新进程的ldt表
int copy_mem(int nr,struct task_struct * p)
{
    unsigned long old_data_base,new_data_base,data_limit;
    unsigned long old_code_base,new_code_base,code_limit;

    //0x0f为代码段选择子
    //下面有对段选择子的说明
    code_limit=get_limit(0x0f);
    //0x17为数据段选择子
    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");
    //进程线形地址的基地址为 64M X 进程号
    new_data_base = new_code_base = nr * TASK_SIZE;
    p->start_code = new_code_base;
    //设置ldt[1]代码段的线性地址
    set_base(p->ldt[1],new_code_base);
    //设置ldt[2]数据段的线性地址
    set_base(p->ldt[2],new_data_base);
    //拷贝页表,在内存管理中详细说明
    if (copy_page_tables(old_data_base,new_data_base,data_limit)) {
        free_page_tables(new_data_base,data_limit);
        return -ENOMEM;
    }
    return 0;
}

2. copy_process函数重点

  • 函数中新进程的状态改变,创建时设置为TASK_UNINTERRUPTIBLE,完成创建后,态设置为task_running
  • 新进程的内核堆栈的设置以及新进程的返回值
p->tss.ep0 = PAGE_SIZE+ (long)p;            //设置新进程的内核堆栈
p->tss.ss0 = 0x10;                          //内核数据段选择子
        ...
p->tss.eax = 0;                             //新进程的返回值为0

下表是任务状态段(tss)的字段表格,这里可以参考任务状态段的描述,tss是task_struct中的一个字段.

进程的内核堆栈和用户堆栈的区别,内核堆栈分配在分配给task_struct结构的一页内存的顶端,也就是地址(long)p + PAGE_SIZE的位置,
进程的内核堆栈示意如图:

进程的用户堆栈示意图:

  • 设置进程的tss段和ldt段
    代码参考fork.c第130行

    //gdt为gdt表的首地址,nr<<1表示每个任务有两项状态段和局部段
    //p->tss表示tss在task_struct中的偏移
    //p->ldt表示ldt在task_struct中的偏移
    set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));
    set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));

每个任务都有一个tss段和ldt段,存放在gdt表中,如下图所示:

三. 进程管理(创建)依赖内存管理子系统

copy_process的第77行,用来分配一页物理内存(通常是4k)给新进程

struct task_struct* p;
...
p = (struct task_struct*)get_free_page();

《linux内核完全剖析》笔记03-进程创建的更多相关文章

  1. Linux内核设计第六周 ——进程的描述和创建

    Linux内核设计第六周 ——进程的描述和创建 第一部分 知识点总结 一.进程描述符task_struct数据结构 1.操作系统的三大功能: 进程管理.内存管理.文件系统 2.进程的作用: 将信号.进 ...

  2. Linux内核分析(四)----进程管理|网络子系统|虚拟文件系统|驱动简介

    原文:Linux内核分析(四)----进程管理|网络子系统|虚拟文件系统|驱动简介 Linux内核分析(四) 两天没有更新了,上次博文我们分析了linux的内存管理子系统,本来我不想对接下来的进程管理 ...

  3. 《Linux内核分析》 第八节 进程的切换和一般的执行过程

    张嘉琪 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 Linux内核分析 第八 ...

  4. Linux内核设计第八周 ——进程的切换和系统的一般执行过程

    Linux内核设计第八周 ——进程的切换和系统的一般执行过程 第一部分 知识点总结 第二部分 实验部分 1.配置实验环境,确保menu内核可以正常启动 2.进入gdb调试,在shedule和conte ...

  5. Linux内核分析课程笔记(一)

    linux内核分析课程笔记(一) 冯诺依曼体系结构 冯诺依曼体系结构实际上就是存储程序计算机. 从两个层面来讲: 从硬件的角度来看,冯诺依曼体系结构逻辑上可以抽象成CPU和内存,通过总线相连.CPU上 ...

  6. Linux内核分析第八周——进程的切换和系统的一般执行过程

    Linux内核分析第八周--进程的切换和系统的一般执行过程 李雪琦+原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/cou ...

  7. 【读书笔记】《Linux内核设计与实现》进程管理与进程调度

    大学跟老师做嵌入式项目,写过I2C的设备驱动,但对Linux内核的了解也仅限于此.Android系统许多导致root的漏洞都是内核中的,研究起来很有趣,但看相关的分析文章总感觉隔着一层窗户纸,不能完全 ...

  8. 《linux 内核全然剖析》 fork.c 代码分析笔记

    fork.c 代码分析笔记 verifiy_area long last_pid=0; //全局变量,用来记录眼下最大的pid数值 void verify_area(void * addr,int s ...

  9. Linux内核入门到放弃-进程管理和调度-《深入Linux内核架构》笔记

    进程优先级 硬实时进程 软实时进程 普通进程 O(1)调度.完全公平调度器 抢占式多任务处理(preemptive multitasking):各个进程都分配到一定的时间段可以执行.时间段到期后,内核 ...

随机推荐

  1. appium之java API(2)

    TouchAction AppiumDriver的辅助类,主要针对手势操作,比方滑动.长按.拖动等.TouchAction的原理是讲一系列的动作放在一个链条中,然后将该链条传递给server.serv ...

  2. testNg自动化,读取excel的数据

    自己写了一个testng执行excel用例的小程序,主要是运行.xlsx的,需要支持xls可以自己扩展,分享一下.下载地址:http://yun.baidu.com/share/link?sharei ...

  3. 使用TeamCity对项目进行可持续集成管理

    使用TeamCity对项目进行可持续集成管理 一.可持续集成管理   持续集成,CI:即Continuous integration. 可持续集成的概念是基于团队(小组)协作开发而提出来的,为了提高团 ...

  4. 基于NHibernate二级缓存的MongoDB组件

    设计一套基于NHibernate二级缓存的MongoDB组件(上)   摘要:NHibernate Contrib 支持很多第三方的二级缓存,如SysCache,MemCache,Prevalence ...

  5. linux下的watch命令

    linux下的watch命令 在linux下,watch是周期性的执行下一个命令,并全屏显示执行结果. watch [-dhvt] [-n <seconds>] [--difference ...

  6. 自己动手实现Expression翻译器 – Part Ⅱ

    上一节我们了解了Linq查询大体上是如何运转的,并针对SQL表达式进行建模(DbExpression),这一节的重点在于如何将表达式转换为DbExpression. 可以说只要能生成结构清晰的DbEx ...

  7. openGL剪裁区

    /** * 缓冲区工具类 */public class BufferUtil { /**  * 将浮点数组转换成字节缓冲区  */ public static ByteBuffer arr2ByteB ...

  8. mvc日期控件datepick的几篇文章,日后再总结吧

    instinctcoder里有两篇,入门级的 http://instinctcoder.com/asp-net-mvc-4-jquery-datepicker/ http://instinctcode ...

  9. 定步长的龙格库塔7(8)阶C++程序(RKF78)

    定步长,可以在稍微修改之后变为变步长形式,代码如下: void rkf78c( double h, double& T, vxd& X, double& err) { int ...

  10. ant svn

    <!-- 检出代码 这里使用 export 不是checkout 二者区别 checkout会svn相关信息文件检出,export只是检出最新的文件--> <target name= ...