Linux下的进程地址空间
Linux下的进程地址空间
- 程序地址空间回顾
- 从代码结果推结论
- 引入进程地址空间
- 页表
- 为什么要有进程地址空间
- 重新理解进程地址空间
程序地址空间回顾
我们在初学C/C++的时候,我们会经常看见老师们画这样的内存布局图:
可是这真的是内存吗?
如果不是它内存,那它是什么呢?
从代码结果推结论
在回答上面的问题之前我们先看一段代码:
运行结果:
通过运行结果我们可以看出,父进程的g_val一直都是保持为10,但是子进程的g_val却是一直在变化!这还不是最恐怖的,最恐怖的是父子进程的g_val是一样的地址,那么说明父子进程的g_val是同一块空间啊,那么是同一块空间的话,子进程去修改了g_val,父进程再去读取g_val应该是子进程修改过后的,可是父进程似乎还是读取的以前的值(10);
首先一块空间是不可能保存两份值的!父子进程能从统一个变量里读取两份值出来,那么说明父子进程的g_val绝对不是表示的同一块空间,&g_val出来的地址也绝对不可能是真实的物理地址!
如果&g_val出来的是物理地址的话,那么父子进程的g_val就表示的同一块空间,那么从同一块空间读取出来的值就应该是一样的,但是事实却不是这样的!
那么就说明我们在语言级别上的取地址,取出来的绝对不是真实的物理地址,相对的我们把这种地址叫做虚拟地址!
引入进程地址空间
通过上面的例子我们知道了我们在语言层面上取出来的地址绝对不是真实的物理地址,这个我们知道了,可是这与我们的进程地址空间有什么关系?
在讲解进程地址空间之前,我们先讲一个故事:
在遥远的美国,有一个大富豪,这个大富豪有100亿美金,同时他有4个私生子:A、B、C、D这4个私生子都不知道彼此的存在,都认为自己是大富豪唯一的孩子:
有一天呢大富豪对分别A、B、C、D单独说:你好好干,以后我的这100亿家产都是你的!用我们现在的话说,大富豪的承诺相当于在给A、B、C、D画饼!A、B、C、D都认为自己拥有100亿美金,因为他们都认为自己是大富豪的唯一继承人!在某一天A对大富豪说:“爹,给我1000美金,我需要买个表”,大富豪说没问题,大富豪就从100亿美金中拿出了1000美金给了A,B这时候也想大富豪申请了900美金,大富豪毫不犹豫的就答应了,但是C对大富豪说:“爹,快给我50亿美金,我这出了点事,需要摆一下”,大富豪说:“滚!”,大富豪无情的拒绝了C的请求!但是C还是认为自己拥有100亿美金,因为它认为自己是大富豪的唯一继承人,等到大富豪驾鹤西去之时就是100亿美金到账之日!
在上面的故事中呢:A、B、C、D相当于我们的进程;
大富豪给A、B、C、D画的“饼”就是进程地址空间!
100亿美金就是物理内存;
大富豪就是OS;
其中A、B、C、D向大富豪借钱的动作就是向OS申请物理内存!申请的太多,OS是会拒绝我们的!
OS最为我们计算机中的管理者,那么它要不要把给进程们画的“饼”管理起来呢?
答案是要的!为什么呢?如果不管理起来的画,在进程多起来的时候OS也不知到他给进程们到底画的什么饼!
那么如何管理这些“饼”呢?
先描述,再组织
在Linux中,OS利用了一个struct mm_struct{}的结构体将这个饼管理了起来,每个进程都有属于自己的专属大饼,其中进程的pcb是中具有指向该大饼的指针!OS呢会将这些大饼做一个区域划分!比如规定大饼的这个区域是干嘛的,那个区域是干嘛的!这些区域,也就是我们看到的什么堆区、栈区、代码区等等!这个大饼也就是我们老师在日常中讲解的C/C++内存布局:
Linux下的mm_struct 结构体就是专门记录这张大饼的!
struct mm_struct{
long Code_start;
long Code_end;
long init_start;
long init_end;
……
long stack_start;
long stack_end;
}
当我们向堆区申请空间时heap_end就会变大,free时heap_end就会变小!
说白了进程地址空间就是OS欺骗进程的一种手段,让内存误以为自己拥有全部的物理内存;
页表
可是进程地址空间毕竟只是逻辑上的内存,并不是真正的物理内存,是不能存储数据,进程的数据和代码是只能存储在物理内存上的,但是进程使用的是虚拟内存!进程也只能访问虚拟地址,但是实际的数据是存储在物理内存上的,那么进程是如何通过虚拟地址拿到数据和代码的?
实际上在虚拟地址与物理地址之间是有一种映射关系的,这种映射关系被存储在页表中!每个进程都有自己的页表!
当进程需要访问虚拟地址上某一处的数据时,OS就会拿着进程提供的虚拟地址,根据该进程提供的页表转换成对于的物理地址,然后去对于的物理内存上取数据在交给进程!这个过程进程是看不到的,站在进程的角度就是,我(进程)需要访问虚拟地址为0x11223344处的数据,然后就直接拿到了数据,在进程看来它就认为自己的数据是存储在虚拟内存上的,只要自己需要,随时都可以拿到,殊不知其真实数据是存储在物理内存上的,进程之所以能随时拿到数据,都是由OS完成的!我们来画个图来理解:
只要理解了这一层,我们就能回答开头的问题了:
老师们经常给我们讲的内存分布实际上并不是真实(物理内存)的内存的分布,而是OS给进程画的一张“大饼”,就是让进程认为自己一个就拥有整个内存!说白了进程空间就是OS欺骗进程的一种手段!
每个进程都有属于自己的一张进程地址空间和对应的页表!
回答了开头的问题,我们再来解释一下,上面代码表现出的情况,父子进程对于g_val取地址取出的地址是一样的,但是父子进程从g_val取出来的值却不一样:
首先父进程会有自己的pcb、进程空间地址、页表,那么子进程也会拥有这些东西,但是子进程作为父进程的儿子,它会继承父进程的大部分属性,包括进程空间地址、页表等,画图表示就是:
那么根据上面图的表示的话,父子进程的g_val不就是同一块空间嘛,取出来的值也应该是一样的,可是为什么父子进程g_val取出来不同的值?
我们需要记得进程之间是具有独立性的!包括父子进程之间也是如此!当我们的子进程在尝试对g_val变量的值进行修改时,为了不影响父进程的正常读取g_val,OS会启动"写时拷贝"技术,当父子进程中的某个进程需要对父子进程共享的同一块空间进行修改时,OS会在物理内存重新开辟一块一摸一样大的空间,然后再将数据拷贝过来,修改需要修改数据的进程的页表映射关系!此时需要修改数据的进程就可以随意的修改了,同时不会影响另一个进程的数据!保持了进程之间的独立性;
画个图来表示:
这也就解释了为什么父子进程的g_val是同一个地址,但是却存着不同的值!
地址相同的原因就是:g_val都处于父子进程的进程地址空间的同一个位置(这里说的“处于”并不是真实的存储,而是逻辑上的存储!),取地址取出来的地址当然一样,但是由于子进程的g_val++造成了写实拷贝,就导致了父子进程的g_val映射到不同的物理地址空间,取出来的值自然不一样!
写时拷贝是发生在物理内存,对于虚拟内存没有影响!
注意:我们平时&地址,取出来的全是虚拟地址(也就是进程地址空间中的地址),我们用户没办法取到真实的物理地址,毕竟谁叫我们的进程被OS欺骗了,痴痴的认为自己享有全部内存!
明白了上面的例子,那么我们也就能很好的明白了使用fork函数时,利用变量接受fork返回值时,明明是同一个变量(虚拟地址相同),但是再父子进程中却输出了不同的值;
主要是应为再fork函数的内部也就是return的前一步的时候,子进程就已经被创建出来了,此时对于接受fork返回值的变量在父子进程中也还是映射的同一块物理空间,但是当return的时候,就会向这个接受返回值的变量中写入数据,此时就会触发写时拷贝,那么这时候父子进程中的某个进程就会为自己的这个接受fork返回值的变量重新映射一块新的物理空间!这也就是fork函数能返回两个返回值的秘密!实际上并不是真的能返回两个返回值,只是父子进程中的接受返回值的变量已经是两块独立的物理空间了,不在是同一块!在虚拟内存上他们也许是同一块,但是,真实情况并不是!!!
同时页表也不止是会映射虚拟地址的物理地址,页表同时也会记录一下映射的物理地址的读写权限!
比如:
这也是为什么我们平常所说的代码区的数据只能读!不可修改的原因!
因为当我们进程试图修改代码区的数据时,OS会拿着进程提供的虚拟地址(代码区的地址),然后根据页表映射到对应的物理内存上去,但是OS这时候发现这次映射的物理空间在页表中的权限也就只有可读,不可修改!我们的操作属于权限放大了,OS会直接拒绝我们的请求!并不是这块空间(物理内存)本身就只是可读的!而是我们通过一些手段从逻辑上限制了这块空间(物理内存)的权限!
为什么要有进程地址空间
上面我们大概讲解了什么是进程地址空间和怎么使用进程地址空间,但是为什么要有这个东西呢?
1、防止地址随意访问,保护我们进程的安全和独立;
假设我们不使用虚拟内存,就直接使用物理内存:
我们现在在物理内存中加载了两个程序,现在A程序是我们写的,但是我们的代码能力有问题,我们的A程序有bug,当我们cpu在处理进程A的时候,会从进程中读取到不属于进程A的地址,也就是说A进程存在野指针问题,但是刚好这个野指针被OS分配给了进程B使用;
要是这时候我们的进程A有个对该野指针解引用并修改数据的操作的话,那就完了因为这就造成了我们明明实在运行进程A但是由于野指针的问题间接的将B进程的数据修改了,如果进程B是个银行的账户信息的话,那么后果就会很严重!这也就破坏了进程之间的独立性!!同时也对进程的安全运行造成了威胁!但是我们使用虚拟内存时,我们如果造成了越界访问,OS会在映射该虚拟地址的时候检测出来,从而拒绝我们的访问!这也就让我们无法随意的根据地址访问其他空间了,同时进程的独立性和安全性也就增加了!
2、进程管理与内存管理解耦合了;
再此之前我们先来谈谈malloc的本质!
请问只要是我们已使用malloc或new申请空间OS就会立即给我们吗?
答案:显然不是!OS作为整个计算机最基础的软件,也是整个计算机中的管理者!它是不允许发生任何不高效和浪费的操作的!
如果有,那么一定是OS的bug;
如果OS在我们申请的时候就把空间给我们了,那么我们能保证我们申请了就一定使用吗?我们一定写过这样的代码:在程序的开头就先申请了一段空间,但是我们可能写了几十行代码才开始使用这块空间!那么在你从申请空间开始到你真正使用这块空间之间,这块空间就一直被我们占着,其他进程也用不到,实属有点“站着茅坑不拉屎”的感觉!你说一个进程这样!OS还能理解,但是如果每个进程都像这样了!这就会严重的造成内存资源使用不充分、不高效;
如果在我们并未真正使用这段空间的时间段内,OS将这块空间拿去给需要的进程使用,当我们真正需要使用这块空间的时候OS再给我们,这样的话内存使用率不就起来了!
那也有人会说,我们申请了立马使用就好了嘛,对不起!在你刚好申请完这块空间的时候CPU处理你的时间到了,该换下一个进程被CPU处理了!在你等待下一次CPU处理的时候,你又是单独站在这块空间,自己不用其他进程也用不了!又会造成内存资源的浪费!OS也不会允许!
为此在我们向OS申请空间的时候,OS不会立马给我们!而是当我们真正需要的时候才会给我们!
我们平常使用的malloc、new就是这样的原理;malloc、new是在虚拟内存上开辟空间(也就是逻辑上开辟的空间)返回的指针也自然是虚拟指针,虽然我们有了空间,但这些空间毕竟是逻辑上的,并不能真实的存储数据,也就是说这些虚拟空间还没有在页表中建立起与物理内存的映射关系,进程现在拿到的只是一张空头支票,具体的兑换,还是得靠OS!只有当我们真正需要使用这块空间的时候,OS才会将我们申请的虚拟空间映射到对应的物理内存!也就是为我们的虚拟空间在页表中建立起物理地址!只有完成映射关系,我们进程才能算是真正的拥有自己的空间(物理空间)!
同时我们进程也不必关心,OS到底给我们映射的那块空间,在物理内存中是否连续等!OS可以在物理内存的任何位置映射空间,物理内存并不一定是连续的,但是我们在虚拟内存上申请的空间一定是连续的!
我们作为进程是不关心我们的数据到底存储在物理内存的那一块空间的、申请的物理空间是否连续等等,在进程看来进程空间地址就是它的“内存”,只要在进程空间地址上连续就行了!至于映射到物理内存上是什么情况,我们进程压根不关心!
为此我们把就把内存管理与进程管理分开了!内存管理就处理内存的事!进程管理就专门管理进程!两个管理之间互不干扰!
如果没有进程空间地址的话,我们的进程在申请空间的时候,OS就会立马给它,这样导致内存资源浪费不说!OS会需要去刻意寻找一块物理内存,这时候就造成进程管理与内存管理耦合!也就是说我们我们在进行进程管理的时候就必须借助内存管理的力量!这是我们不希望看到的,我们希望进程管理能够单独完成自己的事情,内存管理也能单独完成自己的事情,两个进程耦合度不要太高!保证我们内存管理崩溃的时候不影响进程管理!进程管理崩溃的时候不影响内存管理!
有了进程地址空间,我们在申请空间的时候就只启用进程管理,先申请虚拟内存,当我们真正需要的时候,再启动内存管理来为我们分配物理空间!这样的话就算内存管理崩溃掉了也不影响进程管理!
3、让进程以统一的视角看待内存;
虚拟内存是OS欺骗进程的一种手段,进程在看待进程的时候都认为自己拥有整块内存,然后开始对着“这块内存”开始布局自己的代码和数据,但是实际上这些代码和数据到底存没存储起来,还得看OS,但是站在进程的角度,他是认为我们已经布局完整个内存了!进程是看不到真实物理内存的!进程只能看到进程地址空间!
4、可以充分的利用内存资源,让内存的利用率变的高效起来!
比如:两个进程可能都需要访问某个动态库;如果没有进程地址空间的话,OS就会将这个动态库加载内存两次,也就是内存中会有两份一模一样的数据!这是没必要的!但是有了进程地址空间过后,我们可以让两个进程的虚拟地址同时映射到这同一份数据!也就是说两个进程可以共享这份数据!这份数据也就只需要在内存中存在一份就行了!但是在进程看来他们都认为这份数据是自己独享的!
重新理解进程地址空间
请问我们的程序在编译完毕,但是还没有加载进内存的时候,我们的程序内部是否有地址呢?
答案是当然有的!
我们可以来看看一段代码的汇编文件:
我们将这段程序先编译成可执行程序,然后利用命令objdump -S
对其进行反汇编:
我们会发现,在我们的程序在未加载进内存的时候,编译器就已经确定好了各条指令的地址!这是为什么??
答:进程地址空间不止是欺骗进程的,也会连同编译器也一起欺骗!当然这都是非常不标准的描述,严格意义上来说,源代码在被编译的时候,就已经按照虚拟地址空间的方式对代码和数据进行了地址的编制,只不过只些代码和数据的地址都是虚拟地址,并不是真实的物理地址!
只有当我们的程序被加载进内存了,才会真正的拥有物理地址!
:现在我们来理一理整个程序的运行过程:
1、将我们的程序加载进内存(注意并不是一次性全部加载进去,而是先加载一些比较重要的代码和数据);
2、OS为该程序建立pcb,来管理该进程;
3、OS为该进程创建地址空间地址和页表;
4、cpu从特定的进程空间地址处读取数据!然后OS在根据cpu提供的虚拟地址,映射到对应物理地址,获取对应的数据给cpu,cpu开始处理!如果OS在根据cpu提供虚拟地址没有建立起对应的物理地址时,OS会暂停cpu对于该进程的处理,然后重新加载一部分数据进入内存,然后再建立映射关系,出现这种情况:叫做缺页中断!
我们画个图来理解:
注意:在CPU上读取到的地址,全是进程空间上的地址,也就是虚拟地址!CPU也不会直接去物理内存上读取数据!
相关文章:

