【香橙派系列教程】(五)Linux的热拔插UDEV机制

news2024/9/23 7:22:03

【五】Linux的热拔插UDEV机制

在上一篇中我们发现,当手机接入开发板时,系统并不认识,当我们在/etc/udev目录下创建一个规则后,就可以通过adb访问到手机了,这里到底是怎么回事?

文章目录

  • 【五】Linux的热拔插UDEV机制
    • 1.简介
    • 2.守护进程(重要概念)
    • 3.守护进程开发
      • 1.daemon()函数
      • 2.time()函数
      • 3.asctime()函数
      • 4.localtime()函数
      • 5.代码逻辑
    • 4.开机自启动
    • 5.守护进程应用
      • 一、编写判断某进程是否在运行的程序:
      • 二、守护进程不让控制程序退出
    • 6.守护进程和后台进程的区别
    • 7.UDEV的配置文件详解
      • 1.使用 udev 的好处
      • 2.udev 添加/删除 设备文件的过程
      • 3.手动去编写一个规则
      • 4.实战:自动挂载U盘

1.简介

  • udev是一个设备管理工具,udev以守护进程的形式运行,通过侦听内核发出来的uevent来管理/dev目录下的设备文件。
  • udev在用户空间运行,而不在内核空间 运行。它能够根据系统中的硬件设备的状态动态更新设备文件,包括设备文件的创建,删除等。
  • 设备文件通常放在/dev目录下。使用udev后,在/dev目录下就只包含系统中真正存在的设备。

当一个硬件接入时,内核是首先知道的,而在用户应用层,并不知道这个设备接入了,如何将他们连接起来->udev机制

/dev/bus/usb目录底下,进入001文件中,可以看到003是我接入的手机文件,当我拔掉之后就没有了

当设备插入,内核知道后,发出uevent,udev设备工具一直在监听,监听到uevent后,根据规则文件的规则(可以是自己创建的),判断它是什么类型的设备,并在/dev下面为它创建对应的设备文件,这样应用层就通过文件句柄访问到设备了。

创建规则文件

创建规则文件是为了让udev机制能认识他是usb类型设备,或者其他类型设备,并为usb设备创建文件,否则即使内核识别到了usb设备,发出uevent,udev也不会在/dev下面创建对应的设备文件。

如何建立自己的规则文件

//1.进入规则目录
cd /etc/udev/rules.d/

//2.比如创建usb热拔插的规则:
sudo vim 51-android.rules
    
//在51-android.rules 中输入
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", MODE="0666"

2.守护进程(重要概念)

1.简介

**Linux Daemon(守护进程)**是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务。

Linux系统的大多数服务器就是通过守护进程实现的。【常见的守护进程包括系统日志进程syslogd、 web服务器httpd、邮件服务器sendmail和数据库服务器mysqld等】

守护进程的名称通常以d结尾

UDEV守护进程,它能够根据系统中的硬件设备的状态动态更新设备文件,包括设备文件的创建,删除等。

2.基本特点

  • 生存周期长[非必须],一般操作系统启动的时候就启动,关闭的时候关闭。
  • 守护进程和终端无关联,也就是他们没有控制终端,所以当控制终端退出,也不会导致守护进程退出
  • 守护进程是在后台运行,不会占着终端,终端可以执行其他命令
  • 一个守护进程的父进程是init进程,因为它真正的父进程在fork出子进程后就先于子进程exit退出了,所以它是一个由init继承的孤儿进程

linux操作系统本身是有很多的守护进程在默默执行,维持着系统的日常活动。大概30-50个

那么不是说:你是守护进程,操作系统就知道你是守护进程,然后启动它。

需要我们人为的,在开机脚本里面,自动的通过脚本的方式去启动这个进程(下面会讲到)

说明:

  1. PPID = 0:内核进程,跟随系统启动而启动,生命周期贯穿整个系统。
  2. CMD列中:名字带[]这种,叫内核守护进程
  3. 老祖init:也是系统守护进程,它负责启动各运行层次特定的系统服务;所以很多进程的PPID是init,也负责收养孤儿进程。
  4. CMD列中:名字不带[]的普通守护进程(用户集守护进程)。

查看udev进程:

ps -ef|grep udev|grep -v grep  //grep -v grep 是过滤grep进程

3.守护进程开发

1.daemon()函数

