Linux_应用篇(02) 文件 I/O 基础

news2024/11/27 9:42:48

本章给大家介绍 Linux 应用编程中最基础的知识,即文件 I/O(Input、 Outout) , 文件 I/O 指的是对文件的输入/输出操作,说白了就是对文件的读写操作; Linux 下一切皆文件,文件作为 Linux 系统设计思想的核心理念,在 Linux 系统下显得尤为重要,所以对文件的 I/O 操作既是基础也是最重要的部分。
本章将向大家介绍 Linux 系统下文件描述符的概念,随后会逐一讲解构成通用 I/O 模型的系统调用,譬如打开文件、关闭文件、从文件中读取数据和向文件中写入数据以及这些系统调用涉及的参数等内容。
本章将会讨论如下主题内容。
⚫ 文件描述符的概念;
⚫ 打开文件 open()、关闭文件 close();
⚫ 写文件 write()、读文件 read();
⚫ 文件读写位置偏移量。

文件 IO 示例

本章主要介绍文件 IO 操作相关系统调用, 一个通用的 IO 模型通常包括打开文件、读写文件、关闭文件这些基本操作, 主要涉及到 4 个函数: open()、 read()、 write()以及 close(), 我们先来看一个简单地文件读写示例,应用程序代码如下所示:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(void)
{
    char buff[1024];
    int fd1, fd2;
    int ret;

    /* 打开源文件 src_file(只读方式) */
    fd1 = open("./src_file", O_RDONLY);
    if (-1 == fd1)
        return fd1;

    /* 打开目标文件 dest_file(只写方式) */
    fd2 = open("./dest_file", O_WRONLY);
    if (-1 == fd2) {
        ret = fd2;
        goto out1;
    }

    /* 读取源文件 1KB 数据到 buff 中 */
    ret = read(fd1, buff, sizeof(buff));
    if (-1 == ret)
        goto out2;

    /* 将 buff 中的数据写入目标文件 */
    ret = write(fd2, buff, sizeof(buff));
    if (-1 == ret)
        goto out2;

    ret = 0;

out2:
    /* 关闭目标文件 */
    close(fd2);
out1:
    /* 关闭源文件 */
    close(fd1);

    return ret;
}

这段代码非常简单明了,代码所要实现的功能在注释当中已经描述得很清楚了, 从源文件 src_file 中读取 1KB 数据,然后将其写入到目标文件 dest_file 中(这里假设当前目录下这两个文件都是存在的);在进行读写操作之前,首先调用 open 函数将源文件和目标文件打开,成功打开之后再调用 read 函数从源文件中读取 1KB 数据,然后再调用 write 函数将这 1KB 数据写入到目标文件中,至此,文件读写操作就完成了,读写操作完成之后,最后调用 close 函数关闭源文件和目标文件。接下来我们给大家详细介绍这些函数以及相关的内容。

文件描述符

调用 open 函数会有一个返回值, 譬如示例代码中的 fd1 和 fd2, 这是一个 int 类型的数据,在 open函数执行成功的情况下, 会返回一个非负整数, 该返回值就是一个文件描述符(file descriptor) , 这说明文件描述符是一个非负整数; 对于 Linux 内核而言,所有打开的文件都会通过文件描述符进行索引。当调用 open 函数打开一个现有文件或创建一个新文件时,内核会向进程返回一个文件描述符, 用于指代被打开的文件,所有执行 IO 操作的系统调用都是通过文件描述符来索引到对应的文件,譬如示例代码中,当调用 read/write 函数进行文件读写时,会将文件描述符传送给 read/write 函数, 所以在代码中,fb1 就是源文件 src_file 被打开时所对应的文件描述符, 而 fd2 则是目标文件 dest_file 被打开时所对应的文件描述符。
一个进程可以打开多个文件, 但是在 Linux 系统中,一个进程可以打开的文件数是有限制,并不是可以无限制打开很多的文件, 大家想一想便可以知道,打开的文件是需要占用内存资源的,文件越大、打开的文件越多那占用的内存就越多,必然会对整个系统造成很大的影响,如果超过进程可打开的最大文件数限制,内核将会发送警告信号给对应的进程,然后结束进程; 在 Linux 系统下,我们可以通过 ulimit 命令来查看进程可打开的最大文件数,用法如下所示:

该最大值默认情况下是 1024,也就意味着一个进程最多可以打开 1024 个文件,当然这个限制数其实是可以设置的,这个就先不给大家介绍了, 当然除了进程有最大文件数限制外,其实对于整个 Linux 系统来说,也有最大限制,那么关于这些问题, 如果后面的章节内容中涉及到了再给大家进行介绍。所以对于一个进程来说,文件描述符是一种有限资源, 文件描述符是从 0 开始分配的,譬如说进程中第一个被打开的文件对应的文件描述符是 0、第二个文件是 1、第三个文件是 2、第 4 个文件是 3……以此类推,所以由此可知,文件描述符数字最大值为 1023(0~1023) 。 每一个被打开的文件在同一个进程中都有一个唯一的文件描述符,不会重复,如果文件被关闭后,它对应的文件描述符将会被释放,那么这个文件描述符将可以再次分配给其它打开的文件、与对应的文件绑定起来。

每次给打开的文件分配文件描述符都是从最小的没有被使用的文件描述符(0~1023)开始,当之前打开的文件被关闭之后,那么它对应的文件描述符会被释放, 释放之后也就成为了一个没有被使用的文件描述符了。当我们在程序中,调用 open 函数打开文件的时候,分配的文件描述符一般都是从 3 开始,这里大家可能要问了,上面不是说从 0 开始的吗,确实是如此,但是 0、 1、 2 这三个文件描述符已经默认被系统占用了,分别分配给了系统标准输入(0)、 标准输出(1)以及标准错误(2),关于这个问题,这里不便给大家说太多,毕竟这是后面的内容,这里只是给大家提一下,后面遇到了再具体讲解。
Tips: Linux 系统下,一切皆文件,也包括各种硬件设备,使用 open 函数打开任何文件成功情况下便会返回对应的文件描述符 fd。每一个硬件设备都会对应于 Linux 系统下的某一个文件,把这类文件称为设备文件。所以设备文件对应的其实是某一硬件设备,应用程序通过对设备文件进行读写等操作、来使用、操控硬件设备,譬如 LCD 显示器、串口、音频、键盘等。标准输入一般对应的是键盘,可以理解为 0 便是打开键盘对应的设备文件时所得到的文件描述符;标准输出一般指的是 LCD 显示器,可以理解为 1 便是打开 LCD 设备对应的设备文件时所得到的文件描述符;而标准错误一般指的也是 LCD 显示器。

open 函数

在 Linux 系统中要操作一个文件,需要先打开该文件,得到文件描述符,然后再对文件进行相应的读写操作(或其他操作),最后在关闭该文件; open 函数用于打开文件,当然除了打开已经存在的文件之外,还可以创建一个新的文件,函数原型如下所示:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

在 Linux 系统下,可以通过 man 命令(也叫 man 手册) 来查看某一个 Linux 系统调用的帮助信息, man命令可以将该系统调用的详细信息显示出来, 譬如函数功能介绍、 函数原型、参数、返回值以及使用该函数所需包含的头文件等信息; man 更像是一份帮助手册, 所以也把它称为 man 手册, 当我们需要查看某个系统调用的功能介绍、使用方法时, 不用在上网到处查找,直接通过 man 命令便可以搞定, man 命令用法如下所示:

man 2 open #查看 open 函数的帮助信息