Linux下的进程地址空间
Linux下的进程地址空间程序地址空间回顾从代码结果推结论引入进程地址空间页表为什么要有进程地址空间重新理解进程地址空间程序地址空间回顾 我们在初学C/C的时候,我们会经常看见老师们画这样的内存布局图: 可是这真的是内存吗? 如果不是它…...

Web Spider Ast-Hook 浏览器内存漫游 - 数据检索
文章目录一、资源下载二、通过npm安装anyproxy模块三、anyproxy的介绍以及基本使用1. anyproxy的功能介绍2. anyproxy的基本使用四、给浏览器挂代理五、实操极验demo案例总结提示:以下是本篇文章正文内容,下面案例可供参考 一、资源下载 Github&#x…...

开源启智,筑梦未来!第四届OpenI/O启智开发者大会开幕
2023年2月24日,第四届OpenI/O启智开发者大会在深圳顺利开幕。本次活动由鹏城实验室、新一代人工智能产业技术创新战略联盟(AITISA)主办,OpenI启智社区、中关村视听产业技术创新联盟(AVSA)承办,华…...

CS144-Lab6
概述 在本周的实验中,你将在现有的NetworkInterface基础上实现一个IP路由器,从而结束本课程。路由器有几个网络接口,可以在其中任何一个接口上接收互联网数据报。路由器的工作是根据路由表转发它得到的数据报:一个规则列表&#…...

