进程的描述

  • 操作系统内核实现操作系统的三大管理功能

    • 进程管理(内核中最核心的功能)
    • 内存管理
    • 文件系统
  • 在操作系统中,我们通过进程控制块PCB描述进程。

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

  • 用数据结构struct task_struct 来描述进程(涉及内容过于庞杂)

  • 进程描述符的示意图

  • Linux内核管理的进程状态转换图

  • 开始实验,更新menu

  • 对重要sys_clone等处各设置断点
(gdb)b sys_clone
(gdb)b do_fork
(gdb)b copy_process
(gdb)b dup_task_struct
(gdb)b copy_thread
(gdb)b ret_from_fork

  • 断点常用命令合集

  • 开始调试







do_fork函数

long do_fork(unsigned long clone_flags,
unsigned long stack_start,
unsigned long stack_size,
int __user *parent_tidptr,
int __user *child_tidptr)
{
struct task_struct *p;
int trace = 0;
long nr; // ... // 复制进程描述符,返回创建的task_struct的指针
p = copy_process(clone_flags, stack_start, stack_size,
child_tidptr, NULL, trace); if (!IS_ERR(p)) {
struct completion vfork;
struct pid *pid; trace_sched_process_fork(current, p); // 取出task结构体内的pid
pid = get_task_pid(p, PIDTYPE_PID);
nr = pid_vnr(pid); if (clone_flags & CLONE_PARENT_SETTID)
put_user(nr, parent_tidptr); // 如果使用的是vfork,那么必须采用某种完成机制,确保父进程后运行
if (clone_flags & CLONE_VFORK) {
p->vfork_done = &vfork;
init_completion(&vfork);
get_task_struct(p);
} // 将子进程添加到调度器的队列,使得子进程有机会获得CPU
wake_up_new_task(p); // ... // 如果设置了 CLONE_VFORK 则将父进程插入等待队列,并挂起父进程直到子进程释放自己的内存空间
// 保证子进程优先于父进程运行
if (clone_flags & CLONE_VFORK) {
if (!wait_for_vfork_done(p, &vfork))
ptrace_event_pid(PTRACE_EVENT_VFORK_DONE, pid);
} put_pid(pid);
} else {
nr = PTR_ERR(p);
}
return nr;
}

调用copy_process,将当期进程复制一份出来为子进程,并且为子进程设置相应地上下文信息。初始化vfork的完成处理信息(如果是vfork调用)调用wake_up_new_task,将子进程放入调度器的队列中,此时的子进程就可以被调度进程选中,得以运行。

  • copy_process的部分代码:
static struct task_struct *copy_process(unsigned long clone_flags,
unsigned long stack_start,
unsigned long stack_size,
int __user *child_tidptr,
struct pid *pid,
int trace)
{
int retval;
struct task_struct *p;
...
retval = security_task_create(clone_flags);//安全性检查
...
p = dup_task_struct(current); //复制PCB,为子进程创建内核栈、进程描述符
ftrace_graph_init_task(p);
··· retval = -EAGAIN;
// 检查该用户的进程数是否超过限制
if (atomic_read(&p->real_cred->user->processes) >=
task_rlimit(p, RLIMIT_NPROC)) {
// 检查该用户是否具有相关权限,不一定是root
if (p->real_cred->user != INIT_USER &&
!capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN))
goto bad_fork_free;
}
...
// 检查进程数量是否超过 max_threads,后者取决于内存的大小
if (nr_threads >= max_threads)
goto bad_fork_cleanup_count; if (!try_module_get(task_thread_info(p)->exec_domain->module))
goto bad_fork_cleanup_count;
...
spin_lock_init(&p->alloc_lock); //初始化自旋锁
init_sigpending(&p->pending); //初始化挂起信号
posix_cpu_timers_init(p); //初始化CPU定时器
···
retval = sched_fork(clone_flags, p); //初始化新进程调度程序数据结构,把新进程的状态设置为TASK_RUNNING,并禁止内核抢占
...
// 复制所有的进程信息
shm_init_task(p);
retval = copy_semundo(clone_flags, p);
...
retval = copy_files(clone_flags, p);
...
retval = copy_fs(clone_flags, p);
...
retval = copy_sighand(clone_flags, p);
...
retval = copy_signal(clone_flags, p);
...
retval = copy_mm(clone_flags, p);
...
retval = copy_namespaces(clone_flags, p);
...
retval = copy_io(clone_flags, p);
...
retval = copy_thread(clone_flags, stack_start, stack_size, p);// 初始化子进程内核栈
...
//若传进来的pid指针和全局结构体变量init_struct_pid的地址不相同,就要为子进程分配新的pid
if (pid != &init_struct_pid) {
retval = -ENOMEM;
pid = alloc_pid(p->nsproxy->pid_ns_for_children);
if (!pid)
goto bad_fork_cleanup_io;
} ...
p->pid = pid_nr(pid); //根据pid结构体中获得进程pid
//若 clone_flags 包含 CLONE_THREAD标志,说明子进程和父进程在同一个线程组
if (clone_flags & CLONE_THREAD) {
p->exit_signal = -1;
p->group_leader = current->group_leader; //线程组的leader设为子进程的组leader
p->tgid = current->tgid; //子进程继承父进程的tgid
} else {
if (clone_flags & CLONE_PARENT)
p->exit_signal = current->group_leader->exit_signal;
else
p->exit_signal = (clone_flags & CSIGNAL);
p->group_leader = p; //子进程的组leader就是它自己 p->tgid = p->pid; //组号tgid是它自己的pid
} ... if (likely(p->pid)) {
ptrace_init_task(p, (clone_flags & CLONE_PTRACE) || trace); init_task_pid(p, PIDTYPE_PID, pid);
if (thread_group_leader(p)) {
...
// 将子进程加入它所在组的哈希链表中
attach_pid(p, PIDTYPE_PGID);
attach_pid(p, PIDTYPE_SID);
__this_cpu_inc(process_counts);
} else {
...
}
attach_pid(p, PIDTYPE_PID);
nr_threads++; //增加系统中的进程数目
}
...
return p; //返回被创建的子进程描述符指针P
...
}

