Linux的热插拔UDEV机制和守护进程

news2024/11/23 7:36:16

目录

一、Linux的热插拔UDEV机制

二、守护进程

2.1 守护进程概念和基本特点:

2.2 显示进程信息:

2.3 守护进程和后台进程的区别:

2.4 创建守护进程的步骤和守护进程的特征:

2.4.1 创建守护进程的步骤:

2.4.2 守护进程的特征:

2.5 创建守护进程函数daemon()原型和头文件:

2.6 创建守护进程案例(获取当前时间并写入到日志文件中):

2.7 设置守护进程开机自启动:

三、守护进程应用

3.1 判断某进程是否在运行:

3.2 守护进程不让控制程序退出:

3.3 把相关守护进程设置成开机自启动:

四、UDEV的配置文件(Udev的rules编写)

4.1 UDEV的配置文件:

4.2 UDEV规则的匹配键:

五、自动挂载U盘

5.1 mount手动挂载U盘:

5.2 UDEV自动挂载U盘的配置文件:

5.3 实现自动挂载:


一、Linux的热插拔UDEV机制

Linux 的热插拔机制主要通过 Udev用户空间设备)来实现。Udev 是 Linux 系统中用于动态管理设备的设备管理器。以下是 Udev 实现热插拔的主要机制:

  1. 设备事件监测: Udev 不断监听内核发出的事件,以便检测设备的插入和拔出。内核通过 Netlink 通信机制向 Udev 发送设备事件。

  2. 规则匹配: 当 Udev 检测到设备事件时,它会根据预定义的规则(规则文件)来匹配事件中的设备信息。规则可以基于设备的各种属性、类型、路径等进行匹配。

  3. 规则文件: Udev 的规则文件通常存储在 /etc/udev/rules.d/ 目录下。这些规则文件定义了在特定条件下要执行的操作。规则文件的命名约定通常是 XX-name.rules,其中 XX 是两位数字,用于确定规则文件的加载顺序。

  4. 执行动作: 当规则匹配成功时,Udev 将执行相关的动作。动作可以包括创建设备节点、设置环境变量、执行脚本等。例如,可以在设备插入时创建相应的设备节点、加载适当的驱动程序等。

  5. 设备节点管理: Udev 负责在 /dev 目录下创建或删除设备节点。这确保了用户和应用程序可以在一个标准位置找到设备节点,而无需关心设备插入的确切时刻。

  6. 持久化设备节点: Udev 确保设备节点的持久性,即使设备在重新启动后未连接,也会分配相同的设备节点名称。这有助于确保应用程序可以依赖特定的设备节点。

总体而言,Udev 通过规则匹配和执行动作的方式,实现了对设备事件的监听和处理,从而支持 Linux 系统的热插拔机制。这使得在系统运行时插入或拔出设备时,系统能够动态地适应这些变化。

  • udev是一个设备管理工具udev以守护进程的形式运行,通过侦听内核发出来的uevent来管理 /dev目录下的设备文件。通过侦听内核发出来的硬件数据(事件),在用户空间为这个硬件去创建对应代表该硬件的文件,应用程序例如adb就可以通过该文件操作到硬件设备。

  • udev在用户空间运行,而不在内核空间运行。它能够根据系统中的硬件设备的状态动态更新设备文件,包括设备文件的创建,删除等设备文件通常放在 /dev目录下。使用 udev后,在 /dev目录下就只包含系统中真正存在的设备。

二、守护进程

守护进程(Daemon Process)是在计算机系统后台运行的一类进程,它们通常在系统启动时启动,不依赖于用户直接操作,一直运行在后台,不受用户登录或注销的影响。守护进程的存在主要是为了执行特定的系统任务或服务,例如服务器、网络服务、定时任务等。

2.1 守护进程概念和基本特点:

  • Linux Daemon守护进程)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务。Linux系统的大多数服务器就是通过守护进程实现的。常见的守护进程包括系统日志进程syslogd、 web服务器httpd、邮件服务器sendmail和数据库服务器mysqld等。守护进程的名称通常以d结尾

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

