嵌入式知识点总结 Linux驱动 (七)-Linux驱动常用函数 uboot命令 bootcmd bootargs get_part env_get

针对于嵌入式软件杂乱的知识点总结起来,提供给读者学习复习对下述内容的强化。

目录

1.ioremap

2.open

3.read

4.write

5.copy_to_user

6.copy_from_user

7.总结相关uboot命令以及函数

1.bootcmd

1.1.NAND Flash操作命令

2.bootargs

2.1 root

2.2 rootfstype

3.get_part函数

4.env_get函数


1.ioremap

 

__ioremap() 的作用是将任意的物理地址空间映射到内核虚拟地址空间,使得内核可以直接访问物理内存。这个函数主要用于访问设备寄存器或非缓存内存,例如 MMIO(Memory-Mapped I/O)设备。 

物理地址 → 内核虚拟地址:将一段物理地址范围映射到内核的地址空间,使得驱动程序可以使用虚拟地址访问设备寄存器或内存。

内存访问控制:可以指定访问标志 flags,例如是否使用缓存、是否需要写合并等。

主要用于设备驱动开发:通常用于映射外设寄存器,便于操作硬件。

示例:

假设一个设备的寄存器位于物理地址 0x10000000,大小为 0x1000(4KB),可以使用 __ioremap() 将其映射到内核虚拟地址空间:

#include <linux/io.h>
#include <linux/module.h>
#include <linux/init.h>#define DEVICE_PHYS_ADDR  0x10000000  // 设备物理地址
#define DEVICE_SIZE       0x1000      // 设备寄存器大小static void __iomem *device_base;  // 用于存储映射后的虚拟地址static int __init my_driver_init(void)
{// 通过 __ioremap() 将物理地址映射到内核地址空间device_base = __ioremap(DEVICE_PHYS_ADDR, DEVICE_SIZE, PAGE_KERNEL_NOCACHE);if (!device_base) {pr_err("ioremap failed!\n");return -ENOMEM;}// 读取设备寄存器unsigned int value = readl(device_base);pr_info("Device register value: 0x%x\n", value);return 0;
}static void __exit my_driver_exit(void)
{if (device_base) {iounmap(device_base);  // 取消映射}
}module_init(my_driver_init);
module_exit(my_driver_exit);MODULE_LICENSE("GPL");

示例:修改设备寄存器

假设 0x10000000 地址上的寄存器控制某个设备,我们可以使用 writel() 写入数据:

// 设置设备寄存器值
writel(0x12345678, device_base);
函数作用
__ioremap(phys_addr, size, flags)映射物理地址到虚拟地址
iounmap(void __iomem *addr)解除映射
readl(void __iomem *addr)读取 32 位寄存器
writel(u32 value, void __iomem *addr)写入 32 位寄存器

2.open

open() 函数用于在 Linux 内核或用户态程序中打开一个文件或设备,它的行为取决于调用环境:

用户态 (glibc): 用于打开普通文件或设备文件,通常位于 /dev//sys//proc/ 目录。

内核态 (fs/open.c): 只能在内核驱动或模块中使用 filp_open()open() 仅用于用户态)。

#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>int open(const char *pathname, int flags, mode_t mode);

打开文件并读取

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>int main() {int fd = open("test.txt", O_RDONLY);if (fd < 0) {perror("open failed");return -1;}char buf[100];int n = read(fd, buf, sizeof(buf) - 1);if (n > 0) {buf[n] = '\0';printf("Read: %s\n", buf);}close(fd);return 0;
}

创建文件并写入

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>int main() {int fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);if (fd < 0) {perror("open failed");return -1;}write(fd, "Hello, World!\n", 14);close(fd);return 0;
}

在 Linux 内核驱动中,不能直接使用 open(),需要使用 filp_open()

