使用ioctl扫描wifi信号获取信号属性的实例(二)

news2025/7/19 3:19:06

使用工具软件扫描 wifi 信号是一件很平常的事情,在知晓 wifi 密码的前提下,通常我们会尽可能地连接信号质量比较好的 wifi 信号,但是如何通过编程来扫描 wifi 信号并获得这些信号的属性(比如信号强度等),却鲜有文章提及,本文在前面博文的基础上通过实例向读者介绍如何通过编程扫描 wifi 信号,并获得信号的一系列的属性,本文给出了完整的源代码,本文程序在 ubuntu 20.04 下编译测试完成,gcc 版本号 9.4.0;阅读本文并不需要对 IEEE802.11 协议有所了解,但本文实例中大量涉及链表和指针,所以本文可能不适合初学者阅读。

1 前言

  • 在『网络编程专栏』专栏里写过一篇wifi信号扫描的文章《使用ioctl扫描wifi信号获取信号属性的一个范例(一)》,与该文相比本文所附带的实例将可以获取更多的 wifi 属性;
  • 在阅读本文前,请阅读《使用ioctl扫描wifi信号获取信号属性的一个范例(一)》,并请理解范例中的程序,该文中所涉及的概念以及数据结构,本文将不再做介绍;
  • 在《使用ioctl扫描wifi信号获取信号属性的一个范例(一)》中,我们使用 ioctl() 启动了 wifi 信号的扫描,并获取了 wifi 信号的 SSID、MAC地址、工作频率和工作信道,但有一些重要的信号属性并没有获得,比如:信号强度、信号质量、信号噪音以及加密方式等,本文将讨论如何获取这些属性;
  • 本文提供的实例的基本框架与文章《使用ioctl扫描wifi信号获取信号属性的一个范例(一)》中的基本一致;
  • 本文所提供的实例中并不需要第三方库的支持,所以不需要安装任何其它支持软件和库。

2 遍历网络设备列表

  • 在对无线网卡操作之前,首先要找到无线网卡的设备名,在文章《使用ioctl扫描wifi信号获取信号属性的一个范例(一)》中,使用 getifaddrs() 找到所有的网络接口,然后再用 ioctl() 的 SIOCGIWNAME 命令从中找到无线网卡;

  • 其实我们可以从 /proc/net/dev 中找到所有的网络接口,而不必使用 getifaddrs()

  • 就本文而言,需要知道的就是无线网卡的设备名,我们也可以从文件 /proc/net/wireless 文件中直接获得,这种方法更加简单一些,先来看一下这个文件中有什么内容:

    $ cat /proc/net/wireless 
    Inter-| sta-|   Quality        |   Discarded packets               | Missed | WE
    face  | tus | link level noise |  nwid  crypt   frag  retry   misc | beacon | 22
    wlp1s0: 0000   70. -256.  -256     0      0      0      0      1        0
    $ 
    
  • 可以看到,这个文件的前两行是标题,从第三行起开始是无线网卡的信息,其中接口名称后面紧跟着 ": ";

  • 这台电脑上只有一个无线网卡,其接口名称为:wlp1s0,下面程序片段从 /proc/net/wireless 中提取出无线接口的名称:

    FILE *fh;
    char buff[1024];
    
    fh = fopen("/proc/net/wireless", "r");
    if (fh != NULL) {
        // Skip 2 lines of header
        fgets(buff, sizeof(buff), fh);
        fgets(buff, sizeof(buff), fh);
        // Read each device line
        while (fgets(buff, sizeof(buff), fh)) {
            char name[IFNAMSIZ + 1];
    
            // Skip empty lines.
            if ((buff[0] == '\0') || (buff[1] == '\0')) continue;
            // Extract interface name
            char *p = buff;
            // Skip leading spaces
            while (isspace(*p)) p++;
            char *end;
            end = strstr(buf, ": ");
            // Not found
            if (end == NULL) continue;
            // Copy
            memcpy(name, p, (end - p));
            name[end - p] = '\0';
            printf("The wireless interface name is %s\n", name);
        }
        fclose(fh);
    } else {
        printf("Can't open file /proc/net/wireless\n");
    }
    