Tips: man 命令后面跟着两个参数,数字 2 表示系统调用, man 命令除了可以查看系统调用的帮助信息外,还可以查看 Linux 命令(对应数字 1)以及标准 C 库函数(对应数字 3)所对应的帮助信息;最后一个参数 open 表示需要查看的系统调用函数名。
由于篇幅有限,此截图只是其中一部分内容, 从图中可知, open 函数有两种原型?这是为什么呢?关于这个问题笔者一开始也不理解,大家都知道 C 语言是不支持重载的, 那既然这样,只有一种解释了,那就是可变参函数;对于 C 语言中的可变参函数,对此不了解的朋友可以自行百度, 本文档不作说明!所以由此可知, 在应用程序中调用 open 函数即可传入 2 个参数(pathname、 flags)、也可传入 3 个参数(pathname、 flags、 mode), 但是第三个参数 mode 需要在第二个参数 flags 满足条件时才会有效, 稍后将对此进行说明;从图可知,在应用程序中使用 open 函数时,需要包含 3 个头文件“ #include <sys/types.h>”、“#include <sys/stat.h>”、“#include <fcntl.h>”。

函数参数和返回值含义如下:

pathname: 字符串类型,用于标识需要打开或创建的文件,可以包含路径(绝对路径或相对路径) 信息,譬如: "./src_file"(当前目录下的 src_file 文件)、 "/home/xxx/hello.c"等;如果 pathname 是一个符号链接,会对其进行解引用。
flags: 调用 open 函数时需要提供的标志, 包括文件访问模式标志以及其它文件相关标志,这些标志使用宏定义进行描述,都是常量, open 函数提供了非常多的标志,我们传入 flags 参数时既可以单独使用某一个标志,也可以通过位或运算(|) 将多个标志进行组合。 这些标志介绍如下:

标志用途说明
O_RDONLY以只读方式打开文件这三个是文件访问权限标志,传入的
flags 参数中必须要包含其中一种标
志,而且只能包含一种,打开的文件
只能按照这种权限来操作,譬如使用
了 O_RDONLY 标志,就只能对文件
进行读取操作,不能写操作。
O_WRONLY以只写方式打开文件
O_RDWR以可读可写方式打开文件
O_CREAT如果 pathname 参数指向的文件不存在则创建
此文件
使用此标志时,调用 open 函数需要
传入第 3 个参数 mode,参数 mode 用
于指定新建文件的访问权限, 稍后将
对此进行说明。
open 函数的第 3 个参数只有在使用
了 O_CREAT 或 O_TMPFILE 标志
时才有效。
O_DIRECTORY如果 pathname 参数指向的不是一个目录,则
调用 open 失败
O_EXCL此标志一般结合 O_CREAT 标志一起使用,
用于专门创建文件。
在 flags 参数同时使用到了 O_CREAT 和
O_EXCL 标志的情况下,如果 pathname 参数
指向的文件已经存在,则 open 函数返回错
误。
可以用于测试一个文件是否存在,如
果不存在则创建此文件,如果存在则
返回错误,这使得测试和创建两者成
为一个原子操作;关于原子操作,在
后面的内容当中将会对此进行说明。
O_NOFOLLOW如果 pathname 参数指向的是一个符号链接,
将不对其进行解引用,直接返回错误。
不加此标志情况下,如果 pathname
参数是一个符号链接,会对其进行解
引用。

以上给大家介绍了一些比较常用的标志, open 函数的 flags 标志并不止这些,还有很多标志这里并没有给大家进行介绍,譬如 O_APPEND、 O_ASYNC、 O_DSYNC、 O_NOATIME、 O_NONBLOCK、 O_SYNC 以及 O_TRUNC 等,对于这些没有提及到的标志,在后面学习过程中,也会给大家慢慢介绍。对于初学者来说,我们需要把上表中所列出的这些标志给弄明白、理解它们的作用和含义。
Tips:不同内核版本所支持的 flags 标志是存在差别的, 譬如说新版本内核所支持的标志可能在老版本是不支持的,亦或者老版本支持的标志在新版本已经被取消、替代, man 手册中对一些标志是从哪个版本开始支持的有简单地说明,读者可以自行阅读!前面我们说过, flags 参数时既可以单独使用某一个标志,也可以通过位或运算(|)将多个标志进行组合,譬如:

open("./src_file", O_RDONLY) //单独使用某一个标志
open("./src_file", O_RDONLY | O_NOFOLLOW) //多个标志组合

mode: 此参数用于指定新建文件的访问权限,只有当 flags 参数中包含 O_CREAT 或 O_TMPFILE 标志时才有效(O_TMPFILE 标志用于创建一个临时文件)。 权限对于文件来说是一个很重要的属性,那么在 Linux系统中,我们可以通过 touch 命令新建一个文件,此时文件会有一个默认的权限,如果需要修改文件权限,可通过 chmod 命令对文件权限进行修改,譬如在 Linux 系统下我们可以使用"ls -l"命令来查看到文件所对应的权限。
当我们调用 open 函数去新建一个文件时,也需要指定该文件的权限,而 mode 参数便用于指定此文件的权限, 接下来看看我们该如何通过 mode 参数来表示文件的权限, 首先 mode 参数的类型是 mode_t,这是一个 u32 无符号整形数据,权限表示方法如下所示:

我们从低位从上看,每 3 个 bit 位分为一组,分别表示:
O---这 3 个 bit 位用于表示其他用户的权限;
G---这 3 个 bit 位用于表示同组用户(group)的权限,即与文件所有者有相同组 ID 的所有用户;
U---这 3 个 bit 位用于表示文件所属用户的权限,即文件或目录的所属者;
S---这 3 个 bit 位用于表示文件的特殊权限,文件特殊权限一般用的比较少,这里就不给大家细讲了。
关于什么是文件所属用户、同组用户以及其他用户,这些都是 Linux 操作系统相关的基础知识,相信大家都理解这些概念; 3 个 bit 位中,按照 rwx 顺序来分配权限位(特殊权限除外) ,最高位(权值为 4)表示读权限,为 1 时表示具有读权限,为 0 时没有读权限;中间位(权值为 2)表示写权限,为 1 时表示具有写权限,为 0 时没有写权限;最低位(权值为 1)表示执行权限,为 1 时表示具有可执行权限,为 0 时没有执行权限。 接下来我们举几个例子(特殊权限这里暂时不管,其 S 字段全部为 0) :
最高权限表示方法: 111111111(二进制表示)、 777(八进制表示)、 511(十进制表示);
最高权限这里意味着所有用户对此文件都具有读权限、写权限以及执行权限。
111000000(二进制表示):表示文件所属者具有读、写、执行权限,而同组用户和其他用户不具有任何权限;
100100100(二进制表示):表示文件所属者、同组用户以及其他用户都具有读权限,但都没有写、执行权限。

Tips: open 函数 O_RDONLY、 O_WRONLY 以及 O_RDWR 这三个标志表示以什么方式去打开文件,譬如以只写方式打开(open 函数得到的文件描述符只能对文件进行写操作,不能读)、以只读方式打开(open函数得到的文件描述符只能对文件进行读操作,不能写)、以可读可写方式打开(open 函数得到的文件描述符可对文件进行读和写操作); 与文件权限之间的联系, 只有用户对该文件具有相应权限时,才可以使用对应的标志去打开文件,否则会打开失败!譬如,我们的程序对该文件只有只读权限,那么执行 open 函数使用 O_RDWR 或 O_WRONLY 标志将会失败。关于文件权限表示方法的问题,以上就给大家介绍这么多,在实际编程中,我们可以直接使用 Linux 中已经定义好的宏,不同的宏定义表示不同的权限,如下所示:

宏定义说明
S_IRUSR允许文件所属者读文件
S_IWUSR允许文件所属者写文件
S_IXUSR允许文件所属者执行文件
S_IRWXU允许文件所属者读、写、执行文件
S_IRGRP允许同组用户读文件
S_IWGRP允许同组用户写文件
S_IXGRP允许同组用户执行文件
S_IRWXG允许同组用户读、写、执行文件
S_IROTH允许其他用户读文件
S_IWOTH允许其他用户写文件
S_IXOTH允许其他用户执行文件
S_IRWXO允许其他用户读、写、执行文件
S_ISUID
S_ISGID
S_ISVTX
set-user-ID(特殊权限)
set-group-ID(特殊权限)
sticky(特殊权限)