**函数功能:**当调用这个函数时,此进程变为守护进程,在后台运行

直接借助damon()函数完成。

//1.头文件
#include <unistd.h>

//2.函数原型
int daemon(int nochdir, int noclose);

//3.函数参数:
nochdir:为0时表示将当前目录更改至“/”(工作目录)
noclose:为0时表示将标准输入、标准输出、标准错误重定向至“/dev/null”
    
//4.返回值:
成功则返回0,失败返回-1

2.time()函数

函数功能: 得到当前日历时间或者设置日历时间

//1.头文件
#include <time.h>

//2.函数原型 
time_t time(time_t *timer)
/*time_t是一个unsigned long类型*/
    
//3.参数说明
1.timer=NULL0时得到当前日历时间(从1970-01-01 00:00:00到现在的秒数),
2.timer=时间数值时,用于设置日历时间。
3.如果 timer不为空,则返回值也存储在变量 timer中。

函数返回: 当前日历时间

示例

#include <stdio.h>
#include <time.h>

int main ()
{
  time_t seconds;

  seconds = time(NULL);
  printf("自 1970-01-01 起的秒数 = %ld\n", seconds);

  return(0);
}

3.asctime()函数

简单说就是:一个存时间的结构体,这个存的时间是多少需要我们自己传进去,然后将它以字符串的形式返回来

C 库函数 char *asctime(const struct tm *timeptr) 返回一个指向字符串的指针,它代表了结构 struct timeptr 的日期和时间。

//1.头文件
#include <time.h>

//2.函数原型
char *asctime(const struct tm *timeptr)

//3.参数
timeptr 是指向 tm 结构的指针,包含了分解为如下各部分的日历时间:
struct tm {
   int tm_sec;         /* 秒,范围从 0 到 59                			  */
   int tm_min;         /* 分,范围从 0 到 59               			  */
   int tm_hour;        /* 小时,范围从 0 到 23                			 */
   int tm_mday;        /* 一月中的第几天,范围从 1 到 31                  */
   int tm_mon;         /* 月份,范围从 0 到 11               			 */
   int tm_year;        /* 自 1900 起的年数                             */
   int tm_wday;        /* 一周中的第几天,范围从 0 到 6                   */
   int tm_yday;        /* 一年中的第几天,范围从 0 到 365                 */
   int tm_isdst;       /* 夏令时                                   */    
};

//3.返回值
返回的时间字符串格式为:星期,,,小时::,

示例

#include <stdio.h>
#include <string.h>
#include <time.h>

int main()
{
    struct tm t;
    t.tm_sec = 0;
    t.tm_min = 5;
    t.tm_hour  = 6;
    t.tm_mday  = 25;
    t.tm_mon  = 1;
    t.tm_year  = 59;
    t.tm_wday  = 5;

    puts(asctime(&t));

    return(0);
}

4.localtime()函数

函数功能: 使用 timer 的值来填充 tm 结构。timer 的值被分解为 tm 结构,并用本地时区表示。

C 库函数 struct tm *localtime(const time_t *timer) 使用 timer 的值来填充 tm 结构。timer 的值被分解为 tm 结构,并用本地时区表示。

//1.头文件
#include <time.h>

//2.函数原型
struct tm *localtime(const time_t *timer)

timer -- 这是指向表示日历时间的 time_t 值的指针
//3.返回值
以tm结构表达的时间

示例

#include <stdio.h>
#include <time.h>
 
int main ()
{
   time_t rawtime;
   struct tm *info;
   char buffer[80];
 
   time( &rawtime );
 
   info = localtime( &rawtime );
   printf("当前的本地时间和日期:%s", asctime(info));
 
   return(0);
}

5.代码逻辑

每隔十秒向日志目录里写当地的时间,当收到某个信号时,终止输出

为什么要用信号?

用于进程间通信,当我运行此程序后,他会像一个幽灵一样,运行在后台,这个时候我想要给他一些指令,可以通过进程间的通信

#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>

#include <stdbool.h>   //bool
static bool flag = true;

