什么是进程?

发布于 2024-08-10 15:06:30 字数 7387 浏览 16 评论 0

这篇文章主要还是引入一些思想的一些导论,主要讲的是进程是什么样的,我该怎么加载一个进程,为了跟进时代的变化这里还用了 chatgpt 来做了一些名词的解释之类的(相当好用!)

什么是进程

很多人可能一上来就是概念,阿吧阿巴,进程是: 进程 (英语:process),是指电脑中已执行的 程式 ,曾经是 分时系统 的基本运作单位。在面向进程设计的系统(如早期的 UNIXLinux 2.4 及更早的版本)中,是程式的基本执行实体;在面向线程设计的系统(如当代多数操作系统、 Linux 2.6 及更新的版本)中,行程本身不是基本执行单位,而是 执行绪 的容器。(来源于 wiki)

tl;dr 在冯诺依曼架构下面 进程就是一块内存其中填充了一堆当前 CPU 认识的指令

并且操作系统给他做了特殊标识 并把对应的资源附着在这上面

但是,有没有什么能让我们感觉到真实的进程是什么样的呢?

有,甚至很确切

#include <stdio.h>

int main () {
  printf("hello world\n");
  return 0;
}

这是一个简单的 hello world 代码,我们把它编译成 可执行文件 a.outgdb 调试他,在源代码里面随便打一个断点,之后使用 pmap 命令来获取进程号,具体过程如下面图所示

在 Linux 操作系统中,pmap 命令用于查看指定进程或进程中指定线程的内存映射情况。pmap 命令会输出指定进程或线程使用的内存地址、使用的内存大小、内存权限等相关信息。pmap 命令是一个非常有用的诊断工具,可以帮助用户检测内存泄漏、内存碎片等问题。通常情况下,pmap 命令需要 root 权限才能运行。以下是 pmap 命令的常见用法:

pmap PID  # 查看指定进程的内存映射情况
pmap -x PID # 查看指定进程的详细内存映射情况
pmap -d PID # 显示指定进程或线程的内存映射中的数据段
pmap -s PID # 显示指定进程或线程的内存映射中的栈段

来源于 chatgpt ,有兴趣的可以自己 man 1 pmap 查看

在这里同样你也可以通过 !cat /proc/83220/maps 来查看, pmap 其实对前者封装了一下而已,让他更 human friendly

上面的内存信息就是我们的进程真实的样子啦,很多人可能会很懵,你这进程长这逼样谁看得懂啊,别急,慢慢来。

这里简单介绍一下

  • linux 的内存都是 4k 为一个块,所以这里内存都是 4k 对齐的
  • a.out 就是我们的可执行文件
  • libc.so.6ld-linux-x86-64.so.2 就是我们进程动态链接过来的内存页,比如 hello world 用到的 printf 就是从 libc.so.6 动态链接过来的
  • anno 其实就是匿名页,比如说 mmap 映射的在这里就是匿名页
  • stack 就不用多说了
  • 其实细心同学用别的代码尝试的话还可能发现有 vdsovvar 等,由于篇幅原因可以自行 man 手册和 chatgpt

具体细节可以去看 jyy 老师的课: 进程的地址空间

elf 文件

那操作系统是怎么知道我们要创建这样的进程的呢?

没错, elf 文件 ,也就是我们的 xxx.o 文件(当然我们的 a.out 其实也是,毕竟是从 xxx.o 链接过来的),我们可以通过 file xxx.o 来查看文件类型

ELF(Executable and Linkable Format)文件是一种常见的二进制文件格式,在 Linux 及其他类 UNIX 系统中被广泛使用。ELF 文件可以包含可执行程序、共享库、目标文件等类型的程序代码。ELF 格式的设计目标是提供一种通用、可移植、灵活的二进制格式,能够适应各种 CPU 架构和操作系统。

ELF 文件具有丰富的头部、节区、符号表和重定位等元信息,这些元信息描述了程序的各种属性和结构,如 代码段、数据段、链接信息、调试信息 等等。这些元信息使得在运行时对程序进行加载、链接和执行变得简单和高效。ELF 文件中包含的重定位信息,也可以帮助程序进行动态链接和库加载并在运行时进行代码共享。

ELF 文件的结构十分清晰、灵活,使得他们能够满足不同类型的程序的需要。它被广泛应用于 Linux 系统的各种应用场景,无论是编译器、链接器、动态链接器、调试器,还是其他形式的二进制文件,都使用了 ELF 文件格式。(来源于 chatgpt)

