当前位置: 首页 > news >正文

Linux进程控制详解

目录

  • 前言
  • 一、进程创建
    • 1.1 fork函数初识
    • 1.2 写时拷贝
    • 1.3 fork常规用法
    • 1.4 fork调用失败的原因
  • 二、进程终止
    • 2.1 进程终止时,操作系统做了什么??
    • 2.2 进程终止的常见方式有哪些??
    • 2.3 如何用代码终止一个进程
  • 三、进程等待
    • 3.1 进程等待的必要性
    • 3.2 进程等待的方法
      • wait方法
      • waitpid方法
  • 补充知识
  • 四、waitpid进一步讲解
  • 五、进程替换
    • 5.1 概念及原理
    • 5.2 操作
      • 5.2.1 不创建子进程
        • execl
      • 5.2.2 创建子进程
        • execv
        • execlp
        • execvp
        • execle + 执行其他的C二进制程序
        • 执行其他语言的程序
    • 5.3 execve
  • 六、自己实现一个简易的shell程序
    • 6.1 函数介绍
    • 6.2 myshell.c完整代码
  • 总结

前言

前面的文章都是关于进程概念的学习,今天我们就要进入一个新的章节,关于进程控制的解析,这其中我会穿插着前面已经讲过的知识,在以前知识的基础上学习新的知识,如果其中有哪里不懂的地方,大家可以去看一看我前面的文章,下面我们一起来学习新的知识吧。

一、进程创建

1.1 fork函数初识

在Linux中fork函数是非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
在这里插入图片描述
进程调用fork,当控制转移到内核中的fork代码后,内核做:
1.分配新的内存块和内存数据结构给子进程
2.将父进程部分数据结构内容拷贝至子进程
3.添加子进程到系统进程列表中
4.fork返回,开始调度器调度
在这里插入图片描述
fork之后,父子进程代码的共享情况

在这里插入图片描述

1.2 写时拷贝

通常,父子代码共享,父子在不写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式变为各自一份副本,具体可见下图
在这里插入图片描述
写时拷贝的好处: 因为有写时拷贝技术的存在,所以,父子进程得以彻底分离!完成了进程独立性的技术保证!
写时拷贝是一种延时申请技术,可以提高整机内存的使用率

1.3 fork常规用法

1.一个父进程希望复制自己,使父子进程同时执行不同的代码段。
2.一个进程要执行不同的程序

1.4 fork调用失败的原因

1.系统中有太多的进程
2.实际用户的进程数超过了限制(我们平时在写代码的时候,进程数其实是有限的)

二、进程终止

2.1 进程终止时,操作系统做了什么??

当然是释放进程申请的相关内核数据和对应的数据和代码,本质就是释放系统资源。

2.2 进程终止的常见方式有哪些??

a.代码跑完,结果正确
b.代码跑完,结果不正确
c.代码没有跑完,程序崩溃了(这部分涉及到了信号部分,我们先说一点点,后面再详细的说)
这里我们主要分析a、b两条内容,我先提一个问题:
大家平时在写C/C++程序的时候,一定都会写main函数,main函数最后都会有return 0,那么这里的return 0的含义是什么呢?为什么总是0,main函数返回的意义是什么???
在这里插入图片描述
在做下面的实验前我再补充一个小的知识点
echo $?:获取最近一个进程,执行完毕的退出码。
在这里插入图片描述
现在相信大家已经知道main函数最后的return 0是什么含义了,每一个退出码都有着不同的含义,我们通过下面的这个函数把C语言中的退出码是什么含义打印一下看看。
在这里插入图片描述
在这里插入图片描述
我们今天学习完退出码的概念,大家以后写代码的时候要可以将其应用起来,大家一起看下面的这个例子

int main()
{FILE* fp = fopen();if(fp == NULL)return 1;return 0;
}

当我们需要打开一个文件的时候,就可以使用上述的if判断语句来判断我们的这个文件是否被成功打开,当程序的退出码为1的时候,就是不允许操作,即这个文件打开失败,这样就可以更快地排查错误。