最好的个人品牌策略是什么样的
在这个自我营销的时代,个人品牌越来越受到人们的重视。您的个人品牌的成功与否取决于您在专业领域拥有的知识,以及拥有将这些知识传达给其他用户的能力。如果人们认为您没有能力并且无法有效地分享有用的知识,那么您就很难获得关注并实现长远…...

第四届国际步态识别竞赛HID2023已经启动,欢迎报名
欢迎参加第四届HID 2023竞赛,证明您的实力,推动步态识别研究发展!本次竞赛的亮点:总额人民币19,000元奖金;最新的SUSTech-Competition步态数据集;比上一届更充裕的准备时间;OpenGait开源程序帮您…...

「2」指针进阶——详解
🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀 目录 🐰指向函数指针数组的指针(很少用,了解) 🐰回调函数&…...

计网笔记 网络层(端到端的服务)
第三章 网络层(端到端的服务) **TCP/IP体系中网络层向上只提供简单灵活的、无连接的、尽最大努力交付的数据报服务。**网路层不提供服务质量的承诺,不保证分组交付的时限,所传送的分组可能出错、丢失、重复和失序。进程之间通信的…...

[蓝桥杯 2018 省 B] 日志统计——双指针算法
题目描述小明维护着一个程序员论坛。现在他收集了一份“点赞”日志,日志共有 N 行。其中每一行的格式是 ts id,表示在 ts 时刻编号 id 的帖子收到一个“赞”。现在小明想统计有哪些帖子曾经是“热帖”。如果一个帖子曾在任意一个长度为 DD 的时间段内收到…...