其实 elf 文件 就是我们学到的一种数据结构而已,包括他的程序头,各种 section ,都在 elf.h 中定义,在你的 /usr/include/elf.h 就可以看到,这里随便列举点片段,这边建议搞懂 elf 具体长什么样再看后面的例子,可以看看 csapp 的第七章

typedef struct
{
  unsigned char e_ident[EI_NIDENT];     /* Magic number and other info */
  Elf64_Half    e_type;                 /* Object file type */
  Elf64_Half    e_machine;              /* Architecture */
  Elf64_Word    e_version;              /* Object file version */
  Elf64_Addr    e_entry;                /* Entry point virtual address */
  Elf64_Off     e_phoff;                /* Program header table file offset */
  Elf64_Off     e_shoff;                /* Section header table file offset */
  Elf64_Word    e_flags;                /* Processor-specific flags */
  Elf64_Half    e_ehsize;               /* ELF header size in bytes */
  Elf64_Half    e_phentsize;            /* Program header table entry size */
  Elf64_Half    e_phnum;                /* Program header table entry count */
  Elf64_Half    e_shentsize;            /* Section header table entry size */
  Elf64_Half    e_shnum;                /* Section header table entry count */
  Elf64_Half    e_shstrndx;             /* Section header string table index */
} Elf64_Ehdr; //64 位 elf 文件头

typedef struct
{
  Elf64_Word    sh_name;                /* Section name (string tbl index) */
  Elf64_Word    sh_type;                /* Section type */
  Elf64_Xword   sh_flags;               /* Section flags */
  Elf64_Addr    sh_addr;                /* Section virtual addr at execution */
  Elf64_Off     sh_offset;              /* Section file offset */
  Elf64_Xword   sh_size;                /* Section size in bytes */
  Elf64_Word    sh_link;                /* Link to another section */
  Elf64_Word    sh_info;                /* Additional section information */
  Elf64_Xword   sh_addralign;           /* Section alignment */
  Elf64_Xword   sh_entsize;             /* Entry size if section holds table */
} Elf64_Shdr;//64 位 section 头

typedef struct
{
  Elf64_Word    p_type;                 /* Segment type */
  Elf64_Word    p_flags;                /* Segment flags */
  Elf64_Off     p_offset;               /* Segment file offset */
  Elf64_Addr    p_vaddr;                /* Segment virtual address */
  Elf64_Addr    p_paddr;                /* Segment physical address */
  Elf64_Xword   p_filesz;               /* Segment size in file */
  Elf64_Xword   p_memsz;                /* Segment size in memory */
  Elf64_Xword   p_align;                /* Segment alignment */
} Elf64_Phdr;//64 位程序头

具体 elf 长啥样?

我们可以通过 readelf 命令来读取 elf 文件

readelf 是一个用于查看 ELF 文件的命令行工具。它支持读取并显示 ELF 文件的所有表头信息、段头信息、符号表信息、重定位信息、动态节信息、动态符号表信息等。

常用的 readelf 命令选项包括:

  • -a/--all:显示 ELF 文件的所有内容;
  • -h/--file-header:显示 ELF 文件的文件头信息;
  • -S/--section-header:显示 ELF 文件的节表信息;
  • -s/--symbols:显示 ELF 文件的符号表信息;
  • -r/--relocs:显示 ELF 文件的重定位表信息;
  • -d/--dynamic:显示 ELF 文件的动态节信息;
  • -V/--version-info:显示 ELF 文件使用的动态库版本信息。

示例:

读取 ELF 文件的文件头信息: readelf -h test.out

读取 ELF 文件的节表信息: readelf -S test.out

读取 ELF 文件的程序头信息: readelf -l test.out (来源于 chatgpt)感兴趣可以自行 man 1 readelf

以下是一些 helloworld 的例子

elf 文件头

elf 文件的各种 section

可执行文件的程序头

注意这里是 a.out 这种已经链接过的可执行文件才有的程序头,用于告诉操作系统该怎么加载这个进程,那些东西得放哪里......

来点实际的

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

渡你暖光

暂无简介

0 文章
0 评论
22 人气
更多

推荐作者

忆伤

文章 0 评论 0

眼泪也成诗

文章 0 评论 0

zangqw

文章 0 评论 0

旧伤慢歌

文章 0 评论 0

qq_GlP2oV

文章 0 评论 0

旧时模样

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文