a、b两条内容分析完后,我们还要谈一谈c的内容,代码没有跑完,程序崩溃了。
在这里插入图片描述

2.3 如何用代码终止一个进程

什么是一个正确的终止???
1.return
main函数内的return语句,就是终止进程的,return 退出码
只有在main函数返回,进程退出
调用一些其他的函数,return则是退出当前的函数并返回一个值。
2.exit,C语言层面的函数
在这里插入图片描述
在这里插入图片描述
通过上面的情况再与return进行比较我们可以发现:
return只有在main函数中有结束进程的作用,exit在代码的任何位置调用,都表示直接终止进程!!!
3._exit,系统层面的函数
在这里插入图片描述
上面图片中的_exit与_Exit的作用类似,我们只用_exit来举例子,让大家看一下与_exit与exit有什么不同之处
在这里插入图片描述

从图中大家可以看到_exit()与exit()的区别,在平时写代码的时候还是建议大家使用exit()函数。
在这里插入图片描述

三、进程等待

在讲述进程等待前我们先来试想两种场景
1.子进程退出,父进程不管子进程,子进程就要处于僵尸状态,一直处于僵尸状态不释放资源,就会导致内存泄漏
2.父进程创建了子进程,是要让子进程完成一些任务的,那么子进程把任务完成得怎么样?父进程需不需要关心,如果需要,怎么得知子进程的情况,如果不需要,又要怎么处理呢?
子进程把任务完成的情况也是分为三种情况:
a.代码跑完,结果正确
b.代码跑完,结果不正确
c.代码没有爬完,程序崩溃了

3.1 进程等待的必要性

1.子进程退出,父进程如果不管不顾,就可能造成僵尸进程的问题,进而造成内存泄漏。另外,进程一旦变成僵尸状态,那就刀枪不入,即使是kill -9也无能为力,因为谁也没有办法杀去一个已经死去的进程。
2.最后,父进程派给子进程的任务完成的如何,我们其实是需要知道的,比如子进程运行完成后,结果对不对,或者最后是否正常退出,都是我们需要考虑的问题


父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息

3.2 进程等待的方法

我们先来看一下不让父进程去回收子进程的资源的情景,来进行一下对比
在这里插入图片描述
在上面的代码中,我们使用fork()函数创建了一个子进程,然后让其循环五次,打印同一行代码,循环结束后,结束进程,而父进程则一直进行循环,这样当子进程结束后,父进程还在死循环,无法收集子进程的资源,那么子进程就将成为僵尸进程。下面我们来看结果
在这里插入图片描述

这样就导致了僵尸进程的产生,那么如何解决这个问题呢?

wait方法

主要验证回收僵尸进程的问题
wait接口,我们可以通过man 2 wait来查询关于wait的信息
在这里插入图片描述
当然其中也有waitpid的信息。
下面我们先来简单认识一下wait()
在这里插入图片描述
下面我们来看一下实验代码以及运行结果
实验一:我们先来看一下如果父进程属于睡眠状态不调用wait函数,子进程是不是还是变为僵尸进程
在这里插入图片描述
运行结果分析
在这里插入图片描述
实验二:当父进程不处于睡眠状态时,又是一种怎样的情景
在这里插入图片描述
运行结果分析
在这里插入图片描述

父进程:wait时,父进程处于阻塞式等待,父进程处于阻塞态,等子进程退出。当子进程退出时,操作系统再将父进程唤醒,放到运行队列里,由父进程调用wait函数,再返回。
父进程处于阻塞式的等待意味着: 子进程不退出,父进程也不退出

waitpid方法

