前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux——进程管理篇(详解fork和exec)

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

作者头像
小点点
发布2023-03-18 17:03:43
2.3K0
发布2023-03-18 17:03:43
举报
文章被收录于专栏:小点点小点点

文章目录

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

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


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

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


编写

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

代码语言:javascript
复制
#include <stdio.h>
 int main(int argc, char **argv){
    printf("hello world");
    printf("Usage:%s", argv[1]);
    return 0;
 }

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

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

代码语言:javascript
复制
gedit a.c

编译

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

代码语言:javascript
复制
sudo apt install gcc

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

代码语言:javascript
复制
gcc a.c -o a

运行

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

代码语言:javascript
复制
./a e

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


🚗进程管理

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

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

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

代码语言:javascript
复制
// a.c 主要功能就是看一下自己的进程ID  
#include <unistd.h>  
#include <stdio.h>  
  
int main(int argc, char **argv){  
    printf("my pid is %d.\n", getpid());  
    return 0;  
} 

fork

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

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

🚀🚀接下来我们来写一个程序来测试一下:

代码语言:javascript
复制
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <err.h>

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");
}

🚀🚀运行结果如下所示:

代码语言:javascript
复制
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命令,命令执行完后随即返回原调用的进程。接下来我们来写一个函数来测试一下。

代码语言:javascript
复制
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>

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");
}

🚀🚀运行效果如下所示:

代码语言:javascript
复制
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的用法,其他函数的用法大家可以慢慢尝试。

在这里插入图片描述
在这里插入图片描述

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

代码语言:javascript
复制
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <err.h>

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");
}

🚀🚀运行结果如下所示:

代码语言:javascript
复制
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是覆盖

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-03-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • Linux——进程管理篇(详解fork和exec)
    • 🚗如何在Linux编写与运行代码
      • 编写
      • 编译
      • 运行
    • 🚗进程管理
      • fork
      • system
      • exec
    • 🚗总结
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档