3 信号质量、信号强度、信号噪音的获取

  • 通过阅读文章《使用ioctl扫描wifi信号获取信号属性的一个范例(一)》,应该可以了解如何使用 ioctl() 启动 wifi 信号的扫描并获得扫描结果,在此简单回顾一下:
    • struct iwreq 定义,其中 struct iwreq_data 见下面定义;
      struct iwreq {
          union
          {
              char  ifrn_name[IFNAMSIZ];  /* if name, e.g. "eth0" */
          } ifr_ifrn;
      
          /* Data part (defined just above) */
          union iwreq_data  u;
      };
      
    • ioctl() 的调用方式:int ioctl(int socket, unsigned long request, struct iwreq *wrq)
    • 启动 wifi 扫描时,request=SIOCSIWSCANwrq->ifr_name 设置为无线接口名称,wrq->u.data.pointer=NULLwrq->u.data.flags=0wrq->u.data.length=0,然后调用 ioctl();
    • 获取扫描结果时,request=SIOCGIWSCANwrq->u.data.pointer=接收数据缓冲区指针wrq->u.data.length=接收缓冲区的长度wrq->u.data.flags=0,然后调用 ioctl(),调用时,wrq->ifr_name 也是要设置为无线接口名称的,只是因为在启动扫描时已经设置过,所以这里通常不需要再设置;
    • 如果返回的扫描结果数据比较大,设置的接收缓冲区不够用,ioctl() 将返回 -1,errno 为 E2BIG,此时应该重新为缓冲区分配内存并再次调用 ioctl() 获取扫描结果;
    • 如果在使用 ioctl() 获取扫描结果时,扫描还没有完成,ioctl() 将返回 -1,errno 为 EAGAIN,此时应该等待一会再次调用 ioctl() 获取扫描结果;
    • 正常获取扫描结果时,wreq->u.data.falgs 将被设为 1(调用时为 0),wreq->u.data.length 中为返回数据的实际长度,返回的数据被存放在 wreq->u.data.pointer 指向的数据缓冲区中;
  • 返回的扫描结果是一个数据流(stream),其中包含着许多的事件(event),每个 event 包含着一个属性,返回的扫描结果数据符合 struct iw_event,每个 event 数据也符合 struct iw_event,这个结构定义在 wireless.h 中:
    union iwreq_data {
        /* Config - generic */
        char                name[IFNAMSIZ];
        /* Name : used to verify the presence of  wireless extensions.
         * Name of the protocol/provider... */
    
        struct iw_point     essid;      /* Extended network name */
        struct iw_param     nwid;       /* network id (or domain - the cell) */
        struct iw_freq      freq;       /* frequency or channel :
                                         * 0-1000 = channel
                                         * > 1000 = frequency in Hz */
    
        struct iw_param     sens;       /* signal level threshold */
        struct iw_param     bitrate;    /* default bit rate */
        struct iw_param     txpower;    /* default transmit power */
        struct iw_param     rts;        /* RTS threshold threshold */
        struct iw_param     frag;       /* Fragmentation threshold */
        __u32               mode;       /* Operation mode */
        struct iw_param     retry;      /* Retry limits & lifetime */
    
        struct iw_point     encoding;   /* Encoding stuff : tokens */
        struct iw_param     power;      /* PM duration/timeout */
        struct iw_quality   qual;       /* Quality part of statistics */
    
        struct sockaddr     ap_addr;    /* Access point address */
        struct sockaddr     addr;       /* Destination address (hw/mac) */
    
        struct iw_param     param;      /* Other small parameters */
        struct iw_point     data;       /* Other large parameters */
    };
    struct iw_event {
        __u16               len;        /* Real length of this stuff */
        __u16               cmd;        /* Wireless IOCTL */
        union iwreq_data    u;          /* IOCTL fixed payload */
    }
    
  • struct iw_event 的前两个字段,len 表明了这个 event 的数据长度,cmd 表明了这个 event 的类别,不同的 event,字段 u 中对应的数据结构也不相同;
  • 在文章《使用ioctl扫描wifi信号获取信号属性的一个范例(一)》中,我们只解析了三个 cmd,SIOCGIWAP(MAC地址)、SIOCGIWESSID(SSID) 和 SIOCGIWFREQ(Frequence 和 Channel):
    • 当 cmd 为 SIOCGIWAP 时,字段 u 对应的数据结构为 struct sockaddr,MAC 地址存放在 u.addr.sa_data 的前 6 个字节中;
    • 当 cmd 为 SIOCGIWESSID 时,字段 u 对应的数据结构为 struct iw_essid,这个是自己定义的,在上面 union 中并没有列出,这个定义可以使问题更加简单一些;
    • 当 cmd 为 SIOCGIWFREQ 时,字段 u 对应的数据结构为 struct iw_freq freq,当计算出的频率大于 1000 时,则结果为 wifi 信号的工作频率,否则为该信号的工作信道;
  • 当 cmd 为 IWEVQUAL,获得的信息为统计数据的信号质量部分(Quality part of statistics),这部分数据中包括信号质量、信号强度、信号噪音等信息;
    • 此时字段 u 对应的数据结构为 struct iw_quality,在 wireless.h 中定义,如下:

      struct iw_quality {
          __u8        qual;       /* link quality */
          __u8        level;      /* signal level (dBm) */
          __u8        noise;      /* noise level (dBm) */
          __u8        updated;    /* Flags to know if updated */
      };
      
    • qual 字段为信号连接质量,先看一下使用无线工具 sudo iwlist [wifname] scan 扫描信号看到的信号连接质量是什么样子的,[wifname] 是无线网络接口的名称,在我的电脑上是 wlp1s0,不同电脑可能会不一样;

      Screenshot of wifi scanning

    • 图中红线所示部分就是信号连接质量,其表达方式为 47/70,这是什么含义呢?

    • WE(Wireless Extension) 假设信号范围为 -110dBm ~~ -40dBm,信号质量的值为信号强度 +110 得出的,这样信号质量值的范围为 0~~7047/70 的 47 表示信号连接质量值为 47,70 标志信号质量值最大为 70;

    • level 字段为信号的强度,其单位为 dBm(分贝毫瓦),通常用于表示无线电信号的功率,如上所述,正常情况下,level + 110 = qual

    • noise 字段为信号背景噪音的强度,这个字段在我的电脑上并不支持,如何判断是否支持,请看下面对 updated 字段的介绍;

    • updated 字段是一个位掩码(bit mask),在 wireless.h 中定义了该字段每个 bit 表达的含义,如下:

      /* Statistics flags (bitmask in updated) */
      #define IW_QUAL_QUAL_UPDATED    0x01    /* Value was updated since last read */
      #define IW_QUAL_LEVEL_UPDATED   0x02
      #define IW_QUAL_NOISE_UPDATED   0x04
      #define IW_QUAL_ALL_UPDATED     0x07
      #define IW_QUAL_DBM             0x08    /* Level + Noise are dBm */
      #define IW_QUAL_QUAL_INVALID    0x10    /* Driver doesn't provide value */
      #define IW_QUAL_LEVEL_INVALID   0x20
      #define IW_QUAL_NOISE_INVALID   0x40
      #define IW_QUAL_RCPI            0x80    /* Level + Noise are 802.11k RCPI */
      #define IW_QUAL_ALL_INVALID     0x70
      
      
    • 如果网卡驱动程序不支持 quality、level 或者 noise,则 IW_QUAL_QUAL_INVALID、IW_QUAL_LEVEL_INVALID 或者 IW_QUAL_NOISE_INVALID 对应的 bit 就会被置 1;

    • 如果自上次读取 quality、level 或者 noise 后,数据已经被网卡驱动程序再次更新,则 IW_QUAL_QUAL_UPDATED、IW_QUAL_LEVEL_UPDATED 或者 IW_QUAL_NOISE_UPDATED 对应的 bit 会被置 1;

