环境介绍

i40e 驱动版本为 5.0 内核主线版本,网卡为 x710 网卡。需要测试 flow directory 功能能否正常使用。

第一个问题:不支持 flow-type 为 ether

使用下面这条命令,设定一个 flow-type 为 ether 类型的过滤条件时报了 NOTSUPP 的错误。

ethtool -U enp11s0f0  flow-type ether  ....

分析 flow-type 为 ether 时的执行过程

ethtool 中的流程分析

ethtool 中解析代码如下:

else if (!strcmp(argp[0], "ether"))flow_type = ETHER_FLOW;fsp->flow_type = flow_type;

当 ethtool 命令行中设定了 flow-type 为 ether 时,ethtool 会将 flow_type 设定为 ETHER_FLOW

然后立刻判断 flow_type,分发到不同的 options 中,相关代码如下:

 case ETHER_FLOW:options = rule_nfc_ether;n_opts = ARRAY_SIZE(rule_nfc_ether);break;

options 表示解析参数的规则,rule_nfc_ether 定义如下:

static const struct rule_opts rule_nfc_ether[] = {{ "src", OPT_MAC, NFC_FLAG_SADDR,offsetof(struct ethtool_rx_flow_spec, h_u.ether_spec.h_source),offsetof(struct ethtool_rx_flow_spec, m_u.ether_spec.h_source) },{ "dst", OPT_MAC, NFC_FLAG_DADDR,offsetof(struct ethtool_rx_flow_spec, h_u.ether_spec.h_dest),offsetof(struct ethtool_rx_flow_spec, m_u.ether_spec.h_dest) },{ "proto", OPT_BE16, NFC_FLAG_PROTO,offsetof(struct ethtool_rx_flow_spec, h_u.ether_spec.h_proto),offsetof(struct ethtool_rx_flow_spec, m_u.ether_spec.h_proto) },{ "action", OPT_U64, NFC_FLAG_RING,offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },{ "vf", OPT_RING_VF, NFC_FLAG_RING_VF,offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },{ "queue", OPT_RING_QUEUE, NFC_FLAG_RING_QUEUE,offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },{ "loc", OPT_U32, NFC_FLAG_LOC,offsetof(struct ethtool_rx_flow_spec, location), -1 },{ "vlan-etype", OPT_BE16, NTUPLE_FLAG_VETH,offsetof(struct ethtool_rx_flow_spec, h_ext.vlan_etype),offsetof(struct ethtool_rx_flow_spec, m_ext.vlan_etype) },{ "vlan", OPT_BE16, NTUPLE_FLAG_VLAN,offsetof(struct ethtool_rx_flow_spec, h_ext.vlan_tci),offsetof(struct ethtool_rx_flow_spec, m_ext.vlan_tci) },{ "user-def", OPT_BE64, NTUPLE_FLAG_UDEF,offsetof(struct ethtool_rx_flow_spec, h_ext.data),offsetof(struct ethtool_rx_flow_spec, m_ext.data) },
};

解析完所有的参数同时配置好 struct ethtool_rx_flow_spec 结构体,然后调用 ioctl 来下发配置到网卡。

配置方式有两种类型,相关代码如下:

     /* attempt to add rule via N-tuple specifier */err = do_srxntuple(ctx, &rx_rule_fs);if (!err)return 0;/* attempt to add rule via network flow classifier */err = rxclass_rule_ins(ctx, &rx_rule_fs, rss_context);if (err < 0) {fprintf(stderr, "Cannot insert"" classification rule\n");return 1;}

第一种配置方式是 ntuple,第二种是 classifier,这两种不同方式传递给 ioctl 的 ethtool 子命令存在区别

当驱动不支持 ntuple 方式配置时(返回值为 EOPNOTSUPP 时),不会打印失败信息,继续尝试 classifier 方式

当两种方式都失败后,软件执行如下代码打印错误信息:

         fprintf(stderr, "Cannot insert"" classification rule\n");

