MSI_MSI-X中断之体验与使用

news2024/10/3 10:45:37

MSI_MSI-X中断之体验与使用


文章目录

  • MSI_MSI-X中断之体验与使用
    • 1. 怎么发出MSI/MSI-X中断
      • 1.1 在RK3399上体验
        • 1.1.1 安装工具
        • 1.1.2 查看设备MSI-X信息
        • 1.1.3 验证MSI-X信息
    • 2. 怎么使用MSI/MSI-X
    • 3. MSI/MSI-X中断源码分析
      • 3.1 IRQ Domain创建流程
        • 3.1.1 GIC
        • 3.1.2 ITS
        • 3.1.3 PCI MSI
        • 3.1.4 PCIe控制器
      • 3.2 分配中断
  • 致谢



开发板资料:

  • 开发板Firefly-rk3399资料

参考内核文件:

  • Documentation\PCI\MSI-HOWTO.txt
  • drivers\pci\host\pcie-rockchip.c
  • drivers\nvme\host\pci.c
  • drivers\irqchip\irq-gic-v3.c
  • drivers\irqchip\irq-gic-v3-its.c
  • drivers\irqchip\irq-gic-v3-its-pci-msi.c


1. 怎么发出MSI/MSI-X中断

PCIe设备向发出中断,它发出TLP包,往某个地址写入某个数据即可:

  • 往哪个地址?GICv3 ITS的GITS_TRANSLATER寄存器,TLP包里使用的是PCI地址
  • 写什么数据?0、1、2、……,要发出第1个中断时写0,要发出第2个中断时写1,……

在设备树文件rk3399.dtsi中,可以看到ITS的基地址是0xfee20000:

its: interrupt-controller@fee20000 {
                        compatible = "arm,gic-v3-its";
                        msi-controller;
                        reg = <0x0 0xfee20000 0x0 0x20000>;
                };

IHI0069G_gic_architecture_specification.pdf中有ITS寄存器的偏移地址:

在这里插入图片描述
GITS_TRANSLATER寄存器的CPU地址是:0xfee20000 + 0x010000 + 0x0040 = 0xfee30040。

对应的PCI地址也是0xfee30040(驱动程序里为例方便,故意使得CPU地址跟PCI地址相同,这2个地址属于不同地址空间),

所以下图中PCI地址都是0xfee30040。

在这里插入图片描述

1.1 在RK3399上体验

使用RK3399开发板,插上了NVMe SSD固态硬盘。

1.1.1 安装工具

请给RK3399刷入Ubuntu映像文件,然后在开发板上执行:

udhcpc  # 获取IP
apt update  # 更新源
apt install pciutils  # 安装lspci工具
apt install devmem2   # 安装devmem2工具

1.1.2 查看设备MSI-X信息

执行lspci -vvv,得到如下信息:

01:00.0 Non-Volatile memory controller: Silicon Motion, Inc. Device 2263 (rev 03) (prog-if 02 [NVM Express])
        Subsystem: Silicon Motion, Inc. Device 2263
        Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx+
        Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
        Latency: 0
        Interrupt: pin A routed to IRQ 231
        Region 0: Memory at fa000000 (64-bit, non-prefetchable) [size=16K]
        Capabilities: [40] Power Management version 3
                Flags: PMEClk- DSI- D1- D2- AuxCurrent=0mA PME(D0-,D1-,D2-,D3hot-,D3cold-)
                Status: D0 NoSoftRst+ PME-Enable- DSel=0 DScale=0 PME-
        Capabilities: [50] MSI: Enable- Count=1/8 Maskable+ 64bit+
                Address: 0000000000000000  Data: 0000
                Masking: 00000000  Pending: 00000000
        Capabilities: [70] Express (v2) Endpoint, MSI 00
                DevCap: MaxPayload 128 bytes, PhantFunc 0, Latency L0s unlimited, L1 unlimited
                        ExtTag- AttnBtn- AttnInd- PwrInd- RBE+ FLReset+ SlotPowerLimit 0.000W
                DevCtl: Report errors: Correctable- Non-Fatal- Fatal- Unsupported-
                        RlxdOrd+ ExtTag- PhantFunc- AuxPwr- NoSnoop- FLReset-
                        MaxPayload 128 bytes, MaxReadReq 512 bytes
                DevSta: CorrErr- UncorrErr- FatalErr- UnsuppReq- AuxPwr+ TransPend-
                LnkCap: Port #0, Speed 8GT/s, Width x4, ASPM L1, Exit Latency L0s <1us, L1 <8us
                        ClockPM+ Surprise- LLActRep- BwNot- ASPMOptComp+
                LnkCtl: ASPM Disabled; RCB 64 bytes Disabled- CommClk-
                        ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt-
                LnkSta: Speed 2.5GT/s, Width x4, TrErr- Train- SlotClk+ DLActive- BWMgmt- ABWMgmt-
                DevCap2: Completion Timeout: Range ABCD, TimeoutDis+, LTR+, OBFF Not Supported
                DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis-, LTR-, OBFF Disabled
                LnkCtl2: Target Link Speed: 8GT/s, EnterCompliance- SpeedDis-
                         Transmit Margin: Normal Operating Range, EnterModifiedCompliance- ComplianceSOS-
                         Compliance De-emphasis: -6dB
                LnkSta2: Current De-emphasis Level: -3.5dB, EqualizationComplete-, EqualizationPhase1-
                         EqualizationPhase2-, EqualizationPhase3-, LinkEqualizationRequest-
        Capabilities: [b0] MSI-X: Enable+ Count=16 Masked-
                Vector table: BAR=0 offset=00002000
                PBA: BAR=0 offset=00002100
        Capabilities: [100 v2] Advanced Error Reporting
                UESta:  DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt- RxOF- MalfTLP- ECRC- UnsupReq- ACSViol-
                UEMsk:  DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt- RxOF- MalfTLP- ECRC- UnsupReq- ACSViol-
                UESvrt: DLP+ SDES+ TLP- FCP+ CmpltTO- CmpltAbrt- UnxCmplt- RxOF+ MalfTLP+ ECRC- UnsupReq- ACSViol-
                CESta:  RxErr- BadTLP- BadDLLP- Rollover- Timeout- NonFatalErr-
                CEMsk:  RxErr- BadTLP- BadDLLP- Rollover- Timeout- NonFatalErr+
                AERCap: First Error Pointer: 00, GenCap+ CGenEn- ChkCap+ ChkEn-
        Capabilities: [158 v1] #19
        Capabilities: [178 v1] Latency Tolerance Reporting
                Max snoop latency: 0ns
                Max no snoop latency: 0ns
        Capabilities: [180 v1] L1 PM Substates
                L1SubCap: PCI-PM_L1.2+ PCI-PM_L1.1+ ASPM_L1.2+ ASPM_L1.1+ L1_PM_Substates+
                          PortCommonModeRestoreTime=10us PortTPowerOnTime=10us
                L1SubCtl1: PCI-PM_L1.2- PCI-PM_L1.1- ASPM_L1.2- ASPM_L1.1-
                           T_CommonMode=0us LTR1.2_Threshold=0ns
                L1SubCtl2: T_PwrOn=10us
        Kernel driver in use: nvme

从上述信息可以看到:

        Region 0: Memory at fa000000 (64-bit, non-prefetchable) [size=16K]
        Capabilities: [b0] MSI-X: Enable+ Count=16 Masked-
                Vector table: BAR=0 offset=00002000
                PBA: BAR=0 offset=00002100

这表示:

  • MSI-X: Enable+:使用MSI-X功能
  • Vector table: BAR=0 offset=00002000:MSI的向量在BAR 0偏移地址0x00002000处
  • Region 0: Memory at fa000000:
    • BAR 0的PCI地址是0xfa000000,
    • 驱动程序里为了方便令CPU地址等于PCI地址,所以BAR的CPU地址也是0xfa000000。

我们可以去读取 0xfa000000 + 0x00002000开始的向量表,验证里:

  • msg addr为0xfee30040
  • msg data为0、1、……

在这里插入图片描述

1.1.3 验证MSI-X信息

在这里插入图片描述

2. 怎么使用MSI/MSI-X

参考内核文档:Documentation\PCI\MSI-HOWTO.txtdrivers\nvme\host\pci.c

主要函数是这2个:

int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries,
			  int minvec, int maxvec);
int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec);

