0


【Linux】进程概念与fork初识——if与else竟然能够同时执行?!

在这里插入图片描述

文章目录

💐专栏导读

🌸作者简介:***花想云 ***,在读本科生一枚,致力于 C/C++、Linux 学习。

🌸专栏简介:本文收录于 C语言初阶专栏,本专栏主要内容为本专栏主要内容为Linux的系统性学习,专为小白打造的文章专栏。

🌸相关专栏推荐:C语言初阶系列、C语言进阶系列 、C++系列、数据结构与算法。

💐文章导读

本章我们正式进入进程的学习。本章的主要内容有进程的概念、

PCB

说明、进程的先描述再组织、如何查看进程、以及学习

getpid

getppid

fork

等系统调用如何使用~

在这里插入图片描述

🌷进程是什么

开门见山一句话——

  • 进程=内核关于进程的相关数据结构+当前代码的内容和数据

🍁如何理解?

当我们写好一段代码进行编译、链接等一系列过程之后,生成了

可执行程序

。此时的可执行程序是一个

文件

,被存放到硬盘中。当我们运行该可执行程序后,该程序的代码和数据会被预先加载到

内存

中(至于原因上一章我们已经做了很好的铺垫),同时操作系统会对该进程建模,提取该进程有关的状态信息等所有的属性并将这些信息保存到一个叫

PCB

的结构体中,并将该

PCB

的指针保存到某种例如链表一样的

数据结构

当中,以进行对进程的增删查改一系列操作。

简单归纳,“

程序被加载到内存中就成为了进程

”这就话并

不准确

,关键是

该进程相关的信息有没有被操作系统所管理

。就如同,诺大的校园中,如何证明你是学校的学生呢?你说你能从学校的大门进到学校里来,这显然不能说明你就是这个学校的学生,而是当学校的档案系统里记录了你的信息,才能证明你是这个学校的学生。

所以,以前我们任何将程序启动并运行的行为本质上是

操作系统将该程序转化为进程

从而完成特定的任务。

上文中还有一个很中还要的点需要我们来理解——

PCB

是什么?有什么作用?

🌷进程的描述——PCB

操作系统如何管理进程?还记得上一章中最重要的六个字吗?——

先描述,再组织

PCB

就是对进程的一种描述。

  • 进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合
  • 课本上称之为PCB(process control block),Linux操作系统下的PCB是:task_struct

总结为一句话就是——**在Linux中描述进程的结构体叫做

task_struct

task_struct

是Linux内核的一种

数据结构

,它会被装载到

RAM

(内存)里并且包含着进程的信息**。

🍁task_struct包含以下内容:

  • 标示符: 描述本进程的唯一标示符,用来区别其他进程;
  • 状态: 任务状态,退出代码,退出信号等;
  • 优先级: 相对于其他进程的优先级;
  • 程序计数器: 程序中即将被执行的下一条指令的地址;
  • 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针;
  • 上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器;
  • I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表;
  • 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等;
  • 其他信息

🌷进程的组织

所有的进程都会以

task_struct

结构体的形式被描述起来。而这些

task_struct

结构体都会被链接起来,生成一个

task_struct

类型的链表。操作系统对进程的控制就会转化为对

task_struct

增删查改

🌷如何查看进程

首先我们写一个简单的程序(命名为

myprocess

),并运行:

#include<stdio.h>intmain(){while(1){printf("我是一个进程\n");sleep(1);}return0;}
$ gcc myprocess.c -o myprocess
$ ./myprocess

在分屏的情况下,输入指令查看进程:

ps axj |head -1 &&ps axj |grep myprocess |grep -v grep

我们暂且不需要管这条指令的具体指令有什么作用,只需要知道它能显示当前

myprocess

进程的信息。如图所示:

在这里插入图片描述
虽然我们现在可能看不到每个标识符代表什么意思,但是其它先不用管,只需要知道

PID

是当前进程的ID,就如同我们的

学号

🍁第二种方式

还有一种查看进程的方式:

  • 进程的信息可以通过/proc系统文件夹查看;

让我们再以

myprocess

为例。

  • 进入/proc目录;
  • ls 查看目录下的文件;

🍁myprocess未运行时

在这里插入图片描述
我们观察到有很多数字命名的文件,其实猜测一下就能大致猜出这些数字应该就是每个进程对应的PID。

  • 运行myprocess

🍁myprocess运行时

在这里插入图片描述

如图所示,该目录下的确有该进程的相关文件。

🌷如何通过系统调用查看进程PID

除了上述以指令的方式输出

PID

外,我们还可以用系统调用

getpid()

获取当前进程的

pid

getppid()

获取当前进程的

父进程的PID

首先通过

man

手册查询这两个函数如何使用:

$ man getpid

在这里插入图片描述
注意,返回值类型未

pid_t

其实就是

size_t

。通过一下代码进行测试:

#include<stdio.h>#include<sys/types.h>intmain(){while(1){printf("我是一个进程,我的PID是:%d,我的父进程是:%d\n",getpid(),getppid());sleep(1);}return0;}

运行结果为:
在这里插入图片描述
当我们尝试不断地启动程序和结束程序:

在这里插入图片描述
我们可以发现一个规律:

  • 每次启动时,该进程的PID都不同,但是PPID好像每次都一样。

那么

PID

是什么呢?为什么会不变呢?当你在命令行中启动任何一个进程,它的父进程的

PID

都是它,即这些进程都有一个共同的父进程——

bash

(命令行解释器)。如图所示:

在这里插入图片描述

🌷通过系统调用创建进程

接下来的内容为本章的重点内容——认识

fork

与如何通过

fork

创建

子进程

🌺认识fork

首先我们通过

man

手册认识一下

fork

$ man fork

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

简单说明就是**

fork

用来创建子进程,在父进程中,

fork

的返回值为子进程的

PID

;在子进程中返回值为0**。

话不多说,我们来进行测试:

#include<stdio.h>#include<sys/types.h>intmain(){printf("我是fork调用之前的内容.....\n");fork();printf("我是fork调用之后的内容.....\n");return0;}

在这里插入图片描述
很显然,这里

fork

调用之后的内容打印了两遍。原因是当调用

fork

之后,子进程被创建,父进程与子进程同时在运行,于是父进程打印了一遍

fork

调用后的内容,子进程也打印了一遍

fork

调用后的内容。

如何证明呢?修改代码,在打印内容的同时打印父子进程各自的

PID

PPID

#include<stdio.h>#include<sys/types.h>intmain(){printf("我是fork调用之前的内容.....我的PID是:%d,我的父进程是:%d\n",getpid(),getppid());fork();printf("我是fork调用之后的内容.....我的PID是:%d,我的父进程是:%d\n",getpid(),getppid());return0;}

在这里插入图片描述

🌺重点来啦!!!

fork

的功能很强大,我们一般需要与

if

配合使用进行

分流

。还记得上面提到的

fork

的返回值吗?子进程返回值为

0

,父进程返回值为子进程

PID

,可以此作为分流的依据。例如:

#include<stdio.h>#include<sys/types.h>intmain(){
  pid_t ret =fork();if(ret==0){// 子进程while(1){printf("我是子进程,我的PID是:%d,我的父进程是:%d\n",getpid(),getppid());sleep(1);}}else{// 父进程while(1){printf("我是父进程,我的PID是:%d,我的父进程是:%d\n",getpid(),getppid());sleep(1);}}return0;}

在这里插入图片描述

如图所示,此刻父子进程都在运行。那么问题来了——

  • 请问为什么此时ifelse竟然能够同时执行?也就是fork为什么会有两个返回值
  • 又问为什么此时的ret竟然会同时存在两个不同的值?

以上的问题确实有些颠覆初学者的三观。在本章节我们只能够试着解决第一个问题,至于第二个问题,我们在后续进程地址空间中会解释明白。

首先,我们得清楚一个基本概念:

  • 进程间是互相独立的

例如qq与微信同时运行,两个并无关联,互不影响。

其次我们要明白子进程是如何创建的。

我们知道

进程=内核数据结构(PCB)+代码和数据

。当子进程创建时,操作系统会为子进程创建一个

PCB

记录子进程的状态信息等。同时子进程会与父进程共同使用一份代码和数据,**若有任意一个执行流(只父子进程)修改数据时,操作系统会为该进程将代码和数据拷贝一份,再进行修改,此动作我们称之为

写时拷贝

**,

写时拷贝

同样为非常重要的知识,但是现在我们只做了解即可。

最后,因为进程具有独立性,同样父子进程也具有独立性,且由于写时拷贝的存在,父进程中调用

fork

会返回子进程的

PID

,所以

else

执行了,这是父进程的行为;子进程中会

fork

会返回

0

,所以

if

执行了,这是子进程的行为。父子进程互不影响。

本章的内容就到这里了。关于

fork

我们还需要学习很多,本章我们就先简单认识一下即可。

在这里插入图片描述

点击下方个人名片,可添加博主的个人QQ,交流会更方便哦~
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓

标签: linux 运维 服务器

本文转载自: https://blog.csdn.net/gllll_yu/article/details/130414974
版权归原作者 花想云(西安第一深情) 所有, 如有侵权,请联系我们删除。

“【Linux】进程概念与fork初识——if与else竟然能够同时执行?!”的评论:

还没有评论