4 无线信号的工作方式

  • wireless.h 中定义了 8 中无线信号的工作方式:
    /* Modes of operation */
    #define IW_MODE_AUTO        0   /* Let the driver decides */
    #define IW_MODE_ADHOC       1   /* Single cell network */
    #define IW_MODE_INFRA       2   /* Multi cell network, roaming, ... */
    #define IW_MODE_MASTER      3   /* Synchronisation master or Access Point */
    #define IW_MODE_REPEAT      4   /* Wireless Repeater (forwarder) */
    #define IW_MODE_SECOND      5   /* Secondary master/repeater (backup) */
    #define IW_MODE_MONITOR     6   /* Passive monitor (listen only) */
    #define IW_MODE_MESH        7   /* Mesh (IEEE 802.11s) network */
    
  • 我们扫描到的信号,大多数应该是 Master;
  • 当使用 ioctl() 扫描无线信号时,返回的 struct iw_event 中当 cmd 字段为 SIOCGIWMODE,该事件为工作方式;
  • 当 cmd 为 SIOCGIWMODE时,struct iw_event 中的 u.mode 为该无线信号的工作方式;

5 无线信号支持的传输速率

  • 当使用 ioctl() 扫描无线信号时,返回的 struct iw_event 中当 cmd 字段为 SIOCGIWRATE 时,该事件中的数据为信号支持的传输速率;
  • 一个信号支持的传输速率通常有很多种,所以这个数据通常也是有很多组的,下面是一组实际的数据(16进制数):
    0000:   28 00 21 8B 00 00 00 00 80 8D 5B 00 00 00 00 00 
    0010:   00 1B B7 00 00 00 00 00 00 36 6E 01 00 00 00 00 
    0020:   00 6C DC 02 00 00 00 00
    
  • 按照 struct iw_event 的定义,前两个字节是这个 event 的长度,为 0x0028,也就是 40 个字节,后面两个字节 0x8B21 是 cmd 字段,0x8b21 也就是 SIOCGIWRATE(见 wireless.h 中的定义),所以这个 event 的数据是传输速率;
  • 当收到的是传输速率时,struct iw_event 中的 u.bitrate 为对应的传输率的数据结构(见第 3 节关于 union iwreq_data 的介绍),u.bitrate 是一个 struct iw_param,其定义如下(见 wireless.h):
    struct iw_param {
        __s32   value;      /* The value of the parameter itself */
        __u8    fixed;      /* Hardware should not use auto select */
        __u8    disabled;   /* Disable the feature */
        __u16   flags;      /* Various specifc flags (if any) */
    };
    
  • 根据其定义,其中的 u.bitrate.value 字段即为传输速率;
  • 如上数据,一个 wifi 信号通常都是支持多种传输速率的,这时可以将数据部分定义成一个 struct iw_param 的结构数组,并通过 event 的长度和 struct iw_param 的长度计算得出这个 event 中有多少组传输速率的数据,如下:
    ......
    struct iw_event *evp = data;
    int rate_count = (evp.len - IW_EV_LCP_LEN) / sizeof(struct iw_param);
    struct iw_param *rates = &evp->u.bitrate;
    int i = 0;
    for (i = 0; i < rate_count; ++i) {
        ......
        printf("Bit rate: %d Mb/s\n", rates[i].value / 1000000);
    }
    
  • 其中 IW_EV_LCP_LEN 为 struct iw_event 中结构头(len 和 cmd 字段)长度(包含为对齐而填充的空字符),请见文章《使用ioctl扫描wifi信号获取信号属性的一个范例(一)》中的相关解释;
  • 当一个 WiFi 信号支持的传输速率比较多时,可能会收到两个 SIOCGIWRATE 事件(也许会有多个,但我没有遇到过),每个事件中的速率是不一样的,所以都需要处理,不能忽略任何一个事件;