基本特点

  • 生存周期长[非必须],一般操作系统启动的时候就启动,关闭的时候关闭。

  • 守护进程和终端无关联,也就是他们没有控制终端,所以当控制终端退出,也不会导致守护进程退出

  • 守护进程是在后台运行,不会占着终端,终端可以执行其他命令

  • 一个守护进程的父进程是init进程,因为它真正的父进程在fork出子进程后就先于子进程exit退出了,所以它是一个由init继承的孤儿进程

2.2 显示进程信息:

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

  • ps -efj 是一个用于显示进程信息的命令。具体解释如下:

ps -efj		//显示进程信息
    
ps		进程状态命令
-e		显示所有进程,而不仅仅是与终端相关的进程
-f		以全格式显示
-j		以作业格式显示

在这个命令中,-ef 是常用组合,用于显示所有进程的详细信息。而 -j 则以作业格式显示,包括作业控制的信息。

输出的各列含义如下:

  1. UID (User ID): 进程的用户标识。

  2. PID (Process ID): 进程的唯一标识号。

  3. PPID (Parent Process ID): 父进程的标识号。

  4. PGID (Process Group ID): 进程组的标识号。

  5. SID (Session ID): 会话的标识号

  6. TTY (Controlling Terminal): 控制终端。

  7. TPGID (Foreground Group ID of the controlling terminal): 控制终端的前台进程组的标识号。

  8. STAT (Process Status): 进程的状态,如 R(运行)、S(睡眠)、Z(僵尸)等。

  9. TIME (Cumulative CPU time): 进程执行的累计 CPU 时间。

  10. COMMAND (Command with all its arguments): 进程的命令及其参数。

这个命令的输出显示了当前系统上所有进程的详细信息,以及它们的层次结构和作业控制信息。

2.3 守护进程和后台进程的区别:

  1. 守护进程和终端不挂钩;后台进程能往终端上输出东西(和终端挂钩);

  2. 守护进程关闭终端时不受影响,守护进程不会随着终端的退出而退出;

守护进程(Daemon Process)和后台进程(Background Process)是两个不同的概念,它们有以下区别:

  • 启动方式:

守护进程: 通常由系统启动,独立于控制终端,以在后台运行的方式启动。 ​ 后台进程: 可以由用户手动将一个前台进程移至后台,即使它最初是由用户启动的。

  • 是否与终端关联:

守护进程: 通常不与任何控制终端关联,因此它们不受终端关闭的影响。 ​ 后台进程: 可以从终端启动,并且可能受终端的影响。

  • 执行环境:

守护进程: 在后台执行,并且通常在系统引导时启动。它们通常在系统运行期间一直存在。 ​ 后台进程: 通常是用户在前台启动的进程,后来放到后台执行,可能在终端关闭后继续运行。

  • 继承环境:

守护进程: 通常会脱离终端会话,重新设置文件权限,改变工作目录等,以确保独立于用户终端的环境。 ​ 后台进程: 通常继承自其父进程(通常是终端 Shell)的环境。

  • 用户交互性:

守护进程: 通常不与用户进行交互,因为它们不关联于任何终端。 ​ 后台进程: 在启动时可能与用户进行交互,但放到后台后通常不再与用户直接交互。

总的来说,守护进程是一种设计用于在系统后台一直运行的进程,而后台进程是一种在终端启动后被放到后台执行的进程。

2.4 创建守护进程的步骤和守护进程的特征:

2.4.1 创建守护进程的步骤:
  1. 调用 fork 函数: 创建一个子进程,父进程退出,子进程继续执行。

  2. 调用 setsid 函数: 创建一个新的会话组,使得子进程成为新的会话组长。

  3. 修改工作目录: 为了防止卸载文件系统导致守护进程找不到文件,通常需要更改工作目录。

  4. 重定向标准输入输出: 将标准输入、输出、错误重定向到适当的文件。

  5. 关闭文件描述符: 关闭不需要的文件描述符,避免占用系统资源。

  6. 处理信号: 捕获并处理一些信号,如 SIGHUP

