一个可执行程序包含三个部分:
代码段:存放指令,操作以及只读的数据
数据段:全局或者静态变量
管理程序所以创建进程,一个进程是执行的程序段,
进程:一个程序在执行时所创建的用来管理这个程序的东西,在内核中会创建一个pcb,即为进程控制块,其实就是一个结构体,用来管理这个程序的所有的资源。
程序一般存储在磁盘上,我们执行一个程序,是在内存中执行,那么首先将磁盘中的程序移到内存中,并且一一对应起来,便在内存中就有相应的代码段、数据段、和BSS段,与磁盘相比较而言,多了什么东西呢,多了堆段和栈段。
进程时unix/linux中基本的资源管理单元。
进程是执行的代码片段(一个程序可以创建多个进程,一个进程可以执行多个程序的代码)。
将一个代码片段加载到内存让其执行也就创建了一个(后续创建多个)进程。
进程与程序之间的关系,程序存储在磁盘上,是一个文件,而一个进程是加载到内存的程序段是动态的,且有生命周期,创建、执行、退出、等待的状态。
一个进程肯定不仅仅是占用了加载代码的内存,在linux下,使用task_struct来维护进程,在内核中描述进程就用的这个结构体。在这个结构体中一般有进程的状态、与内存相关的东西,堆、栈、以及这个进程打开的文件,struct file *fd_array[NR_OPEN_DEFAULT]这个结构体里面存放的就是文件描述符,还有一些是文件系统的信息、信号相关的内容。
volatile long state; /*-1 unrunnable,0 runnable,0 stopped*/
struct mm_struct *mm;与存储相关。描述了在用户空间中,代码段,数据段,堆,栈,mmap等设计的所有内存信息。
structfiles_struct *files;打开的文件列表,成员 struct file *fd_array[NR_OPEN_DEFAULT]描述了这个进程打开的文件列表。
进程的资源管理(创建、管理)
PID:进程号,每个进程有唯一的编号。
state:linux 是多任务多用户、cpu在各个执行单元中不停的跳转。某个进程需要等待其他的资源时,cpu转而执行其他的进程,充分利用了cpu的资源,在什么时候执行哪一个进程实质是由调度算法来决定的。
运行状态:占有资源、执行
就绪状态:除了cpu之外,其他资源都已经获取,等待调度算法来调度执行。
等待状态:除了cpu之外,还要等待其他资源或者时间,可中断等待(可以被信号打断)和不可中断等待。
停止状态:正在被跟踪或者调度的进程用户。
僵死状态:用户资源已经回收,pcb内核资源没有回收,已经不能执行。
划分进程:
创建进程时,一个比较独立的任务(事务)就创建一个进程,这个事务尽量不与其他事物有太多的交融(耦合性)。
进程的生命周期与进程资源的管理,api应用。
创建:进程创建与进程资源获取,fork/vfork。在进程创建过程中,实际上执行的操作时将程序的代码段、数据段、BSS段从磁盘加载到内存,并且申请堆栈空间。
内核空间中:为这个进程分配唯一的PID标识,同时,在内核中为这个进程申请控制块PCB,初始化相关的信息。
在运行过程中,涉及打开的文件,关联的终端,安装信号,状态等系列信息。
在创建进程时,是由父亲进程来创建新的子进程。fork函数
pid_t fork(void)
在父亲进程中,返回子进程的ID,在子进程本身,返回0.
进程中执行新代码
在进程执行过程中,要执行新的代码,实际上我们创建一个新进程,更多的时期望在这个进程中执行新的代码,而不是原来的代码。
在子进程中,替换了原有进程的代码段和数据段()
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,
..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],
char *const envp[]);
进程退出:
(1)被别人kill掉,kill一个命令,kill -9 pid
(2)显示的执行exit系列函数,exit
(3)进程遇到main函数的,没有代码可以执行,或者,main函数return。
exit与return的区别
exit是一个系统调用函数,是退出这个进程,return是c关键字,退出这个函数。
exit()在退出时会对进程资源做清理,例如刷新流的缓冲区,而_exit()不做任何处理,直接退出。
进程资源回收:
进程退出有退出的状态,另外,进程资源的回收在exit的时候仅仅是释放了它的用户空间资源,而内核空间资源PCB没有回收,转而由它的父亲进程通过wait相关的函数来回收。
子进程在退出时会给父亲进程发送一个信号,父亲进程可以显式的调用wait/waitpid等待子进程结束并回收资源。
回收资源时也可以得到子进程退出的状态。
总结资源申请与释放的问题:
创建时,申请进程所有的资源,内核中PCB,用户空间加载代码数据段,申请堆栈空间,运行时,打开的文件,安装的信号,关联的终端等。
执行新代码时,替换代码段,数据段,bss段,堆栈空间信息等用户空间信息,但是内核PCB的信号没有改。
退出时,释放了自己用户空间的资源(代码段,数据段,bss段,堆、栈,关联所有内存信息),但是没有回收PCB。
进程 已经退出,但是没有回收内核PCB资源的进程叫僵死进程。
父亲进程先于子进程推出的,这个子进程就是孤儿进程,父亲进程会被转移为init这个进程
进程创建详解与父子进程资源:
父子进程执行顺序问题:
父子进程在创建完子进程后互不关联,以独立身份抢占CPU资源,具体谁先执行由调度算法决定,用户空间没有办法干预,子进程执行代码的位置时fork/vfork函数返回的位置。
字资源申请问题
子进程重新申请新的物理内存空间,复制复习呢进程地址空间所有的信息(当然,现在的操作系统实际采用写实复制等策略,真正的物理内存空间发生在需要写入时)
子进程复制父亲进程的代码段,数据段,bss段,堆栈,所有用户空间的信息,在内核中操作系统为其重新申请一个PCB,并且使用父亲进程的PCB来出事化,除了pid等特殊信息外,几乎所有的信息都是一样的。
流的缓冲区会刷新没有刷新的信息,且缓冲区在用户空间中,虽然子进程创建
execx后资源问题:
在execx系列函数替换代码后,对打开的文件能够在处理莫?
默认情况下,exec执行的代码中可以访问在原来代码中打开的文件,操作时同一个文件描述符,即同一个文件对象。
进程属性管理:
进程的属性包括进程组属性和用户属性:
进程组属性决定了进程在运行过程中控制权限以及相关控制信息。
PID:进程号,当前进程在当前系统下唯一的编号,针对这个进程执行的操作多以进程号为标识。
用户可以获取getpid(),但是不能通过相关函数来修改。
PPID:父亲进程号,一般为创建这个进程的那个进程的ID,一般不会修改,当这些进程的父亲进程退出后,当前进程变成孤儿进程,它的PPID会被修改为intit进程,即为PID=1的这个进程。
PGID:进程组号,将完成协同工作的多个进程默认为一个进程组,例如,在终端运行一个新的程序,新程序创建的子进程以及自身在一个进程组下,而第一个进程默认为进程组长,进程组号也是进程组长的ID
getpid(()//参数为某个进程的ID,返回该进程的进程组长编号。
setpgid(pid,pgid)//修改某个进程的进程组长。
修改一个进程的进程组号的意义,kill可以向一个进程组发起信号,要影响整个这个进程组的所有进程。
SID:会话ID。进行交互,在某个终端下执行的程序所创建的进程/进程组,它们的sid就是这个终端进程 的编号,在一个会话下的所有进程都将搜到这个会话终端的影响。