ntuple 方式 ioctl 的参数

 /* send rule via N-tuple */ntuplecmd.cmd = ETHTOOL_SRXNTUPLE;err = send_ioctl(ctx, &ntuplecmd);

classifier 方式 ioctl 的参数

 /* notify netdev of new rule */nfccmd.cmd = ETHTOOL_SRXCLSRLINS;nfccmd.rss_context = rss_context;nfccmd.fs = *fsp;err = send_ioctl(ctx, &nfccmd);

i40e 内核驱动支持哪一种类型?

5.0 内核中,ethtool 框架不支持 ntuple 方式。

ioctl 的内核流程

ethool 调用 ioctl 最终会走到 dev_ethtool 函数中,在这个函数中 ETHTOOL_SRXCLSRLINS 被分发到 ethtool_set_rxnfc 函数中,相关代码如下:

2705     case ETHTOOL_SRXFH:
2706     case ETHTOOL_SRXCLSRLDEL:
2707     case ETHTOOL_SRXCLSRLINS:
2708         rc = ethtool_set_rxnfc(dev, ethcmd, useraddr);
2709         break;

ethtool_set_rxnfc 核心代码如下:

rc = dev->ethtool_ops->set_rxnfc(dev, &info);

可以看到它调用了 ethtool_ops 中的 set_rxnfc 函数指针,此函数指针在 i40e_ethtool.c 中实现,i40e_ethtool.c 中实现的函数代码如下:

4308 static int i40e_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd)
4309 {4310     struct i40e_netdev_priv *np = netdev_priv(netdev);
4311     struct i40e_vsi *vsi = np->vsi;
4312     struct i40e_pf *pf = vsi->back;
4313     int ret = -EOPNOTSUPP;
4314
4315     switch (cmd->cmd) {4316     case ETHTOOL_SRXFH:
4317         ret = i40e_set_rss_hash_opt(pf, cmd);
4318         break;
4319     case ETHTOOL_SRXCLSRLINS:
4320         ret = i40e_add_fdir_ethtool(vsi, cmd);
4321         break;
4322     case ETHTOOL_SRXCLSRLDEL:
4323         ret = i40e_del_fdir_entry(vsi, cmd);
4324         break;
4325     default:
4326         break;
4327     }
4328
4329     return ret;
4330 }

i40e_add_fdir_ethtool 在真正设定到网卡前做了许多检查,其中有 i40e_check_fdir_input_set 这个对 input_set 的检查。

i40e_check_fdir_input_set 中判断 flow_type,当这个字段设定为 ETHER_FLOW 此函数将会返回 -EOPNOTSUPP。相关代码如下:

3801     switch (fsp->flow_type & ~FLOW_EXT) {3802     case SCTP_V4_FLOW:
3803         index = I40E_FILTER_PCTYPE_NONF_IPV4_SCTP;
3804         fdir_filter_count = &pf->fd_sctp4_filter_cnt;
3805         break;
3806     case TCP_V4_FLOW:
3807         index = I40E_FILTER_PCTYPE_NONF_IPV4_TCP;
3808         fdir_filter_count = &pf->fd_tcp4_filter_cnt;
3809         break;
3810     case UDP_V4_FLOW:
3811         index = I40E_FILTER_PCTYPE_NONF_IPV4_UDP;
3812         fdir_filter_count = &pf->fd_udp4_filter_cnt;
3813         break;
3814     case IP_USER_FLOW:
3815         index = I40E_FILTER_PCTYPE_NONF_IPV4_OTHER;
3816         fdir_filter_count = &pf->fd_ip4_filter_cnt;
3817         flex_l3 = true;
3818         break;
3819     default:
3820         return -EOPNOTSUPP;
3821     }

可以确定对于执行 ethtool -U devname ether xxx 的设定 linux 5.0 的 i40e 驱动并不支持!

第二个问题:如何丢掉所有的 ipv4 udp 报文?

在测试前我首先执行 ifconfig enp11s0f0 -promisc 命令打开混淆模式。

