20135202闫佳歆--week6 进程的描述与创建--学习笔记
此为个人学习笔记存档!
week 6 进程的描述与创建
一、进程的描述
1.进程控制块task_struct
以下内容来自视频课件,存档在此。
为了管理进程,内核必须对每个进程进行清晰的描述,进程描述符提供了内核所需了解的进程信息。
- struct task_struct数据结构很庞大
- Linux进程的状态与操作系统原理中的描述的进程状态似乎有所不同,比如就绪状态和运行状态都是TASK_RUNNING,为什么呢?
- 进程的标示pid
- 所有进程链表struct list_head tasks;
- 程序创建的进程具有父子关系,在编程时往往需要引用这样的父子关系。进程描述符中有几个域用来表示这样的关系
- Linux为每个进程分配一个8KB大小的内存区域,用于存放该进程两个不同的数据结构:Thread_info和进程的内核堆栈
- 进程处于内核态时使用,不同于用户态堆栈,即PCB中指定了内核栈,那为什么PCB中没有用户态堆栈?用户态堆栈是怎么设定的?
- 内核控制路径所用的堆栈很少,因此对栈和Thread_info来说,8KB足够了
- struct thread_struct thread; //CPU-specific state of this task
- 文件系统和文件描述符
- 内存管理——进程的地址空间
逐条分析如下节:
2.进程描述符task_struct数据结构
struct task_struct数据结构很庞大
struct task_struct {
volatile long state; /* 进程状态 -1 unrunnable, 0 runnable, >0 stopped */
void *stack; /* 进程的内核堆栈 */
atomic_t usage;
unsigned int flags; /* 每个进程的标识符 */
unsigned int ptrace; / #ifdef CONFIG_SMP // 条件编译,SMP多处理器相关 ……
int on_rq // 运行队列相关,下面几行是进程队列和调度相关。
…… struct list_head tasks // 进程链表 …… next_task
prev_task // 对进程链表的管理 tty_struct // 控制台 fs_struct // 文件系统
struct files_struct *files; // 打开的文件描述符列表 file_struct // 打开的文件描述符 mm_struct // 内存管理描述
struct mm_struct *mm, *active_mm; // 地址空间,内存管理。 signal_struct // 进程间通信、信号描述 struct list_head ptraced // 调试用 utime
stime // 进程时间相关
Linux进程的状态与操作系统原理中的描述的进程状态有所不同,比如就绪状态和运行状态都是TASK_RUNNING,为什么呢?
一般操作系统原理中描述的进程状态有就绪态,运行态,阻塞态,但是在实际内核进程管理中是不一样的。
- 创建新进程后实际的状态是TASK_RUNNING,就绪但是没有运行,调度器选择一个task之后进入运行态,也叫TASK_RUNNING。
- 当进程是TASK_RUNNING时,代表这个进程是可运行的,至于它有没有真的在运行,取决于它有没有获得cpu的控制权,即有没有在cpu上实际的运行。
- 一个正在进行的进程调用do_exit(),进入TASK_ZOMBIE,进程被终止,“僵尸进程”。
- 等待特定时间或者资源的时候,进入阻塞态,如果条件满足就进入就绪态,被选择后进入运行态。
进程的标示pid
pid_t pid;
pid_t tgid; //用来标识进程的。
所有进程链表struct list_head tasks
为了对给定类型的进程进行有效的搜索,内核维护了几个进程链表:
二、进程的创建
1.进程的创建概览及fork一个进程的用户态代码
复习:
道生一(start_kernel....cpu_idle),一生二(kernel_init和kthreadd),二生三(即前面0、1和2三个进程),三生万物(1号进程是所有用户态进程的祖先,0号进程是所有内核线程的祖先),新内核的核心代码已经优化的相当干净,都符合中国传统文化精神了
怎样创建一个子进程?
——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)
{
/* 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系统调用在父进程和子进程各返回一次。
关于fork和进程创建在上学期的课程中已经学习过,在此不作赘述。
2.理解进程创建过程复杂代码的方法
复习:
系统调用,见前几周博客:
第四周学习笔记——系统调用(上)
第五周学习笔记——系统调用(下)
调用fork的过程:
父进程如下:
子进程呢?fork出来的子进程是从哪儿开始执行的?
用户态空间——fork的下一句
fork出的子进程在内核中返回。
内核空间?是从那一句开始执行的?
——与基于mykernel的精简内核对照起来,想象出一个框架。
以下来自课件:
创建一个新进程在内核中的执行过程
fork、vfork和clone三个系统调用都可以创建一个新进程,而且都是通过调用do_fork来实现进程的创建;
Linux通过复制父进程来创建一个新进程,那么这就给我们理解这一个过程提供一个想象的框架:
复制一个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_struct中thread记录的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; //调度到子进程时的第一条指令地址
系统调用内核处理函数sys_fork,sys_vfrok,sys_clone,其实最终执行的都是do_fork。
do_fork里有:
copy_process
里面有:
dup_task_struct // 复制pcb
alloc_thread_info_node // 创建了一个页面,其实就是实际分配内核堆栈空间的效果。
setup_thread_stack // 把thread_info的东西复制过来
然后是大量的修改内容,将子进程初始化。
※copy_thread
copy_thread时都做了什么?
堆栈相关的一些内容
当前进程(父进程)的内核堆栈的栈底拷贝过来
赋值ip,sp……
3.创建的新进程是从哪里开始执行的
*childregs = *current_pt_regs(); //复制内核堆栈
childregs->ax = 0; //为什么子进程的fork返回0,这里就是原因!
p->thread.sp = (unsigned long) childregs; //调度到子进程时的内核栈顶
p->thread.ip = (unsigned long) ret_from_fork; //调度到子进程时的第一条指令地址
ip指向的是ret_from_fork,所以是从这里开始执行的。
复制内核堆栈的时候是复制的pt_regs,即只复制了SAVE_ALL相关的那一部分,即系统调用压栈的那一部分。
pt_regs里面内容有:
Entry(ret_from_fork):
最终会跳转到syscall_exit,这之前的内核堆栈状态和syscall_call的一致,然后返回用户态,变成子进程的用户态。
4.使用gdb跟踪创建新进程的过程
在MenuOs中新加了fork的命令。
准备工作:
rm menu -rf
git clone http://github.com/mengning/menu.git # 更新Menu
cd menu
mv test_fork.c test.c # 把test.c覆盖掉
make rootfs
执行fork,可以看到父进程子进程都输出了信息。
下面进行gdb调试:
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 # 因为fork实际上是执行的clone
b do_fork
b dup_task_struct
b copy_process
b copy_thread
b ret_from_fork
c
n
……
可以看到一系列相关函数:
tsk->stack = ti; //把内核堆栈的地址赋给它
//把内核堆栈压栈的空间地址找到,初始化
sturct pt_regs *childregs = task_pg_regs(p);
//把当前进程的内核堆栈的压的寄存器赋值到子进程中来。
*childregs = *current_pt_regs();
childregs->ax = 0;
//设置子进程被调度的ip,即子进程的起点
p->thread.ip = (unsigned long) ret_from_fork;
jmp syscall_exit; //这之后就跟踪不到了。
20135202闫佳歆--week6 进程的描述与创建--学习笔记的更多相关文章
- 20135202闫佳歆--week2 操作系统是如何工作的--学习笔记
此为个人学习笔记存档 week 2 操作系统是怎么工作的 一.计算机是如何工作的?--三个法宝 (一)三个法宝 1.存储程序计算机 所有计算机的基础性的逻辑框架. 2.函数调用堆栈 在低级语言中并不很 ...
- 20135202闫佳歆--week 8 课本第4章学习笔记
第四章 进程调度 一.多任务 多任务操作系统就是能同时并发的交互执行多个进程的操作系统. 多任务操作系统使多个进程处于堵塞或者睡眠状态,实际不被投入执行,这些任务尽管位于内存,但是并不处于可运行状态. ...
- 20135202闫佳歆--week6 分析Linux内核创建一个新进程的过程——实验及总结
week 6 实验:分析Linux内核创建一个新进程的过程 1.使用gdb跟踪创建新进程的过程 准备工作: rm menu -rf git clone https://github.com/mengn ...
- 20135202闫佳歆--week6 课本第三章学习笔记
第三章 进程管理 一.进程 1.进程 进程就是处于执行期的程序. 进程就是正在执行的程序代码的实时结果. 进程是处于执行期的程序以及相关的资源的总称. 进程包括代码段和其他资源. 2.线程 执行线程, ...
- 20135202闫佳歆--week 7 Linux内核如何装载和启动一个可执行程序--实验及总结
week 7 实验:Linux内核如何装载和启动一个可执行程序 1.环境搭建: rm menu -rf git clone https://github.com/megnning/menu.git c ...
- 20135202闫佳歆--week 9 期中总结
期中总结 前半学期的主要学习内容是学习mooc课程<Linux内核分析>以及课本<Linux内核设计与实现>. 所涉及知识点总结如下: 1. Linux内核启动的过程--以Me ...
- 20135202闫佳歆--week2 一个简单的时间片轮转多道程序内核代码及分析
一个简单的时间片轮转多道程序内核代码及分析 所用代码为课程配套git库中下载得到的. 一.进程的启动 /*出自mymain.c*/ /* start process 0 by task[0] */ p ...
- 20135202闫佳歆--week3 跟踪分析Linux内核的启动过程--实验及总结
实验三:跟踪分析Linux内核的启动过程 一.调试步骤如下: 使用gdb跟踪调试内核 qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd r ...
- 20135202闫佳歆--week 8 实验:理解进程调度时机跟踪分析进程调度与进程切换的过程--实验及总结
week 8 实验:理解进程调度时机跟踪分析进程调度与进程切换的过程 1.环境搭建: rm menu -rf git clone https://github.com/megnning/menu.gi ...
随机推荐
- JAVA-最常用的A题语法
输出 System.out.println(""); if 语句 if(布尔表达式) { //如果布尔表达式为true将执行的语句 } if...else... 语句 if(布尔表 ...
- [pip] pip命令的安装、卸载、查找方法汇总
比如以selenium的为例 1.打开命令窗口(如果是win10,最好是通过管理员方式打开命令窗口,否则会出现安装时提示访问不拒绝) 2.安装selenium的指定版本,命令:pip install ...
- ntp时间服务器--Linux配置
时间服务器作用: 大数据产生与处理系统是各种计算设备集群的,计算设备将统一.同步的标准时间用于记录各种事件发生时序, 如E-MAIL信息.文件创建和访问时间.数据库处理时间等. 大数据系统内不同 ...
- 转,敏感词过滤,PHP实现的Trie树
原文地址:http://blog.11034.org/2012-07/trie_in_php.html 项目需求,要做敏感词过滤,对于敏感词本身就是一个CRUD的模块很简单,比较麻烦的就是对各种输入的 ...
- php header函数导出excel表格
推荐一个除了用PHPExcel导出表格之外的另外一种比较简单不需要引入类文件的表格导入方法——header()导出excel表格. 导出表格的步骤封装成了方法,以便于重复使用,代码如下: /** * ...
- String----是一个对象
* 字符串可以看成是字符组成的数组,但是js中没有字符类型 * 字符是一个一个的,在别的语言中字符用一对单引号括起来 * 在js中字符串可以使用单引号也可以使用双引号 * 因为字符串可以看成是数组,所 ...
- Postman-断言和Runner
断言(部分) // 推荐用全等 ===,确保类型和值都一致 tests['Status code is 200'] = responseCode.code === 200; //判断响应结果是否是20 ...
- QT学习笔记8:QDir类及其用法总结
简介 QDir类提供了访问系统目录结构及其内容的与平台无关的方式. 头文件:#include <qdir.h> QDir类用来操作路径名及底层文件系统,获取关于目录路径及文件的相关信息,也 ...
- 客户端本地存储的比较及使用window.name数据传输
一:cookie: 1. 什么是cookie? Cookie是在客户端用于存储会话信息的,用户请求页面在web服务器与浏览器之间传递.每当同一台计算机通过浏览器请求某个页面时,就会发送这个 cooki ...
- python+requests实现接口测试 - cookies的使用 (转载)
出自:https://www.cnblogs.com/nizhihong/p/6699492.html 在很多时候,发送请求后,服务端会对发送请求方进行身份识别,如果请求中缺少识别信息或存在错误的识别 ...