Linux进程控制

news2024/12/23 20:08:15

文章目录

  • 进程创建
    • fork函数进一步探讨
    • 写时拷贝
    • 进程终止
      • 进程退出场景
      • 进程终止时,操作系统做了什么?
      • 三大终止进程函数
  • 进程等待(阻塞)
    • 进程等待的必要性
    • 进程等待的两种函数
    • 获取子进程参数status
      • 如何通过status获取子进程的退出码。
      • 为什么不用全局变量记录子进程的退出码和退出信号?
  • 崩溃的本质
      • 操作系统如何杀掉该进程的呢?
      • 使用WAITSTATUS和WIFEXITED宏来获取子进程的退出码
  • 进程等待(非阻塞)
  • 进程替换
    • 替换原理
    • 进程替换函数
    • 写时拷贝与进程替换
  • 如何让一个程序去执行另一个程序?
    • Mafile多文件编译
    • exec.e程序执行mycmd程序
  • 进程函数大总结
  • 制定一个简易shell
    • 一:打印提示符
    • 二:获取用户的键盘输入
    • 三:对连续的字符串进行解析为多个子字符串
    • 四:父进程创建子进程并执行进程等待
    • 五:简易shell的目录执行问题——内置命令
    • 六:给ls命令显现的文件名添加颜色
    • 简易shell完整代码

进程创建

fork函数进一步探讨

通过前篇我们了解到fork函数创建子进程,会给子进程分配对应的数据结构,可是一般而言子进程没有加载的过程,也就是说子进程没有属于自己的代码和数据,所以,子进程只能“使用”父进程的代码和数据。
可是当子进程要修改数据时,父进程的数据也被修改了,这不符合进程的独立性。所以,父子进程的代码必须分离。
在这里插入图片描述

写时拷贝

可是,如果在创建进程的时候,子进程就对父进程的代码直接拷贝一份储存在物理内存中时我们会碰到一下问题。
1:我们可能拷贝到子进程根本不会执行的代码,即使执行了那也只是读取而不是写入。
2:提前拷贝一份代码,子进程也有可能并不会立马使用,这会降低内存的使用效率。
所以我们操作系统采取了写时拷贝技术,当父子进程都只是读取代码和数据时,父子进程享用同一份数据和代码,当子进程想要修改数据时,操作系统会重新拷贝一份需要的数据在物理内存中,并将数据的物理内存写入页表的右侧,从而构建页表映射关系。
在这里插入图片描述
写时拷贝的作用:
1:写时拷贝实际上可以看作是一种延时申请策略,它能够再cpu进行访问时才申请物理内存进行写入需要的数据和代码,进而提高了内存的利用率。
2:父子进程读取的代码共用,要修改的数据重新再拷贝一份,父子进程互不干扰,进而父子进程得以彻底分离,这也是进程独立性的体现。

进程终止

进程退出场景

1:代码运行完毕,结果正确。

2:代码运行完毕,结果不正确。

3:代码异常终止。

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

当进程运行完毕,代码运行结果正确与不正确时:
我们知道一个进程的创建,它的父进程就是当前bash。
运行hello运行程序:
在这里插入图片描述
当我们不知道自己的代码运行结果是否正确,我们可以通过对运行结果进行逻辑判断来确定函数代码的运行结果是否正确,进而得以设置main函数的返回值。在这里插入图片描述

echo $?                                                查看最近一次一个进程执行完毕的退出码。

此时的进程退出码为零表明代码运行完毕,结果正确。
在这里插入图片描述
通过strerror函数各种退出码的含义:
在这里插入图片描述

**在这里插入图片描述
测试:
一:当进程代码运行完毕时的退出码。
1:ls这个命令进程代码跑完了结果不正确和退出码表达的含义相同。:
在这里插入图片描述
2:又比如:当我打开一个文件失败的时候,对代码运行结果做出判断,如果打开一个文件失败,就确定了退出码。
在这里插入图片描述
文件打开失败,文件退出码正好是一,用户就可以通过退出码来知道进程运行错误的原因。这便是退出码的意义。
在这里插入图片描述
二:当进程代码没有跑完,程序崩溃时:
如下文,当我们访问野指针时:
在这里插入图片描述
程序打印出三条字符串,然后出现段错误。后面的代码并没有被cpu执行。
程序崩溃的时候,退出码无意义。一般而言程序崩溃了,return语句并没有被执行。
在这里插入图片描述

