进程应用

进程应用

一、进程创建

1.获取进程id

1
2
pit_t getpid()	//当前进程id
pit_t getppid()//父进程id

2.创建和终止进程

1
pit_t fork() //创建子进程

​ 新创建的子进程与父进程几乎但不完全相同。子进程拥有父进程用户级虚拟地址空间的副本和全部打开的文件描述符。

fork函数调用一次,却返回两次。一次在父进程中返回子进程的PID,在子进程中返回0。

写时复制

​ fork创建出的子进程,与父进程共享内存空间。也就是说,如果子进程不对内存空间进行写入操作的话,内存空间中的数据并不会复制给子进程,这样创建子进程的速度就很快了!。在fork之后exec之前两个进程 用的是相同的物理空间(内存区),子进程的代码段、数据段、堆栈都是指向父进程的物理空间,也就是说,两者的虚拟空间不同,但其对应的 物理空间是同一个。当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间。这便是写时复制

​ 原理:fork()之后,kernel把父进程中所有的内存页的权限都设为read-only,然后子进程的地址空间指向父进程。当父子进程都只读内存时,相安无事。当其中某个进程写内存时,CPU硬件检测到内存页是read-only的,于是触发页异常中断(page-fault),陷入kernel的一个中断例程。中断例程中,kernel就会 把触发的异常的页复制一份,于是父子进程各自持有独立的一份。

​ 优点:

​ 1.可减少分配和复制大量资源时带来的瞬间延时

​ 2.可减少不必要的资源分配。比如fork进程时,并不是所有的页面都需要复制,父进程的代码段和只读数据段都不被允许修改,所以无需复制

4.回收子进程

​ 当一个进程由于某种原因终止时,内核并不立刻把它清除。而是让它保持在已终止的状态,知道父进程回收它。

​ 父进程回收时,内核将子进程的退出状态传给父进程,然后抛弃已终止的进程。

一个终止了但未回收的进程称为僵死进程

如果父进程终止了,子进程未回收,这样的子进程叫孤儿进程。内核会让init进程更为它的养父,并回收它们。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
			pid_t waitpid(pit_t pid,int* statusp,int options);
//pid:等待集合的成员。pid>0,等待集合是一个子进程,进程id就是pid
//pid = -1 ,等待集合是所有子进程
/*
status:这个参数将保存子进程的状态信息,有了这个信息父进程就可以了解子进程为什么会退出,是正常退出还是出了什么错误。

WIFEXITED(status) 如果子进程正常结束,它就返回真;否则返回假。
WEXITSTATUS(status) 如果WIFEXITED(status)为真,则可以用该宏取得子进程exit()返回的结束代码。
WIFSIGNALED(status) 如果子进程因为一个未捕获的信号而终止,它就返回真;否则返回假。
WTERMSIG(status) 如果WIFSIGNALED(status)为真,则可以用该宏获得导致子进程终止的信号代码。
WIFSTOPPED(status) 如果当前子进程被暂停了,则返回真;否则返回假。
WSTOPSIG(status) 如果WIFSTOPPED(status)为真,则可以使用该宏获得导致子进程暂停的信号代码。

options提供了一些另外的选项来控制waitpid()函数的行为。如果不想使用这些选项,则可以把这个参数设为0。
WNOHANG 如果pid指定的子进程没有结束,则waitpid()函数立即返回0,而不是阻塞在这个函数上等待;如果结束 了,则返回该子进程的进程号。
WUNTRACED 如果子进程进入暂停状态,则马上返回。
*/

5.加载并运行程序

1
int execve(const char* filename,const char* argv[],const char* envp[])

​ execve函数加载并运行可执行目标文件filename,且带参数列表argv和环境变量envp。

这个函数在当前进程的上下文中加载并运行一个新的程序,它会覆盖当前进程的地址空间,但并没有创建一个新进程。它同时继承了调用时已经打开的所有文件描述符。