趣谈Linux操作系统
刘超
网易杭州研究院云计算技术部首席架构师
立即订阅
19393 人已学习
课程目录
已完结 72 讲
0/4登录后,你可以任选4讲全文学习。
入门准备篇 (3讲)
开篇词 | 为什么要学习Linux操作系统?
免费
01 | 入学测验:你究竟对Linux操作系统了解多少?
02 | 学习路径:爬过这六个陡坡,你就能对Linux了如指掌
核心原理篇:第一部分 Linux操作系统综述 (3讲)
03 | 你可以把Linux内核当成一家软件外包公司的老板
04 | 快速上手几个Linux命令:每家公司都有自己的黑话
05 | 学会几个系统调用:咱们公司能接哪些类型的项目?
核心原理篇:第二部分 系统初始化 (4讲)
06 | x86架构:有了开放的架构,才能打造开放的营商环境
07 | 从BIOS到bootloader:创业伊始,有活儿老板自己上
08 | 内核初始化:生意做大了就得成立公司
09 | 系统调用:公司成立好了就要开始接项目
核心原理篇:第三部分 进程管理 (10讲)
10 | 进程:公司接这么多项目,如何管?
11 | 线程:如何让复杂的项目并行执行?
12 | 进程数据结构(上):项目多了就需要项目管理系统
13 | 进程数据结构(中):项目多了就需要项目管理系统
14 | 进程数据结构(下):项目多了就需要项目管理系统
15 | 调度(上):如何制定项目管理流程?
16 | 调度(中):主动调度是如何发生的?
17 | 调度(下):抢占式调度是如何发生的?
18 | 进程的创建:如何发起一个新项目?
19 | 线程的创建:如何执行一个新子项目?
核心原理篇:第四部分 内存管理 (7讲)
20 | 内存管理(上):为客户保密,规划进程内存空间布局
21 | 内存管理(下):为客户保密,项目组独享会议室封闭开发
22 | 进程空间管理:项目组还可以自行布置会议室
23 | 物理内存管理(上):会议室管理员如何分配会议室?
24 | 物理内存管理(下):会议室管理员如何分配会议室?
25 | 用户态内存映射:如何找到正确的会议室?
26 | 内核态内存映射:如何找到正确的会议室?
核心原理篇:第五部分 文件系统 (4讲)
27 | 文件系统:项目成果要归档,我们就需要档案库
28 | 硬盘文件系统:如何最合理地组织档案库的文档?
29 | 虚拟文件系统:文件多了就需要档案管理系统
30 | 文件缓存:常用文档应该放在触手可得的地方
核心原理篇:第六部分 输入输出系统 (5讲)
31 | 输入与输出:如何建立售前售后生态体系?
32 | 字符设备(上):如何建立直销模式?
33 | 字符设备(下):如何建立直销模式?
34 | 块设备(上):如何建立代理商销售模式?
35 | 块设备(下):如何建立代理商销售模式?
核心原理篇:第七部分 进程间通信 (7讲)
36 | 进程间通信:遇到大项目需要项目组之间的合作才行
37 | 信号(上):项目组A完成了,如何及时通知项目组B?
38 | 信号(下):项目组A完成了,如何及时通知项目组B?
39 | 管道:项目组A完成了,如何交接给项目组B?
40 | IPC(上):不同项目组之间抢资源,如何协调?
41 | IPC(中):不同项目组之间抢资源,如何协调?
42 | IPC(下):不同项目组之间抢资源,如何协调?
核心原理篇:第八部分 网络系统 (7讲)
43 预习 | Socket通信之网络协议基本原理
43 | Socket通信:遇上特大项目,要学会和其他公司合作
44 | Socket内核数据结构:如何成立特大项目合作部?
45 | 发送网络包(上):如何表达我们想让合作伙伴做什么?
46 | 发送网络包(下):如何表达我们想让合作伙伴做什么?
47 | 接收网络包(上):如何搞明白合作伙伴让我们做什么?
48 | 接收网络包(下):如何搞明白合作伙伴让我们做什么?
核心原理篇:第九部分 虚拟化 (7讲)
49 | 虚拟机:如何成立子公司,让公司变集团?
50 | 计算虚拟化之CPU(上):如何复用集团的人力资源?
51 | 计算虚拟化之CPU(下):如何复用集团的人力资源?
52 | 计算虚拟化之内存:如何建立独立的办公室?
53 | 存储虚拟化(上):如何建立自己保管的单独档案库?
54 | 存储虚拟化(下):如何建立自己保管的单独档案库?
55 | 网络虚拟化:如何成立独立的合作部?
核心原理篇:第十部分 容器化 (4讲)
56 | 容器:大公司为保持创新,鼓励内部创业
57 | Namespace技术:内部创业公司应该独立运营
58 | CGroup技术:内部创业公司应该独立核算成本
59 | 数据中心操作系统:上市敲钟
实战串讲篇 (9讲)
60 | 搭建操作系统实验环境(上):授人以鱼不如授人以渔
61 | 搭建操作系统实验环境(下):授人以鱼不如授人以渔
62 | 知识串讲:用一个创业故事串起操作系统原理(一)
63 | 知识串讲:用一个创业故事串起操作系统原理(二)
64 | 知识串讲:用一个创业故事串起操作系统原理(三)
65 | 知识串讲:用一个创业故事串起操作系统原理(四)
66 | 知识串讲:用一个创业故事串起操作系统原理(五)
67 | 期末测试:这些操作系统问题,你真的掌握了吗?
结束语 | 永远别轻视任何技术,也永远别轻视自己
免费
专栏加餐 (2讲)
学习攻略(一):学好操作系统,需要掌握哪些前置知识?
“趣谈Linux操作系统”食用指南
免费
趣谈Linux操作系统
登录|注册