void handler(int sig)
{
    printf("I got a signal %d\nI'm quitting.\n", sig);
    flag = false;
}
int main()
{
    time_t t;
    int fd;
    //创建守护进程
    if(-1 == daemon(0, 0))
    {
        printf("daemon error\n");
        exit(1);
    }
    //设置信号处理函数
    struct sigaction act;
    act.sa_handler = handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    if(sigaction(SIGQUIT, &act, NULL))
    {
        printf("sigaction error.\n");
        exit(0);
    }
    //进程工作内容
    while(flag)
    {
        fd = open("/home/orangepi/daemon.log", O_WRONLY | O_CREAT | O_APPEND,
                  0644);
        if(fd == -1)
        {
            printf("open error\n");
        }
        t = time(0);
        char *buf = asctime(localtime(&t));
        write(fd, buf, strlen(buf));
        close(fd);
        sleep(10);
    }
    return 0;
}

运行结果

热知识:

我们知道守护进程有个特点,随着内核的启动而启动,但是我们只是通过调用daemon函数来将这个进程变为了守护进程,内核并不知道。

其次守护进程会被init进程来启动,但是init进程并不会无缘无故的启动你,他有对应的一些规则。一般说,在/etc/init.d目录下

如果在工作中不是专门做守护进程的,这部分了解即可,不用刻意去学习

4.开机自启动

那如果我想要开机启动怎么办?有一种更为简便的方法

第一步:

sudo vi /etc/rc.local 
在这个里面加入绝对路径加程序名字,就可以开机自启动

进入/etc/rc.local 后输入守护进程的路径/home/orangepi/hardwaresoft/daemon/a.out

第二步:

重启测试是否启动了守护进程

sudo reboot

取消要加sudo超级用户权限

5.守护进程应用

**需求:**要求语音刷手机的程序一直保持运行,防止应用程序崩溃意外

在开始前我们先把生成的程序名字修改一下

gcc uartTest.c uartTool.c -o douyinUtils -pthread

一、编写判断某进程是否在运行的程序:

#include <stdio.h>
#include <string.h>
int main()
{
    FILE *file;
    char buffer[128] = {'\0'};
    char *cmd = "ps -elf |grep douyin|grep -v grep";
    file = popen(cmd, "r");
    fgets(buffer, 128, file);//1.放到哪里?2.读多少个?3.从哪里读?
    if(strstr(buffer, "douyin") != NULL){
        printf("douyinPro is running\n");
    }else{
        printf("douyinPro is not running\n");
    }
    printf("BUFFER:%s\n",buffer);
}

1.当刷抖音程序没有运行时:

2.当刷抖音程序运行时:

二、守护进程不让控制程序退出

程序功能:作为一个守护进程周期性的检测抖音程序是否在运行,当没有运行时,启动抖音程序

当守护进程收到SIGQUIT信号时,关闭对抖音的守护进程

#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>
#include <stdbool.h>
static bool flag = true;
void handler(int sig)
{
    printf("I got a signal %d\nI'm quitting.\n", sig);
    flag = false;
}
//判断抖音程序进程是否在后台运行
int judMent()
{
    FILE *file;
    char buffer[128] = {'\0'};
    char *cmd = "ps -elf |grep douyinUtils|grep -v grep";
    file = popen(cmd, "r");
    fgets(buffer, 128, file);
    if(strstr(buffer, "douyinUtils") != NULL){
        return 0;
    }else{
        return -1;
    }
    printf("BUFFER:%s\n",buffer);
}
int main()
{
    time_t t;
    int fd;
    //创建守护进程
    if(-1 == daemon(0, 0))
    {
        printf("daemon error\n");
        exit(1);
    }
    //设置信号处理函数
    struct sigaction act;
    act.sa_handler = handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    if(sigaction(SIGQUIT, &act, NULL))
    {
        printf("sigaction error.\n");
        exit(0);
    }
    //进程工作内容
    while(flag)
    {
        if( judMent() == -1){
            //路径根据自己存放的抖音程序
            system("/home/orangepi/hardwaresoft/douyinUtils /dev/ttyS5&");//&表示在后台运行
            }
        sleep(2);
     }
      return 0;
}

添加开机自启动

sudo vi /etc/rc.local
    
/home/orangepi/hardwaresoft/douyinUtils /dev/ttyS5 &
/home/orangepi/hardwaresoft/shouhuDouyin
exit 0

6.守护进程和后台进程的区别

  1. 守护进程和终端不挂钩;后台进程能往终端上输出东西(和终端挂钩);
  2. 守护进程关闭终端时不受影响,守护进程不会随着终端的退出而退出;而后台程序会随用户退出而停止

如何成为后台进程

