在了解了什么是进程之后,我们就应该学会如何创建进程。那么有人肯定想问,既然程序运行起来就是进程,那么为什么还要创建进程呢?答案就是为了让一个程序同时走不同的分支,以实现不同的功能。
linux中通过系统调用创建进程:fork()函数;
以前我们接触到的所有函数调用一次要么没有返回值,要么有一个返回值。而今天要说的fork()函数一次调用却有两个返回值,这样就能通过不同的返回值来实现让一个程序同时走不同的分支的功能。那么具体是如何实现的呢?
当在程序代码中调用了fork()函数时,此时该进程就会新创建一个进程。我们把原进程叫做父进程,新创建的进程叫做子进程。因为fork()函数调用一次有两个返回值,那么就可以通过判断返回值的大小来区别哪个是父进程的返回值,哪个是子进程的返回值。可以知道fork()的返回值有正有负,那么当返回值为负的时候认为子进程创建失败,当返回值为0时认为是子进程的返回值,返回值如果大于0当然就是父进程的返回值了。
那么父进程是如何创建子进程的呢?
我们知道每个进程都有自己的PCB,即进程控制块(linux下用task_struct结构体来描述),那么在创建一个进程的时候肯定也会产生相应的PCB。当一个程序中调用了fork()函数之后,子进程就会以父进程为模板,复制父进程的PCB并修改pid(进程标识符),但是内存指针、上下文基本都一样。由于内存指针基本相同,说明此时子进程的内存指针和父进程的内存指针指向同一内存。当fork()函数调用结束后,通过其不同的返回值区分开父子进程。接着父进程会从fork()函数之后的位置继续执行,此时如果子进程创建成功,那么子进程也会从fork()函数之后的语句开始执行代码。所以,接下来父子进程会执行同样的代码,但值得注意的是,父子进程虽然执行的代码相同,但数据却是各自开辟空间!!!
那么如何让父子进程各自实现不同的功能呢?
我们用程序替换来实现:当子进程开始执行的时候,将原本其地址空间中的代码替换成你想要实现的功能的代码,此时由于子进程所要执行的代码已经被替换,那么它自然不会执行跟父进程相同的代码了。
那么如何实现程序替换呢?
替换方法:exec函数族
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[]);
值得注意的是,调用exec函数后子进程就会加载新的程序并执行,如果调用成功则没有返回值,失败则返回-1