在这里插入图片描述
进程异常退出或者崩溃,本质是操作系统杀掉了你的进程!!
操作系统如何杀掉的呢?本质是通过发送信号的方式!
下面我们来看一下实验代码以及运行结果
实验一:子进程正常结束
在这里插入图片描述
运行结果
在这里插入图片描述
实验二:子进程非正常结束(1)
注: 当进程非正常结束时,进程的退出码就没有任何意义,主要观察进程收到的信号编号
在这里插入图片描述
运行结果
在这里插入图片描述
关于信号的信息我们可以通过kill -l(小写的l)来查看
其中1~31为普通信号,没有0号信号
在这里插入图片描述
实验二:子进程非正常结束(2)
在这里插入图片描述
运行结果
在这里插入图片描述
实验二:子进程非正常结束(3)
程序异常,不光光是内部的代码有问题,也可能是外力杀掉了子进程(子进程代码有没有跑完并不确定)
在这里插入图片描述
上面的代码我们让子进程死循环,那么这个进程就会一直运行
在这里插入图片描述
此时我们在另一个窗口将该进程杀掉
在这里插入图片描述
最后父进程收到的信号编号就是9号信号
在这里插入图片描述
总结:
1.如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息
2.如果在任意时刻调用wait/waitpid,子进程存在企鹅撑场运行,则进程可能阻塞
3.如果不存在该子进程,则立即出错返回
4.往后我们编写多进程,基本的写法就是fork + wait/waitpid

补充知识

1.父进程通过wait/waitpid可以拿到子进程的退出结果,为什么要用wait/waitpid函数呢?可不可以使用全局变量?
答:不可以
因为我们在前面就说过进程具有独立性,那么数据就要发生写时拷贝,父进程无法拿到子进程内的数据,更何况是信号了
2.既然进程是具有独立性的,那进程退出码也应该是子进程的数据,父进程凭什么能拿到呢,wait/waitpid究竟做了什么事情?
答:wait/waitpid的本质其实是读取子进程的task_struct结构。
3.这又和task_struct结构有啥关系呢??
其实最后进程退出的时候,一个进程的退出码和退出信号会被写到task_struct中(task_struct结构中有exit_pid,exit_signal两个变量),僵尸进程也是一样的,子进程退出,那么他的代码和数据就不会被运行和应用,就可以释放相关的数据,但是至少他会保留该进程的PCB信息,task_struct里面保留了任何进程退出时的退出结果信息, wait/waitpid读取task_struct结构后再将exit_pid,exit_signal(两个字段)进行位操作设置到传入的status变量中,然后再返回出来,就可以得到子进程的信号编号和退出码
4.wait/waitpid有这个权利么?
答:肯定有这个权利
wait/waitpid属于系统调用函数,本质是操作系统去拿取task_struct中的数据,而task_struct是内核数据结构对象,所以操作系统可以对其进行读取也就没有任何问题了。

四、waitpid进一步讲解

在这里插入图片描述
options中的WNOHANG选项。
WNOHANG选项,代表父进程非阻塞等待!
在这里插入图片描述
非阻塞等待:我们的父进程通过调用waitpid来进行等待,如果子进程没有退出,我们waitpid这个系统调用会立刻返回。
了解了上面的宏定义后,我们先来使用一下,顺便再结合而代码详细介绍一下我们上面所讲的内容
在这里插入图片描述
下面我们来看一下waitpid的伪代码
在这里插入图片描述
阻塞等待我们已经讲述完毕,后面就是非阻塞等待
非阻塞等待我们还是通过代码来演示
其实非阻塞等待的最大意义在于:
当子进程还在处理自己事情的时候,父进程不需要进入阻塞状态,父进程依然可以去做一些自己的事情,当子进程处理完自己的事情以后,父进程再去回收子进程的资源和数据。
在这里插入图片描述
运行结果:
在这里插入图片描述
通过上面的运行结果,我们可以看到,当子进程还在运行的时候,父进程可以腾出来时间来处理自己的任务,这就是非阻塞等待的好处。
如果未来编写网络代码的话,大部分都是IO类别,会不断面临阻塞和非阻塞的接口

五、进程替换

5.1 概念及原理

