文章目录Linux——进程管理篇(详解fork和exec)🚗如何在Linux编写与运行代码编写编译运行🚗进程管理forksystemexec🚗总结Linux——进程管理篇(详解fork和exec)🚀🚀这篇文章,主要的目的就是帮助同学们完成操作系统的实验,因为考虑到很多同学第一次接触Linux,相当不习惯命令行的操作方式,所以我会详细来介绍,相信只要跟着步骤一步一步来,就一定能完成我们的实验,好了,我们接下来就来介绍吧!

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

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

代码语言:javascript复制#include

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中,创建进程有如下两个目的:

将同一个程序分成多个进程进行处理(例如,使用Web服务器接收多个请求)创建另-一个程序(例如,从bash启动一一个新的程序)🚀🚀为了达成这两个目的,Linux 提供了fork()函数与execve()函数,接下来,我们将介绍如何使用这两个函数。在此之前,我们先来编写一个简单的程序,用来测试我们的结论。a.c 主要功能就是看一下自己的进程ID

代码语言:javascript复制// a.c 主要功能就是看一下自己的进程ID

#include

#include

int main(int argc, char **argv){

printf("my pid is %d.\n", getpid());

return 0;

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

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

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

代码语言:javascript复制#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");

}🚀🚀运行结果如下所示:

代码语言: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

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

}🚀🚀运行效果如下所示:

代码语言: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函数是直接覆盖掉当前进程,也就是说,并没有增加新进程,而只是替换了当前进程。主要的作用如下所示:

读取可执行文件,并读取创建进程的内存映像所需的信息。用新进程的数据覆盖当前进程的内存。从最初的命令开始运行新的进程。🚀🚀值得注意的是,exec函数是一类函数的统称,我们这里只展示execve的用法,其他函数的用法大家可以慢慢尝试。

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

代码语言:javascript复制#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");

}🚀🚀运行结果如下所示:

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