Linux基于CentOS学习【进程状态】【进程优先级】【调度与切换】【进程挂起】【进程饥饿】
目录
进程状态
状态决定了什么
进程等待方式——队列
进程状态的表现
挂起状态
基于阻塞的挂起——阻塞挂起
swap分区
进程状态表示
Z僵尸状态
进程的优先级
什么是进程的优先级
为什么会有进程的优先级
进程饥饿
Linux的调度与切换
切换
调度
queue [ 140 ]:优先级
bitmap [ 5 ] :解决效率
*active & *expried:解决饥饿问题
阻塞和等待CPU运行
进程状态
状态决定了什么
状态决定了后续动作,如果是运行状态,那么后续就意味着可以随时被调度运行,如果是阻塞状态那么意味着要等待某种资源
阻塞状态举例:在编写代码的时候,我们会经常写scanf 或者是cin 当运行时,计算机就会在那里等待我们的输入,此刻就是阻塞状态的最好体现,它就是在等待一种资源,它将进程卡在那里,不会被CPU执行,主要是当前要等待键盘资源就绪,等待数据输入进去。它会将这个程序的PCB从运行状态改为阻塞状态,并且将其从运行队列中拿下来,放到等待队列中去
为什么CPU不往后执行呢?因为当前代码没有将scanf 或者 cin执行完,程序无法执行下去
进程等待方式——队列
不仅仅是CPU有队列,我们底层对应的网络设备或者一般的IO设备等,都有自己的队列
所以,当我们对这些设备进行访问的时候
1,OS根据描述的硬件数据结构的对象,以及对象的状态,亦或是通过一些数字,我们可以得知底层的设备的一些状态
2, 我们对应的每种硬件状态所对应的描述结构体,可以帮助我们找到该设备等待的进程,方便我们随时唤醒
3,OS作为硬件的管理者,对硬件当前的状态是非常清楚的,一旦硬件准备就绪,OS会将等待该资源的进程重新唤醒,所谓的唤醒,本质上就是将阻塞状态改为运行状态,并且投入到运行队列里。
让进程状态变更,本质上就是把进程放到不同的队列里
进程状态的表现
所谓进程状态,本质就是一个整型变量,就是task_struct中的一个整型变量,它表示状态的值
比如,通过返回不同的整型来反映它是啥状态的
#define New 1
#define ready 2
#define running 3
#define block 4struct task_struct
{...int status; ...
}
一个CPU维护一个运行队列,主要在运行队列里,它的状态都可以被称为运行状态
硬件的就绪状态只有OS最清楚,因为OS是硬件的管理者
当我们的进程在等待软硬件资源的时候,资源如果没有准备就绪,我们的task_struct只能
1,将自己设置为阻塞状态
2,将自己的PCB链入等待队列
状态的变迁引起的是PCB被OS搬到不同的队列里
挂起状态
基于阻塞的挂起——阻塞挂起
把进程的管理转换为对某种数据结构的增删查改,每一个进程都有匹配的代码和数据
如果当前等待的外设在短期之内不会就绪,那么它就会在阻塞状态里进行等待,但当OS压力特别大,整机或者CPU,内存所对应的剩余资源变得越来越少,那么资源现在严重不足了,不能够分配,对于计算机讲,将面临着大量的操作失败,就不能向上提供良好的运行环境,比如打游戏直接卡成PPT
对于OS,要么直接崩溃,要么需要想办法挤压资源,OS需要对整个系统内进行重新安排,阻塞进程就是浪费的其中一部分,阻塞进程一又还没有等到资源,二又站着OS分配的管理资源,本来我就资源紧张了,你等待队列里还占着那么多不干活的task_struct
所以在资源紧张的时候,会将task_struct对于的代码和数据换出,但是不会将task_struct换出,换出了就管理不了了,如果你将task_struct都赶出去了,谁还知道你有这个进程
进程 = 内核PCB对象 + 可执行程序(代码和数据)
swap分区
磁盘有一个固定区域,是专门供给当OS资源紧张的时候,OS能够将一些不是特别紧要的资源进行换入和换出,即swap分区
当OS压力最大的时候,就会将一些文件啊,进程啊,代码数据啊,传到磁盘的swap区,这个工作叫换出
等到顶住了这波压力,且想调度了的时候,我们就将swap区的资源换回来,这个工作就叫做换入
所以当我们的进程它所对应的代码和数据不在内存中的时候,此时就称该进程为挂起状态,这种叫做阻塞挂起
创建进程时,实现创建PCB呢,还是先加载代码和数据到内存呢?
答案是先创建内核数据结构PCB,也就是先把管理字段创建出来,OS才知道你有这个进程,数据代码还可以晚点换入
swap分区一般不会特别大,因为我们整体的换出,本质是个把数据拷贝到外设的过程,换入本质是将数据拷贝到内存里。如果太大的话,会加深内存与磁盘的频率,从而引发IO的频率升高,无效操作就会变多
进程状态表示
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 */
};

