一例virtio_blk设备中断占用分析

背景:这个是在客户的centos8.4的环境上复现的,dpu是目前很多

云服务器上的网卡标配了,在云豹的dpu产品测试中,dpu实现的virtio_blk

设备在申请中断时报错,在排查这个错误的过程中,觉得某些部分还比较有

趣,故记录之。本身涉及的背景知识有:irq,msi,irq_domain,

affinity,virtio_blk,irqbalance

下面列一下我们是怎么排查并解决这个问题的。

一、故障现象

内核团队接到测试组测试客户前端内核抛栈:

[25338.485128] virtio-pci 0000:b3:00.0: virtio_pci: leaving for legacy driver
[25338.496174] genirq: Flags mismatch irq 0. 00000080 (virtio418) vs. 00015a00 (timer)
[25338.503822] CPU: 20 PID: 5431 Comm: kworker/20:0 Kdump: loaded Tainted: G OE --------- - - 4.18.0-305.30.1.jmnd2.el8.x86_64 #1
[25338.516403] Hardware name: Inspur NF5280M5/YZMB-00882-10E, BIOS 4.1.21 08/25/2021
[25338.523881] Workqueue: events work_for_cpu_fn
[25338.528235] Call Trace:
[25338.530687] dump_stack+0x5c/0x80
[25338.534000] __setup_irq.cold.53+0x7c/0xd3
[25338.538098] request_threaded_irq+0xf5/0x160
[25338.542371] vp_find_vqs+0xc7/0x190
[25338.545866] init_vq+0x17c/0x2e0 [virtio_blk]
[25338.550223] ? ncpus_cmp_func+0x10/0x10
[25338.554061] virtblk_probe+0xe6/0x8a0 [virtio_blk]
[25338.558846] virtio_dev_probe+0x158/0x1f0
[25338.562861] really_probe+0x255/0x4a0
[25338.566524] ? __driver_attach_async_helper+0x90/0x90
[25338.571567] driver_probe_device+0x49/0xc0
[25338.575660] bus_for_each_drv+0x79/0xc0
[25338.579499] __device_attach+0xdc/0x160
[25338.583337] bus_probe_device+0x9d/0xb0
[25338.587167] device_add+0x418/0x780
[25338.590654] register_virtio_device+0x9e/0xe0
[25338.595011] virtio_pci_probe+0xb3/0x140
[25338.598941] local_pci_probe+0x41/0x90
[25338.602689] work_for_cpu_fn+0x16/0x20
[25338.606443] process_one_work+0x1a7/0x360
[25338.610456] ? create_worker+0x1a0/0x1a0
[25338.614381] worker_thread+0x1cf/0x390
[25338.618132] ? create_worker+0x1a0/0x1a0
[25338.622051] kthread+0x116/0x130
[25338.625283] ? kthread_flush_work_fn+0x10/0x10
[25338.629731] ret_from_fork+0x1f/0x40
[25338.633395] virtio_blk: probe of virtio418 failed with error -16

从堆栈看,是某个virtio_blk设备在probe的时候报错,错误码为-16。

二、故障现象分析

从堆栈信息看:

1、virtio418是一个virtio_blk设备,在probe过程中调用 __setup_irq 返回了-16。

2、[25338.496174] genirq: Flags mismatch irq 0. 00000080 (virtio418) vs. 00015a00 (timer),说明我们的virtio_blk

设备去申请了0号中断,由于0号中断被timer占用,irq子系统在比较flags时发现不符合,则打印这行。

具体代码为:


static int __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
{ ...... mismatch:
if (!(new->flags & IRQF_PROBE_SHARED)) {
pr_err("Flags mismatch irq %d. %08x (%s) vs. %08x (%s)\n",
irq, new->flags, new->name, old->flags, old->name); ......
ret = -EBUSY; ...... return ret; }

至于为什么virtio_blk会去申请0号中断,因为我们实现virtio_blk后端设备的时候,并没有支持intx,即virtio_pci类虚拟的pci_dev设备的irq值为0,本文先不管这个。

从堆栈看,virtio 申请中断是走了vp_find_vqs_intx流程,


crash> dis -l vp_find_vqs+0xc7
/usr/src/debug/linux/drivers/virtio/virtio_pci_common.c: 369---行号为369 356 static int vp_find_vqs_intx(struct virtio_device *vdev, unsigned nvqs,
357 struct virtqueue *vqs[], vq_callback_t *callbacks[],
358 const char * const names[], const bool *ctx)
359 {
......
366
367 err = request_irq(vp_dev->pci_dev->irq, vp_interrupt, IRQF_SHARED,
368 dev_name(&vdev->dev), vp_dev); 369 if (err)----压栈的返回地址 ......

我们dpu卡实现的virtio设备,都是使能msix的,按照代码流程,应该是先尝试msix,既然能走到 vp_find_vqs_intx 流程,说明 vp_find_vqs_msix失败了,而且按照如下代码:


395 int vp_find_vqs(struct virtio_device *vdev, unsigned nvqs,
396 struct virtqueue *vqs[], vq_callback_t *callbacks[],
397 const char * const names[], const bool *ctx,
398 struct irq_affinity *desc)
399 {
400 int err;
401
402 /* Try MSI-X with one vector per queue. */
403 err = vp_find_vqs_msix(vdev, nvqs, vqs, callbacks, names, true, ctx, desc);//caq:先尝试单vqueue单中断号
404 if (!err)
405 return 0;
406 /* Fallback: MSI-X with one vector for config, one shared for queues. */
407 err = vp_find_vqs_msix(vdev, nvqs, vqs, callbacks, names, false, ctx, desc);//caq:尝试多个vq共享一个中断号
408 if (!err)
409 return 0;
410 /* Finally fall back to regular interrupts. */
411 return vp_find_vqs_intx(vdev, nvqs, vqs, callbacks, names, ctx);//caq:最后退化成intx模式
412 }

说明vp_find_vqs_msix 失败了两次。第一次是 单vqueue单中断号的非共享方式,第二次是多个vq共用一个中断的方式。

通过打点发现,失败两次的原因是 __irq_domain_activate_irq 返回了-28


__irq_domain_activate_irq return=-28 0xffffffff8fb52f70 : __irq_domain_activate_irq+0x0/0x80 [kernel]
0xffffffff8fb54bc5 : irq_domain_activate_irq+0x25/0x40 [kernel]
0xffffffff8fb56bfe : msi_domain_alloc_irqs+0x15e/0x2f0 [kernel]
0xffffffff8fa5e5e4 : native_setup_msi_irqs+0x54/0x90 [kernel]
0xffffffff8feef69f : __pci_enable_msix_range+0x3df/0x5e0 [kernel]
0xffffffff8feef96b : pci_alloc_irq_vectors_affinity+0xbb/0x130 [kernel]
0xffffffff8ff7472b : vp_find_vqs_msix+0x1fb/0x510 [kernel]
0xffffffff8ff74aad : vp_find_vqs+0x6d/0x190 [kernel]

查看具体的代码:


static int __irq_domain_activate_irq(struct irq_data *irqd, bool reserve)
{
int ret = 0; if (irqd && irqd->domain) {//caq:均不为NULL
struct irq_domain *domain = irqd->domain; if (irqd->parent_data)
ret = __irq_domain_activate_irq(irqd->parent_data,
reserve);//caq:递归,将父domain的对应irq都active一下
if (!ret && domain->ops->activate) {
ret = domain->ops->activate(domain, irqd, reserve);//caq:parent 执行完activate 然后再儿子辈执行。
/* Rollback in case of error */
if (ret && irqd->parent_data)//caq:异常则回退
__irq_domain_deactivate_irq(irqd->parent_data);
}
}
return ret;
}

由于客户 host kernel开启了 CONFIG_IRQ_DOMAIN_HIERARCHY,根据irq_domain 级别 ,该系统的irq_domain 级联如下:

不过上图是arm常见的,盗用arm图,本x86系统类似,irq_domain级别具体跟踪如下:


crash> irq_domain.name,parent 0xffff9bff87d4dec0
name = 0xffff9bff87c1fd60 "INTEL-IR-MSI-1-2"
parent = 0xffff9bff87400000
crash> irq_domain.name,parent 0xffff9bff87400000
name = 0xffff9bff87c24300 "INTEL-IR-1"
parent = 0xffff9bff87c6c900
crash> irq_domain.name,parent 0xffff9bff87c6c900
name = 0xffff9bff87c3ecd0 "VECTOR"-----------最高级的
parent = 0x0---所以parent为空

根据返回-28,根据最高级的irq_domain定位到 调用链为:


//caq:类比于 dma_domain_ops,在x86内是最高级的irq_domain了,因为他的domain parent为NULL
static const struct irq_domain_ops x86_vector_domain_ops = {//caq:x86针对acpi实现的irq_domain_ops
.alloc = x86_vector_alloc_irqs,//caq:分配中断
.free = x86_vector_free_irqs,
.activate = x86_vector_activate,//caq:activate实现
.deactivate = x86_vector_deactivate,
#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
.debug_show = x86_vector_debug_show,
#endif
};
调用链: x86_vector_activate-->activate_managed-->assign_managed_vector-->irq_matrix_alloc_managed

查看 代码如下:


int irq_matrix_alloc_managed(struct irq_matrix *m, const struct cpumask *msk,
unsigned int *mapped_cpu)
{//caq:managed irq 分配
unsigned int bit, cpu, end = m->alloc_end;
struct cpumap *cm; if (cpumask_empty(msk))
return -EINVAL; cpu = matrix_find_best_cpu_managed(m, msk);//caq:找最合适的cpu
if (cpu == UINT_MAX)
return -ENOSPC;//caq:说明没找到
......
}

由于没有开启 CONFIG_GENERIC_IRQ_DEBUGFS,所以没办法直接看到 vector_matrix 具体的值,

借助crash工具查看:


crash> p *vector_matrix
$82 = {
matrix_bits = 256,
alloc_start = 32,
alloc_end = 236,
alloc_size = 204,
global_available = 0,//caq:只剩下了这么多个irq
global_reserved = 151,
systembits_inalloc = 3,
total_allocated = 1922,//caq:只分配了这么多个irq
online_maps = 80,
maps = 0x2ff20,
scratch_map = {18446744069952503807, 18446744073709551615, 18446744073709551615, 18446735277616529407, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
system_map = {1125904739729407, 0, 1, 18446726481523507200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
}

一个疑问涌上心头,为什么总共才分配了1922 个中断,全局的global_available就为0了呢?

然后继续打点,发现virtio_blk设备的vq申请中断时,走的是 apicd->is_managed 流程,而同属于virtio协议的virtio_net设备却不是。

而走managed流程,也就是申请中断时,带有了RQD_AFFINITY_MANAGED:


static int
assign_irq_vector_policy(struct irq_data *irqd, struct irq_alloc_info *info)
{
if (irqd_affinity_is_managed(irqd))//caq:如果是 managed 的irq,也就是irq_data中有 IRQD_AFFINITY_MANAGED 标记
return reserve_managed_vector(irqd);

我们回过来查看vector alloc时的调用链:

x86_vector_alloc_irqs-->assign_irq_vector_policy-->reserve_managed_vector-->irq_matrix_reserve_managed

对一个两个队列的virtio_blk申请中断时,打点发现如下:


m->global_available=15296 0xffffffff87158300 : irq_matrix_reserve_managed+0x0/0x130 [kernel]---从15296减少到15256
m->global_available=15256 call vdev=0xffff8b781ce17000,index=0,callback=0xffffffffc0448000,ctx=0,msix_vec=1----------容量减少了40

由于已经缩小到是因为virtio_blk设备的中断申请流程,使用热插拔确认一下:


118: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 53 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 94371841-edge virtio3-req.0
119: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 49 0 0 0 IR-PCI-MSI 94371842-edge virtio3-req.1 热拔前:
crash> p *vector_matrix
$2 = {
matrix_bits = 256,
alloc_start = 32,
alloc_end = 236,
alloc_size = 204,
global_available = 15215,
global_reserved = 150,
systembits_inalloc = 3,
total_allocated = 553,
online_maps = 80,
maps = 0x2ff20,
scratch_map = {1179746449752063, 0, 1, 18446726481523507200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
system_map = {1125904739729407, 0, 1, 18446726481523507200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
} 热拔后:
crash> p *vector_matrix
$3 = {
matrix_bits = 256,
alloc_start = 32,
alloc_end = 236,
alloc_size = 204,
global_available = 15296,---增长了81个,一个config中断+两个分别为40的req中断,此时req.0和req.1是非共享模式
global_reserved = 150,
systembits_inalloc = 3,
total_allocated = 550,
online_maps = 80,
maps = 0x2ff20,
scratch_map = {481036337152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
system_map = {1125904739729407, 0, 1, 18446726481523507200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
}

说明 对于mask内的cpu号,都需要一个中断容量,两个40就是因为80核的服务器,两个vq,平分中断,感兴趣的同学可以去查看irq_build_affinity_masks这个函数的实现。这样一个vector,因为开启了 IRQD_AFFINITY_MANAGED 属性,导致需要占用80个中断容量。而我们系统由于有512个virtio_blk设备,所以申请到部分设备的时候,就把总的

vector容量耗光了,但其实分配的irq总数才不到2000.

那么,virtio_blk设备什么时候开启的 IRQD_AFFINITY_MANAGED 属性的呢?查看git记录:


6abd6e5a44040 (Amit Shah 2011-12-22 16:58:29 +0530 507) static int init_vq(struct virtio_blk *vblk)
6abd6e5a44040 (Amit Shah 2011-12-22 16:58:29 +0530 508) {
......
6a27b656fc021 (Ming Lei 2014-06-26 17:41:48 +0800 515) struct virtio_device *vdev = vblk->vdev;
ad71473d9c437 (Christoph Hellwig 2017-02-05 18:15:25 +0100 516) struct irq_affinity desc = { 0, };----会导致blk申请中断时,使用内核managed方式来申请,一个dev会占用cpu核数这么多的容量。

看起来是 ad71473d9c437 这个 commit引入了这个问题。

但是根据virtio_blk驱动,第一遍中断申请的时候才有affinity_managed 设置,第二遍应该并没有设置,具体 vp_find_vqs_msix 如下:


//caq:vq申请中断,msix 模式,per_vq_vectors 决定是个vq共享中断还是独占中断
static int vp_find_vqs_msix(struct virtio_device *vdev, unsigned nvqs,
struct virtqueue *vqs[], vq_callback_t *callbacks[],
const char * const names[], bool per_vq_vectors,
const bool *ctx,
struct irq_affinity *desc)
{
struct virtio_pci_device *vp_dev = to_vp_device(vdev);
u16 msix_vec;
int i, err, nvectors, allocated_vectors; vp_dev->vqs = kcalloc(nvqs, sizeof(*vp_dev->vqs), GFP_KERNEL);
if (!vp_dev->vqs)
return -ENOMEM; if (per_vq_vectors) {//caq:单个vq 单个vector 中断号
/* Best option: one for change interrupt, one per vq. */
nvectors = 1;//caq:这个是config的中断,注意要和virtio_net的 ctrl vq区分
for (i = 0; i < nvqs; ++i)
if (callbacks[i])//caq:由于ctrl vq是不设置callbacks的,所以它没有中断
++nvectors;
} else {
/* Second best: one for change, shared for all vqs. */
nvectors = 2;
}
//caq:中断总数最少为2,最多为vq个数+1,1为config的中断,另外单个vq单个vector才具备带亲核属性
err = vp_request_msix_vectors(vdev, nvectors, per_vq_vectors,
per_vq_vectors ? desc : NULL);//caq:nvectors 为总中断数,注意desc的配置取决于 per_vq_vectors //caq:virtio_pci申请msix的vector
static int vp_request_msix_vectors(struct virtio_device *vdev, int nvectors,
bool per_vq_vectors, struct irq_affinity *desc)
{
......
vp_dev->msix_affinity_masks
= kcalloc(nvectors, sizeof(*vp_dev->msix_affinity_masks),
GFP_KERNEL);//caq:中断掩码内存的分配
......
if (desc) {//caq:要求带亲核属性
flags |= PCI_IRQ_AFFINITY;//caq:带上亲核属性
desc->pre_vectors++; /* virtio config vector *///caq:细节,相当于指定了config中断不要设置亲核,走系统默认
}
......

原因,因为前面很多virtio_blk设备因为一个vector占用了80个中断容量,导致整体中断数不够了,

而后面的virtio_blk设备,第一遍使用vp_find_vqs_msix带 managed_affinity属性申请中断时失败,第二遍尽管使用vq共享中断模式,由于os连一个中断都分配不出来, 也会失败,导致走入了第三个流程,也就是 vp_find_vqs_intx 流程。

在另外一个virtio_blk单vq的环境上,具体查看如下:


[root@localhost config_json]# cat /proc/interrupts |grep req |tail -1
986: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 114 0 0 IR-PCI-MSI 93687809-edge virtio180-req.0
[root@localhost config_json]# cat /proc/irq/986/smp_affinity
ffff,ffffffff,ffffffff [root@localhost config_json]# cat /proc/interrupts |grep queues |tail -1
1650: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 120 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 94369793-edge virtio512-virtqueues [root@localhost config_json]# cat /proc/irq/1650/smp_affinity
ffff,ffffffff,ffffffff
[root@localhost config_json]# ls /sys/bus/virtio/devices/virtio180/block/
vdsh
[root@localhost config_json]# ls /sys/bus/virtio/devices/virtio512/block/
vdafj

以上可以看出,virtio180,也就是 vdsh 的block设备,走了 vp_find_vqs_msix 第一遍流程,分配了带 managed_affinity 的vector,所以他的中断名字是req结尾的,

而另外一个 virtio512 ,也就是 vdafj 的block设备,走了 vp_find_vqs_msix 第二遍流程,没有分配 带 managed_affinity 的vector,所以它的中断名字是 virtqueues 结尾的,

而后面的设备,只能走第三个流程,报错了,所以打点发现,除了 activate的时候会报容量不足,在alloc阶段,也会在 irq_matrix_alloc 报容量不足。


int irq_matrix_alloc(struct irq_matrix *m, const struct cpumask *msk,
bool reserved, unsigned int *mapped_cpu)
{ ......
cpu = matrix_find_best_cpu(m, msk);
if (cpu == UINT_MAX)//caq:在msk内,没找到合适的cpu来记账
return -ENOSPC; cm = per_cpu_ptr(m->maps, cpu);
bit = matrix_alloc_area(m, cm, 1, false);//caq:获取一个
if (bit >= m->alloc_end)
return -ENOSPC;//caq:没有资源了
...... 打点记录如下: Returning from: 0xffffffffb7158300 : irq_matrix_reserve_managed+0x0/0x130 [kernel]
Returning to : 0xffffffffb705adb7 : x86_vector_alloc_irqs+0x2d7/0x3c0 [kernel]
0xffffffffb75dc42c : intel_irq_remapping_alloc+0x7c/0x6d0 [kernel]
0xffffffffb7156438 : msi_domain_alloc+0x68/0x120 [kernel]
0xffffffffb715457d : __irq_domain_alloc_irqs+0x12d/0x300 [kernel]
0xffffffffb7156b38 : msi_domain_alloc_irqs+0x98/0x2f0 [kernel]
0xffffffffb705e5e4 : native_setup_msi_irqs+0x54/0x90 [kernel]
0xffffffffb74ef69f : __pci_enable_msix_range+0x3df/0x5e0 [kernel]
0xffffffffb74ef96b : pci_alloc_irq_vectors_affinity+0xbb/0x130 [kernel]
0xffffffffb70635e0 : kretprobe_trampoline+0x0/0x50 [kernel]
0x1b7574a40
0x1b7574a40 (inexact)
irq_matrix_reserve_managed return -28

三、故障复现

1、只要是一开始各个核中断的aviable 容量相当,然后热插拔足够多virtio_blk设备,则必现。

2、如果各个核的中断的available容量相差很多,比如常见的numa节点的第一个cpu的中断占用过多,

使得走第一分支时因为某个核容量不够而reserve_managed 失败,然后

则会使得后面大量的virtio_blk走第二个分支,此时不带managed_affinity,反而能分配成功。

四、故障规避或解决

可能的解决方案之一:


static int init_vq(struct virtio_blk *vblk)//caq:初始化关于vq相关的内容
{
int err;
int i;
vq_callback_t **callbacks;
const char **names;
struct virtqueue **vqs;
unsigned short num_vqs;
struct virtio_device *vdev = vblk->vdev;
struct irq_affinity desc = { 0, };//caq:去掉这行代码

解决方案之二:

开启irqbalance,并让服务器进入 Power-save mode 时,irqbalance 会将中断集中分配给numa节点的第一个 CPU,这样慢慢地,各个核

的available的irq 容量就相差比较大了,当然这种不太靠谱。

解决方案之三:

手工调整中断亲核,使得某些核的容量接近于0,然后再加载virtio_blk设备。

五、作者简介

陈安庆,目前在dpu厂商 云豹智能 负责linux内核及虚拟化方面的工作,

联系方式:微信与手机同号:18752035557。

如何使能512个virtio_blk设备的更多相关文章

  1. [BlueZ] 2、使用bluetoothctl搜索、连接、配对、读写、使能notify蓝牙低功耗设备

    星期三, 05. 九月 2018 02:03上午 - beautifulzzzz 1.前言 上一篇讲了如何编译安装BlueZ-5,本篇主要在于玩BlueZ,用命令行去操作BLE设备: [BlueZ] ...

  2. IIC设备驱动程序

    IIC设备是一种通过IIC总线连接的设备,由于其简单性,被广泛引用于电子系统中.在现代电子系统中,有很多的IIC设备需要进行相互之间通信 IIC总线是由PHILIPS公司开发的两线式串行总线,用于连接 ...

  3. USB 3.0规范中译本第9章 设备框架

    本文为CoryXie原创译文,转载及有任何问题请联系cory.xie#gmail.com. 设备框架可以被分成三层: 最底层是总线接口层,传送和接收包. 中间层处理在总线接口和设备的各种端点之间路由数 ...

  4. 【DSP开发】【Linux开发】IIC设备驱动程序

    IIC设备是一种通过IIC总线连接的设备,由于其简单性,被广泛引用于电子系统中.在现代电子系统中,有很多的IIC设备需要进行相互之间通信 IIC总线是由PHILIPS公司开发的两线式串行总线,用于连接 ...

  5. RT-Thread 设备驱动UART浅析

    OS版本:RT-Thread 4.0.0 芯片:STM32F407 RT-Thread的串口驱动框架与Linux相识,分成 I/O设备框架 + 设备底层驱动: 1. serial设备初始化及使用 将配 ...

  6. usb2.0 规范学习笔记

    1.一个USB HOST 最多可以同时支持128 个地址,地址0 作为默认地址,只在设备枚举期间临时使 用,而不能被分配给任何一个设备,因此一个USB HOST 最多可以同时支持127 个地址,如果一 ...

  7. IIC驱动分析

    IIC设备是一种通过IIC总线连接的设备,由于其简单性,被广泛引用于电子系统中.在现代电子系统中,有很多的IIC设备需要进行相互之间通信 IIC总线是由PHILIPS公司开发的两线式串行总线,用于连接 ...

  8. C# NModbus RTU通信实现

    Modbus协议时应用于电子控制器上的一种通用语言.通过此协议,控制器相互之间.控制器经由网络/串口和其它设备之间可以进行通信.它已经成为了一种工业标准.有了这个通信协议,不同的厂商生成的控制设备就可 ...

  9. Linux归档压缩、分区管理与LVM管理

    归档和压缩命令: 命令格式: gzip [-9] 文件名 bzip2 [-9] 文件名 gzip –d .gz格式的压缩文件 bzip2 –d .bz2格式的压缩文件 选项: -9:高压缩比,多用于压 ...

随机推荐

  1. Vue出现Component template should ...

    当运行vue出现错误Component template should contain exactly one root element. If you ...的时候,我们只需要将<templa ...

  2. sql语句中 int(1)与int(10)有什么区别?资深开发竟然能理解错

    过完春节该投入战斗了,上班第一天发现了一个挺有意思的知识点给大家分享一下:一直以来的的误区我们都认为了int后面的跟的数字为最大显示宽度会对后面插入的参数会有限制,其实倒不是这样的 # 困惑 最近遇到 ...

  3. BUUCTF-被偷走的文件

    被偷走的文件 这题刚开始还以为是单纯的流量题,看流量半天也没发现什么异常. 因为是文件传输过程的,所以我们看到ftp的流量就过滤下看看即可. 在第三个包发现flag.rar存在. 一开始我觉得没啥,后 ...

  4. Mac安装Brew包管理系统

    Mac安装Brew包管理系统 前言 为什么需要安装brew 作为一个开发人员, 习惯了使用centos的yum和ubuntu的apt, 在mac中有没有这两个工具的平替? 有, 就是Brew. Bre ...

  5. oracle备份数据库数据及导入数据库

    1.oracle数据库备份和导入 bat 脚本 scott oracle数据库用户名称 123456 数据库scott用户下的密码 192.168.124.8 本电脑IP orcl 为oracle库 ...

  6. 零基础学Python:元组(Tuple)详细教程

    Python的元组与列表类似,不同之处在于元组的元素不能修改,元组使用小括号,列表使用方括号,元组创建很简单,只需要在括号中添加元素,并使用逗号隔开即可https://jq.qq.com/?_wv=1 ...

  7. Mysql错误:The server time zone value is unrecognized or represents more than one time zone

    方法1.修改Mysql的时区为东8区,执行如下命令即可: PS:这种方式每次开机都要配置的 set global time_zone='+8:00' 方法2.配置改成这样的 spring.dataso ...

  8. [零基础学IoT Pwn] 复现Netgear WNAP320 RCE

    [零基础学IoT Pwn] 复现Netgear WNAP320 RCE 0x00 前言: 这是[零基础学IoT Pwn]的第二篇,上篇我们搭好了仿真环境后,模拟运行了Netgear WNAP320固件 ...

  9. Day01 对前端的初步了解

    了解了工作性质以及流程 产品经理+UI+前端程序员+后端程序员+测试人员 了解了工作会做到的项目 pc端项目,后台管理系统,APP,小程序,移动端网页 了解了后续需要学到的课程 HTML+CSS Ja ...

  10. Tomcat日志乱码解决方法

    将配置文件logging.properties所有含有UTF-8的删除