a.运行程序时,&;例如:./a.out &
b.使用ctrl+z,bg等命令

7.UDEV的配置文件详解

1.使用 udev 的好处

我们都知道,所有的设备在 Linux 里都是以设备文件的形式存在。

在早期的 Linux 版本中,/dev 目录包含了所有可能出现的设备的设备文件。很难想象 Linux 用户如何在这些大量的设备文件中找到匹配条件的设备文件。

现在 udev 只为那些连接到 Linux 操作系统的设备产生设备文件。并且 udev 能通过定义一个 udev 规则(rule)来产生匹配设备属性的设备文件,这些设备属性可以是内核设备名称、总线路径、厂商名称、型号、序列号或者磁盘大小等等。

  • 动态管理:当设备添加/删除时,udev 守护进行帧听来自内核的 uevent,以此添加或者删除 /dev 下的设备文件,所以 udev 只为已经连接的设备产生设备文件,而不会在 /dev 下产生大量虚无的设备文件。
  • 自定义命名规则:通过 Linux 默认的规则文件,udev 在 /dev/ 里为所有的设备定义了内核设备名称,比如 /dev/sda、/dev/hda、/dev/fd 等等。由于 udev 是在用户空间(user space)运行,Linux 用户可以通过自定义的规则文件,灵活地产生标识性强的设备文件名,比如 /dev/boot_disk、/dev/root_disk、/dev/color_printer 等等。
  • 设定设备的权限和所有者/组:udev 可以按一定的条件来设置设备文件的权限和设备文件所有者/组。在不同的 udev 版本中,实现的方法不同。

2.udev 添加/删除 设备文件的过程

img

规则文件是 udev 里最重要的部分,默认是存放在 /etc/udev/rules.d/ 下。所有的规则文件必须以".rules" 为后缀名。
下面是一个简单的规则:

KERNEL=="sda", NAME="my_root_disk", MODE="0660"

KERNEL 是匹配键,NAME 和 MODE 是赋值键。这条规则的意思是:如果有一个设备的内核名称为sda,则该条件生效,

执行后面的赋值:在 /dev 下产生一个名为my_root_disk 的设备文件,并把设备文件的权限设为 0660


3.手动去编写一个规则

当我使用dmesg命令时,可以看到手机接入的信息。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

//rules.d

//1.USB子系统       2.环境变量:USB设备                  3.权限
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", MODE="0666"

1.将之前的手机规则文件删除,打开adb devices调试,依旧是udev出现问题

2.我们可以在/dev/bus/usb/001目录下,看到一些手机接入的信息

002并非固定号,当我拔掉重新插入时,会变化,是随机分配的

查看更为详细的信息输入指令 udevadm info --attribute-walk --name=/dev/bus/usb/001/002

udevadm info --attribute-walk --name=/dev/设备名字

3.根据这些信息,写出我们的一个规则匹配文件

cd  /etc/udev/rules.d/
sudo vi oppo-android.rules

//rules.d
SUBSYSTEM=="usb", ATTRS{idVendor}=="1d6b", ATTRS{idProduct}=="0002",MODE="0666"

4.重新拔插之后,再次adb devices,可以看到能正常识别到了

4.实战:自动挂载U盘