#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>void read_file_in_kernel(void) {struct file *filp;char *buf;mm_segment_t old_fs;loff_t pos = 0;// 打开文件filp = filp_open("/etc/passwd", O_RDONLY, 0);if (IS_ERR(filp)) {printk(KERN_ERR "Failed to open file\n");return;}// 分配内存buf = kmalloc(128, GFP_KERNEL);if (!buf) {printk(KERN_ERR "Failed to allocate buffer\n");filp_close(filp, NULL);return;}// 切换地址空间,防止访问权限问题old_fs = get_fs();set_fs(KERNEL_DS);// 读取文件vfs_read(filp, buf, 128, &pos);// 恢复地址空间set_fs(old_fs);printk(KERN_INFO "Read from file: %s\n", buf);// 释放资源kfree(buf);filp_close(filp, NULL);
}

3.read

read() 用于从文件、设备或管道中读取数据。

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);

fd:文件描述符,由 open() 返回。

buf:存储读取数据的缓冲区。

count:要读取的字节数。

读取文本文件

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>int main() {int fd = open("test.txt", O_RDONLY);if (fd < 0) {perror("open failed");return -1;}char buf[100];int n = read(fd, buf, sizeof(buf) - 1);if (n > 0) {buf[n] = '\0';printf("Read: %s\n", buf);}close(fd);return 0;
}

/dev/urandom 读取随机数据

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>int main() {int fd = open("/dev/urandom", O_RDONLY);if (fd < 0) {perror("open failed");return -1;}unsigned char buf[10];read(fd, buf, sizeof(buf));printf("Random bytes: ");for (int i = 0; i < 10; i++)printf("%02x ", buf[i]);printf("\n");close(fd);return 0;
}

内核态 kernel_read()

Linux 内核驱动 中,不能使用 read(),而是使用 kernel_read()vfs_read()

#include <linux/fs.h>ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos);

内核读取 /etc/passwd

#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>void read_file_in_kernel(void) {struct file *filp;char *buf;mm_segment_t old_fs;loff_t pos = 0;filp = filp_open("/etc/passwd", O_RDONLY, 0);if (IS_ERR(filp)) {printk(KERN_ERR "Failed to open file\n");return;}buf = kmalloc(128, GFP_KERNEL);if (!buf) {printk(KERN_ERR "Failed to allocate buffer\n");filp_close(filp, NULL);return;}old_fs = get_fs();set_fs(KERNEL_DS);kernel_read(filp, buf, 128, &pos);set_fs(old_fs);printk(KERN_INFO "Read: %s\n", buf);kfree(buf);filp_close(filp, NULL);
}

4.write

write() 用于向文件、设备或管道写入数据。

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);

fd:文件描述符,由 open() 返回。

buf:要写入的数据缓冲区。

count:要写入的字节数。

写入文本文件

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>int main() {int fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);if (fd < 0) {perror("open failed");return -1;}const char *data = "Hello, World!\n";ssize_t n = write(fd, data, 14);if (n < 0) {perror("write failed");}close(fd);return 0;
}

写入 /dev/null

#include <fcntl.h>
#include <unistd.h>int main() {int fd = open("/dev/null", O_WRONLY);if (fd < 0) {perror("open failed");return -1;}write(fd, "test", 4);close(fd);return 0;
}

Linux 内核驱动 中,不能使用 write(),而是使用 kernel_write()vfs_write()

#include <linux/fs.h>ssize_t kernel_write(struct file *file, const void *buf, size_t count, loff_t *pos);

内核写入 /tmp/test.txt

#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>void write_file_in_kernel(void) {struct file *filp;mm_segment_t old_fs;loff_t pos = 0;char *buf = "Kernel write test\n";filp = filp_open("/tmp/test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);if (IS_ERR(filp)) {printk(KERN_ERR "Failed to open file\n");return;}old_fs = get_fs();set_fs(KERNEL_DS);kernel_write(filp, buf, strlen(buf), &pos);set_fs(old_fs);filp_close(filp, NULL);
}

5.copy_to_user

在 Linux 内核态(Kernel Space)与 用户态(User Space)进行数据交互时,不能直接访问用户态的内存,需要使用 copy_to_user()copy_from_user() 来进行安全的数据拷贝。

内核空间 复制数据到 用户空间Kernel → User)。

用于驱动程序向用户程序返回数据(如 read() 调用)。