构造 udp 报文开始打流,然后执行如下命令能够丢掉所有的 ipv4 udp 报文:

ethtool -U enp11s0f0  flow-type udp4  action -1

设定完成可以通过 ethtool -u enp11s0f0 来查看配置内容,同时可以通过执行如下命令查看 fdir_match 计数确定配置生效:

ethtool -S enp11s0f0 | grep fdir

设定后会得到一个 id,我测试时 id 是 7679,可以使用 ethtool -U enp11s0f0 delete 7679 来移除这一条配置。

第三个问题:如何使用 flex filter 功能?

linux 内核源码树 networking/device_drivers/intel/i40e.rst 文件中描述了可以通过使用 user-def 字段来匹配协议的 payload 中的字段内容。

manual 说明贴到下面:

The driver also supports matching user-defined data within the packet payload.
This flexible data is specified using the "user-def" field of the ethtool
command in the following way:+----------------------------+--------------------------+
| 31    28    24    20    16 | 15    12    8    4    0  |
+----------------------------+--------------------------+
| offset into packet payload | 2 bytes of flexible data |
+----------------------------+--------------------------+For example,::... user-def 0x4FFFF ...tells the filter to look 4 bytes into the payload and match that value against
0xFFFF. The offset is based on the beginning of the payload, and not the
beginning of the packet. Thus::flow-type tcp4 ... user-def 0x8BEAF ...would match TCP/IPv4 packets which have the value 0xBEAF 8 bytes into the
TCP/IPv4 payload.

上述说明信息讲了下面这几个内容:

  1. user-def 只能从 payload 开始匹配
  2. user-def 由 4 字节组成,高 16 位的两个字节代表相对 payload 的偏移量,低 16 位为匹配的内容

由于 flexible data 的单位是两个字节,offset 只支持偶数,不能使用奇数。实际测试时,我发现按照上面的描述配置能够正常配置,但是过滤功能并不生效。

唯一一次生效的命令如下:

ethtool -U enp11s0f0 flow-type udp4 user-def 0x0 action -1
Added rule with ID 7679

测试仪发送的 udp 报文,其 payload 的前两个字节都是 0,dmesg 有如下信息:

[165763.336866] i40e 0000:0b:00.0 enp11s0f0: Input set change requested for udp4 flows:
[165763.336871] i40e 0000:0b:00.0 enp11s0f0:   Current input set: 2000
[165763.336874] i40e 0000:0b:00.0 enp11s0f0: Requested input set: 2000
[165763.336877] i40e 0000:0b:00.0 enp11s0f0: FLEX index 0: Offset -> 0

根据上面的描述,当针对 UDPv4 设置 flex 的时候,payload 的起始位置指向的是 UDPv4 报文的 data 区域起始位置。

我构造如下 udp payload 内容进行测试:

测试命令如下:

ethtool -U enp11s0f0 flow-type udp4 user-def 0x9013 action -1

dmesg 的打印信息如下:

[163317.273795] i40e 0000:0b:00.0 enp11s0f0: Input set change requested for udp4 flows:
[163317.273801] i40e 0000:0b:00.0 enp11s0f0:   Current input set: 2000
[163317.273805] i40e 0000:0b:00.0 enp11s0f0: Requested input set: 2000
[163317.273808] i40e 0000:0b:00.0 enp11s0f0: FLEX index 0: Offset -> 0

可以确定设置成功了,但是没有生效,怀疑大小端、偏移量的影响并都进行了排查,没有找到问题。

对 flex filter 的认识

flex filter 是针对报文的 payload 进行 filter,也就是说我们无法针对报文的 header 进行过滤,但是在网络报文封装中,上层的协议帧可以看做是下层协议帧的 payload

也就是说如果我们要过滤一个 96 字节的 udp 报文,那么要设定过滤一个 ip 报文的命令,因为在过滤 ip 报文的时候 udp 的头是可见的,有了这个知识,然后计算出 96 字节支持的 udp 包的大小,编写命令。