总结:
main函数的返回意义是返回给父进程,用来评判该进程的运行结果是否正确,可以忽略。
其中表示进程运行代码结果正确,非零表示进程代码运行结果错误,非零值有无数个,不同的非零值可以表示不同进程运行结果错误的原因,从而方便定位错误结果原因。

三大终止进程函数

当运行一个程序时:
在这里插入图片描述
运行效果如下:
当执行到return语句时便不会执行后面代码,退出码作为main函数的返回值,被父进程获取后,进而充当该进程的退出码,并且,只有main函数的return语句才是终止进程的。
在这里插入图片描述

exit 与return的区别:
相同点:exit 和 return 都可以终止进程。
不同点:
1: exit可以在任何地方调用都可终止该进程。
2: main函数在函数体调用代表调用结束,获取返回值,在main函数内代表终止该进程。

exit与_exit的区别:
exit会执行用户定义的清理函数,冲刷缓冲,关闭流等。
例如:
以下代码:
在这里插入图片描述当exit退出后,会自动冲刷缓冲区。
在这里插入图片描述

exit会直接结束进程,不会管理缓冲区。
在这里插入图片描述
进程直接退出。
在这里插入图片描述
特殊说明:

exit函数为应用层,缓冲区由应用层维护._exit函数为系统接口函数,不支持维护缓冲区。

进程等待(阻塞)

进程等待的必要性

父进程需要通过子进程的退出码进行分析子进程完成的效果,然后通过进程等待处理。
进程等待作用:
1:父进程通过进程等待获取子进程的退出码,进而获取子进程的退出结果,根据退出结果来做出不同的处理。
2:回收僵尸进程的资源。

进程等待的两种函数

一:wait函数(int*status)

作用: 1:只要子进程变成僵尸进程,父进程就会立马回收。

2:wait成功时返回子进程的PID,失败了就会返回-1. 3:输出型参数,获取子进程的退出码,不关心可设置NULL

例如:
以下代码为:子进程打印五次后退出变成僵尸进程,父进程sleep7秒后,执行wait语句后执行wait语句将子进程回收。
在这里插入图片描述
在这里插入图片描述
总结:
父进程调用wait函数时处于阻塞状态,此时父进程可能就在等待非CPU资源的某个等待队列中。当子进程退出时,由操作系统将父进程唤醒后放到运行队列中执行,调用wait取得返回值后继续执行剩下的代码。

二:waitpid函数(pid_t pid,int * status,int options)
说明:
返回值:
1:如果成功则返回被等待进程的pid。
2:如果设置了选项WNOHANG,而调用中发现没有已退出的子进程收集,则返回零。

例如:
复用wait代码,将wait(NULL)换成 waitpid( id,NULL,0) 效果等效。
在这里插入图片描述
运行结果如下:
在这里插入图片描述
总结:
1:如果子进程已经退出变成僵尸进程时,调用wait/wailpid 函数时,wait/waitpid:函数会立刻返回,并且释放资源,获得子进程退出信息。
2:如果在任意时刻调用wait和waitpid函数,子进程存在并且正常运行,则父进程可能会一直处于阻塞状态。
3:如果存在该子进程,则会立即出错返回。

获取子进程参数status

说明:
status : 输出型参数,获取子进程的退出码。在使用时为指针类型,则变量status的地址就等于子进程退出码的地址,status相当于退出码的别名,NULL代表不关心status。
status的构成:
status并不是按照整数来整体使用的,而是按照比特位的打方式,将32个比特位进行化分,我们只学习低16位。
在这里插入图片描述

如何通过status获取子进程的退出码。

当我们将status向右移动8位,即左边通过符号数补1,0xFF = 0000 0000 1111 1111 相&就取得了目标子进程的退出码。

在这里插入图片描述
运行结果如下:
此时的status输出参数正好也为1,符合预期。
在这里插入图片描述

为什么不用全局变量记录子进程的退出码和退出信号?

因为去安居变量是用户层面上的,父进程读取时会发生写时拷贝,有可能不会拷贝这些全局变量数据。

总结:
status能够拿到子进程的推出啊,父进程通过子进程的退出码>0或者==0判断子进程是否运行成功,当子进程运行结果错误时,父进程又能够通过退出码查找对应错误的原因,从而告知用户。