这些宏既可以单独使用,也可以通过位或运算将多个宏组合在一起,譬如:

S_IRUSR | S_IWUSR | S_IROTH

返回值: 成功将返回文件描述符,文件描述符是一个非负整数;失败将返回-1。
以上就把 open 函数相关的基础知识给大家介绍完了,包括函数返回值、参数等信息,当然在后面的章节内容中,我们还会更加深入地给大家讲解 open 函数相关的知识点; 接下来我们看一些 open 函数的简答使用示例。

open 使用示例

(1)使用 open 函数打开一个已经存在的文件(例如当前目录下的 app.c 文件),使用只读方式打开:

int fd = open("./app.c", O_RDONLY)
if (-1 == fd)
    return fd;

(2)使用 open 函数打开一个已经存在的文件(例如当前目录下的 app.c 文件),使用可读可写方式打开:

int fd = open("./app.c", O_RDWR)
if (-1 == fd)
    return fd;

(3)使用 open 函数打开一个指定的文件(譬如/home/xxx/hello) , 使用可读可写方式,如果该文件是一个符号链接文件,则不对其进行解引用,直接返回错误:

int fd = open("/home/xxx/hello", O_RDWR | O_NOFOLLOW);
if (-1 == fd)
    return fd;

(4)使用 open 函数打开一个指定的文件(譬如/home/xxx/hello),如果该文件不存在则创建该文件,创建该文件时,将文件权限设置如下:
文件所属者拥有读、写、执行权限;
同组用户与其他用户只有读权限。
使用可读可写方式打开:

int fd = open("/home/xxx/hello", O_RDWR | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH);
if (-1 == fd)
    return fd;

write 函数

调用 write 函数可向打开的文件写入数据,其函数原型如下所示(可通过"man 2 write"查看) :

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

首先使用 write 函数需要先包含 unistd.h 头文件。
函数参数和返回值含义如下:
fd: 文件描述符。 关于文件描述符,前面已经给大家进行了简单地讲解,这里不再重述!我们需要将进行写操作的文件所对应的文件描述符传递给 write 函数。
buf: 指定写入数据对应的缓冲区。
count: 指定写入的字节数。
返回值: 如果成功将返回写入的字节数(0 表示未写入任何字节),如果此数字小于 count 参数,这不是错误,譬如磁盘空间已满,可能会发生这种情况;如果写入出错,则返回-1。
对于普通文件(我们一般操作的大部分文件都是普通文件,譬如常见的文本文件、二进制文件等) ,不管是读操作还是写操作,一个很重要的问题是:从文件的哪个位置开始进行读写操作?也就是 IO 操作所对应的位置偏移量,读写操作都是从文件的当前位置偏移量处开始,当然当前位置偏移量可以通过 lseek 系统调用进行设置,关于此函数后面再讲;默认情况下当前位置偏移量一般是 0,也就是指向了文件起始位置,当调用 read、 write 函数读写操作完成之后, 当前位置偏移量也会向后移动对应字节数,譬如当前位置偏移量为 1000 个字节处,调用 write()写入或 read()读取 500 个字节之后,当前位置偏移量将会移动到 1500 个字节处。

read 函数

调用 read 函数可从打开的文件中读取数据,其函数原型如下所示(可通过"man 2 read"查看):

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

首先使用 read 函数需要先包含 unistd.h 头文件。
函数参数和返回值含义如下:
fd: 文件描述符。与 write 函数的 fd 参数意义相同。
buf: 指定用于存储读取数据的缓冲区。
count: 指定需要读取的字节数。
返回值: 如果读取成功将返回读取到的字节数,实际读取到的字节数可能会小于 count 参数指定的字节数,也有可能会为 0,譬如进行读操作时,当前文件位置偏移量已经到了文件末尾。 实际读取到的字节数少于要求读取的字节数,譬如在到达文件末尾之前有 30 个字节数据,而要求读取 100 个字节,则 read 读取成功只能返回 30;而下一次再调用 read 读,它将返回 0。