创建和管理守护进程需要谨慎处理,确保进程以安全和可靠的方式在后台运行。

2.4.2 守护进程的特征:
  1. 后台运行: 守护进程通常在后台运行,不与任何终端关联,不接受用户的直接输入。

  2. 脱离终端: 守护进程通常会调用 fork 函数创建一个子进程,并使得子进程成为新的会话组长,从而脱离与终端的关联。

  3. 文件描述符: 守护进程通常会关闭与终端相关的文件描述符,以防止被终端关闭影响进程运行。

  4. 重定向标准输入输出: 守护进程通常会将标准输入、输出、错误重定向到 /dev/null 或其他适当的文件,以防止输出信息到终端。

  5. 权限: 守护进程通常以超级用户或其他特定用户的身份运行,以便执行需要特殊权限的任务。

  6. 信号处理: 守护进程通常会捕获并处理一些特定的信号,如 SIGHUP,以便在配置文件更改或其他条件下重新加载配置。

  7. 周期性任务: 守护进程通常执行一些周期性的任务,如定时清理、日志轮转等。

  8. 示例: 常见的守护进程包括 sshd(SSH 服务器守护进程)、httpd(Apache HTTP 服务器守护进程)等

2.5 创建守护进程函数daemon()原型和头文件:

/*
	Linux下 man daemon查看手册
*/
#include <unistd.h>

int daemon(int nochdir, int noclose);

int 			函数返回值,函数成功创建守护进程返回0,失败返回-1
    
int nochdir		参数用于指示是否改变守护进程的当前工作目录。如果 nochdir 为非零值,守护进程的当前工作目录将保持不变,
    			为0时表示将当前目录更改至“/”
    
int noclose		参数用于指示是否关闭所有的文件描述符。如果 noclose 为非零值,守护进程将不会关闭标准输入、标准输出和标准错误。
    			为0时表示将标准输入、标准输出、标准错误重定向至“/dev/null”

2.6 创建守护进程案例(获取当前时间并写入到日志文件中):

#include <unistd.h>     // 包含unistd.h头文件,提供对POSIX操作系统API的访问,包括各种系统调用
#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>    // 包含布尔类型定义的头文件

//C 库函数 char *asctime(const struct tm *timeptr) 返回一个指向字符串的指针,它代表了结构 struct timeptr 的日期和时间。
//C 库函数 struct tm *localtime(const time_t *timer) 使用 timer 的值来填充 tm 结构。timer 的值被分解为 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; 夏令时
};
*/
static bool flag = true;
 
void handler(int sig)                                                                   //信号处理函数
{
    printf("I got a signal %d\nI'm quitting.\n", sig);                                  //打印信息
    flag = false;                                                                   //设置flag为false,退出循环
}
 
int main()
{
    time_t t;
    int fd;
    
    //创建守护进程
    if (-1 == daemon(0, 0)){              //创建守护进程,第一个参数为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");                                                     //打印错误信息
        	exit(0);                                                                    //退出程序
        }
        
        t = time(0);                                                                    //获取当前时间
        char *buf = asctime(localtime(&t));                                             //格式化时间字符串
        write(fd, buf, strlen(buf));                                                    //写入日志文件
        close(fd);                                                                      //关闭日志文件
        sleep(10);                                                                      //睡眠10秒
    }
    return 0;
}

可见,时间信息确实不断的追加打印到了这个文件!并且,只要不调用SIGQUIT,哪怕关掉终端也不会结束运行,只有系统关闭才会关闭

主要步骤:

  1. 使用daemon函数创建守护进程。

  2. 设置信号处理函数,处理SIGQUIT信号。

  3. 在循环中,打开文件/home/orangepi/daemon.log,将当前时间写入文件,然后关闭文件,然后休眠10秒。

  4. 当接收到SIGQUIT信号时,程序会打印信息并退出。

请确保在运行此程序之前,你有对/home/orangepi/目录的写入权限,并且该目录下没有同名的文件。

