三大通识知识:进程,线程,网络(二)

三大通识知识(一) :进程,线程,网络之间的关系
三大通识知识(二):进程实现原理
三大通识知识(三):线程实现原理
三大通识知识(四):网络
三大通识知识(四):TCP服务器
进程,线程,网络视频连接

前言

上一篇,叙述了进程,线程,网络的关系,这一篇我们讲进程的实现原理,这里所说的实现原理是指代码级别的,动手做比一切理论都有效。
实验环境
系统:ubuntu 18.04
编译器: gcc-7.5.0
语言:C语言

进程是如何让两个while 1同时运行的

我们在前一篇中,讲了进程可以让两个while 1同时运行,这是你没有学进程之前,无法做到的。
在这里插入图片描述
可能有很多同学对上面的这张图不是很理解,这里我们就来详细讲解!
我们先看实验用例
下图中,左边是代码,右边是运行结果。
我们通过gcc fork.c编译,等到了可执行程序a.out,运行的时候,星号和点在屏幕上交替打印;其中,星号是子线程打印的,点是父进程打印的。
在这里插入图片描述
子进程的产生,主要靠第7行代码,fork函数,fork这个单词的意思是“分叉”,就是产生另一个分支,我们看下面的图:
在这里插入图片描述
我们在终端运行./a.out这个命令的时候,实际上就产生了一个进程,这个进程就是main函数被调用的时候产生的进程,也叫原始进程或是初始进程。当这个进程执行到第七行代码的时候,调用fork函数,fork函数是系统的一个函数,用于产生一个新的子进程。此时此刻,进程分两路,一路是原进程(父进程),另一路是新产生的子进程。
好了,问题来了,我们怎么知道哪是子进程,哪是父进程呢,程序运行并没有画出一个图形来指示。
这是写代码的关键所在。

fork函数调用一次返回两个值,向子进程返回0,向父进程返回大于0的值!

这一段话,是我学习进程时候看到过的最多的定义,第一次看到这句话的人,感觉是没解释,因为看不懂,文绉绉,这句话神奇的地方在于,当你了解,熟悉了进程后,懂了fork函数的执行机制后,你来下定义,最后也是这句话,起点就是终点
fork调用一次为什么返回两个值
我们先来看这一段,怎么看呢,当然是通过程序研究了,这才是最佳理解途径,比到处翻资料,不耻下问更有效。奉诗一首:
《冬夜读书示子聿》
宋.陆游
古人学问无遗力,少壮工夫老始成。
纸上得来终觉浅,绝知此事要躬行。
(这是陆游训示自己的儿子时,所做的一首诗,子聿yu,陆游的第六子)
在这里插入图片描述
这里在运行左边代码的时候,确实打印了两次,这个现象也是刷新很多人认知的一个超自然现象。第8行代码,怎么会执行两次?
父进程(main函数所在进程)执行到第7行的时候,执行fork函数,此时产生了另一个分支——子进程的执行路径,这样父进程往下执行的时候,会碰到printf打印语句,子进程往下执行的时候也会执行printf语句,所以printf函数会执行两次,当然sleep函数也会执行两次。
这就是fork函数调用一次返回两个值的原因。
在这里插入图片描述
向子进程返回0,向父进程返回大于0的值
第一个问题已经解决(fork调用一次,返回两个值),既然返回两个值,那么这里的2479和0就代表两个值,很好理解了。
但是,为什么是0,为什么是2479?
首先说0,0其实代表fork函数执行成功了,通俗的讲,0代表这个子进程成功来到了这个世界。就这么直白和简单,大道理其实都很简单。
好,那么2479呢?首先,2479这么大的一个数字,其实表示系统中已经产生了很多很多进程,有2479个,这个进程编号其实是从0开始的,0号进程是所有进程的祖先。(注意这个0跟刚才说的0是两回事,刚才的0其实是fork函数调用成功的一个标志,而0号进程是linux系统给各个进程分配的进程编号,就相当于人类的身份证)
那这个2479到底代表谁的进程ID号呢,是谁的身份证呢?
这个理解其实非常关键,其实它是子进程的身份证,代表子进程的ID号。
为什么是这样呢,很好理解,你想想,父进程要生很多孩子(为什么不是母进程,这个是另一个话题,有时间聊),这么多孩子,它怎么管理呢,其实linux系统是这样设计的,当你创建一个子进程,系统就把你所创建子进程的ID号返回给你,告诉你你儿子的身份证号码,至于你怎么管理,是你的事情,你可以不要,你可以保存起来。
所以,那句话说:
向子进程返回0,也就是fork返回值如果是0,其实是子进程在执行,系统告诉子进程,你来到了这个世界,成功降临了。
向父进程返回大于0的值,也就是fork函数返回值如果大于0,其实是父进程在执行,系统告诉父进程,你儿子的进程ID是2479,你记住了。
在这里插入图片描述
好了,进程部分的代码实现,就介绍到这里,如果大家还没有看明白,可以看我的视频,不过需要购买。