#include <linux/uaccess.h>unsigned long copy_to_user(void __user *to, const void *from, unsigned long n);

to:用户空间的目标缓冲区指针。

from:内核空间的源数据指针。

n:要拷贝的字节数。

copy_to_user() 用于 read()

#include <linux/fs.h>
#include <linux/uaccess.h>ssize_t my_read(struct file *filp, char __user *buffer, size_t len, loff_t *offset) {char kernel_data[] = "Hello from Kernel!";size_t data_len = strlen(kernel_data) + 1; // 包含 '\0'if (len > data_len)len = data_len;if (copy_to_user(buffer, kernel_data, len))return -EFAULT;  // 拷贝失败,返回错误码return len;  // 返回实际拷贝的字节数
}

在用户态程序调用 read(fd, buf, 20); 时:

  • 内核使用 copy_to_user(buf, kernel_data, len);"Hello from Kernel!" 复制到 buf
  • 如果用户态 buf 无效,copy_to_user() 失败,返回 -EFAULT

6.copy_from_user

在 Linux 内核态(Kernel Space)与 用户态(User Space)进行数据交互时,不能直接访问用户态的内存,需要使用 copy_to_user()copy_from_user() 来进行安全的数据拷贝。

用户空间 复制数据到 内核空间User → Kernel)。

用于驱动程序接收用户程序传递的数据(如 write() 调用)。

#include <linux/uaccess.h>unsigned long copy_from_user(void *to, const void __user *from, unsigned long n);

to:内核空间的目标缓冲区指针。

from:用户空间的源数据指针。

n:要拷贝的字节数。

copy_from_user() 用于 write()

#include <linux/fs.h>
#include <linux/uaccess.h>ssize_t my_write(struct file *filp, const char __user *buffer, size_t len, loff_t *offset) {char kernel_buf[100];if (len > sizeof(kernel_buf))len = sizeof(kernel_buf);  // 限制最大长度if (copy_from_user(kernel_buf, buffer, len))return -EFAULT;  // 拷贝失败,返回错误码printk(KERN_INFO "Received from user: %s\n", kernel_buf);return len;  // 返回实际写入的字节数
}

在用户态程序调用 write(fd, "Test", 4); 时:copy_from_user(kernel_buf, buffer, len);"Test" 复制到 kernel_buf

  • 如果 buffer 是无效地址,copy_from_user() 失败,返回 -EFAULT
函数方向用途失败返回
copy_to_user()内核 → 用户read() 返回数据未拷贝字节数(通常非 0)
copy_from_user()用户 → 内核write() 传入数据未拷贝字节数(通常非 0)

7.总结相关uboot命令以及函数

位于Uboot模式下的

1.bootcmd

bootcmd是自动启动时默认执行的一些命令,因此你可以在当前环境中定义各种不同配置,不同环境的参数设置,然后设置bootcmd为你经常使用的那种参数,而且在bootcmd中可以使用调用的方式,方便修改。

eg:setenv bootcmd ‘setenv bootargs $(bootargs)root=$(rootfs) nfsroot=$(serverip):$(nsworkdir) ;nboot 0x80800000 0x4000000x200000;bootm 0x80800000’

1.1.NAND Flash操作命令

例如:nand read 0x7c700000 linux 0x40

怎么实现nand命令读内核?

nand read.jffs2 0x30007FC0 kernel 步骤a: 从NAND FILSHE中的kernel分区读出内核 步骤b: 放到内存地址0x30007FC0去

nand erase off size

2.bootargs

例子:bootargs=earlycon=nvt_serial,0x2f0290000 rootwait console=ttyS0,115200 debug_boot_weak_hash root=ubi0:rootf s rootfstype=ubifs ubi.fm_autoconvert=1 init=/linuxrc ubi.mtd=9 ro ubi.mtd=9 ro ubi.mtd=9 ro ubi.mtd=9 ro ubi.mtd=9 ro