2.7 设置守护进程开机自启动:

守护进程一般是开机自启的,实现这一点可以通过“sudo vi /etc/rc.local”,然后添加守护进程的绝对路径来实现:

sudo vi /etc/rc.local

 我们重启开发板之后可以看到这个守护进程是开启自启动的:

三、守护进程应用

实现功能:要求抖音语音交互的程序一直保持运行,防止应用程序崩溃意外

3.1 判断某进程是否在运行:

#include <stdio.h>      // 包含标准输入输出库的头文件
#include <string.h>     // 包含字符串操作库的头文件

int main()
{
    FILE *fp;                                               // 定义文件指针
    char buffer[128] = {'\0'};                              // 定义缓冲区
    char *cmd = "ps -elf |grep douyinexe |grep -v grep";    // 定义命令

    //FILE *popen(const char *command, const char *type);
    
    fp = popen(cmd, "r");                                   // 使用 popen 函数执行命令并读取结果
    fgets(buffer, sizeof(buffer), fp);                      // 读取命令的输出

    if(strstr(buffer, "douyinexe") != NULL){                // 判断是否存在 douyinexe 进程
        printf("Douyin is running.\n");
    }else{
        printf("Douyin is not running.\n");
    }

    printf("buffer: %s\n",buffer);                          // 打印命令的输出
    pclose(fp);                                             // 关闭命令的输出管道
    return 0;
}

3.2 守护进程不让控制程序退出:

#include <unistd.h>     // 包含unistd.h头文件,提供对POSIX操作系统API的访问,包括各种系统调用
#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;                                                                   //设置flag为false,退出循环
}

int existMent()                                                                     //判断是否存在抖音应用程序
{
    FILE *fp;                                               // 定义文件指针
    char buffer[128] = {'\0'};                              // 定义缓冲区
    char *cmd = "ps -elf |grep douyinexe |grep -v grep";    // 定义命令

    //FILE *popen(const char *command, const char *type);
    
    fp = popen(cmd, "r");                                   // 使用 popen 函数执行命令并读取结果
    fgets(buffer, sizeof(buffer), fp);                      // 读取命令的输出

    if(strstr(buffer, "douyinexe") != NULL){                // 判断是否存在 douyinexe 进程
        return 0;                                          // 存在,返回0
    }else{
        return -1;                                          // 不存在,返回-1
    }

    printf("buffer: %s\n",buffer);                          // 打印命令的输出
    pclose(fp);                                             // 关闭命令的输出管道
}
 
int main()
{
    time_t t;
    int fd;
    
    //创建守护进程
    if (-1 == daemon(0, 0)){               //创建守护进程,第一个参数为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(existMent() == -1){
            system("/home/orangepi/douyin/douyinexe /dev/ttyS5 &");      // 启动抖音应用程序,后台运行                                               
            sleep(2);                                                    // 等待2秒,确保抖音应用程序启动成功    
        }
    }
    return 0;
}

3.3 把相关守护进程设置成开机自启动:

设置douyinexe进程和守护进程douyindaemon为开启自启动:

sudo vi /etc/rc.local 开机自启动,绝对路径加程序名字

这样开机之后自动的运行刷抖音程序和守护进程程序,不需要用指令来运行程序就可以直接语音刷抖音了。

四、UDEV的配置文件(Udev的rules编写)

4.1 UDEV的配置文件:

  • 规则文件是 udev 里最重要的部分,默认是存放在 /etc/udev/rule.d/ 下。

  • 所有的规则文件必须以 ".rules"为后缀名。

下面是一个简单的规则:

KERNEL=="sda", NAME="my_root_disk", MODE="0660"
  • KERNEL 是匹配键,NAMEMODE 是赋值键

  • 这条规则的意思是:如果有一个设备的内核名称为sda,则该条件生效,执行后面的赋值:在 /dev 下产生一个名为my_root_disk 的设备文件,并把设备文件的权限设为 0660。