SpringMVC请求转发和重定向
请求转发:forward:重定向:redirect转发:由服务器的页面进行跳转,不需要客户端重新发送请求:特点如下:1、地址栏的请求不会发生变化,显示的还是第一次请求的地址2、请求的次数,有且仅…...

如何建立项目标准化评价体系?【锦狸】
PMO团队面临着管理多个项目,甚至是多个项目集,多个产品集的问题,那么如何对项目们进行标准化评价体系的建设,就是PMO需要首先思考的问题。 首先我们要关注项目的背景,了解了项目背景之后,我们才可以明确项…...

Vue基础入门讲义(二)-语法基础
文章目录1.vue入门案例1.1.HTML模板1.2.vue渲染1.3.双向绑定1.4.事件处理2.Vue实例2.1.创建Vue实例2.2.模板或元素2.3.数据2.4.方法3.生命周期钩子3.1.生命周期3.2.钩子函数3.3.this1.vue入门案例 1.1.HTML模板 在项目目录新建一个HTML文件 01-demo.html 1.2.vue渲染 01-d…...

应广单片机用8位乘法器实现16位乘法运算
应广单片机例如pms150,pms152这种是没有带乘法器的,如果需要进行乘法运算,可以用ide里面“程序产生器”菜单里面 产生乘法函数,把数据填入对应的参数,然后调用函数就可以实现乘法运算了。除此之外,应广还有…...

