上一篇文章介绍了进程的基本概念:
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,今天先到这,下次继续!
关注公众号【一起学嵌入式】,获取更多 “干货内容”