同时在之前我们的语音刷抖音的项目当中,手机接入香橙派不能识别的解决方案也是在udev的rules文件夹下创建规则文件“ 51-android.rules ”,并写入以下规则:

SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", MODE="0666"

那为什么这个规则要这么写呢?

首先插入手机的USB设备对应的设备文件在/dev/bus/usb/002/006下:

通过指令:udevadm info --attribute-walk --name=/dev/设备名字,可以看到设备的详细信息,此处的设备名字就是006  

4.2 UDEV规则的匹配键:

  • ACTION:事件(uevent)的行为,例如:add(添加设备)、remove(删除设备);

  • KERNEL:内核设备名称,例如:sda,cdrom;

  • DEVPATH:设备的 devpath 路径;

  • SUBSYSTEM:设备的子系统名称,例如:sda 的系统为 block;

  • BUS:设备在 devpath 里的总线名称,例如:usb;

  • DRIVER:设备在 devpath 的设备驱动名称,例如:ide-cdrom;

  • ID:设备在 devpath 里的识别号;

  • SYSFS{filename}:设备的 devpath 路径下,设备的属性文件 "filename" 里的内容;

  • ENV{key}:环境变量。在一条规则中,可以设定最多五条环境变量的 匹配键;

  • PROGRAM:调用外部命令;

  • RESULT:外部命令 PROGRAM 的返回结果。

五、自动挂载U盘

把我的U盘拿出来,插到香橙派的USB口:

然后执行dmesg指令:

可以看到Linux内核识别到了这个设备,并显示为sda1

5.1 mount手动挂载U盘:

sudo mount /dev/sda1 /mnt/
cd /mnt/

我们可以看到U盘里面的文件和文件夹,但是是乱码的,乱码的原因是因为文件名是中文的,但是这样很麻烦,每次挂载U盘都得执行两个指令才可以挂载,可以使用UDEV来实现自动挂载,我们先把U盘弹出,然后再使用UDEV机制自动挂载U盘:  

5.2 UDEV自动挂载U盘的配置文件:

我们首先先执行下面一条指令来查看U盘的信息:

udevadm info --attribute-walk --name=/dev/sda1

ACTION=="add", SUBSYSTEMS=="usb", SUBSYSTEM=="block", RUN{program}+="/bin/mkdir /media/%k" ,RUN{program}+="/usr/bin/systemd-mount --no-block --collect $devnode /media/%k"

这是一个udev规则,通常用于在插入USB存储设备时自动创建挂载点并挂载设备。这个规则的含义如下:

  • ACTION=="add":规则只在添加设备时触发。

  • SUBSYSTEMS=="usb":设备必须属于USB子系统。

  • SUBSYSTEM=="block": 设备必须是块设备。

  • RUN{program}+="/bin/mkdir /media/%k": 当设备满足规则条件时,运行命令创建 /media/ 目录下以设备名称(%k)命名的目录。

  • RUN{program}+="/usr/bin/systemd-mount --no-block --collect $devnode /media/%k": 运行 systemd-mount 命令,将设备挂载到之前创建的目录。

这个规则的效果是,当插入一个USB块设备时,udev将创建一个以设备名称为名字的目录(例如,如果设备名称是sdb1,那么将创建 /media/sdb1 目录),然后使用systemd-mount挂载设备。

请注意,使用systemd-mount需要系统使用systemd init系统。此外,一般情况下,挂载点应该在/mnt/目录下而不是/media/,因为/media/通常是由文件管理器等工具使用的默认挂载点。

cd /etc/udev/rules.d/
sudo vim my_usbpan.rules
sudo service udev restart

5.3 实现自动挂载:

重新拔插U盘,我们查看media文件夹是否挂载成功:

cd /media

可以看到我们没有输入任何指令,就可以访问U盘里面的文件,实现了自动挂载。另外我们还可以使用tree指令来查看U盘文件:

sudo apt-get install tree		//安装tree指令

此时我们成功自动挂载U盘,使用tree指令来查看U盘的文件,如果我们现在拔掉U盘我们还会发现什么文件都没有了:

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

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

