【linux】:进程状态(僵尸进程等)以及环境变量

news2024/11/20 3:38:06

 

 

文章目录

  • 前言
  • 一.进程状态
  •     进程的优先级
  • 二.环境变量
  • 总结

 


前言

  本篇文章是接着上一篇【linux】:进程概念的后续,对于有基础的同学可以直接看这篇文章,对于初学者来说强烈建议大家从上一篇的概念开始看起,上一篇主要解释了冯诺依曼体系以及操作系统的概念还有在linux系统中进程是什么样的,如何去查看一个进程,如何给一个进程多开一个子进程以及为什么fork()函数可以有两个返回值的问题。


 

一、进程状态

为了能更深刻的理解linux中的进程状态,我们把linux中的内核源代码拿出来看一下:

/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};

task_struct是一个结构体,内部会包含各种属性,其中就包括状态,如下图:

3df939f7365247b194331616c0242f2d.png

我们先来讲解阻塞和挂起这两个重要的概念,阻塞就是进程因为等待某种资源就绪,而导致的一种不推进的状态。我们经常可以看到不管是手机还是电脑当打开的软件很多的时候,就有出现应用卡顿的情况,这是因为当我们打开很多的软件的时候进程也变多了,操作系统调度不过来了,这个时候卡的那个进程就是阻塞了。再比如说我们下载一个软件,下载了一半没网了,这个时候下载进度就不动了,这个时候这个进程就变成了阻塞状态,只有当网络好了能继续下载了CPU才会继续调度这个进程,所以这个进程卡住了是在等待某种资源就绪,当资源就绪了就会被CPU调度取消阻塞状态。所以进程要通过等待的方式,等具体的资源被别人用完之后,再被自己使用。那么进程等待某种资源就绪的过程中,资源指什么呢?这里的资源指软硬件资源,比如:磁盘,显卡,网卡等各种外设。下面我们用图解释一下阻塞的过程:

2ae1295df6334863ada20d33b28ce3c7.png

我们前面讲过操作系统对于软硬件的管理是先描述在组织,所以对于网卡磁盘等也是通过struct来描述的,当CPU正在跑一个进程的时候,这个进程突然没网了这个时候就将这个进程先变成阻塞状态,然后看下图:

d567697866df45b392b8e42d1f11d9b0.png 这个时候因为网络的问题进程需要等待网络恢复才能继续在CPU上运行所以这个进程就会链接在网卡的尾部等待网卡资源就绪也就是网络恢复才可以正常运行。所以PCB是可以被维护在不同的队列中的。

阻塞:阻塞就是不被CPU调度。一定是因为当前进程需要等待某种资源就绪。一定是进程task_struct结构体需要在某种被OS管理的资源下排队。

下面我们解释一下挂起的概念:

e502b206bc824aa1b851ed5293bc2b46.png

 上图是一个进程正在被CPU调度,然后突然没网了,看下图:

afbc5920d8074e9e8bba28255f603dfd.png

这个时候进程进入阻塞状态等待网卡设备就绪,由于内存中空间有限所以对于阻塞状态的进程的代码和数据来说无疑是浪费空间的,所以操作系统会先将阻塞状态的进程的代码和数据放入磁盘中,将内存中的代码和数据释放掉。

c8392f05a4c54287895a92073efe7a74.png

等过了一段时间,网卡设备就绪了,这个时候进程会继续被CPU调度,在这之前需要把磁盘中的代码和数据继续放入内存中

ca9e009b6ca941d9a5e38b34d4984b32.png 以上的将代码和数据先放入磁盘等待网卡设备就绪然后就绪后再将磁盘中的代码和数据放入内存的过程就叫做挂起。

在这里问一个问题,进程是R状态一定是在CPU上运行吗?答案是不一定。进程一般是什么状态,要看这个进程在哪里排队,看下图:

d4a02721d55540829586b381cb1827ce.png