S状态可称为阻塞状态,也可称为可中断睡眠,浅度睡眠
D状态可称为不可中断睡眠,深度睡眠
操作系统杀不死D状态的进程,但是S状态可以被杀,但是一旦出现系统级别的D状态,也说明系统已经很紧要了
D状态也是阻塞状态
T状态,暂停状态
kill -l 查看信号
其中,9号信号就是平常所使用的杀进程信号
19号信号是让这个进程暂停
再查看一下
一般我们的进程会在什么时候处在暂停状态呢
进程读取某一设备时,但是设备又不允许给你读,OS又不好杀掉,就让其处于暂停状态
恢复暂停状态,18号信号,就是恢复
恢复之后 ,CTRL+c是杀不掉的,因为在暂停的时候,进程就转移到后台了,恢复了也还是后台,只能用信号9来杀掉
t状态,也是属于等待某种资源
暂停状态可不可以也认为是阻塞状态?
可以,因为都是在等待某种资源
Z僵尸状态
我们在执行完一个进程的时候,对于进程里所对应的代码和数据,可以直接被释放,但是进程所对应的PCB不应该立即就被释放掉,这样做是因为为了系统或者其它进程能够读取到子进程或者这个进程的退出数据。
只有将这些数据拿走后,才是真正的死亡状态,才能释放PCB
我们将一个进程执行完毕,但是并没有获取进程退出的相关数据,此时就叫做Z状态
一个父子进程,如果子进程退了,那么父进程就一定要去读取子进程的状态,如果不读取,此时子进程的PCB就不释放,此刻就是Z状态
1 #include<stdio.h>2 #include<unistd.h>3 #include<stdlib.h>4 int main(){5 pid_t id = fork();6 if(id == 0){7 //child8 int cnt = 5;9 while(cnt){10 printf("I am child, pid :%d ,ppid : %d\n",getpid(),getppid());11 sleep(1);12 cnt--; 13 }14 exit(0); //让子进程直接退出15 }16 17 //father18 while(1){19 printf("I am father, pid :%d ,ppid : %d\n",getpid(),getppid());20 sleep(1);21 }}
Q:为什么要有Z状态?
A:创建进程是希望这个进程给用户完成工作的,子进程必须要有结果数据,这些在PCB中
Q:什么是Z:
A:进程已经退出,但是当前的进程状态需要自己维持住,供上层读取,就必须处于Z状态
Q:如果我作为父进程不读取呢?
A:就会带来这个僵尸状态的进程一直存在,task_struct对象就要一直存在,这些就要占据内存空间,一直不释放最终导致的问题就是内存泄漏
所以所有的进程在退出的时候,都必须先处于Z状态,被人状态读取完后才会变成X,最后完全退出
Q: 父进程比子进程先挂,子进程变成僵尸的时候会由谁来收?
A: 不会出现这种情况,如果父进程先挂掉的话,其子进程就会被1号进程所领养,1号进程就是操作系统,被领养进程就会被称为孤儿进程,但是孤儿进程可以有子进程的
进程的优先级
什么是进程的优先级
进程要访问某种资源,进程进行通过一定的方式,比如排队,来确认享受资源的先后顺序
优先级和权限的区别:权限决定的是能不能做的问题,而优先级决定的是谁先的问题
为什么会有进程的优先级
资源过少带来的,但是资源过少是属于一个相对概念,每一个进程都要有它的运行队列,CPU又只有一个,所以谁先跑要排队
Linux的默认优先级是80,优先级可以修改,优先级的范围是 [60,99],总共40个
数字越小,优先级越高!但不建议调整优先级
Linux系统允许用户调整优先级,但是不能直接让你修改PRI,而是修改nice值,nice值不是优先值,而是进程优先值的修正数据 !
pri = pri(old) + nice;
pri (old)每次都是从80开始,在Linux的内核里,它会做判断,如果你的nice值超过系统给它设置的最大值,比如我突然100上去,它也不会取100,而是取19,从而到达99,反之nice值的最小值是20,不会说你取-100,就给你干到-20去
进程饥饿
为什么Linux的优先级的调整是会受到限制?
不加限制的话,就会将自己的进程优先级调整得非常高,别人的优先级就会调成非常低
优先级较高的进程,优先得到资源,其后又会源源不断地产生新的进程,最后导致的结果就是常规进程很难享受到优先级的资源,从而导致进程饥饿问题
好比你正常排队,却老是有人插队,最终饿的却是正常人
所以操作系统一般都会有分时操作系统,调度上,它能够较为公平的进行调度,且会对调度范围进行严格的限制
Linux的调度与切换
Q:进程在运行的时候,放在CPU上,是直接必须把进程代码跑完才行吗?
A:答案是明显不行的,比如是死循环,操作系统是绝对不允许让死循环一直跑下去的
现代操作系统,都是基于时间片进行轮换执行的,防止恶性竞争
竞争性 : 系统进程数目众多,而CPU 资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高 效完成任务,更合理竞争相关资源,便具有了优先级独立性 : 多进程运行,需要独享各种资源,多进程运行期间互不干扰并行 : 多个进程在多个 CPU 下分别,同时进行运行,这称之为并行并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发,非常非常非常高频
CPU里面有大量的寄存器,eip寄存器里保存最经典的就是PC指针
当一个进程在CPU上运行时,CPU上的所有寄存器就要围绕着这个进程来进行展开运算,换句话说,这个进程在CPU运行期间,进程会在CPU内部形成大量的临时数据,这些临时数据放在CPU的寄存器中
切换
所有的保存都是为了恢复,所有的恢复都是为了上一次的运行
进程在CPU的各种数据,当时间片到了,需要我们的进程进行切换的时候我,我们首先要做的就是将CPU上运行的数据进行保存,保存的地点就是该进程的PCB里,一般就是存储在PCB里的tss_struct的结构里
CPU内部的所有临时数据,我们叫做进程的硬件上下文
进程进行保护硬件上下文的工作称为保护上下文
当进程被二次调度的时候(这里的二次调度并不是说被调了两次,而是可以指两次,三次,多次),我们的进程被放到CPU上运行的时候,首先要做的就是,要将曾经保存的硬件上下文进行恢复
所以进程进行切换工作的时候,一定会做的两个工作
一个就是保护前者的硬件上下文,第二个就是恢复后者的硬件上下文
CPU内的寄存器只有一套,寄存器内部保存的数据可以有多套
那我们区分一下,寄存器和寄存器内容
虽然寄存器的数据放在了一个共享的CPU设备里面,但是所有的数据其实都是被进程私有的,CPU内的任意时刻的数据都只是属于某一进程
寄存器不等于寄存器的内容!!!
调度
Linux实现进程调度的算法,要考虑优先级,考虑饥饿,考虑效率
queue [ 140 ]:优先级
在runqueue(运行队列)里,我们有两个queue[140],它们都是属于数组指针,为什么是140呢?
它的0~99是不使用的,只用100~139
接下来需要配合的就是Linux的默认优先级是80,优先级可以修改,优先级的范围是 [60,99],总共40个
好比如果你的优先级是60,它会根据Linux的调度算法,进行运算,将你放到 [ 100 ] 位置上,所以在一定程度上,这个队列可以映射优先级的先后顺序
每次运行之前,其会对着140进行遍历,为空则下一个,不为NULL则运行这个队列的优先级队列
我们可以看出,我们运行队列有140个,但真正需要检测的只有40个,难道我们每次都要多检测100个吗,显然不是的,我们会用到runqueue结构中的另一个数组,bitmap[ 5 ]
bitmap [ 5 ] :解决效率
bitmap 是int 类型的,总共有5个单位,共5*32 = 160个比特位,其每个比特位有各自的含义
比特位的位置表示哪一个队列,比特位的内容表示队列是否为空,我有160个比特位,而运行队列只有140个单元,比特位比它还多20个,我那20个就不用了,其中有队列的位置,比特位标1,没有队列的位置,比特位标0
如此,就转换为了检测比特位就可,因为位操作会比遍历操作快得多
检查二进制中有多少个1
调度算法时间复杂度为O(1)
优先级解决了,效率解决了,那我饥饿问题咋办
也就是说,每次我都把我的优先级调成60,并放入到queue[140]队列的最前端,那我后面的进程岂不是要一直饿着?
解决办法就是:优先级数组不只是只有一个,其实还有一个,前面的那个queue [140] 是活跃队列,另一个叫做过期队列,两个数组互不干扰,CPU调度进程调度的是活跃队列,OS存放进程存放的是过期队列,即便来了优先级更高的进程,其也只会在过期队列里等待
活跃队列queue[140] + bitmap[5] + nr_active 构成一个结构体
过期队列queue[140] + bitmap[5] + nr_active 构成一个结构体
struct q {nr_active;bitmap[5];queue[140];
}
两个结构体可以构成结构体数组
struct q array[2];
*active & *expried:解决饥饿问题
这是两个指针,刚开始struct q *active = &array[0] , struct q *expried = &array[1]
当array[0] 中的queue[140] 不断减少,直至为0的时候,且array[1] 里的内容不断增多的时候
就执行swap ( &active , &expried ) ,交换的时候,更改的是指针变量的内容
阻塞和等待CPU运行
这时候就会问,在运行队列中等待被调度的过程算不算等待资源,既然是等待资源的话,会不会就是阻塞状态,这样做不会矛盾吗
运行队列(runqueue)中的进程处于可运行状态,这意味着它们已经准备好被CPU执行,并且正在等待获取CPU资源。这些进程并不是在等待特定的资源,如I/O操作或页面置换等,而是在等待CPU时间。
当进程处于运行状态(TASK_RUNNING)时,它们在运行队列中,这意味着它们已经准备好运行,并且内核调度器将决定哪个进程将获得CPU时间。这些进程不是在等待资源,而是在等待CPU时间来执行。
如果一个进程需要等待某个资源(如磁盘I/O、网络响应或锁),它通常会被放置在等待队列(wait queue)中,而不是运行队列。等待队列是与特定资源或条件相关的队列,当资源变得可用或条件得到满足时,进程会被唤醒并可能被放入运行队列。
因此,运行队列中的进程是在等待CPU资源,而不是在等待其他类型的资源。
以上就是本博文的学习内容,如果有不正确的地方,还望各位大佬指点出来,谢谢阅读!
相关文章:

Linux基于CentOS学习【进程状态】【进程优先级】【调度与切换】【进程挂起】【进程饥饿】
目录 进程状态 状态决定了什么 进程等待方式——队列 进程状态的表现 挂起状态 基于阻塞的挂起——阻塞挂起 swap分区 进程状态表示 Z僵尸状态 进程的优先级 什么是进程的优先级 为什么会有进程的优先级 进程饥饿 Linux的调度与切换 切换 调度 queue [ 140 ]&am…...

Golang | Leetcode Golang题解之第456题132模式
题目: 题解: func find132pattern(nums []int) bool {candidateI, candidateJ : []int{-nums[0]}, []int{-nums[0]}for _, v : range nums[1:] {idxI : sort.SearchInts(candidateI, 1-v)idxJ : sort.SearchInts(candidateJ, -v)if idxI < idxJ {ret…...

回归预测|基于哈里斯鹰优化最小二乘支持向量机的数据回归预测Matlab程序HHO-LSSVM 多特征输入单输出含基础程序
回归预测|基于哈里斯鹰优化最小二乘支持向量机的数据回归预测Matlab程序HHO-LSSVM 多特征输入单输出含基础程序 文章目录 一、基本原理一、基本原理二、HHO-LSSVM的流程三、优缺点四、应用场景 二、实验结果三、核心代码四、代码获取五、总结 一、基本原理 HHO-LSSVM回归预测结…...

【Android 源码分析】Activity生命周期之onStop-1
忽然有一天,我想要做一件事:去代码中去验证那些曾经被“灌输”的理论。 – 服装…...

【Unity】本地化实现
个人向笔记。 1 前言 记录一下自己的本地化实现思路,暂时只讲本文的本地化实现。 2 文本本地化方案-个人 本地化实现是基于Luban的。自己使用Luban实现了一个“配置表模块”,又实现了一个“全局配置模块”,之后再基于这两个模块实现了“文本…...

Django一分钟:在Django中怎么存储树形结构的数据,DRF校验递归嵌套模型的替代方案
引言 在开发过程中我们可能需要这样的树形结构: [{"data": {"name": "牛奶"},"children": [{"data": {"name": "蒙牛"}, },{"data": {"name": "伊利"}, }]},{"da…...

【Docker从入门到进阶】06.常见问题与解决方案 07.总结与资源
6. 常见问题与解决方案 在使用Docker进行开发和部署过程中,可能会遇到各种问题。以下是一些常见问题及其解决方案: 容器启动失败和调试 在使用 Docker 时,容器启动失败或立即退出可能会导致一定的困扰,以下是进一步深入解决该问…...

快速排序的非递归实现:借助栈实现、借助队列实现
目录 用栈实现快速排序 1.用栈实现非递归快速排序的思路步骤 1.1.思路步骤 2.用栈实现非递归快速排序的代码 3.用栈实现非递归快速排序的整个工程 3.1.QuickSortNonR.h 3.2.QuickSortNonR.c 3.3.Stack.h 3.4.Stack.c 用队列实现非递归快速排序 1.用队列实现非递归快…...

Finops成本优化企业实践-可视化篇
引言:上一章讨论了finops的一些方法论,笔者在拿到finops官方认证finops-engineer certificate之后,将方法论运用到所在项目组中,并于今年完成了40%的费用节省。在此将这些实践方法总结沉淀,与大家分享。实践包括三篇&a…...

Spring Boot中线程池使用
说明:在一些场景,如导入数据,批量插入数据库,使用常规方法,需要等待较长时间,而使用线程池可以提高效率。本文介绍如何在Spring Boot中使用线程池来批量插入数据。 搭建环境 首先,创建一个Spr…...

Python机器学习:自然语言处理、计算机视觉与强化学习
📘 Python机器学习:自然语言处理、计算机视觉与强化学习 目录 ✨ 自然语言处理(NLP) 文本预处理:分词、去停用词词向量与文本分类:使用Word2Vec与BERT 🌆 计算机视觉基础 图像预处理与增强目标…...

Vue2 + ElementUI + axios + VueRouter入门
之前没有pc端开发基础,工作需要使用若依框架进行了一年的前端开发.最近看到一个视频框架一步步集成,感觉颇受启发,在此记录一下学习心得。视频链接:vue2element ui 快速入门 环境搭建和依赖安装 安装nodejs安装Vue Cli使用vue create proje…...

GO网络编程(四):海量用户通信系统2:登录功能核心【重难点】
目录 一、C/S详细通信流程图二、消息类型定义与json标签1. 消息类型定义2. JSON标签3.结构体示例及其 JSON 表示:4.完整代码与使用说明 三、客户端发送消息1. 连接到服务器2. 准备发送消息3. 创建 LoginMes 并序列化4. 将序列化后的数据嵌入消息结构5. 序列化整个 M…...

某项目实战分析代码二
某项目实战分析代码二 此次分析的是protobuf的使用操作流程具体实现 3. 业务数据分析3.1 客户端3.2 服务器端简单案例 此次分析的是protobuf的使用 Protocol Buffer( 简称 Protobuf) 是Google公司内部的混合语言数据标准,它是一种轻便高效的结构化数据存储格式&…...

全面指南:探索并实施解决Windows系统中“mfc140u.dll丢失”的解决方法
当你的电脑出现mfc140u.dll丢失的问题是什么情况呢?mfc140u.dll文件依赖了什么?mfc140u.dll丢失会导致电脑出现什么情况?今天这篇文章就和大家聊聊mfc140u.dll丢失的解决办法。希望能够有效的帮助你解决这问题。 哪些程序依赖mfc140u.dll文件…...

QT学习笔记1(QT和QT creator介绍)
QT学习笔记1(QT和QT creator介绍) Qt 是一个跨平台的应用开发框架,主要用于图形用户界面(GUI)应用的开发,但也支持非GUI程序的开发。Qt 支持多种平台,如Windows、macOS、Linux、iOS和Android&a…...

存储电话号码的数据类型,用 int 还是用 string?
在 Java 编程中,存储电话号码的选择可以通过两种常见方式进行:使用 int 类型或 String 类型。这种选择看似简单,但实际上涉及到 JVM 内部的字节码实现、内存优化、数据表示、以及潜在的可扩展性问题。 Java 基本数据类型与引用数据类型的差异…...

【目标检测】工程机械车辆数据集2690张4类VOC+YOLO格式
数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):2694 标注数量(xml文件个数):2694 标注数量(txt文件个数):2694 标注…...

target_link_libraries()
target_link_libraries() 是 CMake 中的一个命令,用于指定目标(如可执行文件或库)所依赖的其他库。其主要作用包括: 链接库:将指定的库链接到目标上,使目标能够调用这些库中的函数和使用其功能。 管理依赖…...

Javascript数组研究09_Array.prototype[Symbol.unscopables]
Symbol.unscopables 是 JavaScript 中一个相对较新的符号(Symbol),用于控制对象属性在 with 语句中的可见性。它主要用于内置对象,如 Array.prototype,以防止某些方法被引入到 with 语句的作用域中,避免潜在…...

SkyWalking 自定义链路追踪
对项目中的业务方法,实现链路追踪,方便我们排查问题 引入依赖 <!‐‐ SkyWalking 工具类 ‐‐> <dependency> <groupId>org.apache.skywalking</groupId> <artifactId>apm‐toolkit‐trace</artifactId> <vers…...

Linux驱动开发(速记版)--设备模型
第八十章 设备模型基本框架-kobject 和 kset 80.1 什么是设备模型 设备模型使Linux内核处理复杂设备更高效。 字符设备驱动适用于简单设备,但对于电源管理和热插拔,不够灵活。 设备模型允许开发人员以高级方式描述硬件及关系,提供API处理设备…...

动手学深度学习(李沐)PyTorch 第 6 章 卷积神经网络
李宏毅-卷积神经网络CNN 如果使用全连接层:第一层的weight就有3*10^7个 观察 1:检测模式不需要整张图像 很多重要的pattern只要看小范围即可 简化1:感受野 根据观察1 可以做第1个简化,卷积神经网络会设定一个区域,…...

新编英语语法教程
新编英语语法教程 1. 新编英语语法教程 (第 6 版) 学生用书1.1. 目录1.2. 电子课件 References A New English Grammar Coursebook 新编英语语法教程 (第 6 版) 学生用书新编英语语法教程 (第 6 版) 教师用书 1. 新编英语语法教程 (第 6 版) 学生用书 https://erp.sflep.cn/…...

Golang 服务器虚拟化应用案例
推荐学习文档 golang应用级os框架,欢迎stargolang应用级os框架使用案例,欢迎star案例:基于golang开发的一款超有个性的旅游计划app经历golang实战大纲golang优秀开发常用开源库汇总想学习更多golang知识,这里有免费的golang学习笔…...

Elasticsearch基础_4.ES搜索功能
文章目录 一、搜索辅助功能1.1、指定返回的字段1.2、结果计数1.3、结果分页 二、搜索匹配功能2.1、查询所有文档2.2、term级别查询2.2.1、term查询2.2.2、terms查询2.2.3、range查询2.2.4、exists查询 2.3、布尔查询2.3.1、must,should,must_not2.3.2、f…...

Elasticsearch要点简记
Elasticsearch要点简记 1、ES概述2、基础概念(1)索引、文档、字段(2)映射(3)DSL 3、架构原理4、索引字段的数据类型5、ES的三种分页方式(1)深度分页(fromsize)…...

【通信协议】IIC通信协议详解
IIC(Inter-Integrated Circuit)通信协议,又称为I2C(Inter-Integrated Circuit 2)协议,是一种广泛使用的串行通信协议。它由Philips Semiconductor(现NXP Semiconductors)开发&#x…...

2024年中国科技核心期刊目录(社会科学卷)
2024年中国科技核心期刊目录 (社会科学卷) 序号 期刊代码 期刊名称 1 SC02 JOURNAL OF S…...

用Python集成免费IP归属地查询API
IP查询的优势是什么? IP查询是一种强大的工具,能够快速提供关于IP地址的信息,如地理位置、互联网服务提供商(ISP)、连接类型等。这些数据在多种场景下都非常有用,帮助用户理解网络环境和用户行为。 首先&…...