6 beacon 相关信息

  • 当使用 ioctl() 扫描无线信号时,返回的 struct iw_event 中当 cmd 字段为 IWEVCUSTOM 时,该事件在 wireless.h 中定义为 “Driver specific ascii string”,意为:驱动程序特定的 ASCII 字符串;

  • AP 要周期性地在 wifi 上广播 beacon 帧,用于在网络上宣告一个 wifi 信号的存在,之所以可以扫描到 wifi 信号就是因为收到了 beacon 帧;

  • beacon 帧并不是本文要讨论的问题,本文不会展开讨论;

  • 回到 WiFi 信号的扫描主题上,wireless.h 中并没有定义一个事件可以收到有关 beacon 帧的信息,但是我们在事件 IWEVCUSTOM 中看到了 beacon 信息;

  • IWEVCUSTOM 事件中的这个字符串的结构与 essid 是一样的,所以可以用相同的方法提取,可以参考文章《使用ioctl扫描wifi信号获取信号属性的一个范例(一)》;

  • 有意思的是起初并不知道这个字符串中会有什么内容,但把收到的内容显示出来后,发现是类似下面的内容:

    Screenshot of driver specific ascii string

  • 图中红线所示就是收到的字符串,原来这个字符串中藏着与 beacon 帧有关的信息,Last beacon 是最后收到的 beacon 帧时间,TSF 是 Timing Synchronization Function 的缩写,是 beacon 帧中的一个字段,用于 AP 和 Station 之间同步时间;

  • 这个事件与 essid 还是有所不同,一个 WiFi 信号只有一个 essid,所以 SIOCGIWESSID 事件只会收到一次,但是 IWEVCUSTOM 有可能收到多次,而每次收到的字符串是不相同的,所以接收 IWEVCUSTOM 事件比接收 essid 还是要麻烦一些,在本文实例源程序中这部分有详细的中文注释;