为什么说子进程和父进程的空间是独立的

我们在看各种有关进程的书籍,博客,视频的时候,经常会看到下面的结论:
进程之间的空间是独立的;
子进程和父进程的空间是独立存在的;
子进程拷贝了父进程所有的资源,包括代码;
如果你不去编程做实验,你很难理解其中奥秘,其实写代码验证是理解抽象里面最好的方法,没有之一,我不相信一个不写代码的人能够理解得很好,即使你是看代码。
下面,我们来看一个经典案例:
在这里插入图片描述
这个程序运行的结果是
字符串 I am child process 打印了3次
字符串 I am parent process 打印了5次
这两个字符串肯定是在第28行,通过puts输出的,感觉这个for语句有两个“人”在执行?
是的,这两个人,一个是子进程,一个是父进程,他们都会去执行for语句。
关键的地方是:子进程有一个n和str,父进程也有一个n和str,他们名字相同,但是空间不一样。
这就是因为子进程,父进程他们空间独立。
当在第10行,调用fork函数后,产生了子进程,此时子进程复制了父进程所有的资源(最显而易见的是,复制了父进程的n和str),但是,子进程有独立的堆栈空间,用来存放这个复制过来的n和str。
接下来就好理解了,
子进程运行到for语句的时候,它判断的是它的n和str,它的n=3,它的str=I am child process;
父进程运行到for语句的时候,它判断的是它的n和str,它的n=5,它的str=I am parent process;
虽然在一个程序里面,实际上是两条线路在执行。

各位看懂了吗?如果还没有看懂,可以去找我视频看!

为什么说进程可以裂变…

进程可以像人体细胞一样,进行繁殖,繁殖的速度非常快,是2的N次方。请看代码实现:
下面的程序,很简单,在for语句中循环3次调用fork函数,最终生成8个进程,我们通过ps elf命令可以比较形象的看到这个8个进程的父子关系,他们的祖先是编号为2632的进程,
2632的进程生了三个儿子,2633,2634,2635 第一代人2632

2633的进程生了两个儿子,2637,2638 第二代人2633,2634,2635
2634的进程生了一个儿子,2636 第三代人2637,2638,2636
2635的进程没有儿子 第四代人 2639

2637的进程生了一个儿子,2639 有意思吧!!!向unix之父致敬!
在这里插入图片描述

向unix之父致敬

在此,再次向unix之父致敬! 因为unix这个系统就是他们两个发明的,fork这个函数,就像人体中的细胞一样,由一个产生2个,2个产生4个,从而支持操作系统同时运行很多的代码,才可以同时上演璀璨的灯光秀!
在这里插入图片描述
Ken Thompson(肯·汤普森),Dennis M Ritchie(丹尼斯·里奇)他们共同获得了1983年的图灵奖。

END

我们来看一下,linux系统是如何来定义fork函数的:
英文好的,可以直接读英文理解
在这里插入图片描述
下一篇,介绍线程的代码实现机制。
三大通识知识:进程,线程,网络(一)
三大通识知识:进程,线程,网络(二)
三大通识知识:进程,线程,网络(三)
三大通识知识:进程,线程,网络(四)
三大通识知识:进程,线程,网络(五)
想进一步学习的请看我的视频(需要购买):
进程,线程,网络视频连接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

下家山

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值