首先我们创建一个.c文件然后写一个死循环代码,然后创建Makefile文件:

d22d81fc78f746f289140395fbc4ba63.png 接下来我们直接运行并且查看当前进程状态:

14dacd07de7f4873844a786d735f697f.png

e7bad5ae41884d2c943545a068d362a3.png 我们先用ps axj | head -n1指令调出进程属性,然后后面加上grep mytest过滤出mytest可执行程序的进程,后面加的grep -v grep是过滤掉grep自己本身的进程,最后成功的显示出这个进程,我们可以很清楚的看到这个正在运行的程序的状态并不是R而是S+,S代表休眠状态,接下来我们将打印注释掉试一下是什么状态:

7e4da7b97e9741e9b23e5b024d3d5d8f.png

77d11feecf4248a8b7ded3609caa15eb.png

7507eb2bd25146bd9f8a6cf3afb38c22.png 我们可以看到当将打印代码注释掉后这个进程的状态变成了R,这个时候为什么是运行状态了呢?因为printf打印需要打印到屏幕上,而屏幕就是一种外设,频繁的往屏幕打印进程会等待屏幕就绪才可以打印,而CPU的运算非常快外设的速度却很慢,所以CPU早就跑完了代码接下来将进程状态设为阻塞状态让这个进程去屏幕后面等待屏幕就绪当屏幕就绪后又会重新被CPU调度然后执行重复的操作。

所以进程是R状态不代表进程在运行,而代表该进程在运行队列中排队。

S状态是休眠状态,可中断休眠。而S状态就是一种阻塞状态。

接下里我们演示一下,首先修改一下代码:

bcbf6fd0ae7f44d08a9efadd4d41cd52.png

2a353a55f92b451490226f37241198b6.png 这个时候我们去看进程状态。

53c6773db78c48dda6a94a5dff508eac.png

为什么是S状态呢?因为进程会等待键盘资源就绪,也就是说只有键盘输入了才叫键盘资源就绪这个时候才会被CPU调度 

e040603ad4e146abb89fe3f4b56abd9d.png

e4acf87bd34842b18a9d48f3d10a3032.png 当我们终止程序后程序就结束了这个进程也结束了,对应了S状态是可中断休眠状态。

D状态也是一种休眠状态,D状态是不可休眠状态。D状态在生活中我们基本不会遇到,就是磁盘基本快满了还在往磁盘存数据,这个时候你就会发现你的电脑非常卡而且不能强制进行任何关闭操作,如果关电源会导致资料丢失所以不可中断只能等磁盘自己恢复。

T状态叫暂停状态,下面我们来演示一下T状态:

a544bbb1cb7e402583c5563d5c66b6d0.png

 我们先讲代码修改一下方便演示:

aa147b94433c422aa89cd103c6eff458.png

6978af082c6f491c8a572a9a736eb61a.png

 运行后我们可以看到这个时候的进程状态为S,这个时候我们使用一个暂停命令:

f25550a2b0ba4f72958ba82284e6faac.png

19号这个命令sigstop为暂停然后我们使用一下:

b6c9f9041b664231bfd65d69319b2099.png8d582a80ebc44450820d3447ad60d2b6.png

2f4d65e576a543cfb805521e5cb46fb7.png 这个时候我们能很清楚的看到进程变为T状态了并且程序确实暂停了,这个时候我们如何恢复运行呢?

f67fd315f1e940098782d7b7107add68.png

这里的18号代码为sigle continue的简写就是继续的意思

995f09920c694de6a1d03fa8ea89f1a2.png 0ac9404a84c5437384add93dee997e03.png

这个时候程序就又可以继续了。但是我们发现这个时候我们用ctrl+c关不掉了,并且进程状态也变了。

83ab6e9f914b498699b2c148de238f9a.png

4903185d9d2c494f9a7b72f2b33a5a7a.png 从原来的S+变成了S为什么就不能终止程序了呢,因为状态后面带+号是代表程序在前台运行,在前台运行的程序可以ctrl + c结束,没有+就变成了在后台运行,这个时候我们只能通过kill命令杀掉这个进程。