崩溃的本质

本质:
操作系统杀掉了该进程。

操作系统如何杀掉该进程的呢?

本质上是通过信号的方式。
当被信号所杀时,status的最低7个比特位就储存着子进程收到的终止信号。
在这里插入图片描述
获取kull信号编号:
status&0xff
在这里插入图片描述
运行结果如下:
可见子进程kill编号为零,代表子进程正常退出。
在这里插入图片描述
当我们选择让子进程崩溃时:
在这里插入图片描述
运行结果如下:
此时该进程崩溃了,此时的退出码也毫无意义。

在这里插入图片描述

使用WAITSTATUS和WIFEXITED宏来获取子进程的退出码

WIFEXITED:如果子进程为正常终止,则为真,否则为假。 (查看进程是否正常退出。)
WEXITEDSTATUS:如果WIFEXITED为非零,提取子进程退出码。 (查看进程的退出码)

在这里插入图片描述
此时如果使用kill - 9 命令去终止子进程时,通过脚本命令

while :; do ps axj | head -1 && ps axj | grep yzh | grep -v grep;sleep 1;done

可见如果子进程异常退出,那么它的退出码为零。(其实进程异常退出时的退出码也没什么意义)。
在这里插入图片描述

进程等待(非阻塞)

我们知道在waitpid( pid _t pid , int* status ,int options)中。

options: 默认为零,代表阻塞等待,WNOHANG宏默认为1,代表非阻塞等待。

阻塞等待:表明在父进程阻塞在队列中,等待子进程退出。

非阻塞等待:如果父进程检测子进程的退出状态,如果子进程还没有退出,则调用waitpid接口,立马返回,返回值为零。

在这里插入图片描述

测试:
我们让子进程执行5秒后退出,父进程时使用非阻塞等待子进程。
在这里插入图片描述
运行结果如下:
当子进程未退出时,即父进程未获取子进程的相关退出信息时,父进程会采用轮询检测方案,不断获取子进程获取信息,如果子进程依旧不退出,则父进程会以旧循环执行wait以后的代码,直到子进程退出。
在这里插入图片描述

进程替换

替换原理

我们当父进程用fork()函数创建子进程后,执行的适合父进程相同的代码和数据(也有可能执行不同的代码分支),如果我们想让子进程执行不同的程序,子进程往往要调用一种exec函数来执行另外一个程序。当进程调用一种exec等函数时,该进程的用户空间代码和数据完全被新程序所替换(此时旧的物理内存可能被释放),并从页表中重新构建映射关系,从新程序重新开始启动。调用exec中并不会创建新的内核数据结构,所以调用exec前后该进程的pid并未改变。

主要过程简图如下:

在这里插入图片描述

进程替换函数

一:execl函数
在这里插入图片描述

path: 找到程序: 路径 + 目标文件名。

char*arg,… :可变参数列表 :
可以传入多个不定个数的参数,这些字符串类型的参数一个个传入到可执行程序中,且最后一个参数必须以NULL结尾,表示参数传递完毕。

例如:
使用excel函数进行进程替换。
在这里插入图片描述
运行结果如下:
执行出一个上一个程序的printf。但是并没有执行出第二个printf。execl成功执行新的程序。
在这里插入图片描述

如果输入一个打不存在的命令地址,则进程替换失败:
在这里插入图片描述
运行结果如下:
execl进程替换失败后的返回值为1。
在这里插入图片描述

总结:
1:execl是程序替换,调用该函数成功之后,会将当前进程的所有代码和数据都进行替换,上一个程序的后续代码,全都不会执行。

2:不用对替换成功进行判断,因为替换成功后execl本身,包括他对应的返回值全部被新的程序替换了,并对这个新程序重新开始执行,所以exec函数调用成功后看起来是没有返回值的。只要执行了exit(1)就说明调用失败。

二:execv函数
在这里插入图片描述

char* argv[] :
指向一个个字符串的指针数组,将命令行参数的地址一个个传入到指针数组中,最后以NULL结尾,再将数组地址传入到接口之中。

例如:
我们复用一下execl测试代码:
在这里插入图片描述
运行结果如下:
execv与execl函数只有传参形式不同,本质上是相同的。
在这里插入图片描述
三:execlp函数
在这里插入图片描述