close 函数

可调用 close 函数关闭一个已经打开的文件,其函数原型如下所示(可通过"man 2 close"查看):

#include <unistd.h>
int close(int fd);

首先使用 close 函数需要先包含 unistd.h 头文件,当我们对文件进行 IO 操作完成之后,后续不再对文件进行操作时,需要将文件关闭。
函数参数和返回值含义如下:
fd: 文件描述符,需要关闭的文件所对应的文件描述符。
返回值: 如果成功返回 0,如果失败则返回-1。
除了使用 close 函数显式关闭文件之外,在 Linux 系统中,当一个进程终止时,内核会自动关闭它打开的所有文件,也就是说在我们的程序中打开了文件, 如果程序终止退出时没有关闭打开的文件,那么内核会自动将程序中打开的文件关闭。 很多程序都利用了这一功能而不显式地用 close 关闭打开的文件。显式关闭不再需要的文件描述符往往是良好的编程习惯,会使代码在后续修改时更具有可读性,也更可靠,进而言之,文件描述符是有限资源,当不再需要时必须将其释放、归还于系统。

lseek 函数

对于每个打开的文件, 系统都会记录它的读写位置偏移量,我们也把这个读写位置偏移量称为读写偏移量,记录了文件当前的读写位置,当调用 read()或 write()函数对文件进行读写操作时,就会从当前读写位置偏移量开始进行数据读写。读写偏移量用于指示 read()或 write()函数操作时文件的起始位置,会以相对于文件头部的位置偏移量来表示,文件第一个字节数据的位置偏移量为 0。当打开文件时,会将读写偏移量设置为指向文件开始位置处,以后每次调用 read()、 write()将自动对其进行调整,以指向已读或已写数据后的下一字节,因此,连续的调用 read()和 write()函数将使得读写按顺序递增,对文件进行操作。 我们先来看看 lseek 函数的原型,如下所示(可通过"man 2 lseek"查看):

#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);

首先调用 lseek 函数需要包含<sys/types.h>和<unistd.h>两个头文件。
函数参数和返回值含义如下:
fd: 文件描述符。
offset: 偏移量,以字节为单位。
whence: 用于定义参数 offset 偏移量对应的参考值, 该参数为下列其中一种(宏定义) :
⚫ SEEK_SET:读写偏移量将指向 offset 字节位置处(从文件头部开始算) ;
⚫ SEEK_CUR:读写偏移量将指向当前位置偏移量 + offset 字节位置处, offset 可以为正、也可以为负,如果是正数表示往后偏移,如果是负数则表示往前偏移;
⚫ SEEK_END:读写偏移量将指向文件末尾 + offset 字节位置处,同样 offset 可以为正、也可以为负,如果是正数表示往后偏移、如果是负数则表示往前偏移。
返回值: 成功将返回从文件头部开始算起的位置偏移量(字节为单位), 也就是当前的读写位置; 发生错误将返回-1。

lseek 使用示例:

(1)将读写位置移动到文件开头处:

off_t off = lseek(fd, 0, SEEK_SET);
if (-1 == off)
    return -1;

(2)将读写位置移动到文件末尾:

off_t off = lseek(fd, 0, SEEK_END);
if (-1 == off)
    return -1;

(3)将读写位置移动到偏移文件开头 100 个字节处:

off_t off = lseek(fd, 100, SEEK_SET);
if (-1 == off)
    return -1;

(4)获取当前读写位置偏移量:

off_t off = lseek(fd, 0, SEEK_CUR);
if (-1 == off)
    return -1;

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

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

相关文章

微信小程序wx.navigateTo无法跳转到Component组件问题解决。(共享元素动画必备)

