如何使能512个virtio_blk设备
一例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设备的更多相关文章
- [BlueZ] 2、使用bluetoothctl搜索、连接、配对、读写、使能notify蓝牙低功耗设备
星期三, 05. 九月 2018 02:03上午 - beautifulzzzz 1.前言 上一篇讲了如何编译安装BlueZ-5,本篇主要在于玩BlueZ,用命令行去操作BLE设备: [BlueZ] ...
- IIC设备驱动程序
IIC设备是一种通过IIC总线连接的设备,由于其简单性,被广泛引用于电子系统中.在现代电子系统中,有很多的IIC设备需要进行相互之间通信 IIC总线是由PHILIPS公司开发的两线式串行总线,用于连接 ...
- USB 3.0规范中译本第9章 设备框架
本文为CoryXie原创译文,转载及有任何问题请联系cory.xie#gmail.com. 设备框架可以被分成三层: 最底层是总线接口层,传送和接收包. 中间层处理在总线接口和设备的各种端点之间路由数 ...
- 【DSP开发】【Linux开发】IIC设备驱动程序
IIC设备是一种通过IIC总线连接的设备,由于其简单性,被广泛引用于电子系统中.在现代电子系统中,有很多的IIC设备需要进行相互之间通信 IIC总线是由PHILIPS公司开发的两线式串行总线,用于连接 ...
- RT-Thread 设备驱动UART浅析
OS版本:RT-Thread 4.0.0 芯片:STM32F407 RT-Thread的串口驱动框架与Linux相识,分成 I/O设备框架 + 设备底层驱动: 1. serial设备初始化及使用 将配 ...
- usb2.0 规范学习笔记
1.一个USB HOST 最多可以同时支持128 个地址,地址0 作为默认地址,只在设备枚举期间临时使 用,而不能被分配给任何一个设备,因此一个USB HOST 最多可以同时支持127 个地址,如果一 ...
- IIC驱动分析
IIC设备是一种通过IIC总线连接的设备,由于其简单性,被广泛引用于电子系统中.在现代电子系统中,有很多的IIC设备需要进行相互之间通信 IIC总线是由PHILIPS公司开发的两线式串行总线,用于连接 ...
- C# NModbus RTU通信实现
Modbus协议时应用于电子控制器上的一种通用语言.通过此协议,控制器相互之间.控制器经由网络/串口和其它设备之间可以进行通信.它已经成为了一种工业标准.有了这个通信协议,不同的厂商生成的控制设备就可 ...
- Linux归档压缩、分区管理与LVM管理
归档和压缩命令: 命令格式: gzip [-9] 文件名 bzip2 [-9] 文件名 gzip –d .gz格式的压缩文件 bzip2 –d .bz2格式的压缩文件 选项: -9:高压缩比,多用于压 ...
随机推荐
- ES6 - promise(3)
上一篇熟悉了promise的具体过程: promise的过程: 启动异步任务 => 返回promise对象 =>给promise对象绑定回调函数(甚至可以在异步任务结束后指定多个). 从p ...
- 超详细干货!Docker+PXC+Haproxy搭建高可用强一致性的MySQL集群
前言 干货又来了,全程无废话,可先看目录了解. MySQL搭建集群最常见的是binlog方式,但还有一种方式是强一致性的,能保证集群节点的数据一定能够同步成功,这种方式就是pxc,本篇就使用图文方式一 ...
- 技术分享 | app测试中常用的Android模拟器
原文链接 Emulator Emualor 是 Android Studio 自带的模拟器,是官方提供的工具,Android 开发最常使用的就是这一款. 它功能非常齐全,电话本.通话等功能都可正常使用 ...
- 部署ASP.NET Core最简单的办法,使用IIS部署ASP.NET Core应用
本文迁移自Panda666原博客,原发布时间:2021年3月28日.写原文的时候.NET的最新版本是5.0,现在7的preview出来了,时间真快啊.抽空再写个在Windows Server Core ...
- 【2022-06-16】Python解释器的下载安装与使用
一.Python解释器介绍 什么是Python解释器? Python是一门解释型语言,解释器是Python运行必不可少的一种工具.所以,我们搭建Python环境,本质上就是对Python进行配置和定制 ...
- 关于vue cli 使用iview 自定义主题遇到的坑
定制主题,这里讲变量覆盖 当你老老实实的把上面文档中的代码一一复制粘贴到项目文件中时,发现了还没装less,所以你就 npm install less –savenpm install less-lo ...
- 由ASP.NET Core根据路径下载文件异常引发的探究
前言 最近在开发新的项目,使用的是ASP.NET Core6.0版本的框架.由于项目中存在文件下载功能,没有使用类似MinIO或OSS之类的分布式文件系统,而是下载本地文件,也就是根据本地文件路径进行 ...
- 《A Neural Algorithm of Artistic Style》理解
在美术中,特别是绘画,人类掌握了通过在图像的内容和风格间建立复杂的相互作用从而创造独特的视觉体验的技巧.到目前为止,这个过程的算法基础是未知的,也没有现存的人工系统拥有这样的能力.然而在视觉感知的其他 ...
- cup缓存基础知识
目录 cup缓存 缓存结构 直接映射缓存 cup缓存 CPU缓存(CPU Cache)的目的是为了提高访问内存(RAM)的效率,这虽然已经涉及到硬件的领域,但它仍然与我们息息相关,了解了它的一些原理, ...
- elastic-job和spring cloud版本冲突2
***************************APPLICATION FAILED TO START*************************** Description: An at ...