ed0135960216496ca661c0b3d6c8d7c9.png

6f502a7fc2cf458090e5ca1eb76d661f.png 7a01e949aa5b43598fd0643605c7b9c8.png

23f291c5a7784085bf1c5304d6f8b663.png 这个时候程序就结束了,不管是前台或后台我们都可以用kill杀掉。

X状态被称为死亡状态,Z状态称为僵尸状态,下面我们解释一下这两个状态:

为什么我们要创建进程呢?因为我们需要进程为我们做事,我们写过C语言代码,知道每个main函数没必须返回0,这是因为我们要知道函数的结果,如果没有返回值我们就无法确定一个函数是否允许,所以我们需要进程的返回值来确定进程的状态。那么如果一个进程退出了立马变成X状态,作为这个进程的父进程,有没有机会拿到结果呢?linux当进程退出的时候,一般进程不会立即彻底退出,而是要维持一个状态叫Z,也叫作僵尸状态,方便后续父进程(OS)读取该子进程退出的退出结果。那么如何看到僵尸状态呢?子进程退出,但是不要回收子进程。下面我们演示一下:

8f42c19aafdc4a36b0bb591279f1ae2e.png

5ec9051a3b7b43ceb60ab8a4ce8c9e1c.png

893c404472b841888f799d563970654e.png 这个时候两个进程都处于S状态,根据我们刚刚所说的只要让子进程先退出就能看到僵尸状态。

59e025d4c1d44a36baddafcf0282dbae.png

370a95e14a794840b2d23bb85aa64cfa.png

03b920da86e44ef28d32b0df02ba37bf.png 我们可以清楚的看到,当我们将子进程杀掉后子进程的状态变成了Z也就是僵尸状态,在mytest后面的单词<defunct>这个单词就有死人的意思,那么僵尸状态在这会占用资源吗?答案是会的,维持僵尸状态会占用资源,僵尸状态必须释放,如果这样的僵尸状态很多那么机器就很容易卡死。而维护僵尸状态的意义就是让父进程能够读取子进程退出的信息。

僵尸状态的危害:

僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用没有读取到子进程退出的返回代码时就会产生僵死(尸)进程僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态
进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态。 维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话 说,Z状态一直不退出,PCB一直都要维护。那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?是的!因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间! 内存泄漏?是的

刚刚我们看到子进程先退就变成僵尸了,下面我们看看父进程先退会怎么样。 

99fd9a5c77c549098a0bce7551ae7744.png

 我们将代码修改一下方便观察,下面我们用命令观察一下:

a96f149c0b7b40d890c2a156d719976b.png

 我们先用shell编程每隔1秒监测一下进程。然后将进程跑起来:

961767d5a92f49b0873bb469384b1a06.png f3034cf04323476eaa0907e833b9b05d.png

 我们可以看到一开始有两个进程,当父进程结束后只剩下了子进程,这个时候我们发现子进程的状态从S+变成S了,也就是说从前台变成后台了。

451607d1174940e0808b443b39a592a1.png4d68ca3353174cb7a3060059716b962b.png

 我们可以看到这个时候已经不能用ctrl+c终止程序了,只能用kill杀掉子进程。那么为什么父进程先退出的时候没有变成僵尸呢?这是因为这个先退出的父进程被他的父进程回收了,他的父进程就是bash。怎么证明呢?在上图中我们发现pid为15529的进程的父进程一开始为15528,当15528退出的时候,15529这个子进程又重新给自己找了个爹pid为1,pid为1的进程我们都知道,这就是操作系统,也就是说,父进程退出,子进程会被OS自动领养(通过让1号进程成为新的父进程),那么这个被领养的进程就被称为孤儿进程。那么为什么我们上面演示子进程退出的时候子进程变成僵尸状态了呢?因为上面我们为了演示出僵尸状态故意没有将代码写完,因为没有等待,所以子进程变成了僵尸状态。那么为什么子进程会被自动领养呢?因为如果不领养就导致没人能找到子进程,一旦子进程退出就没人回收这个进程了,那么这个子进程就是一种游离状态,这样就会造成资源浪费,也就是内存泄漏。

