🌍Linux系统编程2:进程控制
🌳fork函数 🍀作用与定义方式 作用:创建一个子进程。 定义:pid_t fork(void); 失败返回-1;成功返回0:① 父进程返回子进程的 ID(非负) ②子进程返回 0;pid_t类型表示进程 ID,但为了表示-1,它是有符号整型(0不是有效进程 ID,init最小为 1) ;
🍀 父子进程相同:
刚fork后。 data段、text段、堆、栈、环境变量、全局变量、宿主目录位置、进程工作目录位置、信号处理方式相同。
🍀父子进程不同:
.进程id、返回值、各自的父进程、进程创建时间、闹钟、未决信号集不同。
🍀父子进程共享: 1、全局变量而言:读时共享、写时复制; 2、文件描述符 ; 3、 mmap映射区。
代码实现创建子进程 运行结果: 🍀获取父子进程的两个函数
getpid();
getppid();
编程实现: 运行结果:
观察运行结果: 观察子进程输出:子进程 pid=3140,父进程 pid=1630; 观察父进程输出:子进程pid=3140,自己进程pid=3139;父进程pid=2911。 出现的问题:从父进程返回的子进程和子进程的pid一样,子进程却说自己的父进程为pid=1630,父进程自己的pid=3139。 原因:父进程先死亡,子进程成为孤儿被收到了为pid=1630的孤儿院。
修改代码:给与父进程一个sleep(1)命令,子进程先于父进程死亡。 运行结果: 此时运行结果不再出现上述问题。
🍀循环创建多个子进程 一次 fork函数调用可以创建一个子进程。那么创建 N个子进程应该怎样实现呢? 简单想,for(i = 0; i < n; i++) { fork() } 即可。但这样创建的是 N个子进程吗? 从上图我们可以很清晰的看到,当 n为 3时候,循环创建了(2^n)-1个子进程,而不是 N的子进程。需要在循环的过程,保证子进程不再执行 fork ,因此当(fork() == 0)时,子进程应该立即 break;才正确。
编程实现: 运行结果: 出现了问题:进程多了一个,而且不是按顺序来的。这里多出的一个,是父进程,因为父进程才有i=5跳出循环这一步。所以,对父进程进行判定并处理 修改代码如下: 运行结果: 现在还有两个问题:
- 一个就是包括父进程在内的所有进程不是按顺序出现,多运行几次,发现是随机序列出现的。这是要因为,对操作系统而言,这几个子进程几乎是同时出现的,它们和父进程一起争夺cpu,谁抢到,谁打印,所以出现顺序是随机的。
- 第二问题就是终端提示符混在了输出里,这个是因为,loop_fork(本程序名)是终端的子进程,一旦loop_fork执行完,终端就会打印提示符。就像之前没有子进程的程序,一旦执行完,就出现了终端提示符。这里也就是这个道理,loop_fork执行完了,终端提示符出现,然而loop_fork的子进程还没执行完,所以输出就混在一起了。解决方法如下让父进程加个sleep函数。让父进程在所有子进程结束后运行
.运行结果:
🌳父子进程共享哪些内容 上面已经提到 1、父子进程相同: 刚fork后。 data段、text段、堆、栈、环境变量、全局变量、宿主目录位置、进程工作目录位置、信号处理方式。 父子进程不同: 进程id、返回值、各自的父进程、进程创建时间、闹钟、未决信号集。 父子进程共享: 全局变量:读时共享、写时复制; 文件描述符; mmap映射区。
🌳父子进程gdb调试
设置父进程调试路径:set follow-fork-mode parent (默认) 设置子进程调试路径:set follow-fork-mode child
注意,一定要在fork函数调用之前设置才有效。
🌳exec函数族 fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种 exec函数以执行另一个程序。当进程调用一种 exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用 exec并不创建新进程,所以调用 exec前后该进程的 id并未改变。
将当前进程的.text、.data替换为所要加载的程序的.text、.data,然后让进程从新的.text第一条指令开始执行,但进程 ID不变,换核不换壳。
其实有六种以 exec开头的函数,统称 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[]);
🍀execlp函数 加载一个进程,借助 PATH环境变量 int execlp(const char *file, const char *arg, …); 成功:无返回;失败:-1 参数 1:要加载的程序的名字。该函数需要配合 PATH环境变量来使用,当 PATH中所有目录搜索后没有参数 1则出错返回。 该函数通常用来调用系统程序。如:ls、date、cp、cat等命令。
代码示例:通过execlp让子进程执行ls命令
运行结果 下面使用execl来让子程序调用自定义的程序。 int execl(const char *path, const char *arg, …) 这里要注意,和execlp不同的是,第一个参数是路径,不是文件名。这个路径用相对路径和绝对路径都行。 调用的代码如下
子进程要调用的程序: exec程序: 运行结果:
注:以上内容均从总结于网上学习资源。
|