Linux——进程管理篇(详解fork和exec)
创始人
2025-05-29 21:31:29
0

文章目录

  • Linux——进程管理篇(详解fork和exec)
    • 🚗如何在Linux编写与运行代码
      • 编写
      • 编译
      • 运行
    • 🚗进程管理
      • fork
      • system
      • exec
    • 🚗总结

Linux——进程管理篇(详解fork和exec)

🚀🚀这篇文章,主要的目的就是帮助同学们完成操作系统的实验,因为考虑到很多同学第一次接触Linux,相当不习惯命令行的操作方式,所以我会详细来介绍,相信只要跟着步骤一步一步来,就一定能完成我们的实验,好了,我们接下来就来介绍吧!


🚗如何在Linux编写与运行代码

🚀🚀做实验,首先需要解决的问题就是我应该如何在Linux里面编写我的代码并且运行,这里,我们就以一个最简单的程序:“hello world”为例,来说明这个过程。


编写

🚀🚀如果经常使用Linux的话,大部分代码其实是在vim下写完的,但是,对于初学者来说,这样不太友好,所以我们换一个办法,那就是在Windows环境下把代码写好,再把代码复制进去,这样就好了。首先我们把代码在Windows环境下写好,如下所示:

#include int main(int argc, char **argv){printf("hello world");printf("Usage:%s", argv[1]);return 0;}

🚀🚀有些同学可能要问了,main函数里面不应该是void吗,为什么我的代码不一样,其实理论上,这样才是正确的方式,而这些参数的作用,大家到后面就知道了(实验的要求),目前大家可以简单理解为是向主函数传递的参数。

🚀🚀然后我们使用以下命令去打开我们要编写的文件,然后粘贴即可(记得保存)。

gedit a.c

编译

🚀🚀我们已经把代码写好了,接下来我们如何去运行呢?我们的解决办法就是使用我们的gcc去编译,所以需要先安装gcc ,我们只需要在命令行输入如下命令即可安装。

sudo apt install gcc

🚀🚀在我们安装之后,接下来就是去运行了,我们需要在我们的命令行输入如下的命令(需要注意的是,需要在a.c存在的文件夹里面去运行),然后就算是编译完成了。

gcc a.c -o a

运行

🚀🚀然后我们输入以下的命令去运行我们刚刚生成的可执行文件,注意,后面那个e就是我们传入主函数的参数。

./a e

🚀🚀好了,如何运行编写于运行代码我们已经学会了,接下来就开始介绍我们的进程了。


🚗进程管理

🚀🚀在Linux中,创建进程有如下两个目的:

  1. 将同一个程序分成多个进程进行处理(例如,使用Web服务器接收多个请求)
  2. 创建另-一个程序(例如,从bash启动一一个新的程序)

🚀🚀为了达成这两个目的,Linux 提供了fork()函数与execve()函数,接下来,我们将介绍如何使用这两个函数。在此之前,我们先来编写一个简单的程序,用来测试我们的结论。a.c 主要功能就是看一下自己的进程ID

// a.c 主要功能就是看一下自己的进程ID  
#include   
#include   int main(int argc, char **argv){  printf("my pid is %d.\n", getpid());  return 0;  
} 

fork

🚀🚀fork函数,也就是生成一个子进程,具体的作用如下所示:

  1. 为子进程申请内存空间,并复制父进程的内存到子进程的内存空间。
  2. 父进程与子进程分裂成两个进程,以执行不同的代码。这一点的实现依赖于fork( )函数分别返回不同的值给父进程与子进程。

在这里插入图片描述
🚀🚀接下来我们来写一个程序来测试一下:

#include 
#include 
#include 
#include static void child()
{printf("I'm child! my pid is %d.\n", getpid());exit(0);
}static void parent(pid_t pid_c)
{printf("I'm parent! my pid is %d and the pid of my child is %d.\n",getpid(), pid_c);exit(0);
}int main(void)
{pid_t ret;ret = fork();if (ret == -1)err(0, "fork() failed");if (ret == 0) {// fork() 会返回 0 给子进程,因此这里调用 child()child();} else {// fork() 会返回新创建的子进程的进程 ID(大于 1)给父进程,因此这里调用 parent()parent(ret);}// 在正常运行时,不可能运行到这里err(0, "shouldn't reach here");
}

🚀🚀运行结果如下所示:

I'm parent! my pid is 5284 and the pid of my child is 5285.  
I'm child! my pid is 5285.

🚀🚀有些同学可能好奇了,为什么if和else同时执行了呢?其实很简单,就是因为这是两个进程,一个进程运行了一个。


system

🚀🚀system()会调用fork函数产生子进程,由子进程来执行command命令,命令执行完后随即返回原调用的进程。接下来我们来写一个函数来测试一下。

#include 
#include 
#include 
#include 
#include static void parent(pid_t pid_c)
{printf("I'm parent! my pid is %d and the pid of my child is %d.\n",getpid(), pid_c);exit(0);
}int main(int argc, char **argv)
{pid_t ret;ret = fork();if (ret == -1)err(0, "fork() failed");if (ret == 0) {// fork() 会返回 0 给子进程 printf("I'm child! my pid is %d.\n", getpid());system("./a");// 相当于在终端输入./a,也就是运行这个程序exit(0);} else {// fork() 会返回新创建的子进程的进程 ID(大于 1)给父进程,因此这里调用 parent()parent(ret);}// 在正常运行时,不可能运行到这里err(0, "shouldn't reach here");
}

🚀🚀运行效果如下所示:

ygr@ygr-virtual-machine:~/桌面/C-test$ ./system   
I'm parent! my pid is 5208 and the pid of my child is 5209.  
I'm child! my pid is 5209.  
my pid is 5212. // 与5209不一样

🚀🚀我们可以很清楚的看到,system调用的程序线程与子线程不一样,所以他是申请了一个全新的进程。而这一点与我们后面要介绍的exec函数有点不一样。


exec

🚀🚀我们接下来的exec函数是直接覆盖掉当前进程,也就是说,并没有增加新进程,而只是替换了当前进程。主要的作用如下所示:

  1. 读取可执行文件,并读取创建进程的内存映像所需的信息。
  2. 用新进程的数据覆盖当前进程的内存。
  3. 从最初的命令开始运行新的进程。

🚀🚀值得注意的是,exec函数是一类函数的统称,我们这里只展示execve的用法,其他函数的用法大家可以慢慢尝试。

在这里插入图片描述

🚀🚀我们还是写个程序来验证一下:

#include 
#include 
#include 
#include static void parent(pid_t pid_c)
{printf("I'm parent! my pid is %d and the pid of my child is %d.\n",getpid(), pid_c);exit(EXIT_SUCCESS);
}int main(int argc, char **argv)
{pid_t ret;ret = fork();if (ret == -1)err(0, "fork() failed");if (ret == 0) {// fork() 会返回 0 给子进程,因此这里调用 child()char *args[] = { "./a", NULL , NULL};printf("I'm child! my pid is %d.\n", getpid());execve("./a", args, NULL);err(0, "exec() failed");} else {// fork() 会返回新创建的子进程的进程 ID(大于 1)给父进程,因此这里调用 parent()parent(ret);}// 在正常运行时,不可能运行到这里err(0, "shouldn't reach here");
}

🚀🚀运行结果如下所示:

I'm parent! my pid is 5284 and the pid of my child is 5285.  
I'm child! my pid is 5285.  
my pid is 5285.  // 与5285一样

🚀🚀我们可以看到,exec调用的程序线程与子程序相同,说明只是覆盖了当前的程序。


🚗总结

🚀🚀其实说分析system()和exec()两个函数的区别,也就是分析fork和exec的区别,而他们之间的区别,简单一点来描述就是fork是复制,exec是覆盖

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...