进程的优先级

cpu资源分配的先后顺序,就是指进程的优先权。优先权高的进程有优先执行权力,配置进程优先权对多任务环境的linux很有用,可以改善系统性能。还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。

优先级和权限有什么区别呢?答案是权限代表了能不能的问题,而优先级是你执行的先后顺序,优先级已经确定了你可以干某件事只是取决于先后问题。那么为什么会有优先级呢?因为CPU的资源有限。

我们可以用ps -l命令查看进程中的优先级,如下:

df1fffff63724d0bbbe2924ba3f2d4a4.png

在上面的图片中,PRI代表程序的优先级,NI代表进程优先级的修正数据。PRI的值越小进程的优先级越高,而NI值可以理解为是改变PRI的值从而修改进程的优先值。PRI(新) = PRI(旧)+NI

而在linux系统中,旧的PRI值一定为80。下面来演示一下:

79ffc6fdadad455d8c6fdeeb61458ee9.png 我们先随便写一个死循环程序,然后运行起来。

e4981ebd63204f0e895d8fea845a44b7.png

 可以看到程序已经跑起来了,这个时候用top命令去修改优先级。top进入后输入R,R就是renice的意思,然后输入pid

3d7fa8cbfb9e49199e4739b87de3e0be.png

8e8cdad160a24639bc449b07c165ec7b.png

 接下里让我们输入nice值,我们就调整为-20

f5df746543964949bd26d5abdafac521.png

 我们可以看到确实成功修改了这个时候我们再修改为100

155128a64bc24b899284c2a0d3f9c908.png

c92ae562786a4c9cba1bb673cfc4a9b6.png为什么是不是180呢?因为我们优先级的调整范围是-20到19这个级别,也就是说最小是-20,最大是19。 进程的优先级在我们平时使用中都不会调整,一般都会使用默认的优先级,大家知道该怎么修改就可以了。

二、环境变量

环境变量一般是指操作系统中用来指定操作系统运行环境的一些参数,比如我们在编写c/c++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找,环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性。比如我们在linux中写的可执行程序,要想这个程序先运行起来必须在前面就是 .   /   ,  . 是在当前路径,/ 是路径分隔符,而同样为可执行程序的 ls指令等却不需要在前面加上./,难道就因为一个是被纳入系统的程序一个是我们自己写的吗?其实并不是这样,系统的指令之所以不需要在前面加./是因为有环境变量的帮助,这个环境变量会帮我们去搜索系统中的ls命令,而这个环境变量叫PATH,下面我们查看一下这个环境变量:

eef2400857064d3984f83907a6e6973a.png

 echo是打印一个字符串,PATH是环境变量,前面加上$符号就是获取环境变量的内容,这里与指针解引用相似。

和环境变量相关的命令:

1.echo:显示某个环境变量值

2.export:设置一个新的环境变量

3.env:显示所有环境变量

4.unset:清除环境变量

5.set:显示本地定义的shell变量和环境变量

fa6597d954d5457a9cbf4f001828b532.png

 我们之前讲过linux的指令,which可以查看指令的路径,通过查看我们发现ls指令在usr/bin中,而PATH环境变量是根据冒号一个一个路径进行查找,当找到usr/bin这个路径的时候就不需要我们在使用的时候加./了。那么如何将我们写的可执行程序添加到环境变量中呢?看下图:

db99d84aed4d48f88c3223e9c1526bbf.png

 我们写了一个程序用来演示。接下来我们用命令将我们写的这个程序加入到环境变量中

