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:…...

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 原创 作者 | 周正阳单位 | 中国科学技术大学论文简介动态时空图数据结构在多种不同的学科中均普遍存在,如交通流、空气质量观测、社交网络等,这些观测往往会随着时间而变化,进而引发节点间关联的动态时变特性。本文的主要…...
线程池分享总结
线程池介绍 可以复用线程池的每一个资源 控制资源的总量 为什么要使用线程池 问题一:反复创建线程开销大 问题二:过多的线程会占用太多内存 解决以上两个问题的思路 • 用少量的线程——避免内存占用过多 • 让这部分线程都保持工作,且可…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...

SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...

深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...

AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...