《汇编语言》- 读书笔记 - 第15章-外中断
《汇编语言》- 读书笔记 - 第15章-外中断
- 15.1 接口芯片和端口
- 15.2 外中断信息
- 1. 可屏蔽中断(Maskable Interrupt)
- 2. 不可屏蔽中断(Non-Maskable Interrupt)
- 设计思想
- 15.3 PC 机键盘的处理过程
- 1. 键盘输入
- 2. 引发 9 号中断
- 3. 执行 int 9 中断例程
- 15.4 编写int9 中断例程
- 编程
- 思路
- 代码
- 检测点 15.1
- 15.5 安装新的 int 9 中断例程
- 分析
- 代码
- 运行效果
- 实验 15 安装新的 int9 中断例程
CPU
通过中断机制
得知外部
输入事件,并通过I/O接口
与数据总线
实现与外设的数据交换
,从而实现对外设输入的有效处理。
CPU 对外设输入的通常处理方法:
- 外设的输入送入端口;
- 向 CPU 发出外中断(可屏蔽中断)信息;
CPU
检测到可屏蔽中断信息;
3.1. 如果IF=1
,CPU 在执行完当前指令后响应中断,执行相应的中断例程;- 可在中断例程中实现对外设输入的处理。
端口和中断机制,是 CPU 进行 I/O 的基础。
15.1 接口芯片和端口
CPU
通过端口
和外部设备
进行联系。
15.2 外中断信息
外部设备
在完成一系列自己的工作后,需要将产生的数据
或状态
发给CPU
处理时,就会触发外中断
- 中断信息的发送过程大致如下:
-
中断请求信号:
外设芯片在适当时候会通过自身的中断输出引脚向主板上的中断控制器发送中断请求信号,这通常表现为一个电信号的变化。例如在8086体系结构中,外设可以通过INTR(Interrupt Request)线向CPU发出中断请求。 -
中断控制器:
主板上的中断控制器(如Intel 8259A可编程中断控制器)负责接收来自多个外设的中断请求,并根据它们的优先级和中断向量进行管理。中断控制器会将有效的中断请求打包并通过IRQ(Interrupt Request Line)线传输到CPU。 -
CPU响应中断:
当CPU在执行完当前指令后检查中断请求线时,如果发现有中断请求存在,它会暂停当前任务,保存现场,然后通过读取中断向量表(IVT)获取中断服务程序(ISR)的地址,跳转到相应的处理程序执行中断处理。 -
线路连接:
中断请求信号的传送是通过主板上的硬件连线完成的。例如在8086/8088系统中,外设与CPU间的中断请求线和中断响应线(INTA#)是主板上的物理连接线,它们属于系统总线的一部分,通过这些线路上的电信号变化来进行中断请求和响应的通讯。
注意,随着技术的发展,更现代的计算机系统可能采用了更为复杂和高级的中断控制器和总线标准,但核心原理仍然是外设通过特定线路发送中断信号给CPU,并由CPU依据一定的优先级策略来响应和处理中断。
1. 可屏蔽中断(Maskable Interrupt)
可屏蔽中断 | CPU
在收到可屏蔽中断
时会根据 CF
的状态来决定:执行完当前指令
后是否响应
。
IF 状态 | 响应 | 不响应 |
---|---|---|
IF = 1 | ✅ | |
IF = 0 | ✅ |
- 中断过程
可屏蔽中断
的中断过程
除了中断类型码是通过数据总线
从外部
传进CPU
的,其它与内中断相同。
我们可以通过指令(STI
, CLI
)设置IF
来控制是否响应可屏蔽中断
。
———— 指 令 ———— | — 功 能 — | 描述 |
---|---|---|
cli (Clear Interrupt Flag) | IF=0 禁止中断 | CPU 执行cli 将IF=0 后,仅保留对不可屏蔽中断(NMI) 的响应,直至遇到STI 指令为止。此操作有助于保护在执行重要且不能被打断的代码时,不被中断处理程序打乱。 例如: 在更改栈指针(SS:SP)时,需要防止因中断处理程序介入而破坏栈的状态。 |
sti (Set Interrupt Flag) | IF=1 允许中断 | CPU 执行sti 将IF=1 后,重新开启中断响应 功能。CPU会再次响应挂起 的中断请求,恢复正常的中断处理流程。这对于完成关键区域操作后恢复系统的正常中断响应至关重要。 |
- 在IF=0期间,中断请求不会丢失,但会被延迟处理,等待CPU重新打开中断响应后,按照中断控制器维护的队列顺序逐一处理。是否超时以及超时后的处理方法取决于设备本身的特性以及操作系统的调度策略。
硬件中断控制器(如Intel 8259A可编程中断控制器)负责管理和维持中断请求的排队。 CLI
与STI
结合运用,构建短暂的中断防护区域,确保其间代码免受外部中断影响,待关键操作完成后通过STI
恢复中断响应,保障系统并发处理能力和实时响应性能。在多任务OS中,内核常在切换进程环境、管控硬件资源等关键场景调用这两条指令。
2. 不可屏蔽中断(Non-Maskable Interrupt)
当不可屏蔽中断发生时,CPU会立即停止当前的执行流程,保存必要的上下文信息,然后跳转到预设的NMI处理程序去执行相应的处理代码。
即使在 CPU 执行 CLI 指令 禁止了可屏蔽中断 的情况下也不例外。
由于NMI的重要性,其处理过程一般要求快速、简洁,尽量减少对系统运行的影响。
- NMI触发的原因可能包括但不限于以下几种情况:
- 硬件故障:如电源故障、内存错误、严重硬件故障等;
- 系统调试需求:在调试环境中,开发者可能通过人工或硬件手段触发NMI,以便快速进入调试模式;
- 监控程序:某些嵌入式系统或服务器中,监控程序可能会通过NMI报告严重的系统错误或警告;
- 性能计数器溢出:某些高性能处理器中,性能监控单元(PMU)的计数器溢出可能导致NMI。
设计思想
可屏蔽中断:几乎都是外设触发的。告诉CPU来活了。
不可屏蔽中断:是发生了必须紧急处理的事件。CPU要先解决它了再说别的。
15.3 PC 机键盘的处理过程
1. 键盘输入
键盘上每个键分:按下
、松开
两种状态都会产生相应的扫描码
。
按下时的叫通码
,松开时的叫断码
。
它会被送入主板上的相关接口芯片的寄存器中,该寄存器的端口
地址为 60h
断码 = 通码 + 80h
比如: g 键的通码为 22h,断码为 a2h。
表 15.1 是键盘上部分键的扫描码,只列出通码。
2. 引发 9 号中断
60h
端口收到信号,相关的芯片就会向 CPU
发出中断类型码
为9
的可屏蔽中断
信息。
CPU
收到后,如果 IF=1
,则响应中断,引发中断过程
,转去执行 int 9
中断例程。
3. 执行 int 9 中断例程
BIOS 提供 int 9
中断例程,处理基本的键盘输入:
- 首先,中断例程通常会从60h端口读取扫描码。
- 处理扫描码
2.1. 若是字符键,则将扫描码转换为
ASCII码,存入
键盘缓冲区。 2.2. 若是
功能键或
控制键,则可能更新内存中的
键盘状态字节`或其他内部状态。 - 向键盘控制器发送应答信号,表明中断已被处理。
BIOS键盘缓冲区:
BIOS键盘缓冲区
是系统启动时BIOS设置的一块内存区域,用于存储15
个键盘输入事件,每个事件占用2字节
,其中高位
字节为扫描码
,低位
字节为字符码
。- 这个缓冲区通过
int 9
中断机制收集并暂存键盘输入,确保在操作系统尚未完全加载或正忙时也能记录用户的按键动作。
字单元(1) | 字单元(2) | … | 字单元(15) |
---|---|---|---|
高位-扫描码 | 高位-扫描码 | … | 高位-扫描码 |
高位-字符码 | 高位-字符码 | … | 高位-字符码 |
0040:17
单元存储键盘状态字节,该字节记录了控制键和切换键的状态。字节各位信息如下。
位 | 状态 |
---|---|
0 | 右 Shift 状态,置1表示按下右 Shift 键: |
1 | 左 Shitt 状态,置1表示按下左 Shit 键; |
2 | Ctrl 状态,置1表示按下 Ctrl 键; |
3 | Alt 状态,置1表示按下 Alt 键; |
4 | ScrollLock状态,置1表示 Scroll 指示灯亮; |
5 | NumLock 状态,置1表示小键盘输入的是数字; |
6 | CapsLock 状态,置1表示输入大写字母: |
7 | Insert 状态,置1表示处于删除态。 |
15.4 编写int9 中断例程
键盘输入的处理过程:
- 键盘产生扫描码;
- 扫描码送入
60h
端口; - 引发
9
号中断; - CPU 执行
int 9
中断例程处理键盘输入。
前三步都是硬件系统的活,我们能插手的就只有和4步。但因为 int 9
中断处理程序要与一些硬件细节打交到。所以我们只对 int 9
做下封装扩展。
编程
在屏幕中间依次显示“a”~“z”
,并可以让人看清。在显示的过程中,按下 Esc
键后,改变显示的颜色。
思路
- 先循环显示
a ~ z
。(为了看清,书上给的方案是空循环模拟延迟效果) - 自己写一个
int 9
代替原版。
2.1. 我们自己的int 9
中调用原版 int 9
。
2.2. 拿到原版int 9
返回的扫描码。
2.3. 判断如果是Esc
修改显存改变颜色
。
代码
assume cs:code
stack segmentdb 128 dup(0)
stack endsdata segmentoint9 dw 0,0
data endscode segmentstart: mov ax,stack ; 设置栈段和栈顶位置mov ss,axmov sp,128mov ax,data ; 设置数据段mov ds,axmov ax,0 ; 设置附加段mov es,ax; ------------ 保存原 int 9 中断列和入口到 ds:0, ds:2 ------------push es:[9*4]pop ds:[0]push es:[9*4+2]pop ds: [2]; ---------------- 将我们的新 int 9 写入中断向量表 ----------------mov word ptr es:[9*4],offset int9mov es:[9*4+2],cs; ------------------------- 显示 a 到 z -------------------------mov ax,0b800h ; 设置显存段mov es,axmov ah,'a' ; 要显示的字符串,从 a 开始s: mov es:[160*12+40*2],ah ; 显示字符call delay ; 调用子程序:延时inc ah ; 下一个字符cmp ah,'z' ; 如果不是z继续循环jna s; ------------- 将中断向量表中 int 9恢复为原来的地址 -------------mov ax,0mov es,axpush ds:[0]pop es:[9*4]push ds:[2]pop es:[9*4+2]mov ax,4c00hint 21h
; =======================================================
; --------------------- 子程序 delay -------------------
; 让CPU空循环,模拟延时效果
; -------------------------------------------------------
; 参数: 无
; 返回: 无
; -------------------------------------------------------
delay:push ax ; 备份寄存器push dxmov dx,2h ; 循环 2h 次,可以自己把握mov ax,0
delays: sub ax,1sbb dx,0 ; 10000000h 循环递减cmp ax,0 ; 直到 ax, dx 都为 0 才跳出循环jne delayscmp dx,0jne delayspop dx ; 还原寄存器pop axret ; 返回
; ---------------------- 子程序 delay -------------------
; =======================================================; =======================================================
; --------------------- 子程序 int 9 -------------------
; 调用原 int 9 获取扫描码,实现按 Esc 变色
; -------------------------------------------------------
; 参数: 无
; 返回: 无
; -------------------------------------------------------
int9:push ax ; 备份寄存器push dxpush esin al,60h ; 从60h端口读取数据pushfpushfpop bxand bh,11111100bpush bxpopfcall dword ptr ds:[0] ; 调用原来的 int 9 中断例程cmp al,1jne int9retmov ax,0b800hmov es,axinc byte ptr es:[160*12+40*2+1]int9ret:pop es ; 还原寄存器pop dxpop axiret ; 返回
; --------------------- 子程序 int 9 ------------------
; =======================================================
code ends
end start
检测点 15.1
《汇编语言》- 读书笔记 - 各章检测点归档 - 检测点 15.1
15.5 安装新的 int 9 中断例程
任务 | 安装一个新的 int9 中断例程。 |
---|---|
功能 | 在 DOS下,按F1键后改变当前屏幕的显示颜色,其他的键照常处理 |
分析
- 改变屏幕的显示颜色。
改变从B8000H
开始的4000
个字节中的所有奇
地址单元中的内容
mov ax,0b800hmov es axmov bx,1mov cx,2000
s: inc byte ptr es:[bx]add bx,2loop s
-
按键常规处理
直接调用原 int 9 -
原 int9 入口地址
不能保存在安装程序中。我们把它放在0:0200
。 -
安装新 int9 中断例程
0:0200 ~ 0:0203
用来保存原 int9 的地址了。
我们保存新 int9 时从0:0204
开始。
代码
assume cs:code
stack segmentdb 128 dup(0)
stack endscode segmentstart: mov ax,stack ; 设置栈段和栈顶位置mov ss,axmov sp,128; -------- 安装: 复制中断例程到目标内存 -------mov ax,cs ;设置 ds:si 指向源地址mov ds,axmov si,offset int9mov ax,0 ;设置 es:di 指向目的地址mov es,axmov di,204h mov cx,offset int9end-offset int9 ;设置 cx为传输长度cld ;设置传输方向为正。movsb中si,di递增rep movsb ;重复复制数据次数由 cx 控制; -------- 安装: 复制中断例程到目标内存 -------; ---------- 备份原 int9 入口到 [0:200~0203] ----------push es:[9*4]pop es:[200h]push es:[9*4+2]pop es:[202h]; ---------- 备份原 int9 入口到 [0:200~0203] ----------; ---------- 设置中断向量表 ----------cli ; 临时屏蔽中断mov word ptr es:[9*4],204h ; 设置的偏移地址(0~3用来存原int9地址了)mov word ptr es:[9*4+2],0 ; 设置的段地址sti ; 恢复中断; ---------- 设置中断向量表 ----------ok: mov ax,4c00hint 21h; =======================================================
; --------------------- 子程序 int 9 -------------------
; 调用原 int 9 获取扫描码,实现按 Esc 变色
; -------------------------------------------------------
; 参数: 无
; 返回: 无
; -------------------------------------------------------
int9:push ax ; 备份寄存器push bxpush cxpush esin al,60h ; 从60h端口读取数据; 模拟 int 指令,用 call 调用原 int 9pushf ; 进入中断后 IF、TF已经是0 直接入栈即可call dword ptr cs:[200h] ; 调用原来的 int 9 中断例程cmp al,3bh ; 判断是否 F1 键jne int9ret ; 如果不是直接结束mov ax,0b800h ; 设置显存mov es,axmov bx,1mov cx,2000s: inc byte ptr es:[bx]add bx,2loop sint9ret:pop es ; 还原寄存器pop cxpop bxpop axiret ; 返回
int9end:nop; --------------------- 子程序 int 9 ------------------
; =======================================================
code ends
end start
运行效果
实验 15 安装新的 int9 中断例程
《汇编语言》- 读书笔记 - 第15章-外中断-实验15 安装新的 int9 中断例程
相关文章:

《汇编语言》- 读书笔记 - 第15章-外中断
《汇编语言》- 读书笔记 - 第15章-外中断 15.1 接口芯片和端口15.2 外中断信息1. 可屏蔽中断(Maskable Interrupt)2. 不可屏蔽中断(Non-Maskable Interrupt)设计思想 15.3 PC 机键盘的处理过程1. 键盘输入2. 引发 9 号中断3. 执行…...

【Vue3】CSS 新特性
:slotted <template> <!-- App.vue--><Son ><div class"a">我要插入了</div></Son> </template><script setup lang"ts"> import Son from ./components/Son.vue </script><style></sty…...

四信水电站泄洪预警方案,精准提升防汛应急水平
2022年5月水利部办公厅发布《关于开展水库泄洪设施专项排查整改的紧急通知》,为坚决贯彻落实关于水库大坝安全的重要指示批示精神、关于保障水库泄洪通道通畅的批示要求,全力防范水库可能出现的重大险情,确保水库安全度汛。 2023年3月国家能源…...

k8s中容器的调度与创建:CRI,cgroup
container调度与创建 选自:K8s、CRI与container - packy的文章 - 知乎 https://zhuanlan.zhihu.com/p/102897620 Cgroup创建: cgexec -g cpu,memory:$UUID \ > unshare -uinpUrf --mount-proc \ > sh -c "/bin/hostname $UUID &…...

Unity安装与简单设置
安装网址:https://unity.cn 设置语言: 设置安装位置:否则C盘就会爆了 获取一个个人的资格证: 开始安装: 安装完毕。 添加模块:例如简体中文 新建项目: 布局2*3、单栏布局、 设置…...

数据库的介绍、分类、作用和特点
数据库是用来存储、管理和检索数据的集合系统。根据数据处理模型的不同,数据库可以分为多种类型,主要包括: 1、关系型数据库(RDBMS): 介绍:关系型数据库使用表格形式来存储数据,并通…...

【Unity】机器人末端执行器仿真
机械手臂的末端执行器使用多项式来计算转动角度可能有几个原因: 精确控制:机械臂的运动通常需要高度的精确性,特别是在精密工作或复杂运动轨迹的情况下。多项式,特别是高阶的,可以很好地近似复杂的非线性关系和运动轨迹…...

更换个人开发环境后,pycharm连接服务器报错Authentication failed
原因:服务器中更换个人开发环境后,密码变了。 解决:在pycharm中修改服务器开发环境密码即可。 1 找到Tools-Depolyment-Configuration 2 点击SSH Configuration后的省略号 3 修改这里面的Password即可...
E - Bad Juice
解题思路 由于最后返回一个01字符串表示所选人的状态要求人数最少,即字符串长度最少而要用最少的字符,找出则返回的字符为二进制下的编号这样利用了所有的01字符号人表示二进制下位的情况注意对于2的次方项,只需要有位,可以用位均…...

用HTML5的<canvas>元素实现刮刮乐游戏
用HTML5的<canvas>元素实现刮刮乐游戏 用HTML5的<canvas>元素实现刮刮乐,要求:将上面的“图层”的图像可用鼠标刮去,露出下面的“图层”的图像。 示例从简单到复杂。 简单示例 准备两张图像,我这…...
TypeScript + react 中 TypeScript 的加入后 , 有哪些优化项目
在使用 TypeScript 结合 React 进行开发时,TypeScript 提供了许多优化和增强代码质量的方式。以下是一些关键的优化操作和最佳实践: 强类型组件属性(Props)和状态(State): 使用接口或类型别名定义组件的 pr…...
Redis学习路径(构建体系)
学习路径 掌握数据类型(分析底层数据结构)和缓存的基本使用 (理论使用) 掌握 redis 实现高性能,高可靠、高可用技术 (理论)学习redis源代码底层实现 (底层实现) 先来一个引言,比较宏观的角度…...

【README 小技巧】 展示gitee中开源项目start
【README 小技巧】 展示gitee中开源项目start <a target"_blank" hrefhttps://gitee.com/wujiawei1207537021/wu-framework-parent><img srchttps://gitee.com/wujiawei1207537021/wu-framework-parent/badge/star.svg altGitee star/></a>...
tcping实用小工具
Tcping实用小工具命令详解 一、tcping介绍 tcping:tcping命令基于tcp协议监控,可以从较低级别的协议获得简单的,可能不可靠的数据报服务。 原则上,TCP应该能够在从容硬线连接到分组交换或电路交换网络的各种通信系统之上操作。 …...
【Web】Java反序列化之CC2——commons-collections4的新链之一
目录 关于commons-collections4 一个重要的思维模型 触发Transform的关键类:TransformingComparator 反序列化的入口:PriorityQueue Exp 关于commons-collections4 commons-collections4 是 Apache Commons 组件库中的一个项目,它是对旧…...

golang使用gorm操作mysql1
1.mysql连接配置 package daoimport ("fmt""gorm.io/driver/mysql""gorm.io/gorm""gorm.io/gorm/logger" )var DB *gorm.DB// 连接数据库,启动服务的时候,init方法就会执行 func init() {username : "roo…...
Flutter异常上报及性能监控实现
1. 页面异常监测 在Flutter中,通常用FlutterError监测Flutter框架抛出的异常,用runZonedGuarded监测应用中用户代码异常。 class AppGuarded {run(Widget app) {//1. 用FlutterError监测flutter框架抛出的异常FlutterError.onError (FlutterErrorDetail…...

基于springboot+vue的工厂车间管理系统
博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战,欢迎高校老师\讲师\同行交流合作 主要内容:毕业设计(Javaweb项目|小程序|Pyt…...
Java基础 - Stream 流:Stream API的终端操作
在前两篇博客中,我介绍了构建 Stream 流的多种方式,以及 Stream API 的中间操作,如果你还没有阅读,你可以点击这里和这里查看。 Java基础 - Stream 流:构建流的多种方式 Java基础 - Stream 流:Stream API…...
高级语言期末2009级A卷(计算机学院)
1.编写函数,打印下列序列0,1,1,2,3,5,8,13,21,34...(斐波那契序列)的前n项 #include <stdio.h>int main() {int x0,y1,z,n;scanf("%d",&…...

SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...

(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解
JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...