fork()之后,父子代码是共享的,数据写时拷贝,各自一份,父子进程各自执行父进程代码的一部分,但是如果子进程就想执行一个全新的程序呢?
进程的程序替换可以完成这个功能,程序替换是通过特定的接口,加载磁盘上的一个权限的程序(代码和数据),加载到调用进程的地址空间中,阿里让子进程执行其他的程序


可能上面说的内容比较抽象,大家不好理解,那么我们下面就通过画图的方式,来剖析一下进程替换的原理

在这里插入图片描述

5.2 操作

关于进程替换的函数有以下六个,看似很复杂,其实还是比较好理解的
在这里插入图片描述
下面我们先来进行一下基本的演示
1.不创建子进程
2.创建子进程
从这两个方面介绍,同时只使用最简单的exec函数,先看一看进行进程替换后会发生什么事情
后面我们会详细展开其他函数的用法

5.2.1 不创建子进程

execl

第一个我们要介绍的函数就是execl
在这里插入图片描述
其实该函数的可变参数列表可以参考我们平时最常用的命令行参数
下面我们就以平时用的最多的ls命令进行举例,先来看一下ls命令
在这里插入图片描述
红框内就是ls的路径,那么我们下面就来用ls命令进行进程替换,先看代码
在代码中,我在main函数的开始和结尾都加上了一句打印,这样就可以看到进程进行的情况,然后中间去调用函数进行进程替换。
在这里插入图片描述
代码运行结果
在这里插入图片描述
通过代码运行的结果,我们可以看到一些问题
程序刚开始运行的时候,打印了第一句代码,后面进行了程序替换,执行了ls的命令,同时也将其对应的结果打印了出来,但是最后面的一行代码并没有被打印出来,这个就是程序替换的特性
针对代码结果进行分析
1.为什么最后一句代码不打印呢??
execl是程序替换,调用该函数成功之后,会将当前进程的所有代码和数据都进行替换,包括已经执行的和没有执行的
简单来说,就是执行execl之后,execl之前和之后的代码都会被替换掉,只不过之前的代码运气比较好,在替换之前就已经执行完了,所以才有了最开始的打印结果。而后面所有的代码,全都不会执行
2.execl的返回值
在这里插入图片描述
我们看手册的介绍可以得知,如果调用失败,会返回-1,但是调用成功,并不会返回值,这又是为什么呢?
为什么调用成功没有返回值呢?
这个其实也很好理解,因为execl根本不需要进行函数返回值判定,如果替换成功,execl会将main函数中包含execl本身的代码全部都替换掉,前面用于接受返回值的数据也都已经被替换了,所以也就不需要有返回值了。


在这里插入图片描述
如果想让我们打印的字体也颜色的话,就可以看ls的内容再加一些代码
在这里插入图片描述
在这里插入图片描述
讲解后面知识之前,我们先来回忆一下之前的内容
加载新程序之前,父子的数据和代码的关系:
代码共享,数据写时拷贝
当子进程加载新程序的时候,就是一种写入,那么这个时候代码要不要进程写时拷贝呢?
一定会进行写时拷贝,父子进程的代码必须要分离
所以当我们调用execl函数的时候,父子进程在代码和数据上就彻底分开了。

5.2.2 创建子进程

为什么要创建子进程呢?
为了不影响父进程,我们想让父进程聚焦在读取数据,分析数据,指派进程执行代码的功能,如果不创建,那么我们替换的进程只能是父进程,如果创建了,替换的进程就是父进程,从而不会影响父进程


在上面我们已经看了execl的使用,所以这里就不多说了,关于exec系列还有几个函数,我们一起来看一下

execv

在这里插入图片描述
实验代码及结果
在这里插入图片描述

execlp

在这里插入图片描述
在这里插入图片描述
实验代码及结果
在这里插入图片描述

execvp

在这里插入图片描述
实验代码及结果
在这里插入图片描述


以上的代码基本都没有什么问题,那么我们来探究一个新的问题
1.如何执行其他我自己写的C、C++二进制程序
2.如何执行其他语言的程序

execle + 执行其他的C二进制程序