7 Information Elements

  • 当使用 ioctl() 扫描无线信号时,返回的 struct iw_event 中当 cmd 字段为 IWEVGENIE 时,该事件在 wireless.h 中定义为 “Generic IE (WPA, RSN, WMM, …)”,意为通用 IE(Information Elements);

  • IE 可以提供非常多的信息,本文中仅就 SSID 和速率信息进行示范性的解析;

  • 首先,这个事件在大多数情况下都是提供多个 IE,所以事件通常比较长,而且长度并不固定;

  • 我们先来看一个实际收到的 IE 数据

    Real IE data

  • 数据与普通事件一样,符合 struct iw_event 结构,前两个字节是事件数据的长度,0x00D2(十进制 210)字节,第 3、4 字节为事件类型,0x8C05(IWEVGENIE) 表示这个事件中是 IE 数据;

  • 前四个字节组成了 struct iw_event 的头信息,紧跟着的 4 个字节为按 64 位对齐填充的字符,第 9、10 字节为实际 IE 数据所占的长度 ie_length,第 11-16 字节是对齐填充字节;

  • 所以从第 17 个字节(第 2 行)开始才是真正的 IE 数据,正是因为这个原因,通常 ie_length(第 9、10 字节) 比事件的总长度(第 1、2 字节)小 16(0x10),在这组实际数据中,事件长度为 0x00D2(十进制 210),而 IE 数据的长度为 0x00C2(十进制 194),要注意的是,事件长度是包含 struct iw_event 头信息的 8 个字节,而 IE 数据长度是不包括头信息的长度(仅为 IE 数据长度);

  • IEEE 标准 802.11-2007 文档中定义了 IE 的结构,该文档的下载地址如下:

    • Wireless LAN Medium Access Control (MAC) and Physical Layer (PHY) Specifications
  • 该标准的 7.3.2 Information elements 定义了 IE 的结构:

    IE Structure

  • 每个 IE 符合 type-length-value 格式,即:第 1 个字节表示 IE 类型 Element ID,第 2 个字节表示数据的长度 Length,后面若干字节为实际数据 Information,数据长度为 Length;

    struct ieee80211_ie {
        unsigned char eid;
        unsigned char len;
        unsigned char data[0];
    };
    
  • 当收到一个 IE 数据的事件时,可以考虑如下定义:

    struct iw_event_ie {
        unsigned short len;
        unsigned short cmd;
    #ifdef __x86_64__           // 64位系统按8字节对齐
        unsigned short __attribute((aligned(8)))ie_len;
        struct ieee80211_ie __attribute((aligned(8)))ie[0];
    #else                       // 32位系统按4字节对齐
        unsigned short __attribute((aligned(4)))ie_len;
        struct ieee80211_ie __attribute((aligned(4)))ie[0];
    #endif
    };
    
  • 字段 len 和 cmd 与 struct iw_event 是一致的,ie_len 字段将得到 ie 这个字段所对应的数据的总长度,struct ieee80211_ie 则定义了每个 IE 的结构,具体有多少个 IE 则需要在遍历 IE 时根据 ie_len 字段的值做出判断;

  • struct ieee80211_ie 中的 eid(Element ID) 字段定义了这个 IE 的类型,这些类型在 802.11-2007 的文档中第 100 页有定义,这里摘录其中的一部分:

    Parts of IE type definition

  • 从上面定义可以看到,当 Element ID 为 0 时,其信息内容为 SSID,以上面的实际数据为例,数据的第二行,也就是第 1 个 IE 的数据为:

    00 07 31 35 2D 31 31 30 31 
    
    • 按照 IE 的格式定义,Element ID 为 0,表示其信息为 SSID,数据长度 Length 为 7,所以后面的 7 个字节 31 35 2D 31 31 30 31 为实际数据;
    • 查 ASCII 表,这个数据其实就是 “15-1101”,这就是这个信号的 SSID
  • IE 提供了一种非常灵活的传递信息的方法,内容非常丰富,在本文所载实例中仅就其中的几个进行了解析;

  • 还要简单介绍一下所支持传输速率的 IE,因为在实例中解析了这个 IE;

  • 根据 Element ID 的定义,当 Element ID 为 1 时,IE 的数据为该信号所支持的传输速率,其结构在 802.11-2007 文档的第 102 页有介绍:

    Supported Rates

  • 根据文档,一个 IE 中最多描述 8 个传输速率,单位为 500 kb/s,每个速率占用 1 个字节,其最高位(bit 7)有其它意义,(bit 0 - 6) 表示速率值,所以在计算时要将 bit 7 过滤掉;

  • IE 信息会有空信息,也就是长度字段为 0,这种 IE 通常只有 2 个字节,没有意义,在实际解析中要过滤掉,比如:
    00 00

