进程的描述和创建

谈愈敏 原创作品转载请注明出处 《Linux内核分析》MOOC课程 http://mooc.study.163.com/course/USTC-1000029000

进程的描述

操作系统的三大管理功能:

  • 进程管理(最重要的)
  • 内存管理
  • 文件系统

为了管理进程,内核必须对每个进程进行清晰的描述,进程描述符提供了内核所需了解的进程信息。

进程控制块PCB task_struct:

  • 进程状态
  • 进程打开的文件
  • 进程优先级信息

task_struct总体数据结构的抽象:

tty:控制台
fs:文件系统
files:文件描述符
mm:内存管理
signal:信号描述

进程状态:不同于操作系统(就绪、运行、阻塞),如Linux中就绪状态和运行状态都是TASK_RUNNING

进程的标示:pid

进程描述符task_struct数据结构:

进程的创建

进程起源回顾:

道生一(start_kernel....cpu_idle)
一生二(kernel_init和kthreadd)
二生三(即前面0、1和2三个进程)
三生万物(1号进程是所有用户态进程的祖先,2号进程是所有内核线程的祖先)

0号进程,是代码写死的,1号进程复制0号进程PCB,再修改,再加载可执行程序。

fork创建一个子进程

fork一个子进程的代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char * argv[])
{
int pid;
/* fork another process */
pid = fork();
if (pid < 0)
{
/* error occurred */
fprintf(stderr,"Fork Failed!");
exit(-1);
}
else if (pid == 0) //子进程pid == 0时返回,子进程是在内核中返回
{
/* child process */
printf("This is Child Process!\n");
}
else //父进程可以返回
{
/* parent process */
printf("This is Parent Process!\n");
/* parent will wait for the child to complete*/
wait(NULL);
printf("Child Complete!\n");
}
}

fork在父进程和子进程各返回一次

子进程中返回的是0,父进程中返回值是子进程的id。

创建一个新进程在内核中的执行过程

fork、vfork和clone三个系统调用都可以创建一个新进程,而且都是通过调用do_fork来实现进程的创建;

Linux通过复制父进程来创建一个新进程:

do_fork主要是复制了父进程的task_struct,然后修改必要的信息,从而得到子进程的task_struct。

理解这一个过程的一个想象的框架:

复制一个PCB——task_struct

err = arch_dup_task_struct(tsk, orig);

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

ti = alloc_thread_info_node(tsk, node);
tsk->stack = ti;
setup_thread_stack(tsk, orig); //这里只是复制thread_info,而非复制内核堆栈

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

新进程从哪里开始执行?

从用户态的代码看fork();函数返回了两次,即在父子进程中各返回一次:

  • 父进程从系统调用中返回比较容易理解
  • 子进程从系统调用中返回,那它在系统调用处理过程中的哪里开始执行的呢?

这就涉及子进程的内核堆栈数据状态和task_structthread记录的sp和ip的一致性问题,这是在哪里设定的?copy_thread in copy_process

*childregs = *current_pt_regs(); //复制内核堆栈
childregs->ax = 0; //为什么子进程的fork返回0,这里就是原因! p->thread.sp = (unsigned long) childregs; //调度到子进程时的内核栈顶
p->thread.ip = (unsigned long) ret_from_fork; //调度到子进程时的第一条指令地址

fork出来的子进程是从ret_from_fork开始执行的,然后跳转到syscall_exit,从系统调用中返回。

实验:分析Linux内核创建一个新进程的过程

启动MenuOS

cd LinuxKernel
rm menu -rf
git clone https://github.com/mengning/menu.git
cd menu
mv test_fork.c test.c
make rootfs

gdb调试fork命令

qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S

gdb
file linux-3.18.6/vmlinux
target remote:1234

设置断点:

b sys_clone
b do_fork
b dup_task_struct
b copy_process
b copy_thread
b ret_from_fork

跟踪调试:

总结

首先了解了进程控制块PCB,进程描述符task_struct,先从整体抽象概念讲,然后看具体的实现代码,虽然task_struct数据结构很庞大很复杂,我们也要理解一些重点,如进程运行状态、内核堆栈,还有当前CPU相关的一些,文件系统列表,打开的文件描述符等。

关于进程的创建:0号进程,是代码写死的,1号进程复制0号进程PCB,Linux是通过复制父进程来创建一个新进程,然后修改必要的信息,从而得到子进程的task_struct

fork创建的新的子进程是从ret_from_fork开始执行的,然后跳转到syscall_exit,从系统调用中返回。新进程如何开始执行的关键:

p->thread.sp = (unsigned long) childregs; //调度到子进程时的内核栈顶
p->thread.ip = (unsigned long) ret_from_fork; //调度到子进程时的第一条指令地址

20135220谈愈敏Blog6_进程的描述和创建的更多相关文章

  1. 20135220谈愈敏Blog8_进程的切换和系统的一般执行过程

    进程的切换和系统的一般执行过程 谈愈敏 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-100002 ...

  2. 20135220谈愈敏Blog2_操作系统是如何工作的

    操作系统是如何工作的 谈愈敏 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 计 ...

  3. 20135220谈愈敏Blog7_可执行程序的装载

    可执行程序的装载 谈愈敏 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1000029000 一. ...

  4. 20135220谈愈敏Blog3_构造一个简单的Linux系统MenuOS

    构造一个简单的Linux系统MenuOS 谈愈敏 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1 ...

  5. 20135220谈愈敏Blog4_系统调用(上)

    系统调用(上) 谈愈敏 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1000029000 用户态 ...

  6. 20135220谈愈敏Blog5_系统调用(下)

    系统调用(下) 谈愈敏 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1000029000 给Me ...

  7. 20135220谈愈敏Linux_总结

    Linux_总结 具体博客链接 计算机是如何工作的 操作系统是如何工作的 构造一个简单的Linux系统MenuOS 系统调用(上) 系统调用(下) 进程的描述和创建 可执行程序的装载 进程的切换和系统 ...

  8. 20135220谈愈敏Linux Book_4

    进程调度 进程:程序的运行态表现形式 进程调度程序:确保进程能有效工作的一个内核子系统,决定将哪个进程投入运行.何时运行以及运行多长时间,在可运行态进程之间分配有限的处理器时间资源. 最大限度的利用处 ...

  9. 20135220谈愈敏Linux Book_3

    第3章 进程管理 进程是Unix操作系统抽象概念中最基本的一种,进程管理是操作系统的心脏所在. 3.1 进程 进程:处于执行期的程序以及相关的资源的总称. 线程:在进程中活动的对象,拥有独立的程序计数 ...

随机推荐

  1. 关于String StringBuffer StringBuilder

    0. String对象的创建       1.关于类对象的创建,很普通的一种方式就是利用构造器,String类也不例外:String s=new String("Hello world&qu ...

  2. 《SQL Server企业级平台管理实践》读书笔记——几个系统库的备份与恢复

    master数据库 master作为数据库的主要数据库,记录着SQL Server系统的所有系统级信息,例如登录用户.系统配置设置.端点和凭证以及访问其他数据服务器所需要的信息.master数据库还记 ...

  3. which,whereis, locate, find

    which 在PATH环境变量中的路径中查找目标文件,所以用来查找都是可执行文件,Linux下的各种命令本质上就是一个可执行的文件,所以我们安装新的软件之后通常都会有相应的命令将其打开,就是因为安装的 ...

  4. 一·创建Linux服务器(基于阿里云)

    本系统是基于阿里云服务器,购买请前往https://www.aliyun.com/?spm=5176.8142029.388261.1.taXish ,由于经济能力的限制,本人购买的是最低配置如下 其 ...

  5. 最小的k个数

    // 最小的k个数.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> #include & ...

  6. [嵌入式学习资料]ARM开发学习详解iTOP-4412开发板使用手册

    拿到的最新4412开发板学习使用手册,完全免费,分享一下 下载地址:http://pan.baidu.com/s/1ntrJA8h

  7. [转]Oracle如何实现创建数据库、备份数据库及数据导出导入的一条龙操作

    本文转自:http://www.cnblogs.com/wuhuacong/archive/2012/03/09/2387680.html Oracle中对数据对象和数据的管理,无疑都是使用PL/SQ ...

  8. extern "C" 用法解析

    extern "c"用法解析 作者 作者Jason Ding ,链接http://www.jianshu.com/p/5d2eeeb93590 引言 C++保留了一部分过程式语言的 ...

  9. uGUI练习(三) KeyBoard Navigation

    练习目标 练习通过键盘在按钮或其它Selectable类型组件上导航 步骤 创建一排的Button,及一个右边的Button 2.查看Button的属性里有一栏下拉列表Navigation,默认选择的 ...

  10. Lambda 表达式(C# 编程指南)

    Lambda 表达式是一种可用于创建委托或表达式目录树类型的匿名函数. 通过使用 lambda 表达式,可以写入可作为参数传递或作为函数调用值返回的本地函数.Lambda 表达式对于编写 LINQ 查 ...