示例代码如下:

	// 分配 msix_entry 数组,每一数组项用来保存一个中断的信息
	dev->entry = kzalloc_node(num_possible_cpus() * sizeof(*dev->entry),
							GFP_KERNEL, node);

	// 先尝试使用MSI-X
	vecs = pci_enable_msix_range(pdev, dev->entry, 1, nr_io_queues);
	if (vecs < 0) {
        // 再尝试使用MSI
		vecs = pci_enable_msi_range(pdev, 1, min(nr_io_queues, 32));
		if (vecs < 0) {
			vecs = 1;
		} else {
			for (i = 0; i < vecs; i++)
				dev->entry[i].vector = i + pdev->irq;
		}
	}

	// request_irq: 中断号都保存在dev->entry[i].vector里
	for (i = 0; i < vecs; i++)
		request_irq(dev->entry[i].vector, ...);

注意,在pci_enable_msix_range或者pci_enable_msi_range函数中:

  • minvec从1开始
  • 对于pci_enable_msix_range,中断号保存在entries[i].vector里
  • 对于pci_enable_msi_range,第1个中断号保存在pdev->irq里

3. MSI/MSI-X中断源码分析

3.1 IRQ Domain创建流程

从PCI设备触发,涉及三个IRQ Domain:

  • drivers\irqchip\irq-gic-v3-its-pci-msi.c
  • drivers\irqchip\irq-gic-v3-its.c
  • drivers\irqchip\irq-gic-v3.c

3.1.1 GIC