bootargs是环境变量中的重中之重,甚至可以说整个环境变量都是围绕着bootargs来设置的。bootargs的种类非常非常的多,我们平常只是使用了几种而已。bootargs非常的灵活,内核和文件系统的不同搭配就会有不同的设置方法,甚至你也可以不设置 bootargs,而直接将其写到内核中去(在配置内核的选项中可以进行这样的设置),正是这些原因导致了bootargs使用上的困难。 下面介绍一下bootargs常用参数,bootargs的种类非常的多,而且随着kernel的发展会出现一些新的参数,使得设置会更加灵活多样。

2.1 root

用来指定rootfs(文件系统)的位置, 常见的情况有:

1. root=/dev/ram rw root=/dev/ram0 rw 请注意上面的这两种设置情况是通用的,我做过测试甚至root=/dev/ram1rw和root=/dev/ram2 rw也是可以的,网上有人说在某些情况下是不通用的,即必须设置成ram或者ram0,但是目前还没有遇到,还需要进一步确认,遇到不行的时候可以逐一尝试。此种方法用的也很少,因为大多数是用nandflash。2. 2.

root=/dev/mtdx rw root=/dev/mtdblockx rw root=/dev/mtdblock/x rw 上面的这几个在一定情况下是通用的,当然这要看你当前的系统是否支持,不过mtd是字符设备,而mtdblock是块设备,有时候你的挨个的试到底当前的系统支持上面那种情况下,不过root=/dev/mtdblockxrw比较通用。此外,如果直接指定设备名可以的话,那么使用此设备的设备号也是可以的。这个地方要看你的系统启动时MTD分区情况确认是哪个分区存放文件系统,如下图。或者在内核源码的arch/arm/mach-davinci/board-dm365.evm.或者arch/am/plat-s3c24xx/common-smdk.c中的smdk_default_nand_part结构数组中查看,注意是从mtdblock0开始的

这种配置是在nand中已经拷贝好文件系统时这样配置(如果nand中没有,此参数这样配置会找不到文件系统的,出现的错误很多,可能会说unmount….或者panic –not syncing:VFS:unable timount root fs on unknown-block)

此时的解决方法最好是把板子nand全部擦出,重新烧写。 3.

root=/dev/nfs 在文件系统为基于nfs的文件系统的时候使用,也就是说文件系统不在板子上,而是用NFS共享的服务器上的。当然指定root=/dev/nfs之后,还需要指定nfsroot=serverip:nfs_dir,serverip是服务器的IP,dir即指明文件系统存在那个主机的那个目录下面。

注意:要确保在服务器中把此路径下的文件添加到NFS共享,添加上共享的文件会有个小插头的样子。 用NFS共享服务器上的文件系统这种方法很好,这样板子上的系统就可以起来了,可以再板子的终端里输入命令了,在班子中将存放文件系统的分区挂载一下eg:mount /dev/mtdblock4 /mnt,这样将服务器上做好的文件系统直接拷贝到/mnt文件下就好了eg:cp –rm * /mnt ,*代表当前路径下的所有内容,无需再用maketools将文件系统制作成二进制文件的形式用tftp或者NFS烧写到nand中了。

2.2 rootfstype

这个选项需要跟root一起配合使用,一般如果根文件系统是ext2的话,有没有这个选项是无所谓的,但是如果是jffs2,squashfs等文件系统的话,就需要rootfstype指明文件系统的类型,不然会无法挂载根分区.(具体是怎样无法挂载的这个还待测,是无法挂载nand中的还是虚拟机中的,待测)

eg:rootfstype=yaffs2

3.get_part函数

get_part 不是标准的 Linux API,可能是 驱动或 U-Boot 代码 中的一个 私有函数,用于获取 分区信息(Partition)。如果你是在 U-Boot、Linux 内核或 NAND/UBI 相关代码中看到它,它可能用于获取 MTD 设备的分区信息

在 U-Boot 中,获取例如如下mtdparts=mtdparts=spi_nand.0:0x40000@0x40000(fdt),0x40000@0xc0000(atf),0x1c0000@0x100000(uboot),0x40000@0x2c0000(uenv),0x500000@0x300000(linux),0x1c0000@0x800000(uboot),0x500000@0x9c0000(linux),0x3160000@0xec0000(rootfs0),0x2500000@0x4020000(rootfs1),0x1ae0000@0x6520000(app)