关于Component构造器官方是有文档说明的&#xff0c;然后官方文档内部也给出了组件是可以通过像pages一样跳转的。但是官方文档缺少了必要的说明&#xff0c;会引起wx.navigateTo无法跳转到组件问题&#xff01; 以下是官方文档截图&#xff1a; 解决方式&#xff1a; 组件创建…

IP归属地在互联网行业中的应用

摘要&#xff1a;IP&#xff08;Internet Protocol&#xff09;地址归属地是指互联网上某个IP地址所对应的地理位置信息。在互联网行业中&#xff0c;IP归属地具有重要的应用价值&#xff0c;包括网络安全、广告定向、用户定位等方面。IP数据云将探讨IP归属地在互联网行业中的应…

解锁流量密码:自养号测评助亚马逊、shein店铺销量飙升

流量是影响一款产品销售的重要因素&#xff0c;当一款产品的流量增多&#xff0c;那么其销量也会随之增加&#xff0c;相反的&#xff0c;流量减少&#xff0c;那么销量也会随之降低。那卖家们又该如何提高店铺流量呢&#xff1f;下面我们就来分享一些提高店铺流量的方法。 无…

入门stm32单片机,0基础是怎么做到的

大家好&#xff0c;今天给大家介绍8步就能系统入门stm32单片机&#xff0c;文章末尾附有分享大家一个资料包&#xff0c;差不多150多G。里面学习内容、面经、项目都比较新也比较全&#xff01;可进群免费领取。 对于没有基础的大学生来说&#xff0c;想要系统入门STM32单片机&a…

校园招聘管理系统(源码+文档)

校园招聘管理系统&#xff08;小程序、ios、安卓都可部署&#xff09; 文件包含内容程序简要说明含有功能项目截图客户端热门岗位校园招聘首页个人简历添加个人简历我的界面注册界面查看个人简历界面个人资料界面登录界面消息界面退出登录 管理端登录界面![请添加图片描述](htt…

GLTFExporter是一个用于将3D场景导出为glTF格式的JavaScript库。

demo案例 GLTFExporter是一个用于将3D场景导出为glTF格式的JavaScript库。下面我将逐个讲解其入参、出参、属性、方法以及API使用方式。 入参&#xff08;Input Parameters&#xff09;: GLTFExporter的主要入参是要导出的场景对象和一些导出选项。具体来说&#xff1a; s…

JRT业务开发起步

这是一段充满挑战与奋斗的旅程&#xff0c;自第一行Java代码的写下起&#xff0c;便历经重重险阻。从细微的代码行&#xff0c;逐步汇聚成实用的工具类方法&#xff1b;从工具类方法的积累&#xff0c;逐渐构建起功能强大的工具包&#xff1b;再从工具包的整合&#xff0c;最终…

python Cyber_record 包 一些命令

python Cyber_record 包 1. 查看record文件的统计信息 cyber_record info -f 20231101211111.record.00005 c record_file: 20231101211111.record.00005 version: 1.0 begin_time: 2023-11-01 21:13:48.099650 end_time: 2023-11-01 21:14:19.267622 duration: 3…

jenkins手把手教你从入门到放弃01-jenkins简介(详解)

一、简介 jenkins是一个可扩展的持续集成引擎。持续集成&#xff0c;也就是通常所说的CI&#xff08;Continues Integration&#xff09;&#xff0c;可以说是现代软件技术开发的基础。持续集成是一种软件开发实践&#xff0c; 即团队开发成员经常集成他们的工作&#xff0c;通…

STM32L4R9 的 QuadSPI Flash 通讯速率不理想

1. 引言 客户反应 STM32L4R9 同 QSPI Flash 通讯&#xff0c;测出来的读取速率为 10MB/s&#xff0c; 和理论值相差较大。 2. 问题分析 按照客户的时钟配置和 STM32L4R9 的数据手册中的数据&#xff0c;OSPI 读数速率为 10MB/s 肯定存在问题。同时我们也可以在 AN4760 应用手…

SharedPreferences.Editor 中 apply 与 commit 方法的区别