9179e0db8aa74d2196d602781ee889d4.png

 可以看到我们成功添加,然后我们试试可以直接运行吗?

c3f9e490a15a4b9593cea23c8f8b8584.png我们看到是可以运行的并且不用在输入前面的./了

827ead7ebe764ca78c953512cf28bc88.png

 这个时候我们的其他指令不能使用该怎么办呢?这时只需要重新登录xshell即可。

那么我们怎么样才能既使用系统的指令又用自己的呢?

69c775a1039445efa5443b330d36f084.png

 我们将刚刚的命令修改一下就可以既使用系统的指令又用自己的。当然除了这一种方式我们还可以直接将要添加的可执行程序的目录拷贝到PATH中,而这种方式在linux中相当于软件的安装。

下面我们用env指令查看系统中的环境变量:

d4dbfd76edcf4344be89b8c9c2551257.png

 我们可以看到系统中的环境变量很多,当然我们也可以用history指令查看以往我们用过的指令:

10c64bfd8c7d49a7880dd3db7da4d4da.png

 为什么从10开始呢,因为history只会保留最新的1000条指令,一旦超过就会删掉原来旧的指令。

下面我们用C语言来获取系统中的环境变量:

1053a65ec3f145e0ac903e548dfecbb6.png

 我们再写c/c++的时候从来没有写过main函数的参数,而main函数实际上有3个参数,这三个参数不需要我们手动去写编译器会默认给我们传参,envp这个指针数组中每一个指针都指向一个有效的字符串,而最后一个指针必须以NULL结尾。

437592c9d81447f69511ffb70f3eb002.png

 然后我们将代码写完整如下图:

517d42ddc8ed4c258f8552a19429ef7a.png

 为什么for循环中envp没有写判断呢?因为我们刚刚说过,envp这个指针数组中最后一个指针指向的一定是NULL,而NULL在for循环中对应为假,所以不需要写判断语句。

91984d5470944dd896117db9c15d9955.png

 由于使用main函数的三个参数是c99标准下的,所以我们在编译后面加上c99。

0493d41210ec4fc88805e0a966a6dec6.png

 然后我们直接运行程序发现这里的环境变量与我们用env命令显示的一致。

总结:环境变量本质就是内存级的一张表,这张表由用户在登录系统的时候,进行给特定用户形成属于自己的环境变量表。环境变量中的每一个都有自己的用途,有的是进行路径查找的,有的是进行身份认证的,有的是进行动态库查找的,有的是用来进行确认当前路径等等,每一个环境变量都有自己特定的应用场景。那么环境变量对应的数据都是从哪来的呢?是从系统的相关配置文件中读取进来的。下面我们验证一下:

我们先用ctrl + ~进入家目录,然后输入指令ls -al查找文件:

e0a14d674ac247ba9d4c4c1b727fb701.png

 我们可以看到bash的两个shell脚本,然后我们用vim打开这个脚本:

ae34742d84d447578034c3d119f5a4f0.png

 我们用vim进入etc/bashrc,注意在etc目录下bashrc是全局的。

5463d7bebacc4306bfd430955fc36dfb.png

 比如我们命令行上的# 或者 $提示符就是这样编写的。  环境变量是通常具有全局属性的,当我们写了一个环境变量val=100,然后这个环境变量就会shell的表中,当我们给这个进程在开一个子进程的时候,shell中的这张表也会交给子进程,这样子进程中也就有了环境变量val=100,下面我们来证明一下:

946aac7ad9fc4dce8ad5b3cdc71bf470.png

 我们先自己导入一个环境变量,然后输入env命令查看:

97d7963b289b40c0badbd1e5bb70f892.png 我们看到确实将这个环境变量添加到系统中,下面我们修改一下代码进行演示:

17dd266e3a7b436f9bee421adfb364cc.png

getenv()函数是获取一个环境变量并打印 