同时需要了解的是接口的混淆模式可能对过滤条件有影响,根据测试情况看,在开混淆模式后过滤功能仍然能够生效

有了上面的理解,我继续尝试过滤 udp 报文的 payload 字段,结果都失败了,网上搜索了下发现相关的内容非常少,看来只有怼代码了!

尝试分析代码以找到 flex filter 不能生效的原因

研究代码,发现设定一个 flow directory 规则的流程大致如下:

  1. 选择 input set
  2. 获取 fdir 对应的 ring
  3. 根据 flow-type 等参数设定一个 i40e_filter_program_desc 描述符内容
  4. 构造一个报文,此报文根据 flow-type 等参数来设定,它也需要占一个描述符
  5. 写入 tail 让网卡处理这两个描述符
  6. 检查设定是否生效

i40e_fdir 函数负责填充 i40e_filter_program_desc 描述符,这个描述符的定义如下:

struct i40e_filter_program_desc {__le32 qindex_flex_ptype_vsi;__le32 rsvd;__le32 dtype_cmd_cntindex;__le32 fd_id;
};

i40e_fdir 代码如下:

static void i40e_fdir(struct i40e_ring *tx_ring,struct i40e_fdir_filter *fdata, bool add)
{struct i40e_filter_program_desc *fdir_desc;struct i40e_pf *pf = tx_ring->vsi->back;u32 flex_ptype, dtype_cmd;u16 i;/* grab the next descriptor */i = tx_ring->next_to_use;fdir_desc = I40E_TX_FDIRDESC(tx_ring, i);i++;tx_ring->next_to_use = (i < tx_ring->count) ? i : 0;flex_ptype = I40E_TXD_FLTR_QW0_QINDEX_MASK &(fdata->q_index << I40E_TXD_FLTR_QW0_QINDEX_SHIFT);flex_ptype |= I40E_TXD_FLTR_QW0_PCTYPE_MASK &(fdata->pctype << I40E_TXD_FLTR_QW0_PCTYPE_SHIFT);flex_ptype |= I40E_TXD_FLTR_QW0_PCTYPE_MASK &(fdata->flex_offset << I40E_TXD_FLTR_QW0_FLEXOFF_SHIFT);/* Use LAN VSI Id if not programmed by user */flex_ptype |= I40E_TXD_FLTR_QW0_DEST_VSI_MASK &((u32)(fdata->dest_vsi ? : pf->vsi[pf->lan_vsi]->id) <<I40E_TXD_FLTR_QW0_DEST_VSI_SHIFT);dtype_cmd = I40E_TX_DESC_DTYPE_FILTER_PROG;dtype_cmd |= add ?I40E_FILTER_PROGRAM_DESC_PCMD_ADD_UPDATE <<I40E_TXD_FLTR_QW1_PCMD_SHIFT :I40E_FILTER_PROGRAM_DESC_PCMD_REMOVE <<I40E_TXD_FLTR_QW1_PCMD_SHIFT;dtype_cmd |= I40E_TXD_FLTR_QW1_DEST_MASK &(fdata->dest_ctl << I40E_TXD_FLTR_QW1_DEST_SHIFT);dtype_cmd |= I40E_TXD_FLTR_QW1_FD_STATUS_MASK &(fdata->fd_status << I40E_TXD_FLTR_QW1_FD_STATUS_SHIFT);if (fdata->cnt_index) {dtype_cmd |= I40E_TXD_FLTR_QW1_CNT_ENA_MASK;dtype_cmd |= I40E_TXD_FLTR_QW1_CNTINDEX_MASK &((u32)fdata->cnt_index <<I40E_TXD_FLTR_QW1_CNTINDEX_SHIFT);}fdir_desc->qindex_flex_ptype_vsi = cpu_to_le32(flex_ptype);fdir_desc->rsvd = cpu_to_le32(0);fdir_desc->dtype_cmd_cntindex = cpu_to_le32(dtype_cmd);fdir_desc->fd_id = cpu_to_le32(fdata->fd_id);
}

