理解进程、通过调用 fork 函数创建进程

1.理解进程

进程(Process),其定义如下:“占用内存空间的正在运行的程序”。

假如各位从网上下载了 LBreakout 游戏并安装到硬盘。此时的游戏并非进程,而是程序。因为游戏并未进入运行状态。下面开始运行程序。此时游戏被加载到主内存并进入运行状态,这时才可称为进程。如果同时运行多个 LBreakout 程序,则会生成相应数量的进程,也会占用相应进程数的内存空间。

再举个例子。假设各位需要进行文档相关操作,这时应打开文档编辑软件。如果工作的同时还想听音乐,应打开 MP3 播放器。另外,为了与朋友聊天,再打开 MSN 软件。此时共创建 3 3 3 个进程。从操作系统的角度看,进程是程序流的基本单位,若创建多个进程,则操作系统将同时运行。有时一个程序运行过程中也会产生多个进程。接下来要创建的多进程服务器就是其中的代表。

1.1 CPU核的个数与进程数

拥有 2 2 2 个运算设备的 CPU 称作双核(Daul)CPU,拥有 4 4 4 个运算器的 CPU 称作四核(Quad)CPU。也就是说, 1 1 1 个 CPU 中可能包含多个运算设备(核)。核的个数与可同时运行的进程数相同。相反,若进程数超过核数,进程将分时使用 CPU 资源。但因为 CPU 运转速度极快,我们会感到所有进程同时运行。当然,核数越多,这种感觉越明显。

1.2 进程 ID

无论进程是如何创建的,所有进程都会从操作系统分配到 ID。此 ID 称为“进程ID”,其值为大于 2 2 2 的整数。 1 1 1 要分配给操作系统启动后的首个进程(用于协助操作系统),因此用户进程无法得到 ID 值 1 1 1

观察 Linux 中正在运行的进程:

ps au

在这里插入图片描述

可以看出,通过 ps 指令可以查看当前运行的所有进程。特别需要注意的是,该命令同时列出了 PID(进程ID)。另外,上述示例通过指定 au 参数列出了所有进程详细信息。

查看进程状态的命令如下:

ps -eo pid,ppid,sid,tty,pgrp,comm,stat | grep -E 'bash|PID|test'

在这里插入图片描述

以 BSD 风格显示:

ps aux | grep -E 'bash|PID|test'

在这里插入图片描述

关于 ps 命令及进程状态标志:https://wker.com/linux-command/ps.html

2.通过调用 fork 函数创建进程

#include <unistd.h>

pid_t fork(void);

// 成功时返回进程ID,失败时返回-1

fork 函数将创建调用的进程副本。也就是说,并非根据完全不同的程序创建进程,而是复制正在运行的、调用 fork 函数的进程。另外,两个进程都将执行 fork 函数调用后的语句(准确地说是在 fork 函数返回后)。但因为通过同一个进程、复制相同的内存空间,之后的程序流要根据 fork 函数的返回值加以区分,即利用 fork 函数的如下特点区分程序执行流程:

  • 父进程:fork 函数返回子进程 ID。
  • 子进程:fork 函数返回 0 0 0

此处父进程(Parent Process)指原进程,即调用 fork 函数的主体,而子进程(Child Process)是通过父进程调用 fork 函数复制出的进程。

接下来讲解调用 fork 函数后的程序运行流程,如下图所示。

在这里插入图片描述

从上图可以看到,父进程调用 fork 函数的同时复制出子进程,并分别得到 fork 函数的返回值。但复制前,父进程将全局变量 gval 增加到 11 11 11,将局部变量 lval 的值增加到 25 25 25,因此在这种状态下完成进程复制。复制完成后根据 fork 函数的返回类型区分父子进程。父进程将 lval 的值加 1 1 1,但这不会影响子进程的 lval 值。同样,子进程将 gval 的值加 1 1 1 也不会影响到父进程的 gval。因为 fork 函数调用后分成了完全不同的进程,只是二者共享同一代码而已。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int gval = 10;

int main(int argc, char *argv[])
{
    
    
	pid_t pid;
	int lval = 20;
	gval++;
	lval += 5;
	
	// 创建子进程。对于父进程来说,fork函数返回子进程ID;对于子进程来说,fork函数返回0。
	pid = fork();

	if (pid == 0)    // if Child Process
	{
    
    
		gval += 2;
		lval += 2;
	}
	else    // if Parent Process
	{
    
    
		gval -= 2;
		lval -= 2;
	}
	
	if (pid == 0)
		printf("Child Proc: [%d, %d]\n", gval, lval);
	else
		printf("Parent Proc: [%d, %d]\n", gval, lval);

	return 0;
}

编译运行:

gcc fork.c -o fork
./fork

输出结果:

Parent Proc: [9, 23]
Child Proc: [13, 27]

从运行结果可以看出,调用 fork 函数后,父子进程拥有完全独立的内存结构。

猜你喜欢

转载自blog.csdn.net/qq_42815188/article/details/129376963
今日推荐