02d0eb73320c493e99461da6c3318923.png 我们从上图中可以看到成功获取到了我们自己设的环境变量,我们前面讲过当我们运行一个进程的时候这个进程的父进程是bash,刚刚我们的环境变量是保存在系统中也就是说只有bash可以访问,但是现在这个子进程也可以使用就说明了环境变量是全局的,会由父进程传给子进程。

d9aed7534c1c46f2a3093c444c92f572.png

 我们定义了一个变量前面没有加export,然后我们在前面加$符号打印其内容发现也能正常打印

721859772cf748c09ecbc161b9eacd02.png

 下面我们用getenv获取一下这个环境变量:

a815b9ad3e4d4774a0e5ddc509c16cff.png

3886220543ad459d90366fcce00653ea.png 这个时候我们发现用函数获取环境变量获取不到我们刚刚定义的hello1,也就是说不带export定义的环境变量是不可以被getenv()获取到的,那么也就不可以被子进程继承,那么为什么加了export就能被继承呢?因为不加export定义的环境变量是不会被添加到环境变量表中,这样的变量被称为shell的本地变量,这种变量只能在shell内部有效。

b01629f725ca4cdba22c8b2362c99dd0.png

 因为hello1已经在shell本地了,所以前面加export可以直接将hello1添加到环境变量表,也就可以正常被子进程继承了。

接下来我们继续解释刚刚main函数的三个参数中的另外两个。

6919d0e5236a4f24b8c0fd25e610f666.png

 argv的使用与argc都有一个共同点,就是不需要在判断结束条件。

6e4188fc28384bdb91e521796a593cc6.png

如下图所示:

f97e681055f84c0492bef8c8f721e5be.png-a -b其实是参数选项,my2.24是可执行。那么这个操作有什么作用呢?

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
//int main()
//{
//  int cnt = 10;
//  while (cnt--)
//  {
//    printf("这里在倒计时:%d\n",cnt);
//  }
//  return 0;
//}
void Usage(const char* name)
{
  printf("\nUsage:%s -[a|b|c]\n",name);
  exit(0);
}
int main(int argc,char* argv[])
{
  if (argc!=2) Usage(argv[0]);
  if (strcmp(argv[1],"-a")==0) printf("打印当前目录下的文件名\n");
  else if(strcmp(argv[1],"-b")==0) printf("打印当前目录下的详细信息\n");
  else if(strcmp(argv[1],"-c")==0) printf("打印当前目录下的文件名(包含隐藏文件)\n");
  else printf("其他功能,待开发\n");
//{
//  for (int i = 0;argv[i];i++)
//  {
//    printf("argv[%d]->%s\n",i,argv[i]);
//  }
  return 0;
}
//int main()
//{
//  printf("myenv:%s\n",getenv("hello1"));
//  return 0;
//}

 我们重新写一段代码,然后我们运行起来。

b4680554930146ad93724193fd4aab62.png

 启动软件后这个软件告诉我们使用方法是./my2.24 + abc任意一个字符

6ad89f683c6240bdbbe80480f81ad57f.png

 这样就能完成一个类似于打印目录的操作,这只是简单的演示实际上可以实现一些有用的东西。

 


总结

本篇文章相较于上一篇进程的概念多了很多需要实践的东西,比如测试进程的优先级,理解孤儿进程,学会理解环境变量并且可以自己添加环境变量,环境变量的获取,环境变量的修改等。下一篇继续更深入的学习linux的进程,即使进程这部分概念多也希望大家可以多多练习才能更深入的理解进程。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/388239.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

将 Visual Studio Code 设置为3dmax Maxscript脚本编辑器的方法详解

Visual Studio Code对Autodesk 3dMax脚本语言 (MaxScript) 支持 本教程给大家分享Visual Studio Code对Autodesk 3dMax 脚本语言 (MaxScript) 支持。 MXSPyCOM项目&#xff1a;允许从外部代码编辑器编辑和执行3dMax MaxScript和Python文件。 功能特性 语法高亮。语法错误检查语…