我们能通过环境变量PATH中,不带路径直接给程序名就能找到程序。
所以进程替换中,带p的含义指:操作系统会自己在环境变量PATH中进行查找相符合的搜索路径,我们不用告诉操作系统的程序在哪里,只需要告诉文件名。execlp函数中的其他函数就表示可执行程序传递给main函数命令行参数来实现目标程序的不同子功能。

**简单来说:**第一个参数决定用户执行谁,第二个参数决定了用户要如何去执行这个程序。

例如:
复用以上代码
在这里插入图片描述
运行结果如下;
我们所传的命令行参数会以字符串的形式传递给可执行程序的mian函数中的环境变量。
在这里插入图片描述
总结:

execlp()函数会从PATH 环境变量所指的目录中查找符合参数file的文件名, 找到后便执行该文件, 然后将第二个以后的参数当做该文件的argv[0]、argv[1]……, 最后一个参数必须用空指针(NULL)作结束在这里插入图片描述

四:execvp函数
在这里插入图片描述
示例:
复用execv函数代码:
在这里插入图片描述
运行结果如下
execv 和execvp两个函数传参形式是相同的,不同的是:一个通过程序地址查找程序,一个通过环境变量(只需要填写文件名)查找程序。
在这里插入图片描述
五:execle函数
execle函数相比较execl函数多了一个e,就说明其多了一个参数环境变量,这里我们会在两个程序调用中用到并具体讲解。
在这里插入图片描述

写时拷贝与进程替换

我们知道,加载新程序之前,父子进程的代码共享,数据写时拷贝,那么当子进程加载新程序的时候,也是一种“写入”,代码要进行写时拷贝,将新程序的代码和数据拷贝到物理内存中,这样父子进程代码和数据就彻底分开了,它们之间互不影响,更加体现了进程的独立性。

如何让一个程序去执行另一个程序?

Mafile多文件编译

当我们使用Makefile编译多文件时,我们可以使用ALL命令来指定需要生成的目标文件。通常我们也需要给ALL命令设置伪目标。

例如;
w

exec.e程序执行mycmd程序

我们只需要使用execle函数将mycmd程序的地址传入子进程就可以通过进程替换执行到这个程序。
并且此时我们也可以通过execle函数让子进程继承父进程的环境变量给新的程序。

一: exec.c程序:
在这里插入图片描述

2:mycmd程序:
根据子进程传递的命令行参数来实现mycmd程序的不同子功能。
并且调用getenv函数查看由子进程继承自父进程的环境变量。
在这里插入图片描述

使用make指令同时编译两个文件。
在这里插入图片描述
运行结果如下:
1:成功实现mycmd程序的输出a的功能。
2:输出环境变量yzh。
在这里插入图片描述
加粗样式总结:
环境变量是可以被子进程几次横的的,当子进程待用execle函数时,父进程的环境变量env也可以被子进程继承,继而在进程替换时传递给新的程序。

进程函数大总结

对于所有关于进程替换的函数中,多了字母便有了命令行参数传递的形式与功能不同。
1:带v将命令行参数以指针数组的方式传递,最后只用传入这个指针数组的地址。
2:带e的函数指可以将父进程的环境变量传给子进程。
3:带p的指不用传递目标程序地址,通过环境变量找到目标程序。

制定一个简易shell

原理:
通过让子进程执行传递参数执行程序,父进程等待&&解析命令。
创建子进程的原因:
因为进程的独立性,子进程出错而不会影响父进程的继续运行=。

一:打印提示符

打印时不能使用\n,应为不符合shell打印标准,但是如果步使用\n的话,打印的数据会储存在缓冲区中,所以我们可以尝试使用fflush函数快速将缓冲区里面的数据显现。

在这里插入图片描述

二:获取用户的键盘输入

1:创建一个数组作为缓冲区。
2:如果输入错误,那么则要终止次循环,重新输入命令行参数。
在这里插入图片描述
但是当我们输入命令行参数时,我们会将\n输入进去,此时命令行参数字符串便多了’\n",这样会造成程序错误。
对此我们可以再下一步步骤之前将\n去掉。可以访问该\n将它设为’\n’.在这里插入图片描述

三:对连续的字符串进行解析为多个子字符串

我们可以尝试使用strtok函数,找到分割符" ",并用’\0’分开,有字符串分割时返回该子字符串的首地址,没有字符串分割时即返回NULL。
但是使用第二次时记得将目标字符串地址设为NULL。
在这里插入图片描述