Android中使用GRPC简明教程
引言 Android作为一个开发平台,本身是使用java进行封装的,因此java可以调用的库,在Android中同样可以进行调用,这样就使得Android设备具有丰富的功能,可以进行各种类型的开发。 这篇文章就介绍如何在Android设备中使…...

【Linux】使用U盘自动化安装Linux(VMware虚拟机)
文章目录前言一、准备二、新建虚拟机2.1 创建虚拟机2.2 新增硬盘2.3 系统启动项三、加电运行四、EFI方式五、总结前言 一、准备 基于之前的基础【Linux】Kickstart 配置U盘自动化安装Linux系统,现在我们可以在虚拟机中尝试自动化安装Linux系统。 二、新建虚拟机 …...

内网渗透(五十七)之域控安全和跨域攻击-基于服务账户的非约束委派攻击
系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…...

gitlab 安装到项目上传一篇解决
文章目录1.安装1.1创建挂载目录1.2启动1.3 配置gitlab查看docker admin 账户初始密码注册普通用户2.1进入注册2.2创建后通过登录admin审批3.2 步骤13.2 步骤23.3步骤33.4 项目添加成员4 使用成员用户,上传到新建的项目中4.1 复制项目地址4.2使用 git here 克隆项目4.3进入下载目…...

Verilog 逻辑与()、按位与()、逻辑或(||)、按位或(|)、等于(==)、全等(===)的区别
逻辑与(&&)逻辑与是一个双目运算符,当符号两边为1时输出1,符号两边为0时输出0。真值表:&&01xz00000101xxx0xxxz0xxx两个4bit的数字相与;A4b0x1z;B4b01xx;C4b00xz&am…...

