IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 12 [虚拟化] 进程抽象;fork,execve,exit -> 正文阅读

[系统运维]12 [虚拟化] 进程抽象;fork,execve,exit

12 [虚拟化] 进程抽象;fork,execve,exit

南京大学操作系统课蒋炎岩老师网络课程笔记。

视频:https://www.bilibili.com/video/BV1N741177F5?p=12
讲义:http://jyywiki.cn/OS/2021/slides/8.slides#/

本讲概述

回到“操作系统是管理程序运行的软件”

  • 操作系统中的进程
    • 程序 = 状态机
    • 操作系统 = 多个状态机
  • 进程管理API
    • fork
    • execve
    • exit

再次强调,一定要深入理解:程序(进程)就是一个状态机

操作系统中的进程

复习:应用程序

应用程序 = 代码 + 数据(文件) = 状态机

  • a.out, bash, ls ,grep
  • gcc(cc1, as, collect2, ld)
  • xedit, vscode

复习:操作系统

操作系统是管理多个应用程序执行的软件。

  • 应用视角:操作系统就是一组系统调用
  • 硬件视角:操作系统就是个状态机(C程序)

理解“最小”操作系统:

如果硬件提供一些机制(如虚拟存储来虚拟化内存M和寄存器R,即(M,R))使得各个“线程”不能访问其他“线程”、操作系统的内存,就得到了虚拟化的“进程”,仿佛独占CPU运行。注意:这里是将运行在操作系统上的各个程序(进程)看做了是运行在操作系统这个大程序(进程)上的一些”线程“。

操作系统:状态机的虚拟化

操作系统“模拟”了其中所有程序的状态机

  • 这就是“虚拟化”
  • 程序仿佛自己独占CPU运行,但它独占的只是CPU的一部分,其他部分它“看不见”。

进程:运行的程序。任意时刻,进程都可以看做是状态机的状态。

操作系统在终端以后,可以选择将进程(状态机)调度到CPU上运行。而进程执行系统调用,会使用指令(syscall)等回到操作系统。

“操作系统是一个中断处理程序”

  • 被动的中断:硬件(时钟、I/O设备、NMI,)
  • 主动的中断:系统调用

操作系统运行的两种模式

  • 用户态(ring 3):应用程序运行在用户态
  • 内核态(ring 0):操作系统运行在内核态

二者的切换如上面所述:

  • 中断、系统调用:用户态 -> 内核态
  • 操作系统调度:内核态 -> 用户态

就是这样的切换,使得我们的应用程序(在用户态)实现了虚拟化,同时操作系统仿佛就是一个中断处理程序。

操作系统课的三种调用

  • 进程(状态机)管理

    fork, execve, exit:进程(状态机)的创建、改变和删除

  • 存储(地址空间)管理

    mmap:对进程虚拟地址空间的一部分进行映射

    brk:虚拟地址空间管理

  • 文件(数据对象)管理

    open, close:文件访问管理

    read, write:数据管理

    mkdir, link, unling:目录管理

fork() 状态机管理:创建状态机

如果需要创建状态机,我们需要什么样的API?

UNIX的答案:fork()

  • 做一份状态机的完整的复制(内存M,寄存器现场R)
  • 父进程返回子进程的PID,子进程返回0

fork bomb

fork bomb代码解析:

:(){:|:&};:  # 一行版本的fork bomb

:(){				 # 格式化一下
	: | : &
};:

fork(){			 # 这其实在bash中定义了一个函数,bash允许以冒号作为标识符
	fork | fork &
}; fork

父子进程、进程树

因为状态机是复制的,因此总能找到”父子关系“。

因此有了进程树(pstree),如下:

systemd─┬─ModemManager───2*[{ModemManager}]
        ├─NetworkManager─┬─dhclient
        │                └─2*[{NetworkManager}]
        ├─accounts-daemon───2*[{accounts-daemon}]
        ├─acpid
        ├─avahi-daemon───avahi-daemon
        ├─boltd───2*[{boltd}]
        ├─colord───2*[{colord}]
        ├─cron
        ├─cups-browsed───2*[{cups-browsed}]
        ├─cupsd───dbus
        ├─dbus-daemon
        ├─gdm3─┬─gdm-session-wor─┬─gdm-x-session─┬─Xorg───{Xorg}
        │      │                 │               ├─gnome-session-b─┬─gnome-shell─┬─ibus-daemon─┬─ibus-dconf───3*[{ibus-dconf}]
        │      │                 │               │                 │             │             ├─ibus-engine-sim───2*[{ibus-engine-
...

进程树的存在,是我们用fork()复制创造子进程,所得到的一个很自然的结果。

例程1

猜猜会打印出什么呢?(提示:可以试着画一下状态机,线程树)

#include <unistd.h>
#include <stdio.h>

int main(){
    pid_t pid1 = fork();
    pid_t pid2 = fork();
    pid_t pid3 = fork();

    printf("Hello World from (%d, %d, %d)\n", pid1, pid2, pid3);
}

例程2

猜猜会打印出什么呢?

#include <unistd.h>
#include <stdio.h>

#define N 2
int main(){
    for (int i=0; i<N; i++){
        fork();
        printf("Hello\n");
    }
}

还是可以逐步画一下程序的状态机,

gcc test.c
./a.out

输出结果应该是6。

有趣的是,如果我们将输出重定向到管道,再通过wc -l命令打印出行数,这时会输出8,这不禁令我们大为惊奇。

为什么会这样呢?这其实是因为printf函数将内容直接输出到标准输出stdout时是直接输出的,会按照我们的理解打印出6个Hello。但是如果要重定向到管道(或文本文件等),printf则会将要输出的内容先放置到缓冲区,到最后一起打印,在本例中,由于我们fork()创建进程的时候,会将全部的内存M和寄存器现场R复制一份,导致每次fork()时,缓冲区也被完整地复制,最后我们每个进程的缓冲区有2个Hello,最后共有四个进程,故会有8个Hello。(也可以重定向到文本文件中试一下,确实是有8个Hello)。这恰好进一步验证了我们所说的fork()是对整个(M,R)的完整复制。

可以尝试理解一下N=3时正常打印和重定向打印会有多少个Hello。笔者认为分别是 ∑ i = 1 N 2 i \sum_{i=1}^N2^i i=1N?2i 2 N × N 2^N\times N 2N×N

机器永远是对的,计算机系统的世界没有魔法,一切都是按部就班地进行的

execve() 状态机管理:替换状态机(执行)

只有fork新建还不够,我们还需要能够执行别的程序。

UNIX的答案:execve

execve(filename, argv, enpv)

  • 执行名为filename的程序
  • 分别传入参数argv(v)和环境变量enpv(e)

这刚好对应了main函数的参数:

int main(int argc, char** argv, char** enpv){
  	// ...
}

关于main函数的参数:Linux中 C++ main函数参数argc和argv含义及用法

execve可以看作状态机的重置。

环境变量

环境变量即应用程序执行的环境。

  • 使用env命令来查看
    • PATH:可执行文件的搜索路径
    • PWD:当前路径
    • HOME:home目录
    • DISPLAY:图形输出
    • PS1:shell的提示符
  • export:告诉shell在创建子进程时设置环境变量

PATH环境变量

PATH环境变量是可执行文件的搜索路径

还记得gcc的strace结果吗

[pid 28369] execve("/usr/local/sbin/as", [“as”, “–64”, …
[pid 28369] execve("/usr/local/bin/as", [“as”, “–64”, …
[pid 28369] execve("/usr/sbin/as", [“as”, “–64”, …
[pid 28369] execve("/usr/bin/as", [“as”, “–64”, …

这个搜索顺序恰好是PATH环境变量中指定的顺序:

$ PATH="" /usr/bin/gcc fork-demo.c
gcc: error trying to exec ‘as’: execvp: No such file or directory
$ PATH="/usr/bin/" gcc fork-demo.c

在gcc被execve时,将环境变量PATH传给gcc,它就会按照其顺序来搜索可执行文件的路径。

计算机系统里没有魔法,机器永远是对的

exit() 状态机管理:终止状态机

有了fork,execve,我们可以自由地创建、执行程序(状态机)了,还缺一个销毁状态机的函数

UNIX的答案:exit

  • 销毁当前状态机,并允许有一个返回值
  • 子进程终止会通知父进程(之后会讲)

问题是一个进程(状态机)中有多个线程啊。

结束程序执行的三种方法

exit的几种写法,它们是不同的:

  • exit(0) - stdlib.h中声明的libc函数
    • 它会调用atexit
  • _exit(0) - glibc中的syscall wrapper
    • 执行exit_group系统调用,终止整个进程(所有线程)
    • 不会调用atexit
  • syscall(SYS_exit, 0)
    • 执行exit系统调用终止当前线程
    • 不会调用atexit

最起码要区分好库函数(应用程序的一部分)和系统调用

可以用strace观察各种结束方式的执行。

Fork-Exec vs. Spawn

我们既然fork创建了一个子进程,那我们绝大多情况下肯定是要execve执行这个进程的,也就是说fork后面几乎一定会跟着execve,那为什么不直接把它们合成一个系统调用 spawn(path, argv, enpv) 呢?即spawn = fork + execve

实际上,fork + execve是一个非常优雅的实现,因为要考虑到进程可以持有操作系统中的对象,这使fork、execve、exit还要涉及到操作系统的对象的管理。

例如,在上面用到过的管道技术中:

./a.out | wc -l

其中./a.out持有了操作系统中的对象——管道的写口,而wc -l则持有了管道的读口,从而能够将前者的输出作为后者的输入。而如果./a.out这个进程(状态机)需要fork一个子进程,那么这个子进程就可以自然地复制拿到父进程的全部(M,R)。这样,对于操作系统中的对象——管道,子进程就持有了其写口。

Take aways and Wrap-up

虚拟化

  • 程序 = 状态机
  • 操作系统 = 状态机的管理者
    • 用硬件(物理状态机)实现多个并发执行的虚拟状态机
    • API:fork,execve,exit

Ref:

http://jyywiki.cn/OS/2021/slides/8.slides#/

https://www.bilibili.com/video/BV1N741177F5?p=12

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2021-09-29 10:44:28  更:2021-09-29 10:46:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 17:43:09-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码