上面的代码中,dtype_cmd 是个非常重要的字段,它经过一系列的判断设定其值。

i40e_fdir 中设定描述符字段内容完成后,将 tx_ring->next_to_use 向后拨一个单位,表示占用了一个描述符

i40e_fdir 填充完成后,继续填充 raw_packet,占据下一个描述符,并设定相关的字段。

过滤 udp 报文时构造 raw_packet 报文的代码如下:

 static char packet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0,0x45, 0, 0, 0x1c, 0, 0, 0x40, 0, 0x40, 0x11, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};raw_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_SIZE, GFP_KERNEL);if (!raw_packet)return -ENOMEM;memcpy(raw_packet, packet, I40E_UDPIP_DUMMY_PACKET_LEN);ip = (struct iphdr *)(raw_packet + IP_HEADER_OFFSET);udp = (struct udphdr *)(raw_packet + IP_HEADER_OFFSET+ sizeof(struct iphdr));ip->daddr = fd_data->dst_ip;udp->dest = fd_data->dst_port;ip->saddr = fd_data->src_ip;udp->source = fd_data->src_port;if (fd_data->flex_filter) {u8 *payload = raw_packet + I40E_UDPIP_DUMMY_PACKET_LEN;__be16 pattern = fd_data->flex_word;u16 off = fd_data->flex_offset;*((__force __be16 *)(payload + off)) = pattern;}fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_UDP;

使用了 ethtool 设定 user-def 的时候 fd_data->flex_filter 为 true,这时可以看到对 udp 报文的 payload + off 字段的填充逻辑。

I40E_UDPIP_DUMMY_PACKET_LEN 的定义如下:

#define I40E_UDPIP_DUMMY_PACKET_LEN  42

上述代码计算的 payload 的起始位置正是 udp 报文的 data 字段的起始位置。

与此类似,过滤 tcpv4 报文时填充 payload 字段的代码如下:

 if (fd_data->flex_filter) {u8 *payload = raw_packet + I40E_TCPIP_DUMMY_PACKET_LEN;__be16 pattern = fd_data->flex_word;u16 off = fd_data->flex_offset;*((__force __be16 *)(payload + off)) = pattern;}

I40E_TCPIP_DUMMY_PACKET_LEN 宏的定义如下:

#define I40E_TCPIP_DUMMY_PACKET_LEN  54

可以确定 tcpv4 flex payload 从报文的第 54 字节开始,同理 ipv4 flex payload 从报文的第 34 字节开始,sctp flex payload 从报文的第 46 字节开始。

构造完成一个 raw_packet 后同样需要填充描述符,tx_ring->next_to_use 再次向后拨动一个单位。完成上述过程后,最终将 tx_ring->next_to_use 写入到 tx_ring->tail 以通知网卡处理, tx_ring->tail 是网卡中映射的一个物理地址

修改代码,打印填充的 raw_packet 报文内容

经过上面对 flex filter 设定过程的研究,我觉得可能是填充的 raw_packet 报文的内容存在问题,故而在驱动中添加了打印报文内容的操作重试。

测试过程记录如下:

[root] # dmesg -c >/dev/null ; ethtool -U enp11s0f0 flow-type udp4 user-def 0x212da action -1; dmesg | grep -A 10 'packet\[42\]'
Added rule with ID 7679
[165607.394687] packet[42] is 0
[165607.394688] packet[43] is 0
[165607.394689] packet[44] is 12
[165607.394691] packet[45] is da
[165607.394692] packet[46] is 0
[165607.394693] packet[47] is 0
[165607.394694] packet[48] is 0
[165607.394696] packet[49] is 0
[165607.394697] packet[50] is 0
[165607.394698] packet[51] is 0
[165607.394700] packet[52] is 0

根据打出的报文内容看,设定是正确的!但是仍旧没有真正生效,看来还是哪里没有整对。

唯一一次成功的 use-def 配置时的 raw_packet 内容