剑指 Offer 22. 链表中倒数第k个节点
剑指 Offer 22. 链表中倒数第k个节点 难度:easy\color{Green}{easy}easy 题目描述 输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。 例如,一个链…...

数据结构预算法之买卖股票的最好时机(三)动态规划
目录:一.题目知识点:动态规划二.动态规划数组思路确定1.dp数组以及下标的含义2.确定递推公式3.dp数组如何初始化4.确定遍历顺序5.举例推导dp数组一.题目知识点:动态规划动态规划算法的基本思想是:将待求解的问题分解成若干个相互联…...

【数通网络交换基础梳理2】三层设备、网关、ARP表、VLAN、路由表及跨网段路由下一跳转发原理
一、不同网段如何通讯 同网段可以依靠二层交换机通讯,网络中存在多个网段192.168.1.1/24 172.16.1.1/24 173.73.1.1/24情况下如何互相通讯?上节留一下的问题,这节继续讲解。 1、这里以Ping命令讲解,PC1 ping173.73.1.2…...

Java-排序链表问题
Java-排序链表问题题目题解方法:自顶向下归并排序算法题目 给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。 示例 1: 示例 2: 示例 3: 提示: *链表中节点的数目在范围 [0, 5 * 104]…...

c++之二叉树【进阶版】
前言 在c语言阶段的数据结构系列中已经学习过二叉树,但是这篇文章是二叉树的进阶版,因为首先就会讲到一种树形结构“二叉搜索树”,学习二叉搜索树的目标是为了更好的理解map和set的特性。二叉搜索树的特性就是左子树键值小于根,右…...