在这里插入图片描述
Makefile操作延伸
在这里插入图片描述
在Makefile中替换单词
例如:将Makefile中所有的exec替换为myshell
1.先按Esc
2.输入 :(英文冒号)
3.%s/exec/myshell/g + 回车

exec.c代码
在这里插入图片描述
mycmd.c代码
在这里插入图片描述
经过我们上面的代码,成功地实现了一个程序调用另一个程序
回顾 + 总结: 环境变量具有全局属性,可以被子进程继承下去,通过execle就可以将父进程的环境变量传给子进程

执行其他语言的程序

我们这里就简单的演示一下执行python语言的程序和执行shell程序
python代码,只输出一下,能够看到我们调用了即可
在这里插入图片描述
shell程序代码
在这里插入图片描述
运行python: python test.py
运行test.sh: bash test.sh
在这里插入图片描述
如果我们使用shmod + x test.py也可以执行python程序,这句话的意思就是给test.py加上可执行权限,就可以将test.py变为可执行程序,那么也就能直接执行该程序,了解了这些以后,下面开始进行一下简单的调用
1.调用python程序
在这里插入图片描述
1.调用shell程序
在这里插入图片描述
以上就是调用其他程序的全部内容。大家只需要了解就可以了

5.3 execve

在这里插入图片描述
其实在程序替换中,操作系统只提供了这一个接口,这个才是真正的系统调用接口。前面讲的函数是系统提供了基础封装(C语言做的封装),从而用来满足上层的不同的调用场景,上面的函数接受的数据经过整合后再传给execve函数
execve没有带p,所以需要带全路径

六、自己实现一个简易的shell程序

shell执行的命令通常有两种
1.第三方提供的对应的在磁盘中有具体二进制文件的可执行程序(由子进程执行)
2.shell内部,自己实现的方法,由自己(父进程)来执行(shell代表的是用户)
有些命令就是要影响shell本身,比如cd,export
我们在自己写shell的时候需要创建子进程去完成命令,为什么呢?
因为我们执行的一些命令可能会出现一些错误,子进程执行的话只会影响子进程的运行,不会影响到父进程。

6.1 函数介绍

fgets函数介绍
在这里插入图片描述
strtok函数介绍
在这里插入图片描述
chdir函数介绍
在这里插入图片描述
父进程设置的环境变量,传递到子进程的模拟实现
我们在父进程可能会设置环境变量,环境变量是具有全局性的,所以我们设置的环境变量也是会传到子进程的,下面我们自己来模拟一下其过程
在这里插入图片描述
我们再写一个测试程序,看看子进程是否能得到我们在父进程设置的环境变量
在这里插入图片描述
运行我们自己写的shell,并设置环境变量,并且调用mytest程序
在这里插入图片描述
总结: 程序替换会替换代码和数据,但是与系统有关的数据比如环境变量,其相关的数据就不会被替换,同时也体现出来了环境变量具有全局属性,进程替换一定和应用场景有关,我们有时候必须让子进程执行新的程序
补充: shell的环境变量是从哪里来的?
环境变量是写在配置文件中的,shell启动的时候,通过读取配置文件获得的起始环境变量。
linux中的.bash_profile 、.bashrc 、/etc/bashrc等文件中就有环境变量的信息
在这里插入图片描述

6.2 myshell.c完整代码

在这里插入图片描述

总结

以上就是进程控制全部的内容了,可以看到其中涉及到的知识点还是非常的多的,有的也不是很好理解,所以我们要多去看,多去总结,同时自己也要将这些函数全都用一下,这样才可以加深印象,如果文章中有错误的话,希望大家评论指出,我后面一定会改正,最后,如果你感觉我的文章对你有用的话,就给波三连吧!!谢谢大家

相关文章:

Linux进程控制详解

目录前言一、进程创建1.1 fork函数初识1.2 写时拷贝1.3 fork常规用法1.4 fork调用失败的原因二、进程终止2.1 进程终止时,操作系统做了什么??2.2 进程终止的常见方式有哪些??2.3 如何用代码终止一个进程三、进程等待3.…...