四:父进程创建子进程并执行进程等待

父进程fork()创建子进程,并让子进程执行进程替换的新程序,让父进程等待子进程,并获取退出码。
在这里插入图片描述

五:简易shell的目录执行问题——内置命令

此时,我们便可以执行简单的shell命令了,但是当我们执行cd…关于目录问题时,我们发现当前进程路径却并没有改变!
在这里插入图片描述
原因:
当我们执行命令行参数cd…时,其实是让子进程通过进程替换执行,此时cd…便是让子进程回到上一层目录,但是子进程执行完便退出了,此时父进程的路径并没有发生改变,所以根本毫无意义。
所以如果是有关cd命令我们不能去让子进程去执行,而是让父进程去执行该命令,我们也称之为——内置命令。

所以:我们可以在解析完参数命令时便检查第一个参数是否cd命令,如果是则通过chdir函数去执行。
在这里插入图片描述

六:给ls命令显现的文件名添加颜色

我们知道Linux中ls命令,命令行参数中其实包含了"–color == auto",其中命令行参数第一个位置被其本身占据了,所以我们可以在完全解析命令行参数字符串前便将"–color ==auto" 放入命令行参数数组第二个位置中。
在这里插入图片描述

简易shell完整代码

在这里插入图片描述

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

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

相关文章

数字IC设计 - 逻辑综合简介与Design Compiler使用(GUI方式)

逻辑综合 定义 逻辑综合就是将前端设计工程师编写的RTL代码,映射到特定的工艺库上,通过添加约束信息,对RTL代码进行逻辑优化,形成门级网表。约束信息包括时序约束,线载模型约束,面积约束,功耗…

我的Mysql突然挂了(Communications link failure)

在一个风和日丽的下午,我照常继续做着我的项目,今天的主题是一个涉及多表的分页查询 老复杂了!写了半天才搞好。当我满怀期待运行项目,进入页面后发现登陆后台却怎么也登陆不上,吓得我连忙回去查看后台日志&#xff0…

互联网快讯:天猫好房正式入驻六安;搜狗又一业务关停

国内要闻 搜狗又一业务关停:搜狗科学百科将于11月11日正式停止服务与运营; 提振生产效能、促进研发创新,smart品牌获逾80亿元银团综合授信; 微博联合淘宝联盟推出“天猫双11”特惠政策:将免除原15%佣金;…

Linux设置终端的个数(tty的个数)。

1.什么是tty? 就是终端设备,比如终端1叫做tty1,终端2,就叫做tty2,以此类推。 官方解释: 在Linux中,TTY也许是跟终端有关系的最为混乱的术语。TTY是TeleTYpe的一个老缩写。Teletypes&#xff…

机器人轨迹规划:On-Line Trajectory Generation in Robotic System关于机器人运动控制的介绍翻译

文章目录写在前面机器人运动控制路径规划与轨迹跟踪基于传感器制导的机器人运动控制这本书的语言问题的制定和动机定义:“sensor-guarded”机器人运动控制游走:人类的神经生理系统On-Line这本书的概要参考文献写在前面 致敬大佬! 毫无疑问&a…

SuperMap GIS基础软件天地图服务QA

目录 一、天地图有哪些类型? 二、国家天地图提供哪些服务? 三、使用前你应该知道的天地图知识 1.天地图服务协议 2.天地图相关参数 3.如何申请天地图key 4.天地图瓦片预览 四、天地图在SuperMap产品中的使用方式 1.iDesktop&iDesktopX 2.iServer 3.i…

【CMU15-445数据库】bustub 项目介绍及环境配置

开新坑啦 突然想起来之前一直想做的 CMU 15-445 课程的 2022 Fall 学期开课了,所以决定把 Pintos 项目先放一放,开个新坑跟着 CMU 同步把这个项目做了。 课程网站:CMU 15-445/645 (FALL 2022) 老规矩课程内容就不说了,课程网站…

第十三届蓝桥杯C++B组国赛H题——机房 (AC)

目录1.机房1.问题描述2.输入格式3.输出格式4.样例输入5.样例说明6.数据范围7.原题链接2.解题思路3.Ac_codetarjan倍增LCA1.机房 1.问题描述 这天, 小明在机房学习。 他发现机房里一共有 nnn 台电脑, 编号为 1 到 nnn, 电脑和电脑之间有网线连 接, 一共有 n−1n-1n−1 根网线…