的获取off偏移还有size大小

4.env_get函数

在 U-Boot 中,获取环境变量的内容函数

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

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

相关文章

《STL基础之vector、list、deque》

【vector、list、deque导读】vector、list、deque这三种序列式的容器&#xff0c;算是比较的基础容器&#xff0c;也是大家在日常开发中常用到的容器&#xff0c;因为底层用到的数据结构比较简单&#xff0c;笔者就将他们三者放到一起做下对比分析&#xff0c;介绍下基本用法&a…

Windows中本地组策略编辑器gpedit.msc打不开/微软远程桌面无法复制粘贴

目录 背景 解决gpedit.msc打不开 解决复制粘贴 剪贴板的问题 启用远程桌面剪贴板与驱动器 重启RDP剪贴板监视程序 以上都不行&#xff1f;可能是操作被Win11系统阻止 最后 背景 远程桌面无法复制粘贴&#xff0c;需要查看下主机策略组设置&#xff0c;结果按WinR输入…

高精度加法乘法

高精度加法&乘法都是把数字转化成数组进行运算&#xff0c;存储 高精度加法 建议多在纸上画画&#xff0c;梳理思路 代码实现 输入字符串 //初始化数组存储 int a[250]{0}; int b[250]{0}; int c[251]{0}; //定义字符串&#xff0c;输入字符串 string s1,s2; getline(c…

Python 列表思维导图

Python 列表思维导图 腾讯云盘下载连接 https://share.weiyun.com/Ri6bUJed

获取snmp oid的小方法1(随手记)

snmpwalk遍历设备的mib # snmpwalk -v <SNMP version> -c <community-id> <IP> . snmpwalk -v 2c -c test 192.168.100.201 .根据获取的值&#xff0c;找到某一个想要的值的oid # SNMPv2-MIB::sysName.0 STRING: test1 [rootzabbix01 fonts]# snmpwalk -v…

【leetcode练习·二叉树】计算完全二叉树的节点数