8 实例

  • 完整的源代码,文件名:wifi-new-scanner.c(点击文件名下载源程序),请务必使用 UTF-8 字符集,否则源程序中的中文注释为乱码;

  • 阅读这个源码最好先阅读文章《使用ioctl扫描wifi信号获取信号属性的一个范例(一)》,并搞懂其中的源代码;

  • 简述一下程序的基本流程:

    • 通过读取文件 /proc/net/wireless 获取无线网络接口的列表(在文章《使用ioctl扫描wifi信号获取信号属性的一个范例(一)》中使用的是另一种方法);
    • 使用 ioctl() 向无线网络接口发出 wifi 信号扫描指令;
    • 使用 ioctl() 获取扫描结果,根据返回的结果生成事件链表;
    • 从事件链表中分析每一个事件,从中解析出每个信号的属性,生成 ap 链表;
    • 如果有 IE 事件,还要在 AP 链表中生成 IE 链表;
    • 从 AP 链表中解析出信息并显示出来;
  • 比较《使用ioctl扫描wifi信号获取信号属性的一个范例(一)》中的实例,本文除了解析出 MAC 地址、ESSID、工作频率、工作信道外,还可以解析出信号质量、工作模式、信号支持的传输速率、驱动程序字符串以及 Information Elements;

  • 源程序中有比较详细的注释请自行参考;

  • 其中比较复杂的是 IE 的解析,IE 的内容极其丰富,作为示范,本例仅解析了 SSID 和速率,需要更多信息的读者可以查阅 802.11-2007 文档;

  • 编译:gcc -Wall wifi-new-scanner.c -o wifi-new-scanner -lm

  • 运行:sudo ./wifi-new-scanner

  • 运行截图:

    GIF of running wifi-new0scanner