【Linux】基本的指令(三)

大家好我是沐曦希💕 Linux专栏:Linux零基础学习 文章目录1.时间相关的指令2.Cal指令3.find指令:(非常重要)-name4.grep指令5.zip/unzip指令6.写在最后1.时间相关的指令 date显示 date 指定格式显示时间: date %Y:%m:%d date 用法: date [OPT…

基于javaweb的oa办公管理系统(java+layui+ssm+mysql+jsp+html)

基于javaweb的oa办公管理系统(javalayuissmmysqljsphtml) 运行环境 Java≥8、MySQL≥5.7、Tomcat≥8 开发工具 eclipse/idea/myeclipse/sts等均可配置运行 适用 课程设计,大作业,毕业设计,项目练习,学习演示等 功能说明 基…

迅为iMX6ULL开发板NXP嵌入式ARM核心板Linux系统i.MX6ULL超STM32

核心板参数 尺寸: 38mm*42mm PCB: 6层 CPU: iMX6ULL ARM Cortex-A7架构 单核 iMX6ULL 商业级: 内存:512M 存储:8G EMMC iMX6ULL 工业级: 内存:256M 存储:512M FL…

python正态分布中的normal函数

python正态分布中的normal函数 概念 1、正态分布又名高斯分布,是人们最常用的描述连续型随机变量的概率分布。 在金融学研究中,收益率等变量的分布假定为正态分布或者对数正态分布(取对数后服从正态分布)。因为形状的原因,正态分布曲线也被…

Mysql实战调优拾遗三

Mysql实战调优拾遗三优化小细节(续)索引监控查询优化查询慢的原因优化数据访问执行过程的优化查询缓存语法解析和预处理查询优化器优化器的优化策略优化器的优化类型关联与排序优化优化特定类型的查询优化count查询优化关联查询优化子查询优化group by 和…

DOS 命令

前提:打开命令行,winr打开窗口输入cmd回车 1、如何操作DOS命令 建议:初学者在虚拟机中完成实验!!! 开始 --- 运行 --- 输入cmd --- 回车,将调出C:\windows\system32\cmd.exe 或者 win R --- 运…

BH1750( GY-302 )光照传感器

文章目录一、产品简介二、IIC通信三、BH1750的使用四、程序源码这里我先简单的介绍一下BH1750光照传感器模块的基本信息(不多废话),我将着重讲解它的使用部分,相信对于屏幕前的你也是更关心它是怎么使用的,OK,gogogo!&…

2019第一届长安杯

检材一 案情简介 在一起电诈案件中,受害者称自己的银行卡被他人冒用,曾收到假冒公安的短信,因为 自己在一个 P2P 网站中理财,假冒公安称该网站已被列外非法网站,要自己到公安备案网站 填写自己的信息,并…

基于javaweb的crm客户关系管理系统(java+springboot+mysql)

基于javaweb的crm客户关系管理系统(javaspringbootmysql) 运行环境 Java≥8、MySQL≥5.7 开发工具 eclipse/idea/myeclipse/sts等均可配置运行 适用 课程设计,大作业,毕业设计,项目练习,学习演示等 功能说明 基于javawebsp…

云扩研习社 | RPA高级开发技巧(上)

高级元素选择技巧 XPath介绍 XPath是标准的结构化查询语言,内置丰富的语法、高阶函数可以提供非常强大的目标元素特征描述能力。 XPath是一种强大、复杂的查询语言;XPath与编辑器中内置的选择器没有本质区别,均可作为元素特征描述&#xff…

毕业设计 基于51单片机老人防跌倒GSM短信报警系统

基于51单片机无线蓝牙APP控LED灯亮灭亮度设计1、项目简介1.1 系统构成1.2 系统功能2、部分电路设计2.1 GSM SIM800A模块电路设计2.2 蜂鸣器报警电路(低电平有效)设计2.3 ADXL345倾角传感器模块电路设计2.4 DS18B20温度传感器模块电路设计3、部分代码展示…

面试官问我 “A + B” 算法,我懵了

🎈 作者:Linux猿 🎈 简介:CSDN博客专家🏆,华为云享专家🏆,Linux、C/C、云计算、物联网、面试、刷题、算法尽管咨询我,关注我,有问题私聊! &…