在 Android 开发中&#xff0c;SharedPreferences 是我们用来存储简单键值对数据的工具。这就像是在口袋里带着一个小笔记本&#xff0c;随时记录下要点或标记。但当涉及到保存这些笔记时&#xff0c;你有两个选择&#xff1a;apply 或 commit。它们乍看之下似乎可以互换使用&a…

【SqlServer】Alwayson收缩日志

Alwayson收缩日志 压缩失败直接压缩压缩失败 直接压缩 加入高可用组之后,不能设置成简单模式。 USE [databasename] CHECKPOINT DECLARE @bakfile nvarchar(100) SET @bakfile=D:\data

DHCP服务搭建

DHCP搭建 一、DHCP简介 1、概念 DHCP&#xff08;Dynamic Host Configuration Protocol&#xff0c;动态主机配置协议&#xff09;是一种网络协议&#xff0c;用于自动分配IP地址和其他网络配置信息给网络上的设备。通过DHCP&#xff0c;计算机或其他设备可以自动获取IP地址、…

Android算法部署项目 | 在Android平台基于NCNN部署YOLOv5目标检测算法

项目应用场景 面向 Android 安卓平台使用 NCNN 部署 YOLOv5 目标检测算法&#xff0c;使用 Android Studio 进行开发&#xff0c;项目具有 App UI 界面。 项目效果&#xff1a; 项目细节 > 具体参见项目 README.md (1) 安装编译 NCNN&#xff0c;或者直接去 Releases Tenc…

深入理解鸿蒙生命周期:从应用到组件

在开发鸿蒙&#xff08;HarmonyOS&#xff09;应用时&#xff0c;理解生命周期的概念至关重要。生命周期不仅关乎应用的性能优化&#xff0c;还涉及到资源管理和用户体验等多个方面。本文将详细解析鸿蒙操作系统中应用、页面和组件的生命周期&#xff0c;帮助开发者更好地掌握这…

HC-SR04使用指南(STM32)

基于STM32和HC-SR04模块实现超声波测距功能 本文用的单片机是STM32F103C8T6&#xff0c;超声波测距模块是HC-SR04&#xff0c;显示距离是通过上位机显示。 HC-SR04硬件概述 HC-SR04超声波距离传感器的核心是两个超声波传感器。一个用作发射器&#xff0c;将电信号转换为40 K…

目标检测——工业安全生产环境违规使用手机的识别

一、重要性及意义 首先&#xff0c;工业安全生产环境涉及到许多复杂的工艺和设备&#xff0c;这些设备和工艺往往需要高精度的操作和严格的监管。如果员工在生产过程中违规使用手机&#xff0c;不仅可能分散其注意力&#xff0c;降低工作效率&#xff0c;更可能因操作失误导致…

最大的三位数-第15届蓝桥第5次STEMA测评Scratch真题精选

[导读]&#xff1a;超平老师的《Scratch蓝桥杯真题解析100讲》已经全部完成&#xff0c;后续会不定期解读蓝桥杯真题&#xff0c;这是Scratch蓝桥杯真题解析第177讲。 如果想持续关注Scratch蓝桥真题解读&#xff0c;可以点击《Scratch蓝桥杯历年真题》并订阅合集&#xff0c;…

【合合TextIn】AI构建新质生产力,合合信息Embedding模型助力专业知识应用

目录 一、合合信息acge模型获MTEB中文榜单第一 二、MTEB与C-MTEB 三、Embedding模型的意义 四、合合信息acge模型 &#xff08;一&#xff09;acge模型特点 &#xff08;二&#xff09;acge模型功能 &#xff08;三&#xff09;acge模型优势 五、公司介绍 一、合合信息…

目标检测——植物病害图像数据集

一、重要性及意义 首先&#xff0c;植物病害图像是了解农业中植物生长和受病害情况的重要信息来源。通过对这些图像的分析&#xff0c;可以直观地观察到植物的生长状况&#xff0c;及时发现病害的存在。这不仅有助于农民和研究人员快速、准确地诊断植物病害&#xff0c;还能为…