C语言深度剖析之程序环境和预处理

1.程序的翻译环境和执行环境 第一种是翻译环境,在这个环境中源代码被转换为可执行的机器指令 第二种是执行环境,它用于实际执行代码 2.翻译环境 分为四个阶段 预编译阶段 ,编译,汇编,链接 程序编译过程:多个…...

【Spark分布式内存计算框架——Spark Core】9. Spark 内核调度(上)

第八章 Spark 内核调度 Spark的核心是根据RDD来实现的,Spark Scheduler则为Spark核心实现的重要一环,其作用就是任务调度。Spark的任务调度就是如何组织任务去处理RDD中每个分区的数据,根据RDD的依赖关系构建DAG,基于DAG划分Stag…...

Vulkan教程(15): Graphics pipeline之Render passes(渲染通道)

Vulkan官方英文原文: https://vulkan-tutorial.com/Drawing_a_triangle/Graphics_pipeline_basics/Render_passes对应的Vulkan技术规格说明书版本: Vulkan 1.3.2Setup设置Before we can finish creating the pipeline, we need to tell Vulkan about the…...

乐观锁、雪花算法、MyBatis-Plus多数据源

乐观锁、雪花算法、MyBatis-Plus多数据源e>雪花算法2、乐观锁a>场景b>乐观锁与悲观锁c>模拟修改冲突d>乐观锁实现流程e>Mybatis-Plus实现乐观锁七、通用枚举a>数据库表添加字段sexb>创建通用枚举类型c>配置扫描通用枚举d>测试九、多数据源1、创建…...

详解Redisson分布式限流的实现原理

我们目前在工作中遇到一个性能问题,我们有个定时任务需要处理大量的数据,为了提升吞吐量,所以部署了很多台机器,但这个任务在运行前需要从别的服务那拉取大量的数据,随着数据量的增大,如果同时多台机器并发…...

[python入门㊹] - python测试类

目录 ❤ 断言方法 assertEqual 和 assertNotEqual assertTrue 和 assertFalse assertIsNone 和 assertIsNotNone ❤ 一个要测试的类 ❤ 测试AnonymousSurvey类 ❤ setUp() 和 teardown() 方法 ❤ 断言方法 常用的断言方法: 方法 用途 assertEqual(a, b) 核实a …...

Web 框架 Flask 快速入门(二)表单

课程地址:Python Web 框架 Flask 快速入门 文章目录🌴 表单1、表单介绍2、表单的简单实现1. 代码2. 代码的执行逻辑3、使用wtf扩展实现4、bug记录:表单验证总是失败🌴 表单 1、表单介绍 当我们在网页上填写账号密码进行登录的时…...

C++基础(5) - 复合类型(上)

文章目录数组1、什么是数组2、数组的声明3、数组的初始化4、数组的访问5、二维数组6、memset —— 给数组中每一个元素赋同样的值字符串(字符数组)1、string.h 头文件1.1 strlen()1.2 strcmp()1.3 strcpy()1.4 strcat()string 类简介1、C11 字符串初始化…...

java重写(@Override)介绍及实例说明

1.概述方法的重写(override)是封装的特性之一。在子类中可以根据需要对基类中继承来的方法进行重写。重载和重写没有任何关系。作用:通过重写,子类既可以继承父类的东西,又可以灵活的扩充。1.override注解是告诉编译器…...

基于STM32的虚拟示波器

仓库地址 https://github.com/shuai132/ScopeMCU ScopeMCU Oscilloscope for MCU MCU: STM32F103C8Tx 需配合ScopeGUI使用 截图说明见wiki 最新版Releases Introduction 用最少的硬件成本,做一个实用的虚拟示波器。 这是硬件部分,基于STM32最小…...

搭建云端vscode-server,使用web ide进行远程开发