14 | 进程数据结构(下):项目多了就需要项目管理系统

刘超 2019-04-29
上两节,我们解读了 task_struct 的大部分的成员变量。这样一个任务执行的方方面面,都可以很好地管理起来,但是其中有一个问题我们没有谈。在程序执行过程中,一旦调用到系统调用,就需要进入内核继续执行。那如何将用户态的执行和内核态的执行串起来呢?
这就需要以下两个重要的成员变量:
struct thread_info thread_info;
void *stack;

用户态函数栈

在用户态中,程序的执行往往是一个函数调用另一个函数。函数调用都是通过栈来进行的。我们前面大致讲过函数栈的原理,今天我们仔细分析一下。
函数调用其实也很简单。如果你去看汇编语言的代码,其实就是指令跳转,从代码的一个地方跳到另外一个地方。这里比较棘手的问题是,参数和返回地址应该怎么传递过去呢?
我们看函数的调用过程,A 调用 B、调用 C、调用 D,然后返回 C、返回 B、返回 A,这是一个后进先出的过程。有没有觉得这个过程很熟悉?没错,咱们数据结构里学的栈,也是后进先出的,所以用栈保存这些最合适。
在进程的内存空间里面,栈是一个从高地址到低地址,往下增长的结构,也就是上面是栈底,下面是栈顶,入栈和出栈的操作都是从下面的栈顶开始的。
我们先来看 32 位操作系统的情况。在 CPU 里,ESP(Extended Stack Pointer)是栈顶指针寄存器,入栈操作 Push 和出栈操作 Pop 指令,会自动调整 ESP 的值。另外有一个寄存器 EBP(Extended Base Pointer),是栈基地址指针寄存器,指向当前栈帧的最底部。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《趣谈Linux操作系统》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(25)

  • why
    - 用户态/内核态切换执行如何串起来
    - 用户态函数栈; 通过 JMP + 参数 + 返回地址 调用函数
        - 栈内存空间从高到低增长
        - 32位栈结构: 栈帧包含 前一个帧的 EBP + 局部变量 + N个参数 + 返回地址
            - ESP: 栈顶指针; EBP: 栈基址(栈帧最底部, 局部变量起始)
            - 返回值保存在 EAX 中
        - 64位栈结构: 结构类似
            - rax 保存返回结果; rsp 栈顶指针; rbp 栈基指针
            - 参数传递时, 前 6个放寄存器中(再由被调用函数 push 进自己的栈, 用以寻址), 参数超过 6个压入栈中
    - 内核栈结构:
        - Linux 为每个 task 分配了内核栈, 32位(8K), 64位(16K)
        - 栈结构: [预留8字节 +] pt_regs + 内核栈 + 头部 thread_info
        - thread_info 是 task_struct 的补充, 存储于体系结构有关的内容
        - pt_regs 用以保存用户运行上下文, 通过 push 寄存器到栈中保存
        - 通过 task_struct 找到内核栈
            - 直接由 task_struct 内的 stack 直接得到指向 thread_info 的指针
        - 通过内核栈找到 task_struct
            - 32位 直接由 thread_info 中的指针得到
            - 64位 每个 CPU 当前运行进程的 task_struct 的指针存放到 Per CPU 变量 current_task 中; 可调用 this_cpu_read_stable 进行读取
    2019-04-30
    21
  • 第九天魔王
    从网络协议课学到这里,满满的干货啊。特别是配图,太用心了,一看就是非常有心才能做出来的。谢谢刘老师!

    作者回复: 谢谢鼓励

    2019-04-29
    10
  • ZYecho
    为什么32位的处理器还需要单独压ss和esp呢? pt_regs结构体中不是已经含有这两个寄存器了么?谢谢老师
    2019-09-09
    2
  • ZYecho
    老师您好,在 64 位上,修改了这个问题,变成了定长的。这句话怎么理解呢?
    具体在64位上是如何进行操作的呢?
    2019-09-06
    2
  • 青石
    看这篇内容时,查了几篇资料。

    在汇编代码中,函数调用的参数传递是通过把参数依次放在靠近调用者的栈的顶部来实现的。调用者获取参数时,只要相对于当前帧指针的向上偏移即可取到参数。即取调用者函数参数时执行movl 8(%ebp), %edx。

    作者回复: 赞,推荐这种查资料的方式,这样学到的东西就多多了

    2019-04-29
    2
  • Liber
    0点自动发,应该是自动发布的吧

    作者回复: 是的,是的,是自动发的。

    2019-04-29
    2
  • k先生
    老师,有个问题能否解答?
    task_struct找内核栈过程理解了,但是反过来找,这句“而 thread_info 的位置就是内核栈的最高位置,减去 THREAD_SIZE,就到了 thread_info 的起始地址。”没懂,烦请解答一下

    这里说的最高位置应该是栈顶,到了栈顶就是thread_info最低地址,那不就直接找到了吗

    作者回复: 不是的,对着图可以看的比较清楚

    2019-07-25
    2
    1
  • 青石
    在12节里面提到“Linux的任务管理由统一的结构task_struct进行管理”。那么多核CPU的任务切换时,是不是就是将current_task切换到另一个task_struct呢?

    THREAD_SIZE是固定的大小,32位系统中是8K(页大小左移一位),64位系统是16K(页大小左移二位)。TOP_OF_KERNEL_STACK_PADDING就是图“内核栈是一个非常特殊的结构”中的“预留8个字节”。

    通过task_struct找内核栈的过程:
    1. task_struct找内核栈是通过stack指针,直接找到内核线程栈,stack指针记录的是内核栈的首地址。
    2. task_struck找内核寄存器是通过 内核栈的首地址(1中的stack指针) + (THREAD_SIZE - TOP_OF_KERNEL_STACK_PADDING)定位到pt_regs的最高位地址,再减一得到pt_regs的最低位地址(首地址)。

    作者回复: 不仅仅切换这个变量,要切换的还挺多的

    2019-04-29
    1
    1
  • hua168
    老师,我求您了,能不能把您讲的专栏中涉及的书名加个豆瓣之类的链接😂😂,有些都重名,作者不同,😭😭不带这样玩的😭😭😭

    作者回复: 啊,好的,看来应该给英文名加作者的

    2019-04-29
    1
  • 石维康
    文中说“接下来就是 B 的栈帧部分了,先保存的是 A 栈帧的栈底位置,也就是 EBP。因为在 B 函数里面获取 A 传进来的参数,就是通过这个指针获取的,”感觉主流编译器还是直接能通过当前 RBP 或者 RSP 来进行偏移定位到传进来的参数了吧?保存这个 A 栈底位置更多的是为了回复 A 的现场吧?

    作者回复: 能的。

    2019-04-29
    2
    1
  • 珠闪闪
    刘老师,请教一下,什么时候用内核栈找task_struct?什么时候又反过来找?
    2019-11-26
  • 陈志恒
    摘抄:如果说 task_struct 的其他成员变量都是和进程管理有关的,内核栈是和进程运行有关系的。
    2019-11-25
  • Doubleuhy
    老师 64位架构函数调用栈 图中是不是有错鸭 第1-6个参数应该是由caller存入寄存器中 在callee中使用参数是通过mov指令来使用 没有用到push指令 所以rsp寄存器的值应该和rbp值一样才对 应该指向同一个地址
    2019-11-19
  • 希夷
    内核栈这个图里边预留8个字节下边的pg_regs,应该是pt_regs吧?
    2019-10-22
  • 小橙子
    而 thread_info 的位置就是内核栈的最高位置,减去...

    极客时间版权所有: https://time.geekbang.org/column/article/93014

    这句话怎么理解?
    2019-10-14
  • kdb_reboot
    看懂这章,需要知道进程的内存分布图
    2019-09-24
  • ZYecho
    老师你好,文中说涉及权限的改变,会压栈保存 SS、ESP 寄存器,我有个疑问是这个地方为什么只单独push这两个寄存器呢? cs、ip以及eflags寄存器呢?
    2019-09-06
  • ...
    老师有个疑问 A调用B B应该是被调用者吧 是不是应该放在图上面部分

    作者回复: 上面是栈的底部

    2019-08-14
  • 追风筝的人
    被调用函数通过栈指针获取入参内容,那函数调用的参数副本怎么理解?

    作者回复: 本来入栈就是一个副本了,不是原来的变量了

    2019-07-12
  • garlic
    X86_64 调用函数时避免调整栈指针的位置,存在一个128byte的red zone, 另外在实验过程中 与另外与专栏里讲到的不太一样的的是, 传递参数大于6个的,会被单独的放入栈顶,不过这个操作是调用函数做的。环境centos7 gcc: gcc version 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC), 这是我的课堂作业笔记
    2019-07-09
    1
收起评论
25
返回
顶部