0

0

初识Linux · 进程(3)

看不見的法師

看不見的法師

发布时间:2025-06-20 12:58:01

|

992人浏览过

|

来源于php中文网

原创

前言:

承接上文中对进程的内部属性及在操作系统层面的组织方式、系统接口调用、task_struct等方面的介绍,今天我们将从进程的相关属性出发,继续探讨进程的创建过程。

进程的创建承接上文。

上文提到,进程的创建是通过调用系统接口fork实现的,因此有时对子进程理解不深的人可能会编写如下代码:

while(1){
    printf("aaa\n");
    fork();
    printf("...\n");
}

这段代码本身并无错误,但运行时会发现打印的结果并非单一的父子进程输出。因为子进程会执行父进程的后续代码,导致子进程也会进入死循环,进而创建更多的子进程,形成指数级增长。因此第二个printf语句的输出会呈1、2、4、8的模式递增,这容易让人感到困惑。

接下来,我们来看另一个场景,编写了如下代码:

printf("I am a child process, my pid is %d\n", getpid());
printf("I am a parent process, my pid is %d\n", getppid());

初识Linux · 进程(3)运行这段代码时,你会发现pid会变化,这是正常的现象。但为什么ppid保持不变呢?这也是正常的吗?

当我们使用指令ps -xaj打开任务管理器界面时,会发现2878进程是bash(注意,2878不一定是bash,具体取决于./test,每个进程的pid都会变化)。

初识Linux · 进程(3)那么,什么是bash呢?

当我们运行:

初识Linux · 进程(3)bash会提示我们./是个目录,无法运行。

实际上,bash就是命令行解释器。我们刚才运行的./test就像是公司的实习生,而bash则是公司的boss,管理着所有的实习生(子进程)。

为什么我们要创建子进程呢?

根据前面的代码,我们知道子进程会继承父进程的后续所有代码。但我们创建子进程的目的是为了执行这些代码吗?父进程也可以执行啊,为什么要创建子进程呢?这时候,前面埋下的伏笔就起作用了。这里有一个容易让人迷惑的问题:一个函数的返回值可以返回几次?

大多数初学者可能都会回答一次,但fork的奇异之处就在这里:

初识Linux · 进程(3)当我们man fork后,在手册中查询返回值的描述时,翻译过来就是:成功创建子进程,那么子进程的Pid就会返回给父进程,0返回给子进程,失败了则返回-1给父进程。也就是说,成功时会有两个返回值,我们就可以编写如下代码:

pid_t id = fork();
if(id == 0)
{
    printf("hehehe\n");
}
else
{
    printf("hahahha\n");
}

最后的打印结果是:

初识Linux · 进程(3)按照传统C语言的逻辑,只会打印一个,但这里却颠覆了我们的代码观念。

奇怪的是,明明变量只有一个id,为什么会有两份呢?

MvMmall 网店系统
MvMmall 网店系统

免费的开源程序长期以来,为中国的网上交易提供免费开源的网上商店系统一直是我们的初衷和努力奋斗的目标,希望大家一起把MvMmall网上商店系统的免费开源进行到底。2高效的执行效率由资深的开发团队设计,从系统架构,数据库优化,配以通过W3C验证的面页模板,全面提升页面显示速度和提高程序负载能力。3灵活的模板系统MvMmall网店系统程序代码与网页界面分离,灵活的模板方案,完全自定义模板,官方提供免费模

下载

实际上,在fork的时候,fork的实现部分,return id是代码吧?在fork的函数体内,已经创建了子进程,所以会返回两份id,因为父进程也会进入fork函数,因此有了两份id。至于为什么会有“两份”,我们先埋个伏笔。

由此得出结论,子进程的创建是为了执行不同的任务,而不是为了执行与父进程相同的工作。

那么,我们如何创建指定数量的进程呢?

如果我们采用最开始的指数级别创建方式,显然不好控制。我们可以利用fork的返回值来帮助我们创建指定数量的进程。因为创建成功时,会返回0给子进程,我们可以让这个子进程进入一个函数体中不再出来,这样就不会导致子进程创建子进程的情况:

void Run()
{
    while(1)
    {
        printf("My pid is %d, ppid is %d\n", getpid(), getppid());
        sleep(1);
    }
}

int main()
{
    for(int i = 0; i < 5; i++)
    {
        pid_t id = fork();
        if(id == 0)
        {
            Run();
            break;
        }
    }
    Run();
    return 0;
}

来看一下结果。