x86 平台利用 qemu-user-static 实现 arm64 平台 docker 镜像的运行和构建

文章目录[toc]关于 docker 版本查看是否开启 experimental 功能开启 experimental 功能查看当前环境平台拉取一个 arm 平台的容器运行一个 arm 平台的容器整一个 qemu-user-static注册可支持的架构解释器尝试启动 arm64 镜像尝试启动 ppc64le 镜像后台运行 arm64 容器build 一个…

Libgdx 导入blender 3D动画

导入blender3D模型可以看上篇libgdx导入blender模型 本篇3D动画在上篇的基础上。 具体参考官网 3D 动画和蒙皮 blender动画参考 八个案例教程带你从0到1入门blender【已完结】 打开上次的blender的&#xff0c;选则物体属性 点击位置和旋转x&#xff0c;y&#xff0c;z后面…

一个好的工程项目管理软件所包含的主要功能

工程项目管理软件哪个好&#xff1f;借助Zoho Projects强大的工程项目管理软件&#xff0c;您的团队可以在预算范围内按时交付。从质量保证到预算规划&#xff0c;Zoho Projects的工程项目管理平台旨在推动切实的成果是Zoho Projects工程项目管理软件的优势。 高质量的可交付成…

骨传导耳机是什么意思,骨传导耳机的好处具体有哪些

​在这个全民都是手机的时代&#xff0c;各种蓝牙耳机&#xff0c;入耳式耳机&#xff0c;真无线耳机等各种款式琳琅满目。而骨传导耳机是一种全新的科技产物&#xff0c;顾名思义就是通过头骨振动将声音传至外耳内的耳机。由于无需入耳&#xff0c;不会对耳朵造成任何影响。那…

Linux用户空间与内核空间通信(Netlink通信机制)

一&#xff0c;什么是Netlink通信机制 Netlink是linux提供的用于内核和用户态进程之间的通信方式。但是注意虽然Netlink主要用于用户空间和内核空间的通信&#xff0c;但是也能用于用户空间的两个进程通信。只是进程间通信有其他很多方式&#xff0c;一般不用Netlink。除非需要…

JVM详解——执行引擎

如果有兴趣了解更多相关内容&#xff0c;欢迎来我的个人网站看看&#xff1a;耶瞳空间 一&#xff1a;执行引擎介绍 “虚拟机”是一个相对于“物理机”的概念&#xff0c;这两种机器都有代码执行能力&#xff0c;其区别是物理机的执行引擎是直接建立在处理器、缓存、指令集和…

程序、进程、线程的基本概念、信号量的PV操作、前趋图的PV操作

程序、进程、线程的基本概念 进程控制块PCB的组织方式&#xff1a;顺序方式、链接方式、索引方式、Hash。 在JVM 中进程与线程关系 进程&#xff1a; 拥有资源的独立单位。可以被独立调度。可以分配资源。 线程&#xff1a; 可以被独立调度。同一进程中的多个线程&#xff0c;…

Java高效率复习-SpringMVC[SpringMVC-2]

SpringMVC获取请求参数 SpringMVC获取请求参数的两种方式↓ 通过ServletAPI获取请求参数 将HttpServletRequest作为控制器方法的形参&#xff0c;此时HttpServletRequest类型的参数表示封装了当前请求的请求报文的对象 通过request的API——getParameter(String s)方法来获取…

LQB,手打,PCF8591,ADDA转换,AD1是光敏电阻,AD3是电位器,DA输出

在上述at24c02de 基础上&#xff0c;添加三个函数 一个是读取通道1光敏电阻的数据&#xff1b; 一个是读取通道3的电压&#xff1b; 一个是输出DA的数据。。 5V的AD DA。 如果读入的电压是5V&#xff0c;输入AD&#xff0c;就是255&#xff1b; 如果是0V&#xff0c;就是00000…

05 比特币价格的含义