一番折腾后,我想到也许可以把那唯一一次生效的 raw_packet 内容打出来,看看有什么不同,结果也没有找到怀疑点,测试过程还是记录到下面。

[root] # dmesg -c >/dev/null ; ethtool -U enp11s0f0 flow-type udp4 user-def 0x0 action -1; dmesg | grep -A 10 'packet\[42\]'
Added rule with ID 7679[165763.336866] i40e 0000:0b:00.0 enp11s0f0: Input set change requested for udp4 flows:
[165763.336871] i40e 0000:0b:00.0 enp11s0f0:   Current input set: 2000
[165763.336874] i40e 0000:0b:00.0 enp11s0f0: Requested input set: 2000
[165763.336877] i40e 0000:0b:00.0 enp11s0f0: FLEX index 0: Offset -> 0
[165763.337433] packet[0] is 0
[165763.337435] packet[1] is 0
[165763.337436] packet[2] is 0
[165763.337437] packet[3] is 0
[165763.337439] packet[4] is e6
[165763.337440] packet[5] is ff
[165763.337442] packet[6] is ff
[165763.337443] packet[7] is ff
[165763.337444] packet[8] is 0
[165763.337446] packet[9] is 0
[165763.337447] packet[10] is 0
[165763.337448] packet[11] is 0
[165763.337450] packet[12] is 8
[165763.337451] packet[13] is 0
[165763.337452] packet[14] is 45
[165763.337454] packet[15] is 0
[165763.337455] packet[16] is 0
[165763.337456] packet[17] is 1c
[165763.337458] packet[18] is 0
[165763.337459] packet[19] is 0
[165763.337460] packet[20] is 40
[165763.337462] packet[21] is 0
[165763.337463] packet[22] is 40
[165763.337464] packet[23] is 11

总结

x710 flow directory 是个挺高级的功能,但是我们的业务场景中从来没有使用过,没有相应的技术积累,这次搞起来就显得困难重重。

flex filter 功能没有用起来,但是五元组过滤算是上手了,这个功能刚好能够解决我们遇到的问题,不过当场景切换到 dpdk 中时,即便一个简单的过滤 udp 报文的功能也研究了好几天,期间一度想放弃,被搞到想哭,最终搞定了后又激动到想哭。

现在想想在某天 21:00 的时候,我心怀忐忑的准备测试看 dpdk 中的 flow directory 功能是否生效,之前已经失败了好多次了,仔细 check,最终确定成功了后,我真的激动到眼泪快掉下来!

这并不只是技术上的突破,这更是突破自我局限的一次成功案例,我的成就感又回来了!