本文参考labuladong算法笔记[拓展&#xff1a;如何计算完全二叉树的节点数 | labuladong 的算法笔记] 如果让你数一下一棵普通二叉树有多少个节点&#xff0c;这很简单&#xff0c;只要在二叉树的遍历框架上加一点代码就行了。 但是&#xff0c;力扣第第 222 题「完全二叉树的…

WebStorm安装及配置迁移

一、安装 官方下载安装包 WebStorm&#xff1a;JetBrains 出品的 JavaScript 和 TypeScript IDE 适用于2024版本及以下 按需安装后重启电脑 运行WebStorm,注意不要选择大陆地区&#xff0c;语言不选择中文&#xff0c;运行激活文件 二、配置迁移 根据已有软件导出相关配置…

ARM内核:嵌入式时代的核心引擎

引言 在当今智能设备无处不在的时代&#xff0c;ARM&#xff08;Advanced RISC Machines&#xff09;处理器凭借其高性能、低功耗的特性&#xff0c;成为智能手机、物联网设备、汽车电子等领域的核心引擎。作为精简指令集&#xff08;RISC&#xff09;的典范&#xff0c;ARM核…

离线大模型-通义千问

关部署离线模型的教程就不写了&#xff0c;百度一搜一大堆 Kamailio介绍 1. Kamailio介绍 user: 您了解kamailio吗&#xff1f;assistant: 是的&#xff0c;我了解Kamailio。Kamailio是一个开源的SIP服务器和代理&#xff0c;用于处理VoIP&#xff08;Voice over Internet…

Hypium+python鸿蒙原生自动化安装配置

Hypiumpython自动化搭建 文章目录 Python安装pip源配置HDC安装Hypium安装DevEco Testing Hypium插件安装及使用方法​​​​​插件安装工程创建区域 Python安装 推荐从官网获取3.10版本&#xff0c;其他版本可能出现兼容性问题 Python下载地址 下载64/32bitwindows安装文件&am…

细说机器学习算法之ROC曲线用于模型评估

系列文章目录 第一章&#xff1a;Pyhton机器学习算法之KNN 第二章&#xff1a;Pyhton机器学习算法之K—Means 第三章&#xff1a;Pyhton机器学习算法之随机森林 第四章&#xff1a;Pyhton机器学习算法之线性回归 第五章&#xff1a;Pyhton机器学习算法之有监督学习与无监督…

ROS_noetic-打印hello(√)

笔者创建的路径如下 进入到src&#xff0c; catkin_create_pkg helloworld roscpp rospy std_msgs Helloworld-C hello_C.cpp #include "ros/ros.h" int main(int argc, char *argv[]) { //执行 ros 节点初始化 ros::init(argc,argv,"hello"); //创…

冲刺蓝桥杯之速通vector!!!!!

文章目录 知识点创建增删查改 习题1习题2习题3习题4&#xff1a;习题5&#xff1a; 知识点 C的STL提供已经封装好的容器vector&#xff0c;也可叫做可变长的数组&#xff0c;vector底层就是自动扩容的顺序表&#xff0c;其中的增删查改已经封装好 创建 const int N30; vecto…

Golang 并发机制-2:Golang Goroutine 和竞争条件

在今天的软件开发中&#xff0c;我们正在使用并发的概念&#xff0c;它允许一次执行多个任务。在Go编程中&#xff0c;理解Go例程是至关重要的。本文试图详细解释什么是例程&#xff0c;它们有多轻&#xff0c;通过简单地使用“go”关键字创建它们&#xff0c;以及可能出现的竞…

C++并发编程指南07

文章目录 [TOC]5.1 内存模型5.1.1 对象和内存位置图5.1 分解一个 struct&#xff0c;展示不同对象的内存位置 5.1.2 对象、内存位置和并发5.1.3 修改顺序示例代码 5.2 原子操作和原子类型5.2.1 标准原子类型标准库中的原子类型特殊的原子类型备选名称内存顺序参数 5.2.2 std::a…

智慧园区如何融合五大技术实现全方位智能管理与服务创新

内容概要 在现代社会&#xff0c;智慧园区正逐渐成为管理与服务创新的风向标。以快鲸智慧园区管理系统为例&#xff0c;它为园区的数字化管理提供了一种全新的模式。该系统的核心在于如何充分应用物联网技术&#xff0c;自动化与信息化的结合&#xff0c;使得园区能够实现实时…

JxBrowser 7.41.7 版本发布啦!

JxBrowser 7.41.7 版本发布啦&#xff01; • 已更新 #Chromium 至更新版本 • 实施了多项质量改进 &#x1f517; 点击此处了解更多详情。 &#x1f193; 获取 30 天免费试用。

DeepSeek R1-Zero vs. R1:强化学习推理的技术突破与应用前景

&#x1f4cc; 引言&#xff1a;AI 推理的新时代 近年来&#xff0c;大语言模型&#xff08;LLM&#xff09; 的规模化扩展成为 AI 研究的主流方向。然而&#xff0c;LLM 的扩展是否真的能推动 通用人工智能&#xff08;AGI&#xff09; 的实现&#xff1f;DeepSeek 推出的 R1…

python学opencv|读取图像(四十七)使用cv2.bitwise_not()函数实现图像按位取反运算

【0】基础定义 按位与运算&#xff1a;两个等长度二进制数上下对齐&#xff0c;全1取1&#xff0c;其余取0。按位或运算&#xff1a;两个等长度二进制数上下对齐&#xff0c;有1取1&#xff0c;其余取0。 按位取反运算&#xff1a;一个二进制数&#xff0c;0变1,1变0。 【1】…

第十四讲 JDBC数据库

1. 什么是JDBC JDBC&#xff08;Java Database Connectivity&#xff0c;Java数据库连接&#xff09;&#xff0c;它是一套用于执行SQL语句的Java API。应用程序可通过这套API连接到关系型数据库&#xff0c;并使用SQL语句来完成对数据库中数据的查询、新增、更新和删除等操作…