1.插入U盘,dmesg,存放于/dev/sda文件里面(这个有时候是sda,有时sdb

2.查看U盘里面的数据:

					//挂到mut根目录底下
sudo mount /dev/sda /mnt/
//进入目录
cd /mnt 
//打开
ls

输入命令udevadm info --attribute-walk --name=/dev/sdb查看更为详细的信息

可以查看到U盘的更多数据

udevadm info --attribute-walk --name=/dev/设备名字


拓展知识:卸载操作(umount)

sudo umount /mnt

命令umount 已挂载的设备源(/dev/sdb1) 或已挂载目的点(/mnt)
命令umount 文件系统/挂载点

umount /dev/sdb1 或者 umount /mnt

如果出现device is busy报错,表示该文件系统正在被使用;


1.如果报错mount:unknown filesystem type ‘exfat‘,是因为在 ubuntu下,由于版权的原因,默认不支持 exfat 格式的 u 盘,

USB 连接硬盘时无法正常映射,添加如下命令,并重新插拔硬盘。

sudo apt-get install exfat-utils exfat-fuse

2.如果报错fuse: mountpoint is not empty fuse: if you are sure this is safe, use the ‘nonempty’ mount option

这是因为当挂载路径下已经有这个同名路径时,为了避免冲突,会报这个信息,并且新的文件还挂载不上去。

此时需要添加参数 -o nonempty


3.编写规则自动挂载U盘

第二点所描述的,每次插U盘都需要手动的去挂载U盘,很麻烦,那么现在我们需要编写一个udev规则,当每次U盘插进来时,都能够识别到

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

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

相关文章

【Python】数据类型(上)

本篇文章将讲解&#xff1a; &#xff08;1&#xff09;整型 &#xff08;2&#xff09;布尔类型 一&#xff1a;整型 整型其实就是十进制整数的统称&#xff0c;例如&#xff1a;1 666 都属于整型。 &#xff08;1&#xff09;定义 num11 age45 &#xff08…

【网络】网络入门(第一篇)

网络入门可以从多个方面开始&#xff0c;以下是一个基本的网络入门指南&#xff0c;涵盖了网络的基本概念、网络类型、网络协议、网络拓扑、网络设备以及网络地址等方面。 一、网络基本概念 计算机网络&#xff1a;将多个计算机系统和设备连接在一起&#xff0c;以实现资源共…

Opencv学习-LUT函数

这个函数大概意思根据自己设定的查找表&#xff0c;改变原本像素点值 例如&#xff1a;我们想将一张图片灰度为0-100的像素的灰度变成0,101-200的变成100,201-255的变成255。我们就可已建立如下的一张表格 ​​​​​​​ ​​​​​​​ ​​​​​​​…

Studying-代码随想录训练营day52| 101.孤岛的总面积、102沉没孤岛、103.水流问题、104.建造最大岛屿

第52天&#xff0c;图论part03&#xff0c;岛屿问题继续&#xff01;&#xff01;&#x1f4aa;(ง •_•)ง&#xff0c;编程语言&#xff1a;C 目录 101.孤岛的总面积 102沉没孤岛 103.水流问题 104.建造最大岛屿 101.孤岛的总面积 文档讲解&#xff1a;手撕孤岛的总…

昇思25天学习打卡营第XX天|SSD目标检测

感觉目标检测还是yolo相对最火&#xff1f;ssd有点老了可以更新下 SSD算法数学描述 SSD算法使用卷积神经网络&#xff08;CNN&#xff09;进行特征提取&#xff0c;并通过多尺度的特征图进行目标检测。设 ( C ) 为CNN输出的特征层数量&#xff0c;( F_i ) 为第 ( i ) 层特征…

【Postman的接口测试工具介绍】

🌈个人主页: 程序员不想敲代码啊 🏆CSDN优质创作者,CSDN实力新星,CSDN博客专家 👍点赞⭐评论⭐收藏 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共同学习、交流进步! 👉Postman接口.👋 👉Postman是一款常用的API开发测试工具,它提…

Harmony学习(四)(应用程序框架基础)

1.应该程序框架基础 多Module设计机制 模块化开发&#xff1a;一个应用多个功能&#xff0c;每个功能可作为一个模块&#xff0c;Module中可以包含源代码、资源文件、第三方库、配置文件等&#xff0c;每一个Module可以独立编译&#xff0c;实现特定的功能支持多设备&#xf…

jdk的版本匹配 Usage of ApI documented as @since 11+

IDEA 解决 Usage of API documented as since XX 的方法 如下所示&#xff0c;代码已经报错提示。 这个问题的原因是IDEA 设置的jdk Language level 语法级别太低&#xff0c;不适配代码导致的&#xff0c;只要在项目结构中将语法级别调相应的级别就可以了。具体解决思路见下图…

vue-创建自己的CLI脚手架

1.自定义命令和入口配置 首先创建一个文件夹&#xff0c;然后npm init -y生成package.json文件 添加bin命令配置入口文件 新建lib/index.js文件 然后在控制台npm link 建立软连接 、然后执行felix-cli 就可以输出代码 2.查看当前版本号命令 安装 commander npm i commander…

系统架构设计师 - 企业信息化战略与实施

企业信息化战略与实施 企业信息化战略与实施信息与信息化的概念信息的定义信息的特点信息化的概念信息化对组织的意义 信息系统生命周期 ★立项阶段开发阶段运维阶段消亡阶段 信息系统战略规划 ★ ★ ★政府信息化与电子政务 ★企业信息化与电子商务 ★ ★ ★企业资源计划企业资…

2024年让短片制作不再难,4款剪辑软件助你一臂之力!

在这个短视频流行的时代&#xff0c;每一个创意都值得被展现&#xff0c;每一份热情都值得被激发。你是不是也曾经想过&#xff0c;用镜头来讲述你的故事&#xff0c;用剪辑来展示你的才华&#xff1f;今天&#xff0c;我们一起来探索2024年制作高质量短片的秘密武器——4款强大…

gitignore文件设置,git提交时忽略部分文件

在git提交时&#xff0c;出现了非常多无用的文件&#xff0c;包括.idea、.iml文件等等&#xff0c;使得commit变得麻烦&#xff0c;要自己在勾选框中点击半天。 右键单击项目名&#xff0c;选择New 选择File,命名为.gitignore&#xff08;注意&#xff1a;开头符号是英文.&…

文件描述符(fileno)及文件系统

fileno: #include <stdio.h> main() {FILE *fp;int fd;fp fopen("/etc/passwd", "r");fd fileno(fp);printf("fd %d\n", fd);fclose(fp); } 一&#xff0e;fileno()函数-CSDN博客https://blog.csdn.net/TuxedoLinux/article/detai…

利用开源可视化报表工具进入流程化办公!

很多客户朋友都希望能实现流程化办公&#xff0c;因为只有这样才能帮助企业顺利降本、增效、提质&#xff0c;利用好企业内部数据资源&#xff0c;打破信息孤岛壁垒&#xff0c;实现高效发展。低代码技术平台、开源可视化报表工具优势功能特点多&#xff0c;是提质高效的办公利…

日本软文发稿:日本主流发稿媒体有哪些?

日本软文发稿&#xff1a;日本主流发稿媒体有哪些 在日本发布软文时&#xff0c;选择合适的主流媒体进行推广是非常关键的。以下是一些在日本广受欢迎、影响力较大的媒体推荐&#xff08;排列不区分媒体排名顺序&#xff09;&#xff1a; 1. 朝日新闻 (Asahi Shimbun) 朝日新…

ChildLife童年时光创始人Murray Clarke亮相CBME并解析技术创新

2024年7月17日至19日&#xff0c;全球知名的孕婴童产品展览会——CBME国际孕婴童展在上海盛大开幕。作为这一领域最具影响力的展会之一&#xff0c;CBME吸引了众多国际知名品牌前来参展。美国知名儿童营养品牌ChildLife童年时光也携其重磅新品“液体钙小绿钙”亮相本次展会。Ch…

【测试架构师修炼之道】读书笔记

六大质量属性 效率性能 测试类型&#xff1a;六种-XX属性转化为XX测试 产品测试车轮图 一个软件测试者要从哪些方面(测试类型)用哪些方法(测试方法)去测试产品(质量属性)的关系图 全面性与深度 稳定性测试&#xff1a;多并复异 性能测试&#xff1a; 系统能够正确处理新业…

格式化的硬盘怎么恢复数据?格式化数据恢复的7个小妙招,助你快速恢复文件

硬盘格式化不仅可以提升计算机性能、释放空间&#xff0c;还可修复部分错误。通常&#xff0c;在进行硬盘格式化前&#xff0c;系统会发出数据将被删除的警告。然而&#xff0c;有时即使有警告&#xff0c;也可能不慎格式化硬盘导致重要文件丢失。在这种情况下&#xff0c;您需…

微软GraphRAG,开启智能检索新篇章

©作者|YXFFF 来源|神州问学 1. 引言 检索增强生成&#xff08;RAG&#xff09;是一种根据用户的查询语句搜索信息&#xff0c;并以搜索结果为 AI 参考从而生成回答。这项技术是多数基于 LLM 工具的重要组成部分&#xff0c;而多数的 RAG 都采用向量相似性作为搜索的技术。…

【过题记录】 8.2 hddx

飞行棋 关于这一题 我在考场上手莫了n2和n3的情况 发现一点规律&#xff0c;大力猜想蒙了一个结论 结果蒙对了… 关于正确做法&#xff0c;发现零号点和其他几个点是不一样的。 因为对于0而言&#xff0c;他没有赠送的情况(只要摇到n就直接胜利) 因此0和其他点要分开讨论 对于…