相关文章

京东Android一面凉经(2024)

京东Android一面凉经(2024) 笔者作为一名双非二本毕业7年老Android, 最近面试了不少公司, 目前已告一段落, 整理一下各家的面试问题, 打算陆续发布出来, 供有缘人参考。今天给大家带来的是《京东Android一面凉经(2024)》。 面试职位: Android开发工程师 技术一面 面试时长: 50…

Plant simulation 中快速创建相同属性轨道的方法

问题&#xff1a;默认的轨道宽度是0.3m&#xff0c;默认轨道是水平的。如果要创建大量宽度1m&#xff0c;竖着的轨道有没有效率的方法呢&#xff1f;答案肯定是有的&#xff0c;按下面方法操作即可。 1、复制两个轨道模型到用户对象中&#xff0c;命名为水平轨道和竖直轨道。 …

开源免费的一个企业级商城系统

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 系统简介 ShopXO&#xff1a; 是一款企业级免费开源商城系统&#xff0c;具备可视化DIY拖拽装修功能&#xff0…

[240720] X-CMD 发布 v0.4.1:新增 OpenAI GPT-4o mini 模型|优化 df ip dns ...

目录 X-CMD 发布 v0.4.1✨ openai✨ chat✨ df✨ ip✨ kev✨ dns✨ shodan✨ pick✨ theme X-CMD 发布 v0.4.1 ✨ openai 更新 X-CMD 现已支持 OpenAI 最新模型 GPT-4o mini&#xff01; &#x1f389; 用户只需使用 gpt4om 即可直接调用这款强大的模型。gpt 的默认模型由 g…

PyTorch张量数值计算

文章目录 1、张量基本运算2、阿达玛积3、点积运算4、指定运算设备⭐5、解决在GPU运行PyTorch的问题 &#x1f343;作者介绍&#xff1a;双非本科大三网络工程专业在读&#xff0c;阿里云专家博主&#xff0c;专注于Java领域学习&#xff0c;擅长web应用开发、数据结构和算法&am…

计算机的错误计算(三十二)

摘要 在计算机的错误计算&#xff08;二十八&#xff09;与&#xff08;三十 一&#xff09;中&#xff0c;我们探讨了 Visual Studio 对 6个随机exp(x)函数的计算精度问题。根据网友的反馈&#xff0c;本节将展示 Python 对它们的输出&#xff1a;结果几乎与 Visual Studio …

C# —— CRC16 算法

CRC16:即循环冗余校验码。数据通信当中一种常用的查错校验码 其特征信息字段和校验字段的长度可以是任意选定的&#xff0c;对数据进行指定多项式计算 并且将得到的结果附加在帧的后面&#xff0c;接受的设备也执行类似的算法&#xff0c;以保证数据传输的正确性和完整性 crc…

ELK日志收集

一、什么是ELK ELK 是由 Elasticsearch、Logstash、Kibana 三个开源软件的组成的一个组合体&#xff0c;ELK 是 elastic 公司研发的一套完整的日志收集、分析和展示的企业级解决方案。 ELK 的好处&#xff1a; ELK 组件在大数据运维系统中&#xff0c;主要可解决的问题如下&…

录取查询老师在哪里制作?

随着考试的落幕&#xff0c;家长们焦急等待的心情终于可以稍微缓解&#xff0c;因为录取结果即将揭晓。然而&#xff0c;对于老师来说&#xff0c;这仅仅是另一项繁重工作的开始。他们需要将每一份录取通知单逐一发送给学生家长&#xff0c;这个过程不仅耗时而且容易出错。面对…

基于SSM的高考志愿选择辅助系统

基于SSM的高考志愿选择辅助系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringSpringMVCMyBatis工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 前台 前台首页 院校展示 后台 后台首页 学校管理 摘要 随着高考制度的不断完…

数据结构 day4