设备树:

        gic: interrupt-controller@fee00000 {
                compatible = "arm,gic-v3";
                #interrupt-cells = <4>;
                #address-cells = <2>;
                #size-cells = <2>;
                ranges;
                interrupt-controller;

                reg = <0x0 0xfee00000 0 0x10000>, /* GICD */
                      <0x0 0xfef00000 0 0xc0000>, /* GICR */
                      <0x0 0xfff00000 0 0x10000>, /* GICC */
                      <0x0 0xfff10000 0 0x10000>, /* GICH */
                      <0x0 0xfff20000 0 0x10000>; /* GICV */
                interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH 0>;
                its: interrupt-controller@fee20000 {
                        compatible = "arm,gic-v3-its";
                        msi-controller;
                        reg = <0x0 0xfee20000 0x0 0x20000>;
                };

驱动代码:`drivers\irqchip\irq-gic-v3.c
在这里插入图片描述

3.1.2 ITS

设备树:

its: interrupt-controller@fee20000 {
                        compatible = "arm,gic-v3-its";
                        msi-controller;
                        reg = <0x0 0xfee20000 0x0 0x20000>;
                };

驱动代码:drivers\irqchip\irq-gic-v3-its.c
在这里插入图片描述

3.1.3 PCI MSI

对应的设备节点跟ITS驱动使用的一样的:

its: interrupt-controller@fee20000 {
                        compatible = "arm,gic-v3-its";
                        msi-controller;
                        reg = <0x0 0xfee20000 0x0 0x20000>;
                };

驱动代码:drivers\irqchip\irq-gic-v3-its-pci-msi.c,它只是在ITS下面再增加了一个处理层:

在这里插入图片描述

3.1.4 PCIe控制器

设备树:

        pcie0: pcie@f8000000 {
                compatible = "rockchip,rk3399-pcie";
                #address-cells = <3>;
                #size-cells = <2>;
                aspm-no-l0s;
                clocks = <&cru ACLK_PCIE>, <&cru ACLK_PERF_PCIE>,
                         <&cru PCLK_PCIE>, <&cru SCLK_PCIE_PM>;
                clock-names = "aclk", "aclk-perf",
                              "hclk", "pm";
                bus-range = <0x0 0x1f>;
                max-link-speed = <1>;
                linux,pci-domain = <0>;
                msi-map = <0x0 &its 0x0 0x1000>;

里面的msi-map = <0x0 &its 0x0 0x1000>;是用来把PCIe设备映射到MSI控制器,它的格式为:

msi-map = <rid-base &msi-controller msi-base length>;
  • rid-base:第1个Request ID,就是使用<bus, dev, function>组成的一个数字
  • msi-controller:这个PCIe设备映射到哪个MSI控制器?
  • msi-base:第1个PCIe设备映射到MSI控制器哪个中断?
  • length:能映射多少个设备

3.2 分配中断

代码:drivers\nvme\host\pci.c

nvme_probe > nvme_probe_work > nvme_setup_io_queues 
    	pci_enable_msix_range
    		pci_enable_msix(dev, entries, nvec);
				msix_capability_init(dev, entries, nvec);
    	
    	pci_enable_msi_range
            	msi_capability_init(dev, nvec);
    
msix_capability_init/msi_capability_init
    pci_msi_setup_msi_irqs
        pci_msi_domain_alloc_irqs
            msi_domain_alloc_irqs
    			ret = ops->msi_prepare(domain, dev, nvec, &arg); // its_pci_msi_prepare
					its_pci_msi_prepare // irq-gic-v3-its-pci-msi.c
                        // rid = (bus << 8) | (dev << 4) | function
                        info->scratchpad[0].ul = pci_msi_domain_get_msi_rid(domain, pdev);	
						return msi_info->ops->msi_prepare(...) // 上一层irq-gic-v3-its.c
                            	its_msi_prepare
                            		dev_id = info->scratchpad[0].ul;  // rid
									its_dev = its_create_device(its, dev_id, nvec);
												// 从ITS全局的位图里找到空闲位 chunk
												// 一个chunk表示32个中断
												// its的hwirq = (chunk << 5) + 8192
												// 这也是GIC的hwirq
												lpi_map = its_lpi_alloc_chunks(nvecs, &lpi_base, &nr_lpis);
														// 等于(chunk << 5) + 8192 
														dev->event_map.lpi_base = lpi_base;
                            		
    			__irq_domain_alloc_irqs
    				irq_domain_alloc_irqs_recursive
    					ret = domain->ops->alloc(domain, irq_base, nr_irqs, arg);
								its_irq_domain_alloc
                                    err = its_alloc_device_irq(its_dev, &hwirq);
												*hwirq = dev->event_map.lpi_base + idx;
									irq_domain_set_hwirq_and_chip
                                        irq_data->hwirq = hwirq;
										irq_data->chip = chip ? chip : &no_irq_chip;
    			irq_domain_activate_irq(irq_data);
					domain->ops->activate(domain, irq_data);
						msi_domain_activate
                            irq_chip_compose_msi_msg(irq_data, &msg)	
                            	   // 构造msg,里面含有MSI或msi-x的addr/val
                                   its_irq_compose_msi_msg
                            			addr = its->phys_base + GITS_TRANSLATER;
                                        msg->address_lo		= addr & ((1UL << 32) - 1);
                                        msg->address_hi		= addr >> 32;
										// its_get_event_id:
										// d->hwirq - its_dev->event_map.lpi_base;
                                        msg->data		= its_get_event_id(d);                   
                            // 设置msi-x的entry地址	
                            irq_chip_write_msi_msg(irq_data, &msg);
                            data->chip->irq_write_msi_msg(data, msg);
                            pci_msi_domain_write_msg
                                __pci_write_msi_msg(desc, msg);

__pci_write_msi_msg(desc, msg);
    // 对于MSI-X
    writel(msg->address_lo, base + PCI_MSIX_ENTRY_LOWER_ADDR);
    writel(msg->address_hi, base + PCI_MSIX_ENTRY_UPPER_ADDR);
    writel(msg->data, base + PCI_MSIX_ENTRY_DATA);

	// 对于MSI
	pci_write_config_word(dev, pos + PCI_MSI_FLAGS, msgctl);
	pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_LO,
				       msg->address_lo);


// 为PCI设备确定hwirq
its_domain_ops.alloc
its_irq_domain_alloc
    its_alloc_device_irq
        *hwirq = dev->event_map.lpi_base + idx;



致谢

以上笔记源自韦东山老师的视频课程,感谢韦老师,韦老师是嵌入式培训界一股清流,为嵌入式linux开发点起的星星之火,也愿韦老师桃李满园。聚是一团火,散是满天星!

在这样一个速食的时代,坚持做自己,慢下来,潜心琢磨,心怀敬畏,领悟知识,才能向下扎到根,向上捅破天,背着世界往前行!
仅此向嵌入行业里的每一个认真做技术的从业者致敬!



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

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

相关文章

【Flutter】【Unity】使用 Flutter + Unity 构建(AR 体验工具包)

使用 Flutter Unity 构建&#xff08;AR 体验工具包&#xff09;【翻译】 原文&#xff1a;https://medium.com/potato/building-with-flutter-unity-ar-experience-toolkit-6aaf17dbb725 由于屡获殊荣的独立动画工作室 Aardman 与讲故事的风险投资公司 Fictioneers&#x…

最大公约数:常用的四大算法求解最大公约数,分解质因数法、短除法、辗转相除法、更相减损法。

常用的四大算法求解最大公约数&#xff0c;分解质因数法、短除法、辗转相除法、更相减损法。 (本文获得CSDN质量评分【91】)【学习的细节是欢悦的历程】Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免费“圣经”教程《 python 完全自学教程》&#x…

网络基础-虚拟化工具-网桥

系列文章目录 本系列文章主要是回顾和学习工作中常用的网络基础命令&#xff0c;在此记录以便于回顾。 该篇文章主要是讲解虚拟化的工具网桥相关的概念和常用命令 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录系…

C++之多态【详细总结】

前言 想必大家都知道面向对象的三大特征&#xff1a;封装&#xff0c;继承&#xff0c;多态。封装的本质是&#xff1a;对外暴露必要的接口&#xff0c;但内部的具体实现细节和部分的核心接口对外是不可见的&#xff0c;仅对外开放必要功能性接口。继承的本质是为了复用&#x…

MySQL(主从、半同步、组复制、MHA高可用)

文章目录一、MySQL源码编译以及初始化二、mysql主从复制、半同步MySQL组复制MySQL读写分离MHA高可用一、MySQL源码编译以及初始化 源码编译使用cmake&#xff0c;所以要提前安装cmake&#xff0c;完成之后make install即可 这里要创建mysql用户&#xff0c;以及用普通用户方式…

电子秤专用模拟数字(AD)转换器芯片HX711介绍

HX711简介HX711是一款专为高精度电子秤而设计的24 位A/D 转换器芯片。与同类型其它芯片相比&#xff0c;该芯片集成了包括稳压电源、片内时钟振荡器等其它同类型芯片所需要的外围电路&#xff0c;具有集成度高、响应速度快、抗干扰性强等优点。降低了电子秤的整机成本&#xff…

分享112个JS菜单导航,总有一款适合您

分享112个JS菜单导航&#xff0c;总有一款适合您 112个JS菜单导航下载链接&#xff1a;https://pan.baidu.com/s/1Dm73d2snbu15hZErJjTXxg?pwdfz1c 提取码&#xff1a;fz1c Python采集代码下载链接&#xff1a;https://wwgn.lanzoul.com/iKGwb0kye3wj base_url "h…

【游戏逆向】RPG游戏背包镶嵌系统分析

镶嵌系统是很多3D游戏都有的功能&#xff0c;玩家可以向镶嵌槽内附加宝石来提升装备的属性&#xff0c;这也直接提升了物品的价值。在一些扫拍卖和摆摊的外挂中经常利用这个属性来低价购入高价值装备。以这款游戏为例&#xff0c;我们来对装备上的镶嵌槽和镶嵌宝石进行分析。 …

Nacos,一款非常优秀的注册中心(附视频)

Nacos 核心源码精讲 - IT贱男 - 掘金小册全方位源码精讲&#xff0c;深度剖析 Nacos 注册中心和配置中心的核心思想。「Nacos 核心源码精讲」由IT贱男撰写&#xff0c;375人购买https://s.juejin.cn/ds/BuC3Vs9/ 先简单说两句 你好&#xff0c;很高兴你能够点开本小册&#x…

python 的 if 语句如何使用说明

文章目录1. 一个示例2. 条件测试2.1 检查是否相等2.2 检查是否相等时不考虑大小写2.3 检查是否不相等2.4 比较数字2.5 检查多个条件2.6 布尔表达式3. if 语句4. 使用 if 语句处理列表1. 一个示例 关于 if 条件语句的使用&#xff0c;我们来写一个示例进行说明&#xff1a; #写…

6.14 Rayleigh商

定义 矩阵在某个向量处的瑞利商Rayleigh quotient是这样定义的: ρ(x):xHAxxHx\rho(x) :\frac{x^HAx}{x^Hx} ρ(x):xHxxHAx​   这个怎么理解呢?上面是埃尔米特内积的表达式&#xff0c;下面是标准埃尔米特内积。但是矩阵不一定是对称阵&#xff0c;如果不是复数的话&#x…

ChatGPT 这个风口,普通人怎么抓住:比如APP集成ChatGPT,公众号集成ChatGPT...

文章目录1. 引出问题2. 简单介绍ChatGPT2.1 ChatGPT是什么2.2 如何使用ChatGPT3. 普通人利用ChatGPT 变现方式1. 引出问题 最近几天OpenAI发布的ChatGPT聊天机器人如日中天&#xff0c;连着上了各个平台的热搜榜。 很多平台也都已集成了ChatGPT&#xff0c;比如csdn的客户端A…

json-server使用

文章目录json-server使用简介安装json-server启动json-server操作创建数据库查询数据增加数据删除数据修改数据putpatch配置静态资源静态资源首页资源json-server使用 简介 github地址 安装json-server npm install -g json-server启动json-server json-server --watch db…

Linux系统位运算函数以及相应CPU ISA实现收录

以32位数据的二进制表示为例&#xff0c;习惯的写法是LSB在左&#xff0c;MSB在右&#xff0c;注意BIT序和大小端的字节序没有关系。Linux和BIT操作有关的接口在定义在头文件bitops.h中&#xff0c;bitops.h定义有两层&#xff0c;通用层和架构层&#xff0c;对应两个bitops.h&…

【重要】2023年上半年有三AI新课程规划出炉,讲师持续招募中!

2023年正式起航&#xff0c;想必大家都已经完全投入到了工作状态中&#xff0c;有三AI平台今年将在已有内容的基础上&#xff0c;继续进行新课程开发&#xff0c;本次我们来介绍今年上半年的课程计划&#xff0c;以及新讲师招募计划。2023年新上线课程我们平台的课程当前分为两…

【Python爬虫案例】批量采集网站壁纸,实现自动更换桌面壁纸

前言 美照天天换&#xff0c;才不会腻 不知道你们是不是这样&#xff0c;我的手机壁纸电脑壁纸&#xff0c;隔三岔五就喜欢换&#xff0c;看久了 我就腻了&#xff0c;索性就用python把这个网站的壁纸都采集下来&#xff0c;顺便再让电脑自动更换我的桌面壁纸 ~ 一篇文章教会…

【AI简报第20230210期】 ChatGPT爆火背后、为AIoT和边缘侧AI喂算力的RISC-V

1. ChatGPT爆火背后&#xff1a;AI芯片迎接算力新挑战原文:https://www.163.com/dy/article/HT7BHN3C05199NPP.htmlChatGPT的出圈走红为AIGC打开全新市场增量&#xff0c;催生了更高的算力需求。作为人工智能三大核心要素之一&#xff0c;算力也被誉为人工智能“发动机”。华泰…

使用服务器搭建alist和webdav

docker镜像官网&#xff1a;https://hub.docker.com/r/xhofe/alist 一、准备工作 环境&#xff1a;centos7、docker 二、步骤 1.拉取alist镜像 在根目录下执行以下命令&#xff1a; docker pull xhofe/alist:latest2.运行alist docker run -d --restartalways -v /etc/ali…

互联网医院源码 线上问诊 智慧医院源码 C#源码

互联网医院平台源码 智慧医院管理系统源码 开发环境&#xff1a;ASP.NET C# VS2019 SQL2008 依托于实体医院利用互联网技术对接院内业务信息系统&#xff0c;向患者提供基于线上问诊、预约挂号、缴费结算、医患互动、诊后随访、健康科普和复诊等全面的医疗健康互联网服务。…

一文读懂 Zebec Chain 的“先行网络” Nautilus 链

最近&#xff0c;Zebec上线了DAO治理系统后&#xff0c;上线并通过了关于Nautilus链的提案&#xff0c;这也是DAO系统上线后通过的首个提案。Nautilus链可以被看作是Zebec Chain上线前的“先行”链&#xff0c;并且是目前行业内为数不多的以“Layer3”作为特点的模块化通用链&a…