初识Linux · 进程(3)初识Linux · 进程(3)不出所料,我们创建了6个进程,主函数是第一个进程,后面通过for循环创建了5个子进程,且5个子进程之间没有关联。通过它们的pid我们发现5个子进程的ppid都是父进程的12607。我们大胆预测父进程的ppid 4335是bash进程,这一点我们就不验证了。

现在我们已经基本熟悉了进程的创建,那么如何查看进程的大部分信息呢?

首先引入一个问题,指令是进程吧?那么我们运行指令后,指令的工作路径在哪里呢?这个问题可能有点让人摸不着头脑,我们用文件举例,如果我们编写如下代码:

int main(){
    chdir("/home/whb/111");
    FILE *fp = fopen("log.txt", "w");
    (void)fp; // 忽略警告
    fclose(fp);
    while(1)
    {
        printf("I am a process, pid: %d\n", getpid());
        sleep(1);
    }
    return 0;
}

那么log.txt文件默认会在哪里创建呢?应该是当前代码的工作路径吧?

初识Linux · 进程(3)在VS中就像这样,同理,Linux中的进程工作时也应该有自己的工作路径,我们从哪里查看工作路径呢?

在根目录的proc中,proc是process的缩写。

初识Linux · 进程(3)此时,我们就可以看到proc中的信息了。如果我们要查看某个具体进程的信息,只需要:

初识Linux · 进程(3)同时运行其他两条指令,我们在27847父进程中可以注意以下事项:

初识Linux · 进程(3)本文首先关注两个属性,一个是cwd,一个是exe,cwd是current working directory,当前工作目录的意思,exe则记录对应进程中可执行文件的路径。

当你对某个进程进行cd操作时,发现可以cd进去,说明进程本质上就是一个个的目录,目录中记录了进程的所有信息。此时,对Linux中“一切皆文件”的理解又加深了一层!

感谢阅读!

相关专题

更多
C语言变量命名
C语言变量命名

c语言变量名规则是:1、变量名以英文字母开头;2、变量名中的字母是区分大小写的;3、变量名不能是关键字;4、变量名中不能包含空格、标点符号和类型说明符。php中文网还提供c语言变量的相关下载、相关课程等内容,供大家免费下载使用。

390

2023.06.20

c语言入门自学零基础
c语言入门自学零基础

C语言是当代人学习及生活中的必备基础知识,应用十分广泛,本专题为大家c语言入门自学零基础的相关文章,以及相关课程,感兴趣的朋友千万不要错过了。

613

2023.07.25

c语言运算符的优先级顺序
c语言运算符的优先级顺序

c语言运算符的优先级顺序是括号运算符 > 一元运算符 > 算术运算符 > 移位运算符 > 关系运算符 > 位运算符 > 逻辑运算符 > 赋值运算符 > 逗号运算符。本专题为大家提供c语言运算符相关的各种文章、以及下载和课程。

353

2023.08.02

c语言数据结构
c语言数据结构

数据结构是指将数据按照一定的方式组织和存储的方法。它是计算机科学中的重要概念,用来描述和解决实际问题中的数据组织和处理问题。数据结构可以分为线性结构和非线性结构。线性结构包括数组、链表、堆栈和队列等,而非线性结构包括树和图等。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

256

2023.08.09

c语言random函数用法
c语言random函数用法

c语言random函数用法:1、random.random,随机生成(0,1)之间的浮点数;2、random.randint,随机生成在范围之内的整数,两个参数分别表示上限和下限;3、random.randrange,在指定范围内,按指定基数递增的集合中获得一个随机数;4、random.choice,从序列中随机抽选一个数;5、random.shuffle,随机排序。

597

2023.09.05

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

524

2023.09.20

c语言get函数的用法
c语言get函数的用法

get函数是一个用于从输入流中获取字符的函数。可以从键盘、文件或其他输入设备中读取字符,并将其存储在指定的变量中。本文介绍了get函数的用法以及一些相关的注意事项。希望这篇文章能够帮助你更好地理解和使用get函数 。

640

2023.09.20

c数组初始化的方法
c数组初始化的方法

c语言数组初始化的方法有直接赋值法、不完全初始化法、省略数组长度法和二维数组初始化法。详细介绍:1、直接赋值法,这种方法可以直接将数组的值进行初始化;2、不完全初始化法,。这种方法可以在一定程度上节省内存空间;3、省略数组长度法,这种方法可以让编译器自动计算数组的长度;4、二维数组初始化法等等。

599

2023.09.22

高德地图升级方法汇总
高德地图升级方法汇总

本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

43

2026.01.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Django 教程
Django 教程

共28课时 | 3.2万人学习

C# 教程
C# 教程

共94课时 | 6.9万人学习

Java 教程
Java 教程

共578课时 | 47.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号