欢迎订阅 『网络编程专栏』

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

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

相关文章

第16天:信息打点-CDN绕过业务部署漏洞回链接口探针全网扫描反向邮件

第十六天 本课意义 1.CDN服务对安全影响 2.CDN服务绕过识别手法 一、CDN服务-解释差异识别 1.前置知识&#xff1a; 传统访问&#xff1a;用户访问域名–>解析服务器IP–>访问目标主机普通CDN&#xff1a;用户访问域名–>CDN节点–>真实服务器IP–>访问目标…

C++项目 -- 负载均衡OJ(一)comm

C项目 – 负载均衡OJ&#xff08;一&#xff09;comm 文章目录 C项目 -- 负载均衡OJ&#xff08;一&#xff09;comm一、项目宏观结构1.项目功能2.项目结构 二、comm公共模块1.util.hpp2.log.hpp 一、项目宏观结构 1.项目功能 本项目的功能为一个在线的OJ&#xff0c;实现类似…

LLM生成模型在生物单细胞single cell的应用:scGPT

参考&#xff1a; https://github.com/bowang-lab/scGPT https://www.youtube.com/watch?vXhwYlgEeQAs 主要是把单细胞测序出来的基因表达量的拼接起来构建成的序列&#xff0c;这里不是用的基因的ATCG&#xff0c;是直接用的基因名称 训练数据&#xff1a;scGPT全人模型是在3…

ARMv8-A架构下的外部debug模型之外部调试事件(external debug events)概述

外部调试器与处理器之间的握手与external debug events 一&#xff0c;External Debug的使能二&#xff0c;外部调试器和CPU之间的握手三&#xff0c;外部调试事件 External debug events1. External debug request event2. Halt instruction debug event3. Halting step debug…

如何在宽松许可证下构建有竞争力的Milvus Cloud商业服务?

如何在宽松许可证下构建有竞争力的商业服务? 开源公司并不一定要修改许可证作为唯一的出路,不过其挑战在于找到开源与商业化之间的平衡。 以 Zilliz 为例,我们选择将开源向量数据库 Milvus 捐赠给 Linux 基金会。在过去四年的时间里,Milvus 已经在 GitHub 上获得了超过 …

一款廉价USB转485转换器原理

USB转485转换器实物 USB转485转换器外观 USB转485转换器内部 USB转485转换器电路板元件面TOP USB转485转换器电路板焊接面BOT 工作原理 SOP14芯片为USB-TTL转换器&#xff0c;虽然没有型号&#xff0c;应为南京沁恒的CH340C&#xff0c;无外部晶振。 SOP8为MAX485&#xff0c…

[Java、Android面试]_18_详解Handler机制 常见handler面试题(非常重要,非常高频!!)

本人今年参加了很多面试&#xff0c;也有幸拿到了一些大厂的offer&#xff0c;整理了众多面试资料&#xff0c;后续还会分享众多面试资料。 整理成了面试系列&#xff0c;由于时间有限&#xff0c;每天整理一点&#xff0c;后续会陆续分享出来&#xff0c;感兴趣的朋友可关注收…

数字乡村发展新模式:科技创新引领农业现代化与乡村振兴协同发展

随着信息技术的飞速发展&#xff0c;数字乡村已成为新时代农业现代化与乡村振兴协同发展的新模式。科技创新作为推动这一模式的核心动力&#xff0c;正引领着乡村产业结构的优化升级&#xff0c;促进农村经济的全面振兴&#xff0c;让农民在现代化的进程中共享发展成果。 一、科…

安装 k8s集群的问题:默认容器运行时从 Docker 改为 Containerd

安装 k8s集群的问题&#xff1a;默认容器运行时从 Docker 改为 Containerd 1、背景2、容器运行时从 Docker 改为 Containerd2.1、安装 Containerd&#xff1a;2.2、生成 Containerd 的配置文件2.3 、创建 /etc/crictl.yaml 文件2.4 、配置 Containerd 服务开机自启 &#x1f49…

Vue3用户交互——事件

