一、进程的概念
1、操作系统(OS)角度:程序的一个执行实例;正在执行的程序;能分配处理器并由处理器执行的实体。
2、内核观点:担当分配系统资源(cpu时间,内存)的实体。
3、进程的两个基本元素:
二、进程的描述
每个进程在内核中都有一个进程控制块(pcb)来维护进程的信息,Linux内核的进程控制块是task_struct结构体。它会被装载到RAM(随机访问存储器,即Random-Access Memory)里并且包含着进程的信息。每个进程都把它的信息放在tast_struct这个数据结构里。
进程的标识符:进程id(PID),父进程id(PPID)
tast_struct的内容如下:
标识符:描述本进程的唯一标识符,用来区别其他进程。
状态:任务状态,退出代码,提出信号等。
优先级:相对于其他进程的优先级。
程序计数器:程序中即将被执行的下一条指令的地址。
内存指针:包括程序代码和程序相关数据的指针,还有和其他进程共享的内存块的指针。
上下文数据:进程执行时处理器的寄存器中的数据。
I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
记账信息:可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
所有运行在系统的进程都以tast_struct链表的形式存在内核里。
三、进程的位置
1、进程将程序读入内存,为程序分配内存空间。
2、内核为该进程保存pid及相应的状态信息,把进程放到运行队列中等待执行。
3、进程的内存映像 ----- > 指的是内核在内存中如何存放可执行程序文件。在程序转化为进程的过程中,操作系统将可执行程序从硬盘复制到内存中。
内存映像与可执行程序文件的区别:
1)可执行程序文件是位于硬盘上的,而内存映像位于内存上。
2)可执行程序文件没有堆栈,而内存映像存在堆栈。因为可执行程序文件只有当程序被加载到内存中的时候才会分配相应的堆栈。
3)可执行程序文件是静态的,因为它没有运行;而内存映像是动态的,数据是随着运行过程改变的。
Linux下的C程序生成进程的内存映像主要由4个步骤组成:预编译、编译、汇编和链接。编译器gcc经过预编译、编译、汇编3个步骤将源程序文件转换成目标文件。如果程序有多个目标文件或程序中使用了库函数,则编译器还需要将所有目标文件及所需的库文件链接起来,最后生成可执行程序。当程序执行时,操作系统将可执行程序复制到内存中。其布局如下图所示:
注:其中正文所在的区域为只读代码和只读数据区域,也就是通常所说代码段,用来存放程序的执行代码,包括了只读的常数变量。例如:字符串常量等。而未初始化的数据区域为BBS段(Block Started by Symbol),用来存放未初始化的全局变量,属于静态内存分配的一部分。初始化的数据区域称为数据段,用来存放已初始化的全局变量,也属于静态内存分配的一部分,所以一般习惯地将BBS段和数据段统称为数据段。
如下程序对此进行分析:
运行结果如下:
在堆栈中修改函数调用地址,使得程序运行跳转到自己指定的函数,而原程序的创作者完全不知道这段程序的执行中已经执行了别人的代码【可能是恶意的】
修改栈帧中的变量的值,不通过变量名。如果修改变量b的值,不引用变量b的名称,这就需要利用栈帧实现。
四.进程状态
static const char* const task_state_array[] =
{
"R(running)",/*0*/
"S(sleeping)",/*1*/ //可唤醒状态的睡眠状态
"D(disk sleep)",/*2*/ //不可中断的睡眠状态,深度睡眠,不要轻易删除D进程,只有关机重启可以唤醒
"T(stopped)",/*4*/ //暂停状态
"t(tracing stop)",/*8*/ //暂停状态
"X(dead)",/*16*/
"Z(zombie)",/*32*/ //僵死状态 ,进程挂掉,进程信息等待其他进程查看退出信息,最后又init回收资源
};
僵死状态是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用)没有读取到的子进程退出的返回代码时就会产生僵死进程。僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。也就是进程结束(退出)后资源没有立刻被释放,等另一个进程来读取回收资源,没有释放会内存释放。
创建一个维持30秒的僵死进程例子:
父进程睡眠了30秒,子进程处于30秒的僵死状态,在30秒后退出程序
进程的创建以及优先级问题,可查看本人下一篇博文。
fock()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。