进程是一个动态的实体,它是程序的一次执行过程,是操作系统中资源分配的基本单位。程序和进程的最大区别在于进程是动态运行着的,它存在于内存中;程序是静态的,它存在于磁盘上。
这里的程序指的是可执行文件(并不包含脚本文件),通常我们所说的源程序要通过预编译、编译、汇编和链接四个步骤转化为可执行程序。当我们运行可执行程序时,操作系统将可执行程序读入内存,程序摇身一变转化为一个进程。
1.进程的状态
进程是一个活体,因此也就随之诞生了进程的状态。在Linux内核中,几种经典的进程状态如下:
可运行状态:表示进程正在运行或者正在等待被运行,也就是操作系统原理中的运行态和就绪态。Linux使用TASK_RUNNING宏表示此状态。
可中断的等待状态:进程正在等待某个事件完成,即操作系统原理中等待态(或阻塞态、睡眠态)。处于该状态的进程属于“轻度睡眠”,它可以被信号或者定时器唤醒。Linux中使用TASK_INTERRUPTIBLE来表示此状态。
不可中断的等待状态:也是一种等待状态,只不过处于该状态的进程处于“深度睡眠”,它不能被信号或者定时器唤醒,只有当其等待的事件发生时才可以被唤醒。Linux中使用TASK_UNINTERRUPTIBLE来表示该状态。
僵死状态:进程已经终止,但是它的进程描述符仍然没有被操作系统收回,此时的进程只有身躯并无灵魂,需要父进程通过wait族函数为其收尸。Linux中使用EXIT_ZOMBIE表示该状态。
停止状态:进程因为收到SIGSTOP等信号停止运行。Linux中使用__TASK_TRACED表示该状态。
除了上述几种经典状态外,可以在sched.h中查看其他几种进程的状态。我们通过ps命令在shell中查看当前系统所有进程的状态信息。比如:
edsionte@edsionte-desktop:~/linux-3.0.4$ ps -eo pid,stat,command PID STAT COMMAND 1 Ss /sbin/init 2 S [kthreadd] 3 S [migration/0] 4 S [ksoftirqd/0] 5 S [watchdog/0] 6 S [migration/1] 7 S [ksoftirqd/1] 8 S [watchdog/1]
每行依次显示的是进程的pid、进程当前的状态和进程名。ps命令所显示的进程状态通过一组字母符号来表示,各种符号的含义如下:
D Uninterruptible sleep (usually IO) R Running or runnable (on run queue) S Interruptible sleep (waiting for an event to complete) T Stopped, either by a job control signal or because it is being traced. W paging (not valid since the 2.6.xx kernel) X dead (should never be seen) Z Defunct ("zombie") process, terminated but not reaped by its parent.
2.进程的内存映像
在Linux中可执行文件(此处先忽略脚本文件)的格式为ELF,即Executable and Linking Format。虽然可执行文件是二进制文件,但是它的内部还是划分着一些区域,这些区域称为可执行文件的段。这里的段和X86体系架构中的段是两个完全不同的概念。
通常可执行文件包含一个文本段(代码段),数据段。有时候可执行文件中还额外包含一个BSS段,这个段中存放着那些在源程序中没有被初始化的全局变量。
当运行可执行文件时,内核将可执行文件读入内存,我们将读入内存中的可执行文件称之为进程的内存映像。进程的内存映像虽然也根据存放内容的不同将进程的虚拟地址空间划分为一块一块的,但是这些区域在内存中并不称之为段,而是虚拟内存区域,内核使用vm_area_struct结构来表示这片内存区域。
可执行文件和进程的内存映像虽然都有代码段和数据段,但两者其实是不同的。首先可执行文件(也就是程序)是静态的,存放在磁盘上,而进程的内存映像只有在程序运行时才产生;其次,可执行文件没有堆栈段,而进程的内存映像是包含堆栈段的,因为堆(heap)用于动态分配内存,而栈(stack)需要保存局部变量、临时数据和传递到函数的参数等。从这些不同点也可以说明程序是动态的。
可执行文件的段在进程地址空间中的分布图可以参考如下:
需要注意的是,进程的地址空间并不只包含一个文本段或数据段,由于大多数源程序的目标文件在链接时都需要链接一些动态库,最终形成可执行文件,因此进程的地址空间中会有好几个文本段和数据段区域。而且在链接过程中,还需要加入链接器(ld),因此进程地空间精确的示意图如下:
在一个进程的内存映像中,文本段用来存放二进制的可执行代码,而数据段用来存放全局变量和静态变量,BSS段用来存放未初始化的全局变量,堆段用于为malloc函数分配空间,栈段用来存放局部变量和形参等临时数据。
参考:
1. Linux C编程实战
2. C专家编程