Linux编程入门(14)-进程(二)

上一篇文章介绍了进程的基本概念:

Linux编程入门(13)-进程(一)

这篇继续进程相关内容的学习。

进程和程序

进程是一个执行中程序的实例,那么进程和程序之间到底有什么区别呢?

可执行程序

程序是包含了一系列信息的可执行文件,这些信息描述了如何在运行时创建一个进程。在 Linux 中,其可执行文件的存储格式为 ELF(Executable Linkable Format)。文件内包含的内容如下:

  • 二进制格式标识。每个程序文件都包含用于描述可执行文件格式的元信息。内核利用这个信息来解释文件中的其他信息。
  • 机器语言指令。机器能识别并执行的机器码。
  • 程序入口地址。标识程序开始执行时的起始指令位置。
  • 数据。变量初始值以及程序使用的常量值。
  • 符号表以及重定位表。描述程序中函数和变量的位置及名称。
  • 共享库和动态链接信息。程序运行时需要使用的共享库,以及动态链接器(用于加载共享库)的路径名称。
  • 其他信息。用于描述如何创建一个进程。

通过 file 指令可以查看文件类型信息

/* 查看程序源文件 */
$ file hello.c  
hello.c: C source, ASCII text

/* 查看可执行文件 */
$ file hello
hello: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=85bf88589da06576f5fea38706e07fde9e47dddd, not stripped

进程

从内核角度来看,进程由用户内存空间和一系列的内核数据结构组成,用户空间用于存放可执行程序的指令和数据,内核数据结构用于维护进程的状态信息。

记录在内核数据结构中的信息包括:

  • 与进程相关的标识号
  • 虚拟内存表
  • 打开的文件描述表
  • 信号传递和处理的有关信息
  • 进程资源
  • 当前的工作目录
  • 还有其他信息

进程的内存布局

进程还有一种定义:进程是程序运行时的内存空间和设置。那么,进程的内存布局具体是什么样的呢?

进程是内存中一些字节的抽象,系统为每个进程分配的内存由很多部分组成,以 x86-32 体系结构为例,来看看典型的进程内存结构,如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yYEqzx3q-1640667366091)(.\image-20211220235157409.png)]

这些组成部分,通常称为 “段”。几个重要段的功能解释:

  • 文本段。包含了进程运行的机器指令,这部分内容是只读的,不可写。

  • 初始化数据段。包含了经过显示初始化的全局变量和静态变量。程序加载时从可执行文件中读取这些变量的值。

  • 未初始化数据段。包含了未初始化的全局变量和静态变量。程序启动之前,系统将这段内存初始化为 0。在磁盘上存储的可执行文件,这部分内容实际上没有分配磁盘空间,只是记录了这个段的位置和所需的大小。

  • 栈(stack)。用于函数调用。系统会为当前调用的函数分配一个栈帧,用于存储函数的局部变量、实参、返回值,以及函数用到的一些 CPU 寄存器。

  • 堆(heap)。程序运行时,可以进行动态内存分配的一块区域。但要注意,使用完毕后要及时释放,否则会出现内存泄漏的问题。

  • 命令参数 argv。当程序执行时,命令行参数可以通过 main 函数的两个入参传入(如果程序需要外部传入指令参数)。参数个数 int argc 和 指向命令行参数的指针数组 char *argv[]

  • 环境列表 environ。可以用于将信息从父进程传递给子进程。实际上是字符串数组,每个字符串以 “名称=值”形式定义。系统新创建的进程,会继承其父进程的环境副本。

进程信息的存储

通过 ps 指令可以查看进程的很多属性:用户 ID、进程大小、运行时间、优先级和 nice 级别等等。

对于 Linux “一切皆文件”的设计思想,这些信息可以通过文件的方式进行存储和访问吗?当然,答案是肯定的。

Linux 提供了一个虚拟文件系统 /proc,用于以文件的形式存储和展示内核信息。但是,这个文件系统并不存储在磁盘中,而是存在于内存之中。此文件系统由内核动态创建,并挂载到目录 /proc 下。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NqcZclKM-1640667366093)(.\image-20211221010139170.png)]

获取进程信息

内核为系统中的每个进程提供了相应的目录:/proc/PID,其中 PID 是进程的 ID。这个目录中的各个文件和子目录记录了进程的相关信息。

例如,init 进程的 ID 为 1,此进程的信息存储在 proc/1 目录下。看看这个目录下都包括哪些内容(注意,需要超级用户权限,否则有的符号链接会拒绝访问)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8yGNxnjh-1640667366094)(.\image-20211221011433067.png)]

进程的一系列信息存储在一个名称为 status 的文件中,可以通过指令 cat /proc/PID/status 查看

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3ihnl7bF-1640667366095)(.\image-20211221012041616.png)]

/proc/PID/fd 目录

这个目录包含了进程打开的每个文件描述符的一个符号链接,每个符号链接都与文件描述符的数值相匹配。还是以 init 进程为例,查看 /proc/1/fd 目录下的文件列表信息:

$ sudo ls /proc/1/fd -l
total 0
lrwx------ 1 root root 64 12月 21 00:47 0 -> /dev/null
lrwx------ 1 root root 64 12月 21 00:47 1 -> /dev/null
lrwx------ 1 root root 64 12月 21 01:26 10 -> 'anon_inode:[eventpoll]'
lrwx------ 1 root root 64 12月 21 01:26 100 -> /dev/input/event5
lrwx------ 1 root root 64 12月 21 01:26 101 -> /dev/input/event4
lrwx------ 1 root root 64 12月 21 01:26 102 -> /dev/input/event2
lrwx------ 1 root root 64 12月 21 01:26 103 -> /dev/input/event3
lrwx------ 1 root root 64 12月 21 01:26 104 -> 'socket:[31391]'
lrwx------ 1 root root 64 12月 21 01:26 105 -> 'socket:[26692]'
lrwx------ 1 root root 64 12月 21 01:26 106 -> 'socket:[26802]'
lrwx------ 1 root root 64 12月 21 01:26 107 -> 'socket:[26807]'
lrwx------ 1 root root 64 12月 21 01:26 108 -> 'socket:[26808]'
lrwx------ 1 root root 64 12月 21 01:26 109 -> 'socket:[26922]'
...

/proc 文件系统的访问

可以使用常规的文件 I/O 系统进行访问 /proc 目录下的文件。但是,访问这些文件时,会有一些限制:

  • /proc 目录下的一些文件是只读的,用户进程无法进行修改。

  • /proc 目录下的一些文件仅能由文件拥有者(或特权级进程)读取。

  • /proc 目录下的大部分文件属于 root 用户,并且仅有 root 用户能够修改那些可以修改的文件。

小结

本文学习进程的主要内容有:

  • 可执行文件的内部结构。
  • 进程的内存逻辑上划分成许多个段:文本段、数据段、栈和堆。
  • 进程内存布局的详细信息。
  • 进程的信息存储和访问的方式。

OK,今天先到这,下次继续!


关注公众号【一起学嵌入式】,获取更多 “干货内容”

上一篇:玄虚子:巧记易经64卦,分宫卦象次序表。


下一篇:适合centos7 64位虚拟机添加硬盘 详细 步骤