使用内核驱动上手 x710 flow directory 功能相关推荐

  1. Linux内核驱动之efi-rtc

    Linux内核驱动之efi-rtc 1. UEFI与BIOS概述 1.1. BIOS 概述 1.1.1. BIOS缺点: 1.1.2. BIOS的启动流程 1.2 UEFI 概述 1.2.1 Boot ...

  2. 嵌入式 Linux 内核驱动开发【The first day: 36093万字】

    嵌入式 Linux 内核驱动开发[1] 嵌入式 Linux 内核驱动开发前言 第1章 Linux 内核裁剪和定制 [1]Linux 内核开发简介 [2] Linux 源码阅读工具 [1.2.1]Sou ...

  3. 西数加密linux,Symantec PGP Desktop pgpwded.sys内核驱动任意代码执行漏洞

    发布日期:2012-12-24 更新日期:2012-12-27 受影响系统: Symantec PGP Desktop 10.2.0 Build 2599 描述: ------------------ ...

  4. vmlinux 反汇编_ARM Linux内核驱动异常定位方法分析--反汇编方式

    通常认为,产生异常的地址是lr寄存器的值,从上面的异常信息可以看到[lr]的值是c01a4e30. 接下来,我们可以通过内核镜像文件反汇编来找到这个地址.内核编译完成后,会在内核代码根目录下生成vml ...

  5. 驱动框架2——内核驱动框架中LED的基本情况、初步分析

    以下内容源于朱有鹏嵌入式课程的学习,如有侵权,请告知删除. 一.内核驱动框架中LED的基本情况 1.相关文件 (1)drivers/leds目录 驱动框架规定的LED这种硬件的驱动应该待的地方. (2 ...

  6. Linux内核驱动如何编写?我们先从字符驱动入门开始

    几年前正式转到linux开发岗位的时候,由于项目急需编写linux驱动来控制项目采集设备(板卡),我便被安排做这一部分工作.那时候挺慌的-,在之前的一年多时间里基本都是window应用开发,对于lin ...

  7. NVDLA软件架构和源码解析 第一章—内核驱动

    驱动整体设计介绍 不同的processor Nvidia DLA的内核驱动KMD(Kernel mode driver)中,并不是把DLA当成一个设备来控制,而是把不同的功能模块当做不同的proces ...

  8. linux内核驱动之 设备和模块的分类

    目录 字符设备 块设备 网络接口 其他划分方式 以 Linux 的方式看待设备可区分为 3 种基本设备类型. 每个模块常常实现 3 种类型中的 1 种, 因此可分类成字符模块, 块模块, 或者一个网络 ...

  9. linux内核创建字符节点,Tiny6410学习ing—(四)、嵌入式Linux内核驱动进阶—(7)、高级字符设备驱动(自动创建节点)—#931...

    按照国嵌的视频教程上来说的,最后就是-自动创建设备文件! 其实我感觉以前完全可以直接是手动创建了设备文件,然后就可以直接讲述自动创建设备文件,为啥非要拖到最后来讲述,我也就不清楚了!! 不管了,写完收 ...

最新文章

  1. x86 cpu卷积网络的自动调谐
  2. Unity制作2D动作平台游戏视频教程
  3. R语言回归模型构建、回归模型基本假设(正态性、线性、独立性、方差齐性)、回归模型诊断、car包诊断回归模型、特殊观察样本分析、数据变换、模型比较、特征筛选、交叉验证、预测变量相对重要度
  4. 三相滤波器怎么接线_您知道家用电表如何接线吗?小编来告诉你!
  5. Antenna Placement--POJ 3020
  6. IIS6.0发布后对路径“D:\xxx\xxxx\web.config”的访问被拒绝问题的解决方法
  7. 【转】Linux系统安装Redis详细过程
  8. 2019年7月前CSDN最新排名
  9. Linux学习:第二章-Linux安装
  10. java 日期只计算年月日大小_Java 计算两个日期相差多少年月日
  11. 十步让 WebForm项目 变为 Mvc项目
  12. Springboot 拦截器无法注入对象的解决
  13. SRAM和DRAM详解
  14. Windows许可证过期(‘slmgr.vbs‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件)
  15. 在CentOS7上使用LXC管理容器
  16. tdr 定位公式_时域反射计TDR原理详细解析
  17. Python装逼指南——五行代码实现批量抠图!
  18. .md文件转.pdf文件
  19. 弹性盒模型 Flex
  20. 【文献阅读】A2-Nets: Double Attention Networks

热门文章

  1. java哪个软件编程好学吗_java编程好学吗
  2. 编辑部已成羊村,这几天幸亏有ChatGPT(doge)
  3. fMRI脑影像特征提取——静息态与任务态,ALFF/fALFF和ReHo(Dpabi,Rest1.8)
  4. 马士兵Python基础版2020教程P58-P96 PPT笔记+课堂代码
  5. AndroidStudio的 Caused by java.lang.ClassNotFoundException Didn't find class com.gizwits.opensource.a
  6. IDEA代码提示快捷键Ctrl+Space不生效问题
  7. 学习BLE蓝牙一个月总结
  8. Linux source文件后提示 export:command not found
  9. 记录unity WebGL出现Uncaught ReferenceError: myGameInstance is not defined的问题
  10. jQuery实现无刷新切换主题皮肤功能