创建进程描述符以及子进程所需要的其他所有数据结构,为子进程准备运行环境。调用dup_task_struct复制一份task_struct结构体,作为子进程的进程描述符。复制所有的进程信息。调用copy_thread,设置子进程的堆栈信息,为子进程分配一个pid。

  • dup_task_struct的代码:
static struct task_struct *dup_task_struct(struct task_struct *orig)
{
struct task_struct *tsk;
struct thread_info *ti;
int node = tsk_fork_get_node(orig);
int err; // 分配一个task_struct结点
tsk = alloc_task_struct_node(node);
if (!tsk)
return NULL; // 分配一个thread_info结点,其实内部分配了一个union,包含进程的内核栈
// 此时ti的值为栈底,在x86下为union的高地址处。
ti = alloc_thread_info_node(tsk, node);
if (!ti)
goto free_tsk; err = arch_dup_task_struct(tsk, orig);
if (err)
goto free_ti; // 将栈底的值赋给新结点的stack
tsk->stack = ti; ... /*
* One for us, one for whoever does the "release_task()" (usually
* parent)
*/
// 将进程描述符的使用计数器置为2
atomic_set(&tsk->usage, 2);
#ifdef CONFIG_BLK_DEV_IO_TRACE
tsk->btrace_seq = 0;
#endif
tsk->splice_pipe = NULL;
tsk->task_frag.page = NULL; account_kernel_stack(ti, 1); // 返回新申请的结点
return tsk; free_ti:
free_thread_info(ti);
free_tsk:
free_task_struct(tsk);
return NULL;
}

先调用alloc_task_struct_node分配一个task_struct结构体。调用alloc_thread_info_node,分配了一个union。这里分配了一个thread_info结构体,还分配了一个stack数组。返回值为ti,实际上就是栈底。tsk->stack = ti将栈底的地址赋给task的stack变量。最后为子进程分配了内核栈空间。执行完dup_task_struct之后,子进程和父进程的task结构体,除了stack指针之外,完全相同

总结

进程的创建过程大致是复制进程描述符、一一复制其他进程资源(采用分时复制技术)、分配子进程的内核堆栈并对内核堆栈关键信息进行初始化。