【数据库】 SQLServer
SQL Server 安装 配置 修改SQL Server默认的数据库文件保存路径_ 认识 master :是SQL Server中最重要的系统数据 库,存储SQL Server中的元数据。 Model:模板数据库,在创建新的数据库时,SQL Server 将会复制此数据…...

Linux 4.19 内核中 spinlock 概览
Linux内核中 spinlock相关数据结构和代码实现涉及的文件还是挺多的,这篇博客尝试从文件的角度来梳理一下 spinlock的相关数据结构和代码实现,适合想大概了解 Linux内核中 spinlock从上层 API到底层实现间的调用路径和传参变化,尤其适合了解 s…...

TensorFlow 1.x学习(系列二 :1):基本概念TensorFlow的基本介绍,图,会话,会话中的run(),placeholder(),常见的报错
目录1.基本介绍2.图的结构3.会话,会话的run方法4.placeholder5.返回值异常写在前边的话:之前发布过一个关于TensorFlow1.x的转载系列,自己将基本的TensorFlow操作敲了一遍,但是仍然有很多地方理解的不够深入。所以重开一个系列&am…...

javaEE 初阶 — 关于 IPv4、IPv6 协议、NAT(网络地址转换)、动态分配 IP 地址 的介绍
文章目录1. IPv42. IPv63. NAT4. 动态分配 IP 地址1. IPv4 在互联网的世界中只有 0 和1 ,所以每个人都有一个由 0 和 1 组成的地址来让别人找到你。 这段由 0 和 1 组成的地址叫 IP 地址,这是互联网的基础资源,可以简单的理解为互联网的土地。…...

《Qt 6 C++开发指南》简介
我们编写的新书《Qt 6 C开发指南》在2月份终于正式发行销售了,这本书是对2018年5月出版的《Qt 5.9 C开发指南》的重磅升级。以下是本书前言的部分内容,算是对《Qt 6 C开发指南》的一个简介。1.编写本书的目的《Qt 5.9C开发指南》是我写的第一…...

CleanMyMac是什么清理软件?及使用教程
你知道CleanMyMac是什么吗?它的字面意思为“清理我的Mac”,作为软件,那就是一款Mac清理工具,Mac OS X 系统下知名系统清理软件,是数以万计的Mac用户的选择。它可以流畅地与系统性能相结合,只需简单的步骤就…...

Linux小黑板(9):共享内存
"My poor lost soul"上章花了不少的篇幅讲了讲基于管道((匿名、命名))技术实现的进程间通信。进程为什么需要通信?目的是为了完成进程间的"协同",提高处理数据的能力、优化业务逻辑的实现等等,在linux中我们已经谈过了一个通信的大类…...