2009年1月&#xff0c;在赫尔辛基的一台小型服务器上&#xff0c;「中本聪」创建了一份简陋的开源代码&#xff0c;比特币的首个区块在这一天诞生。自此之后的十余年间&#xff0c;比特币和以其为代表作的区块链技术&#xff0c;踏上了波谲云诡的大航海征途。比特币的价格是指比…

Android---进程间通信机制3

1 服务如何注册到 SM 中 getIServiceManager().addService(name, service, false); getIServiceManger --- new ServiceManagerProxy(new BinderProxy()) BinderInternal.getContextObject --- 返回 BinderProxy 对象 ProcessState::self()->getContextObject: 创建一个 BpB…

k8s学习之路 | Day19 k8s 工作负载 Deployment(上)

文章目录1. Deployment 基础1.1 什么是 Deployment1.2 简单体验 Deployment1.3 Deployment 信息描述1.4 如何编写 Deployment2. Deployment 简单特性2.1 赋予 Pod 故障转移和自愈能力2.2 更新 Deployment2.3 回滚 Deployment2.4 暂停、恢复 Deployment 的上线过程2.5 Deploymen…

Gateway网关选型

网关一般分为流量网关和业务网关&#xff0c;流量网关负责接入所有的流量&#xff0c;并分发给不同的子系统&#xff0c;那在具体的业务接入之前&#xff0c;还有一层业务网关。流量网关提供全局性的、与后端业务应用无关的策略&#xff0c;例如 HTTPS证书卸载、Web防火墙、全局…

【教学典型案例】01.redis只管存不管删除让失效时间删除的问题

目录一&#xff1a;背景介绍二&#xff1a;redis1&#xff09;redis数据类型①String&#xff08;字符串&#xff09;②Hash&#xff08;哈希&#xff09;③List&#xff08;列表&#xff09;④Set&#xff08;集合&#xff09;2)缓存同步①设置有效期②同步双写③异步通知3&am…

java实现UDP及TCP通信

简介UDP(User Datagram Protocol)用户数据报协议&#xff0c;TCP(Transmission Control Protocol) 传输控制协议&#xff0c;是传输层的两个重要协议。UDP是一种无连接、不可靠传输的协议。其将数据源IP、目的地IP和端口封装成数据包&#xff0c;不需要建立连接&#xff0c;每个…

引用是否有地址的讨论的

说在前头&#xff0c;纯属个人理解&#xff0c;关于引用是否有地址&#xff0c;实际上并没有一个很统一的说法&#xff0c; C标准没有规定一个引用是否需要占用一块内存。 这里引用知乎“C 中引用是一块内存的标记&#xff0c;那引用本身有地址吗_百度知道 (baidu.com)”里面的…

Python爬虫之Js逆向案例(17)- Scrapy JD版店铺详情|问答

本案例是案例(16)的Scrapy版本 一次运行程序&#xff0c;同时获取内容&#xff1a;获取商店详情、商品问题、商品答案&#xff1b; 效果如下图&#xff1a; 一.Scrapy框架从安装到运行的过程 1.安装scrapy框架 控制台输入&#xff1a;pip3 install scrapy 2.验证安装结…

基于Flink CDC datastream mysql to mysql 序列化sql 数据同步

基于Flink CDC datastream mysql to mysql 序列化sql 数据同步 Flink CDC有两种方式同步数据库&#xff1a; 1. 一种是通过FlinkSQL直接输入两表数据库映射进行数据同步&#xff0c;缺点是只能单表进行同步&#xff1b; 2. 一种是通过DataStream开发一个maven项目&#xff0c…

华为机试题:HJ106 字符逆序(python)

文章目录&#xff08;1&#xff09;题目描述&#xff08;2&#xff09;Python3实现&#xff08;3&#xff09;知识点详解1、input()&#xff1a;获取控制台&#xff08;任意形式&#xff09;的输入。输出均为字符串类型。1.1、input() 与 list(input()) 的区别、及其相互转换方…