2019-2020-1 20199312《Linux内核原理与分析》第七周作业的更多相关文章

  1. 2019-2020-1 20199329《Linux内核原理与分析》第九周作业

    <Linux内核原理与分析>第九周作业 一.本周内容概述: 阐释linux操作系统的整体构架 理解linux系统的一般执行过程和进程调度的时机 理解linux系统的中断和进程上下文切换 二 ...

  2. 2019-2020-1 20199329《Linux内核原理与分析》第二周作业

    <Linux内核原理与分析>第二周作业 一.上周问题总结: 未能及时整理笔记 Linux还需要多用 markdown格式不熟练 发布博客时间超过规定期限 二.本周学习内容: <庖丁解 ...

  3. 20169212《Linux内核原理与分析》第二周作业

    <Linux内核原理与分析>第二周作业 这一周学习了MOOCLinux内核分析的第一讲,计算机是如何工作的?由于本科对相关知识的不熟悉,所以感觉有的知识理解起来了有一定的难度,不过多查查资 ...

  4. 20169210《Linux内核原理与分析》第二周作业

    <Linux内核原理与分析>第二周作业 本周作业分为两部分:第一部分为观看学习视频并完成实验楼实验一:第二部分为看<Linux内核设计与实现>1.2.18章并安装配置内核. 第 ...

  5. 2018-2019-1 20189221 《Linux内核原理与分析》第九周作业

    2018-2019-1 20189221 <Linux内核原理与分析>第九周作业 实验八 理理解进程调度时机跟踪分析进程调度与进程切换的过程 进程调度 进度调度时机: 1.中断处理过程(包 ...

  6. 2017-2018-1 20179215《Linux内核原理与分析》第二周作业

    20179215<Linux内核原理与分析>第二周作业 这一周主要了解了计算机是如何工作的,包括现在存储程序计算机的工作模型.X86汇编指令包括几种内存地址的寻址方式和push.pop.c ...

  7. 2019-2020-1 20209313《Linux内核原理与分析》第二周作业

    2019-2020-1 20209313<Linux内核原理与分析>第二周作业 零.总结 阐明自己对"计算机是如何工作的"理解. 一.myod 步骤 复习c文件处理内容 ...

  8. 2018-2019-1 20189221《Linux内核原理与分析》第一周作业

    Linux内核原理与分析 - 第一周作业 实验1 Linux系统简介 Linux历史 1991 年 10 月,Linus Torvalds想在自己的电脑上运行UNIX,可是 UNIX 的商业版本非常昂 ...

  9. 《Linux内核原理与分析》第一周作业 20189210

    实验一 Linux系统简介 这一节主要学习了Linux的历史,Linux有关的重要人物以及学习Linux的方法,Linux和Windows的区别.其中学到了LInux中的应用程序大都为开源自由的软件, ...

  10. 2018-2019-1 20189221《Linux内核原理与分析》第二周作业

    读书报告 <庖丁解牛Linux内核分析> 第 1 章 计算工作原理 1.1 存储程序计算机工作模型 1.2 x86-32汇编基础 1.3汇编一个简单的C语言程序并分析其汇编指令执行过程 因 ...

随机推荐

  1. 可能这些是你想要的H5软键盘兼容方案

    前言 最近一段时间在做 H5 聊天项目,踩过其中一大坑:输入框获取焦点,软键盘弹起,要求输入框吸附(或顶)在输入法框上.需求很明确,看似很简单,其实不然.从实验过一些机型上看,发现主要存在以下问题: ...

  2. Python Paramiko实现sftp文件上传下载以及远程执行命令

    一.简介 Paramiko模块是基于Python实现的SSH远程安全连接,用于SSH远程执行命令.文件传输等功能. 安装模块 默认Python没有自带,需要手动安装: pip3 install par ...

  3. python 获取mysql数据库列表以及用户权限

    一.需求分析 需要统计出当前数据库的所有数据库名,以及每个用户的授权信息. 获取所有数据库 在mysql里面,使用命令: show databases 就可以获取所有数据库了 获取所有用户 执行命令: ...

  4. Unity性能优化-音频设置

    没想到Unity的音频会成为内存杀手,在实际的商业项目中,音频的优化必不可少. 1. Unity支持许多不同的音频格式,但最终它将它们全部转换为首选格式.音频压缩格式有PCM.ADPCM.Vorbis ...

  5. asp.net WEB简单打印

    ASP.NET网页打印 2018.08.26 18:50 1096浏览   昨晚朋友要求在前段时间完成的新闻的网站上加上一个功能,就是在每篇新闻浏览的页面, 加一个打印铵钮.让用户一点打印,能把整篇文 ...

  6. 使用requests简单的页面爬取

    首先安装requests库和准备User Agent 安装requests直接使用pip安装即可 pip install requests 准备User Agent,直接在百度搜索"UA查询 ...

  7. BUAA_OO第三单元总结性博客作业——JML

    一.JML 在第三单元的面向对象课程中我们第一次接触了JML语言以及基于JML规范的规格化设计.在之前一系列关于面向对象思想的学习认识中,我们知道了Java是一种面向对象的语言,面向对象思想的一个重要 ...

  8. 浅谈Vue.js2.0某些概念

    Vue.js2.0是一套构建用户界面的渐进式框架,目标是实现数据驱动和组件系统.   A 渐进式框架 Vue.js是一个提供MVVM数据双向绑定的库,只专注于UI层面,这是它的核心.它本身没有解决SP ...

  9. ubuntu安装mysql数据库方法

    ubuntu基于linux的免费开源桌面PC操作系统,十分契合英特尔的超极本定位,支持x86.64位和ppc架构.一个比较流行的Linux操作系统,不仅简单易用,而且和Windows相容性非常好.那么 ...

  10. MySql 严格模式相关配置

    目录 MySql 严格模式 MySQL的sql_mode合理设置 sql model 常用来解决下面几类问题 sql_mode常用值 注意 改为严格模式后可能会存在的问题 模式设置和修改(以解决上述问 ...