目录 思维导图&#xff1a; 学习内容&#xff1a; 1. 链表的引入 1.1 顺序表的优缺点 1.1.1 优点 1.1.2 不足 1.1.3 缺点 1.2 链表的概念 1.2.1 链式存储的线性表叫做链表 1.2.2 链表的基础概念 1.3 链表的分类 2. 单向链表 2.1 节点结构体类型 2.2 创建链表 2.…

C语言 ——— 浮点数类型 在 内存中 的 存储模式

目录 浮点数存储规则 单\双精度浮点数 存储 S、M、E 的布局 有效数字M 和 指数位E 的特殊规定 浮点数在内存中是否存储的S、M、E 浮点数存储规则 根据国际标准IEEE754&#xff08;电气和电子工程协会&#xff09;规定&#xff1a;任意一个 浮点数F的二进制 都可以表示成…

19集 两款ESP32开发板如何选择?-《MCU嵌入式AI开发笔记》

19集 两款ESP32开发板我们用哪款&#xff1f;-《MCU嵌入式AI开发笔记》 有两款ESP32的开发板分别是ESP32 S3 和C3的&#xff0c;我们该如何选择&#xff1f; 1、ESP32-S3-BOX-3 在乐鑫官网上&#xff0c;https://www.espressif.com.cn/zh-hans/products/devkits 有ESP32S3 B…

Unity UGUI 之 Canvas画布

本文仅作学习笔记与交流&#xff0c;不作任何商业用途 本文包括但不限于unity官方手册&#xff0c;唐老狮&#xff0c;麦扣教程知识&#xff0c;引用会标记&#xff0c;如有不足还请斧正 1.UGUI是什么 首先官方手册导向了这两个连接&#xff0c;里面是ugui的基本教程 帆布 |U…

【全面升级!短视频运营利器揭秘】短视频矩阵系统

短视频矩阵通常是指一种内容分发策略&#xff0c;它通过构建一个包含多种不同类型、主题或风格短视频的内容平台架构。这个矩阵可能会包括搞笑、教育、音乐、美食、旅游等各种短视频形式&#xff0c;让用户可以根据兴趣选择观看。每个视频可以视为矩阵中的一个节点&#xff0c;…

JVM-垃圾回收与内存分配

目录 垃圾收集器与内存分配策略 引用 对象的访问方式有哪些?&#xff08;句柄和直接指针&#xff09; Java的引用有哪些类型? 如何判断对象是否是垃圾? 请列举一些可作为GC Roots的对象? 对象头了解吗? mark word&#xff08;hashcode、分代、锁标志位&#xff09;、…

Linux 12:多线程2

1. 生产者消费者模型 生产者消费者模型有三种关系&#xff0c;两个角色&#xff0c;一个交易场所。 三种关系&#xff1a; 生产者之间是什么关系?竞争 - 互斥 消费者和消费者之间?竞争 - 互斥 消费者和消费者之间?互斥和同步 两个角色&#xff1a; 生产者和消费者 一个交…

记录|C# winform布局学习

目录 前言一、自适应布局Step1. 添加AutoAdaptWindowsSize类Step2. Form中引用Step3. 创建SizeChanged事件函数Step4. 在Fram.Disiger中添加 更新时间 前言 参考视频&#xff1a; C#5分钟winform快速自适应布局 参考文章&#xff1a; 其他参考&#xff1a; 写这篇文章&#xff…

【python学习】python的知识点总结、特点和思考及解答(代码示例)

引言 python 是一种高级编程语言&#xff0c;具有简洁的语法和丰富的库&#xff0c;被广泛应用于Web开发、数据分析、人工智能、科学计算等领域 文章目录 引言一、python知识点总结1.1 基础语法1.1.1 变量和数据类型1.1.2 控制结构1.1.3 函数和模块 1.2 面向对象编程1.2.1 类和…

vue的三大核心知识点

响应式&#xff1a; 监听data属性getter setter(包括数组)模板编译&#xff1a; 模板到render函数再到vnodevdom&#xff1a; patch(elem, vnode)和patch(vnode, newVnode) vue组件初次渲染过程 解析模板为render函数&#xff08;或在开发环境已完成&#xff0c;vue-loader&a…