Linux 进程控制 :进程创建,进程终止,进程等待,程序替换


进程创建

fork函数

创建一个子进程,父子进程代码共享,数据独有

#include <unistd.h>
pid_t fork(void);
返回值:自进程中返回0,父进程返回子进程id.出错返回1。

当调用fork函数时,通过使用写时拷贝技术来拷贝父进程的信息。

写时拷贝技术:子进程通过复制父进程的pcb,使得复制进程指向同一块物理内存,同时运行位置,和代码相同。但又因为进程之间需要独立性,所以当某一个进程数据发生改变的时候会重新给子进程开辟物理内存,将数据拷贝过去。(之所以这样使用是因为如果数据不修改的话还开辟空间拷贝数据会使效率降低)

这也就是数据独有的原因。
代码共享:通过页表来实现访问控制,使代码段是只读的,不可修改。

vfork函数

创建一个子进程,并且阻塞父进程,直到子进程退出或者程序替换,父进程才继续运行。

#include <unistd.h>
pid_t vfork(void);
返回值:自进程中返回0,父进程返回子进程id.出错返回1。

vfork创建子进程的效率比fork要高,因为vfork所创建的子进程和父进程共用同一个虚拟地址空间。

但也因为这样,进程之间就不具备独立性,父子进程不能同时访问代码段和数据段,所以当子进程运行的时候必须要阻塞父进程,防止产生冲突。

虽然vfork效率高,但是fork因为实现了写时拷贝技术,效率提高了不少,所以vfork已经很少使用了。


进程终止

在linux下有三种终止进程的方法。

return:

只能在main函数中使用,退出后刷新缓冲区

exit:

库函数调用接口

#include <unistd.h>
void exit(int status);
退出刷新缓冲区

_exit:

系统函数调用接口

#include <unistd.h>
void _exit(int status);
参数:status 定义了进程的终止状态,父进程通过wait来获取该值
退出不刷新缓冲区


进程等待

之前讲过,如果子进程退出时没有给父进程返回退出的信息,父进程就会以为他并没有退出,所以一直不释放他的资源,使子进程进入僵尸状态。
之前的解决方法是退出父进程,但是那个不是一个合理的解决方法,这里有更好的方法,就是进程等待。

wait

阻塞等待任意一个进程退出,获取退出子进程的pid,并且释放子进程资源。

#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);
返回值:
成功返回被等待进程pid,失败返回-1。
参数:
输出型参数,获取子进程退出状态,不关心则可以设置为NULL

阻塞:为了完成某个功能发起调用,如果不具备完成功能的条件,则调用不返回一直等待。
非阻塞:为了完成某个功能发起调用,如果不具备完成功能的条件,则立即报错返回

waitpid

可以指定等待一个子进程的退出

#include<sys/wait.h>
pid_ t waitpid(pid_t pid, int *status, int options);
返回值:
当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数:
pid:
Pid=-1,等待任一个子进程。与wait等效。
Pid>0.等待其进程ID与pid相等的子进程。
status:
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
options:
WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。


程序替换

创建子进程必定是想让子进程做与父进程不一样的事情,如果采用判断pid的方法来进行代码分流,这样的程序会非常庞大,所以还有更好的方法,就是程序替换。

程序替换

替换一个进程正在调度的程序信息(加载另一个程序到内存中,让PCB不在调度原来的程序,调度这个新的程序。只是改变了映射关系,所以原本的PCB和程序还在 )

exec

#include <unistd.h>`
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 execve(const char *path, char *const argv[], char *const envp[]);

乍一看这么多接口很不容易记,其实是有规律的。

l(list) : 表示参数采用列表
v(vector) : 参数用数组
p(path) : 有p自动搜索环境变量PATH
e(env) : 有e表示自己维护环境变量

发布了60 篇原创文章 · 获赞 78 · 访问量 6322

猜你喜欢

转载自blog.csdn.net/qq_35423154/article/details/105344117