使用乌班图系统,搭建自己的网页vs code开发环境github地址:GitHub - coder/code-server: VS Code in the browser安装脚本curl -fsSL https://code-server.dev/install.sh | sh出现deb package has been installed.表示已经正确安装。测试启动2.1修改配置…...

Linux clock子系统及驱动实例

文章目录基本概念CLK子系统时钟API的使用clock驱动实例1、时钟树2、设备树3、驱动实现fixed_clk固定时钟实现factor_clk分频时钟实现gate_clk门控时钟实现基本概念 晶振:晶源振荡器 PLL:Phase lock loop,锁相环。用于提升频率 OSC&#xff1a…...

GIS数据格式坐标转换(地球坐标WGS84、GCJ-02、火星坐标、百度坐标BD-09、国家大地坐标系CGCS2000)

文章目录前言一、坐标系1.地球坐标 (WGS84)2.国测局坐标系(GCJ-02、火星坐标系)3.百度坐标(BD-09)4.国家大地2000坐标系(CGCS2000)二、百度坐标系(BD-09) 与火星坐标系(GCJ-02)的转换1.核心代码2.转换验证百度地图高德地图腾讯地图三、火星坐标系 (GCJ-02) 与百度坐标系 (BD-09…...

流媒体传输系列文章汇总

流媒体传输系列文章汇总 文章目录流媒体传输系列文章汇总引言流媒体交互协议详解视频封装协议详解流媒体环境搭建其他引言 从去年开始编写有关流媒体传输相关知识的文章,已发表文章22篇,阅读量也超过了10万,为了方便各位阅读,本文…...

“万字“ Java I/O流讲解

Java I/O流讲解 每博一文案 谁让你读了这么多书,又知道了双水村以外还有一个大世界,如果从小你就在这个天地里,日出而作,日落而息。 那你现在就会和众乡亲抱同一理想:经过几年的辛劳,像大哥一样娶个满意的…...

数据库(Spring)事务的四种隔离级别

文章目录Spring(数据库)事务隔离级别分为四种(级别递减)1、Serializable(串行化)2、REPEATABLE READ(可重复读)3、READ COMMITTED(读以提交)4、Read Uncommit…...

RabbitMQ详解(一):RabbitMQ相关概念

RabbitMQ是目前非常热门的一款消息中间件,不管是互联网大厂还是中小企业都在大量使用。作为一名合格的开发者,有必要对RabbitMQ有所了解,本系列是RabbitMQ快速入门文章,主要内容包括RabbitMQ是什么、RabbitMQ核心概念、五种消息模…...

​ICLR 2023 | GReTo:以同异配关系重新审视动态时空图聚合

©PaperWeekly 原创 作者 | 周正阳单位 | 中国科学技术大学论文简介动态时空图数据结构在多种不同的学科中均普遍存在,如交通流、空气质量观测、社交网络等,这些观测往往会随着时间而变化,进而引发节点间关联的动态时变特性。本文的主要…...

线程池分享总结

线程池介绍 可以复用线程池的每一个资源 控制资源的总量 为什么要使用线程池 问题一:反复创建线程开销大 问题二:过多的线程会占用太多内存 解决以上两个问题的思路 • 用少量的线程——避免内存占用过多 • 让这部分线程都保持工作,且可…...

微信小程序之bind和catch

这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...

golang循环变量捕获问题​​

在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下: 问题背景 看这个代码片段: fo…...

Mac软件卸载指南,简单易懂!

刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"&#xff0…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...

并发编程 - go版

1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...

C语言中提供的第三方库之哈希表实现

一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...

Qt 事件处理中 return 的深入解析

Qt 事件处理中 return 的深入解析 在 Qt 事件处理中,return 语句的使用是另一个关键概念,它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别:不同层级的事件处理 方…...

Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?

Pod IP 的本质与特性 Pod IP 的定位 纯端点地址:Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址(如 10.244.1.2)无特殊名称:在 Kubernetes 中,它通常被称为 “Pod IP” 或 “容器 IP”生命周期:与 Pod …...