Vue3用户交互——事件 1. 鼠标单击事件、输入框双向绑定和修饰符 html代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1…

动态规划|62.不同路径

力扣题目链接 class Solution { public:int uniquePaths(int m, int n) {vector<vector<int>> dp(m, vector<int>(n, 0));for (int i 0; i < m; i) dp[i][0] 1;for (int j 0; j < n; j) dp[0][j] 1;for (int i 1; i < m; i) {for (int j 1; …

Netty学习——高级篇2 Netty解码技术

接上篇&#xff1a;Netty学习——高级篇1 拆包 、粘包与编解码技术&#xff0c;本章继续介绍Netty的其他解码器 1 DelimiterBasedFrameDecoder分隔符解码器 DelimiterBasedFrameDecoder 分隔符解码器是按照指定分隔符进行解码的解码器&#xff0c;通过分隔符可以将二进制流拆分…

nginx+keepalived集群-双主

1. nginx部署 单机部署可参考&#xff1a;https://blog.csdn.net/ym5209999/article/details/119897237 主备模式可参考&#xff1a;https://blog.csdn.net/ym5209999/article/details/132983080 2. keepalived安装 [rootnginx1 ~]# yum -y install keepalived3. keepalived…

Unity DOTS1.0 入门(3) System与SystemGroup 概述

System与SystemGroup 概述 System System是提供一种代码逻辑,改变组件的数据状态,从一个状态到另外一个状态System在main thread里面运行, system.Update方法每一帧执行一次(其他线程中运行的就是JobSystem的事情了&#xff09;System是通过一个System Group这个体系来决定它…

【动态规划】路径问题模型

【动态规划】路径问题模型 文章目录 【动态规划】路径问题模型前言一、不同路径二、不同路径-2三、珠宝的最高价值四、下降路径最小和五、最小路径和六、地下城游戏总结 前言 ​ 本文将从基础的不同路径问题开始&#xff0c;逐步深入到更复杂的最小路径和等问题&#xff0c;最…

webpack or vite? vuex or pinia?

2022.2.18, 新建一个vue3的项目&#xff0c;过程如下&#xff1a; 目录结构如下&#xff1a; 当还在犹豫选择webpack还是vite&#xff0c;vuex或者pinia的时候&#xff0c;尤大大已经给出了默认选择&#xff0c;vite && pinia。

Spring Boot 学习(5)——开发流程:快速入门

花了几天的时间&#xff0c;整出个 “hello spring boot”&#xff0c;并且把它从 2 搞到了 3。 纸上得来终觉浅&#xff01;自己实践出真知&#xff01;现在再回头来囫囵一遍&#xff0c;加深下印象。回想下从前自觉某一编程语言大都如此&#xff0c;先找到简单示例照着画一遍…

【LAMMPS学习】八、基础知识(2.7)NEMD 模拟

8. 基础知识 此部分描述了如何使用 LAMMPS 为用户和开发人员执行各种任务。术语表页面还列出了 MD 术语&#xff0c;以及相应 LAMMPS 手册页的链接。 LAMMPS 源代码分发的 examples 目录中包含的示例输入脚本以及示例脚本页面上突出显示的示例输入脚本还展示了如何设置和运行各…

深入微服务框架:构建高效、可扩展与弹性的现代应用架构

前言&#xff1a;当今快速迭代和多变的商业环境中&#xff0c;传统的单体应用程序面临着一系列挑战&#xff0c;包括难以管理复杂性、缺乏灵活性以及无法有效扩展等问题。随着业务需求的不断增长和技术栈的不断演进&#xff0c;企业亟需一种更加模块化、易于管理和扩展的应用程…

【Godot4.2】CanvasItem绘图函数全解析 - 8.绘制点索引

概述 在示意图绘制过程中或者测试过程中&#xff0c;可能需要标记点的索引。 最常见的形式就是用一个圆圈作为背景&#xff0c;用阿拉伯数字作为索引。 实现的重点是动态计算背景圆的半径。原理是&#xff0c;获取字符串的矩形&#xff0c;取对角线长度的一半作为外接圆的半…