Linux0.11之进程0创建进程1(1)
进程0是由linus写在操作系统文件中的,是预先写死了的。那么进程0以后的进程是如何创建的呢?本篇文章主要讲述进程0创建进程1的过程。
在创建之前,操作系统先是进行了一系列的初始化,分别为设备号、块号、内存大小的设置、内存管理、中断、字符设备、时间、LDT和GDT、缓冲区、硬盘、软盘以及开启之前关闭的中断。由于操作系统通常情况下是运行在用户模式下的,因此还进行的一个很重要的工作就是特权级的切换。具体对应linux0.11代码中main.c文件中的内容。
接下来一个很有意思且很重要的事情就是,调用了fork()函数,好家伙,上来就给你创建进程1。这个函数并没有被定义,它是由编译器编译产生的。
fork是通过内联函数static inline _syscall0(int,fork)引入的。_syscall0()定义在unistd.h文件中,具体代码是这样的。
途中第一个冒号后对应输出,a对应寄存器eax,第二个冒号后对应输入,其意思就是将_NR_##name的值给eax,在这里,就是eax = __NR_fork = 2。展开以后,这个函数就是c语言中的fork函数。
linus为什么这么费力的做这件事情呢?有以下几个原因,首先,c语言中的函数在调用的时候,都是要进行保护操作的,也就是入栈,这会占据额外的内存开销,要知道,我们操作系统的内存都是很紧张的,如果所有函数都这样做会很容易导致操作系统内存的不够用。而内联函数不一样,它与宏函数有点像,是直接将函数代码插入在调用位置,这样能避免入栈操作,也就能避免这样的问题了。此外,除了fork,操作系统还有各种相似的系统调用来写,系统调用里面还有不同的参数.如果挨个写,可能要写上百个,但是这里只给了一个程序架子,就能自动生成所需要的代码。
这个函数首先是调用了int 0x80中断,这个中断首先去中断描述符表中找自己对应的中断服务程序在哪里,然后呢去中断服务程序对应的程序入口地址去执行对应程序,执行的是_system_call(在system_call.s文件中)。它大致做了这么些事情,首先,由用户特权级跳转到内核特权级,接下来将当前系统状态入栈保存(TSS),然后通过sys_call_table数组去调用对应的系统调用函数,这里显然是_sys_fork。具体代码是这个样子的:
_sys_fork长这样:
显然这个函数先调用了c中find_empty_process()函数,这个函数在fork.c文件中,它做了这么一件事情,为当前创建的进程争取在task数组中空闲的位置。
last_pid的初始值为0,为long类型,显然系统中最大的last_pid为0x8000,当超过这个值后就会回转计算了。第一个if判断起到两种作用,是的pid每次都从1开始,因为0是进程0,已经创建且会一直在task数组中存在,为什么呢?这可是所有进程的最大的父亲,当然不能动,不然后续怎么进行复制呢?然后每次让last_pid + 1,用last_pid来标记当前进程的进程号,显然,当一个进程在task数组中存在了,就会对应一个last_pid值,两个创建的进程显然不能一样呀,这可是进程的标识符。在我们这呢,由于是第一次创建,因此,不会出现这样的情况,直接for循环结束进入下一个循环,这个循环就是返回一个空闲的task数组中的位置。如果没找到合适的task数组中的位置怎么办呢?没找到,显然意味着这个数组已经满了,没有空闲的位置了。因此返回一个-EAGAIN(-11)标识符。
执行完当然是回到_sys_fork里面。首先肯定要判断下咱返回来的是个啥,就是testl %eax,%eax这行。这里如果是负数那么直接就返回去了,后续需要进程调度,在这里不展开讲,因为和这篇文章的主题离得太远了。现在咱已经在task数组里面占到坑了,那当然是赶紧创建,创建操作是调用c函数copy_process()来实现的,这个函数也在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)
{
struct task_struct *p;
int i;
struct file *f; p = (struct task_struct *) get_free_page();
if (!p)
return -EAGAIN;
task[nr] = p;
*p = *current; /* NOTE! this doesn't copy the supervisor stack */
p->state = TASK_UNINTERRUPTIBLE;
p->pid = last_pid;
p->father = current->pid;
p->counter = p->priority;
p->signal = ;
p->alarm = ;
p->leader = ; /* process leadership doesn't inherit */
p->utime = p->stime = ;
p->cutime = p->cstime = ;
p->start_time = jiffies;
p->tss.back_link = ;
p->tss.esp0 = PAGE_SIZE + (long) p;
p->tss.ss0 = 0x10;
p->tss.eip = eip;
p->tss.eflags = eflags;
p->tss.eax = ;
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;
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;
p->tss.ldt = _LDT(nr);
p->tss.trace_bitmap = 0x80000000;
if (last_task_used_math == current)
__asm__("clts ; fnsave %0"::"m" (p->tss.i387));
if (copy_mem(nr,p)) {
task[nr] = NULL;
free_page((long) p);
return -EAGAIN;
}
for (i=; 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++;
set_tss_desc(gdt+(nr<<)+FIRST_TSS_ENTRY,&(p->tss));
set_ldt_desc(gdt+(nr<<)+FIRST_LDT_ENTRY,&(p->ldt));
p->state = TASK_RUNNING; /* do this last, just in case */
return last_pid;
}
它首先做的一件事情是申请一个空页,调用了get_free_page()函数。该函数是在memory.c文件中定义。
回到copy_process()函数,它先做了个强制类型转换,将这个页面的指针强制类型转换为指向task_struct的指针向量,并挂接在task[nr=1]上。nr是find_empty_process返回的任务号。需要注意的是,c语言中的指针有地址和类型的含义,强制类型转换的意思是认定这个页面的低地址 端就是进程1的task_struct的首地址,同时暗示了高地址部分是内核栈,长这样:
空白部分是stack数组。
下面就要开始讲进程创建部分的很有意思的内容了,请听下回分解。
Linux0.11之进程0创建进程1(1)的更多相关文章
- 对Linux0.11 中 进程0 和 进程1分析
1. 背景 进程的创建过程无疑是最重要的操作系统处理过程之一,很多书和教材上说的最多的还是一些原理的部分,忽略了很多细节.比如,子进程复制父进程所拥有的资源,或者子进程和父进程共享相同的物理页面,拥有 ...
- Linux内核堆栈使用方法 进程0和进程1【转】
转自:http://blog.csdn.net/yihaolovem/article/details/37119971 目录(?)[-] 8 Linux 系统中堆栈的使用方法 81 初始化阶段 82 ...
- C语言 进程控制---创建进程fork()函数
#include "sys/types.h" #include "stdio.h" #include "stdlib.h" #include ...
- c语言 进程控制---创建进程 vfork()函数
#include "stdio.h" #include "unistd.h" #include "sys/types.h" int gvar ...
- Linux0.11启动过程
从开机加电,到执行main函数之前的过程 好吧,这里应该是有执行3个汇编的文件,但是我不太了解.囧 从main函数,到启动OK(即可以响应用户操作了) 这个步骤做了3件事情: 创建进程0,使之具备在主 ...
- Linux下0号进程的前世(init_task进程)今生(idle进程)----Linux进程的管理与调度(五)【转】
前言 Linux下有3个特殊的进程,idle进程(PID = 0), init进程(PID = 1)和kthreadd(PID = 2) idle进程由系统自动创建, 运行在内核态 idle进程其pi ...
- linux的0号进程和1号进程
linux的 0号进程 和 1 号进程 Linux下有3个特殊的进程,idle进程(PID = 0), init进程(PID = 1)和kthreadd(PID = 2) * idle进程由系统自动创 ...
- 【系统之音】Android进程的创建及启动简述
Android系统中的进程(这里不包括init等底层的进程)都是通过Zygote fork而来的,那这些进程的启动流程都是怎样的呢? 这里将Android进程分为两个部分: (1)系统框架进程Syst ...
- LINUX编程学习笔记(十四) 创建进程与 父子进程内存空间
1什么是进程:进程是一个执行中的程序 执行的程序: 代码->资源->CPU 进程有很多数据维护:进程状态/进程属性 所有进程属性采用的一个树形结构体维护 ps -a//所有进程 ps - ...
随机推荐
- 在linux中创建新用户-再次安装python
原来的阿里云python软件安装错了,用了root安装软件,搞得我后面的软件全部都要用root,软连接也搞不定,卸载也不好卸载.只能格式化,实例什么的都不用重建,系统也不用安装,直接创建用户就行了,磁 ...
- 《Python基础教程》第五章:条件、循环和其他语句
在Python中赋值运算和比较运算是可以连接的,运算符可以连在一起使用,如:0<age<100 ==运算符判定两个对象是否相等,is判定两者是否等同(同一个对象) 断言,在错误条件出现时直 ...
- Java常用类(二) Scanner类和大数类
二.Scanner类 有C系语言基础的可能都比较熟悉scanf("%d",&a);和cin>>a;这种代码,也打开了程序交互的第一道门.因此,这些程序员开始学J ...
- 利用jQuery实现图片无限循环轮播(不借助于轮播插件)
原来我主要是用Bootstrap框架或者swiper插件实现轮播图的功能,而这次是用jQuery来实现图片无限循环轮播! 用到的技术有:html.css.JavaScript(少).jQuery(主要 ...
- struts2-052漏洞
转:https://thief.one/2017/09/06/1/ s2-052漏洞介绍 s2-052漏洞是当用户使用带有XStream组件的Struts-REST插件对XML格式的数据包进行反序列化 ...
- Codecombat 游戏攻略——JavaScript编辑语言——关卡(计算机科学四)Ⅱ
第16关:潜伏 // 用findEnemies把敌人存在数组enemies中 // 只攻击萨满巫师,不要攻击牦牛! var enemies = hero.findEnemies(); var enem ...
- redis 关闭持久化 实验验证
前言 由于redis持久化(RDB),导致我们的线上的磁盘被写炸 线上服务器是 64H 512G 大概写了rdb文件是 200G左右,写满了当时的目录 处理策略 关闭持久化,由于之前的现象表示,我们线 ...
- Ansible常用模块之命令类模块
Command模块 在远程节点上执行命令 [root@tiandong ~]# ansible all -m command -a "ls" 在远程主机上执行ls命令. [root ...
- [题解] [TJOI2011] 构造矩阵
题面 题解 很容易看出来是道网络流的题目, 要是没有这个字典序最小, 直接建图跑一遍就好了, 考虑如何输出字典序最小的方案 我们可以贪心地去选择, 若当前点可以选0就选0, 不能选0就选1, 有一点像 ...
- springboot @Configuration配置类里面使用@Value获取不到.yml配置文件属性的值
之前一个项目里面分为很多子工程的那种结构,要求让我改成一个项目的结构.我这边手动将代码合并之后出现下面的这种问题,然后尝试进行用各种方式解决 Error creating bean with name ...