Linux内核中断子系统

news2024/12/28 20:22:33

查看中断控制相关的设备树节点

*********************gpiof控制器*************************
    pinctrl: pin-controller@50002000 {
        #address-cells = <1>;
        #size-cells = <1>;
        compatible = "st,stm32mp157-pinctrl";
            
        interrupt-parent = <&exti>;
                gpiof: gpio@50007000 {
            
            interrupt-controller;//中断标识
            #interrupt-cells = <2>;
    //引用gpiof节点用于中断时,成员需要有两个值            
            status = "disabled";//gpiof为启用
            };
       &pinctrl {
               gpiof: gpio@50007000 {
        status = "okay";
        ngpios = <16>;
        gpio-ranges = <&pinctrl 0 80 16>;
    };

        };
        ************************exti**************************************          
        soc {
        
        #address-cells = <1>;
        #size-cells = <1>;
        interrupt-parent = <&intc>;                           
        exti: interrupt-controller@5000d000 {
            compatible = "st,stm32mp1-exti", "syscon";
            interrupt-controller;
            #interrupt-cells = <2>;
            reg = <0x5000d000 0x400>;
                           };
                           };
  ****************************GIC****************************************
  
    intc: interrupt-controller@a0021000 {
        compatible = "arm,cortex-a7-gic";
        #interrupt-cells = <3>;
        interrupt-controller;
        reg = <0xa0021000 0x1000>,
              <0xa0022000 0x2000>;
    };

编写设备树将按键和中断控制器对接起来

/内核顶层目录/Documentation/devicetree/bindings/interrupt-controller
vi interrupts.txt

 Example:
    interrupt-parent = <&intc1>;//引用中断父节点
    interrupts = <5 0>, <6 0>;//填写的时中断index,第二个成员填0表示默认属性
 
two cells
  ------------
  The #interrupt-cells property is set to 2 and the first cell defines the
  index of the interrupt within the controller, while the second cell is used
  to specify any of the following flags:
    - bits[3:0] trigger type and level flags
        1 = low-to-high edge triggered
        2 = high-to-low edge triggered
        4 = active high level-sensitive
        8 = active low level-sensitive
        
  vi stm32mp157a-fsmp1a.dts
  
    myirq{
    compatible="hqyj,irq";
    interrupt-parent = <&gpiof>;
    interrupts = <9 0>,<7 0>,<8 0>;//节点中引入的中断管脚  pf9 pf8 pf7
    };                                 

按键中断实例

#include <linux/init.h>
#include <linux/module.h>
#include<linux/of.h>
#include<linux/of_gpio.h>
#include<linux/gpio.h>
#include<linux/timer.h>
#include<linux/of_irq.h>
#include<linux/interrupt.h>
/* 
 myirq{
    compatible="hqyj,irq";
    interrupt-parent = <&gpiof>;
    interrupts = <9 0>,<7 0>,<8 0>;
};                 
 };*/
unsigned int irqno[3];
 struct device_node *node;
char *irqname[3]={"key1_int","key2_int","key3_int"};
//中断 处理函数
irqreturn_t irq_handler(int irqno,void *dev)
 {
     //根据request_irq函数的第5个参数来确定是哪一个按键产生的中断
    switch((int)dev)
    {
      case 0:
        printk("key1_int\n");
        break;
      case 1:
        printk("key2_int\n");
        break;
      case 2:
        printk("key3_int\n");
        break;
    }
    return IRQ_HANDLED;
}
 

static int __init mycdev_init(void)
{
 int i,ret;
    //通过名字获取设备树节点信息
    node=of_find_node_by_name(NULL,"myirq");
    if(node==NULL)
    {
        printk("通过路径解析设备树节点信息失败\n");
        return -ENODATA;
    }
      printk("通过路径解析设备树节点信息成功\n");
      //获取软中断号
      //3个按键对应的是3个软中断号,循环注册
  for(i=0;i<ARRAY_SIZE(irqno);i++)
  {
    irqno[i]=irq_of_parse_and_map(node,i);
    if(irqno[i]==0)
    {
      printk("获取软中断号失败 %d\n",i);
      return -EINVAL;
    }
    //注册中断
    ret=request_irq(irqno[i],irq_handler, IRQF_TRIGGER_FALLING,irqname[i],(void *)i);
    if(ret)
    {
      printk("中断注册失败\n");
      return ret;
    }
  }
     
     return 0;
}
static void __exit mycdev_exit(void)
{
  int i;
  //注销中断
  for(i=0;i<ARRAY_SIZE(irqno);i++)
  {
    free_irq(irqno[i],(void *)i);
  }
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

中断底半部

1.什么是中断底半部

在linux内核的中断处理程序里,不允许做延时、耗时甚至休眠的操作,但是有的时候又希望中断到来的时候尽可能多做一些事情,所以两个需求产生了矛盾。linux内核为了解决这个矛盾,引入了中断底半部机制。
中断顶半部:处理紧急的不耗时的任务
中断底半部:处理不紧急的、耗时的操作
中断底半部的实现机制:软中断、tasklet、工作队列

2.软中断

软中断这个底半部实现机制存在个数限制(32),一般留给内核的开发工作者使用

3.tasklet

tasklet是基于软中断来实现,特点和软中断一样,但是没有个数限制(对象通过链表维系)。
tasklet必须基于中断工作,是中断的一部分。
tasklet的底半部处理函数能够做相对耗时的操作,但是不能进行延时或者休眠操作。
tasklet工作于中断上下文。

中断顶半部处理函数执行结束时,将标志位置位,中断标志位清除后底半部机制会判断中断标志位是否置位,如果置位了,就回调底半部处理函数。
对于tasklet来说最多可以同时处理五个底半部事件,此时如果想要再处理多的底半部事件,就需要创建内核线程来进行处理

3.1tasklet相关API 

tasklet对象结构体:
struct tasklet_struct
{
    struct tasklet_struct *next;//tasklet对象链表下一个节点指针
    unsigned long state;//是否执行底半部的状态标志位
    atomic_t count;//底半部触发的次数
    bool use_callback;//false使用func回调函数  true使用callback回调函数
    union {
        void (*func)(unsigned long data);
        void (*callback)(struct tasklet_struct *t);
    };
    unsigned long data;//向底半部传的参数
};

1.分配一个tasklet对象
struct tasklet_struct tasklet;

2.对象初始化
void tasklet_setup(struct tasklet_struct *t,
           void (*callback)(struct tasklet_struct *))
void tasklet_init(struct tasklet_struct *t,
          void (*func)(unsigned long), unsigned long data)

3.启用中断底半部
void tasklet_schedule(struct tasklet_struct *t)

3.2tasklet实例

#include <linux/init.h>
#include <linux/module.h>
#include<linux/of.h>
#include<linux/of_gpio.h>
#include<linux/gpio.h>
#include<linux/timer.h>
#include<linux/of_irq.h>
#include<linux/interrupt.h>
/* 
 myirq{
    compatible="hqyj,irq";
    interrupt-parent = <&gpiof>;
    interrupts = <9 0>,<7 0>,<8 0>;
};                 
 };*/
unsigned int irqno;
 struct device_node *node;
 //1.分配一个tasklet对象
struct tasklet_struct tasklet;


//中断底半部函数
void task_callback(struct tasklet_struct *t)
{
    //进行耗时操作
    int i=50;
    while(--i)
    {
      printk("i=%d\n",i);
    }
}
//中断顶半部处理函数
irqreturn_t irq_handler(int irqno,void *dev)
 {
   //开启中断底半部
   tasklet_schedule(&tasklet);
    return IRQ_HANDLED;
}
 

static int __init mycdev_init(void)
{
      int ret;
      //tasklet对象的初始化
      tasklet_setup(&tasklet,task_callback);
    //通过名字获取设备树节点信息
    node=of_find_node_by_name(NULL,"myirq");
    if(node==NULL)
    {
        printk("通过路径解析设备树节点信息失败\n");
        return -ENODATA;
    }
      printk("通过路径解析设备树节点信息成功\n");
      //获取软中断号

  
    irqno=irq_of_parse_and_map(node,0);
    if(irqno==0)
    {
      printk("获取软中断号失败 %d\n",0);
      return -EINVAL;
    }
    //注册中断
    ret=request_irq(irqno,irq_handler, IRQF_TRIGGER_FALLING,"key1_int",(void *)0);
    if(ret)
    {
      printk("中断注册失败\n");
      return ret;
    }

     
     return 0;
}
static void __exit mycdev_exit(void)
{

  
    free_irq(irqno,(void *)0);
 
 

}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

4.工作队列

工作队列时linux内核启动的时候就默认开启的一个events线程,这个线程默认处于休眠状态。
如果你要是有任务要去执行,只需要将任务提交到工作队列中,唤醒这个线程即可。
工作队列不仅可以用于中断,也可以用于进程上下文。
在底半部处理函数中可以进行延时、耗时甚至休眠的操作。

4.1工作队列相关API

工作队列结构体
struct work_struct {
    atomic_long_t data;//用于进行数据传递的
    struct list_head entry;//工作队列的结构入口
    work_func_t func;//工作队列底半部处理函数指针
    };
1.分配一个工作队列对象
struct work_struct work;
2.初始化对象
typedef void (*work_func_t)(struct work_struct *work);//函数指针
//定义底半部函数
void work_func(struct work_struct *work)
{
    
}
INIT_WORK(&work,work_func);

3.开启底半部处理函数
 bool schedule_work(struct work_struct *work)

4.2工作队列实例

#include <linux/init.h>
#include <linux/module.h>
#include<linux/of.h>
#include<linux/of_gpio.h>
#include<linux/gpio.h>
#include<linux/timer.h>
#include<linux/of_irq.h>
#include<linux/interrupt.h>
/* 
 myirq{
    compatible="hqyj,irq";
    interrupt-parent = <&gpiof>;
    interrupts = <9 0>,<7 0>,<8 0>;
};                 
 };*/
unsigned int irqno;
 struct device_node *node;
 //1.分配一个工作队列对象
struct work_struct work;


//中断底半部函数
void work_func(struct work_struct *work)
{
    int i=50;
    while(--i)
    {
      printk("i=%d\n",i);
    }
}
//中断顶半部处理函数
irqreturn_t irq_handler(int irqno,void *dev)
 {
   //开启中断底半部
    schedule_work(&work);
    return IRQ_HANDLED;
}
 

static int __init mycdev_init(void)
{
      int ret;
      //工作队列对象的初始化
     INIT_WORK(&work,work_func);
    //通过名字获取设备树节点信息
    node=of_find_node_by_name(NULL,"myirq");
    if(node==NULL)
    {
        printk("通过路径解析设备树节点信息失败\n");
        return -ENODATA;
    }
      printk("通过路径解析设备树节点信息成功\n");
      //获取软中断号

  
    irqno=irq_of_parse_and_map(node,0);
    if(irqno==0)
    {
      printk("获取软中断号失败 %d\n",0);
      return -EINVAL;
    }
    //注册中断
    ret=request_irq(irqno,irq_handler, IRQF_TRIGGER_FALLING,"key1_int",(void *)0);
    if(ret)
    {
      printk("中断注册失败\n");
      return ret;
    }

     
     return 0;
}
static void __exit mycdev_exit(void)
{
    free_irq(irqno,(void *)0);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

5、platfrom总线驱动

 5.1什么是总线驱动

linux内核中所有的总线驱动都要遵循总线驱动模型

内核在设计总线驱动模型的时候将一个驱动分为三部分:device、driver、bus。
device是用来描述硬件信息的。
bus是总线用来连接device和driver;driver是用来描述驱动的对象。
在内核中所有的device都是存在内核的klist_device链表中。
内核中所有的driver都是以klist_driver链表中管理。
内核中的device和driver通过bus完成关联,当driver和device通过match函数匹配成功之后,执行驱动里的probe函数,在probe函数中完成硬件的驱动工作

5.2platfrom总线驱动原理

platform总线驱动遵循总线驱动模型,platform是linux内核抽象出来的软件代码,并没有真实的硬件总线协议与之对应。
platform总线驱动的思想是将设备和驱动进行分离。
platform_device和platform_driver通过总线进行匹配成功后执行驱动中的probe函数,在probe函数中获取硬件的设备信息从而操作硬件

 5.3platfrom总线驱动相关API

设备端

#include<linux/platform_device.h>
1.device对象结构体
struct platform_device {
    const char  *name;//用来进行匹配的名字
    int     id;//总线编号  PLATFORM_DEVID_AUTO(自动分配总线号)
    struct device   dev;//父类
    u32     num_resources;//设备信息的个数
    struct resource *resource;//存放设备信息的空间的首地址
};
struct device {
    void    (*release)(struct device *dev);//用于释放device申请的资源,卸载驱动时执行
    };
    
    //资源结构体
    struct resource {
    resource_size_t start;//资源的起始数值 0X50006000       0XC0008000        71(中断号)
    resource_size_t end;//资源的结束数值   0X50006000+4     0XC0008000+49     71
    const char *name;//资源的名字
    unsigned long flags;//资源的类型    IORESOURCE_IO|IORESOURCE_MEM|IORESOURCE_IRQ
};



2.对象的初始化
//填充设备信息
struct resource res[]={
    [0]={
        .start=0x12345678,
        .end=0x12345678+49,
        .flags=IORESOURCE_MEM,  
    },
    [1]={
        .start= 71,
        .end=71,
        .flags=IORESOURCE_IRQ,    
    },
};
//定义一个release函数用于卸载驱动时回收device资源
void    pdev_release(struct device *dev)
{
    
}
//给对象分配空间并且完成对象的初始化
struct platform_device pdev={
    .name="hahahha",
    .id=PLATFORM_DEVID_AUTO,
    .dev={
        .release=pdev_release,    
    },
    .num_resources=ARRAY_SIZE(res),
    .resource=res,
};

3.对象的注册
int platform_device_register(struct platform_device *pdev)
4.对象的注销
void platform_device_unregister(struct platform_device *pdev)

驱动端

1.driver对象结构体
struct platform_driver {
    int (*probe)(struct platform_device *);//匹配成功后执行
    int (*remove)(struct platform_device *);//设备和驱动分离时执行remove
    struct device_driver driver;//父类,用于设置和device端的匹配方式
    const struct platform_device_id *id_table;//设备和device端的匹配方式位idtable
};
//父类结构体
struct device_driver {
    const char      *name;//设置和device端匹配方式位名字匹配
     const struct of_device_id   *of_match_table;//设备树匹配
     };
2.对象的初始化
//probe函数
int pdrv_probe(struct platform_device *pdev)
{
    return 0;
}
//remove函数
 int pdrv_remove(struct platform_device *pdev)
 {
     return 0; 
 }
struct platform_driver pdrv={
    .probe=pdrv_probe,
    .remove=pdrv_remove,
    .driver={
        .name="hahahha",   
    },
};
3.对象的注册
#define platform_driver_register(drv) \
    __platform_driver_register(drv, THIS_MODULE)
4.对象的注销
void platform_driver_unregister(struct platform_driver *drv)

5.4 platfrom总线驱动编程实例

pdev.c

#include <linux/init.h>
#include <linux/module.h>
#include<linux/platform_device.h>

//填充设备信息
struct resource res[]={
    [0]={
        .start=0x12345678,
        .end=0x12345678+49,
        .flags=IORESOURCE_MEM,  
    },
    [1]={
        .start= 71,
        .end=71,
        .flags=IORESOURCE_IRQ,    
    },
};
//定义一个release函数用于卸载驱动时回收device资源
void    pdev_release(struct device *dev)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
}

//给对象分配空间并且完成对象的初始化
struct platform_device pdev={
    .name="hahahha",
    .id=PLATFORM_DEVID_AUTO,
    .dev={
        .release=pdev_release,    
    },
    .num_resources=ARRAY_SIZE(res),
    .resource=res,
};

static int __init mycdev_init(void)
{
    //对象的注册
    platform_device_register(&pdev);
    return 0;
}
static void __exit mycdev_exit(void)
{
    //对象的注销
     platform_device_unregister(&pdev);

}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

pdrv.c

#include <linux/init.h>
#include <linux/module.h>
#include<linux/platform_device.h>
//probe函数
int pdrv_probe(struct platform_device *pdev)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}
//remove函数
 int pdrv_remove(struct platform_device *pdev)
 {
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
     return 0; 
 }

 //定义对象并且初始化
 struct platform_driver pdrv={
    .probe=pdrv_probe,
    .remove=pdrv_remove,
    .driver={
        .name="hahahha",   
    },
};

static int __init mycdev_init(void)
{
    //对象的注册
    platform_driver_register(&pdrv);
    return 0;
}
static void __exit mycdev_exit(void)
{
    platform_driver_unregister(&pdrv);

}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

5.5 一键注册宏

#define module_platform_driver(__platform_driver) \
    module_driver(__platform_driver, platform_driver_register, \
            platform_driver_unregister)

#define module_driver(__driver, __register, __unregister, ...) \
    static int __init __driver##_init(void) \
    { \
        return __register(&(__driver) , ##__VA_ARGS__); \
    } \
    module_init(__driver##_init); \
    static void __exit __driver##_exit(void) \
    { \
        __unregister(&(__driver) , ##__VA_ARGS__); \
    } \
    module_exit(__driver##_exit);

5.6 在platfrom驱动中获取设备信息相关API

1.struct resource *platform_get_resource(struct platform_device *dev,
                       unsigned int type, unsigned int num)
功能:在驱动中获取设备信息
参数:
dev:platform_device对象指针
type:资源类型
num:同类型资源的序号,从0开始
返回值:成功返回资源结构体首地址,失败返回NULL

2.int platform_get_irq(struct platform_device *dev, unsigned int num)
功能:获取中断类型的资源
参数:
    dev:platform_device对象指针
    num:同类型资源的序号,从0开始
返回值:成功返回中断号,失败返回错误码

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

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

相关文章

数据结构与算法三【树】

二叉树性质 满二叉树 深度为k&#xff0c;有2k−12^{k}-12k−1个结点的二叉树&#xff0c;为满二叉树。 完全二叉树 完全二叉树的定义如下&#xff1a;在完全二叉树中&#xff0c;除了最底层节点可能没填满外&#xff0c;其余每层节点数都达到最大值&#xff0c;并且最下面…

【jQuery】常用API——jQuery选择器

一、 jQuery基础选择器原生JS获取元素方式很多&#xff0c;很杂&#xff0c;而且兼容性情况不一致&#xff0c;因此jQuery给我们做了封装&#xff0c;使荻取元素统一标准。$(“选择器”); //里面选择器直接写CSS选择器即可&#xff0c;但是要加号<script src"../jquery…

Ubuntu18.04系统 部署python3.9.0 源码编译安装及pip配置全过程记录

1.Ubuntu系统镜像下载和基本配置 1.1 镜像下载 镜像下载&#xff1a;https://cn.ubuntu.com/download/desktop 1.2 配置静态IP 配置固定IP方式&#xff1a; Ubuntu18之前在/etc/network/interfaces进行配置&#xff0c;Ubuntu18及之后版本在/etc/netplan/*.yaml进行配置&am…

Node.JS(1)

目录 命令行窗口&#xff08;cmd窗口、小黑屏、终端、shell&#xff09; 环境变量 命令行窗口&#xff08;cmd窗口、小黑屏、终端、shell&#xff09; winR快捷键-->cmd 常用指令 dir 列出当前目录下的所有文件 cd 目录名 进入到指定的目录 md 目录名 创建一个文件…

redis安装和使用说明

Redis安装说明大多数企业都是基于Linux服务器来部署项目&#xff0c;而且Redis官方也没有提供Windows版本的安装包。因此课程中我们会基于Linux系统来安装Redis.此处选择的Linux版本为CentOS 7.Redis的官方网站地址&#xff1a;https://redis.io/1.单机安装Redis1.1.安装Redis依…

数字验证学习笔记——SystemVerilog芯片验证23 ——数据采样

一、数据采样 当你coverpoint指定采样一个变量或表达式时&#xff0c;SV会创建很多“仓&#xff08;bin&#xff09;”来记录每个数值被捕捉到的次数。这些bin是衡量功能覆盖率的基本单位。covergroup中可以定义多个coverpoint&#xff0c;coverpoint中可以自定义多个cover bi…

SAP灵活工作流场景模板创建

目录 1. 创建流程对象容器 2. 编辑模板中的灵活块 3. 设置工作流启动事件 4. 设置工作流运行时事件 5. 设置工作流输出结果&#xff08;可选&#xff09; 6. 工作流控制类 7. 创建流程活动 8. 创建流程条件 9. 代理规则 9. 值帮助 10. 参考时间 11. 电子邮件模版 …

[ AWS - SAA ] 解决方案架构师之设计弹性架构 - 选择可靠的弹性存储(如何选择 SSD vs. HDD)

本系列博文会围绕AWS Well-Architected 和六大支柱进行讲解&#xff0c;这些领域的内容对成为AWS亚马逊云科技上的 解决方案架构师&#xff08;SAA&#xff09; 非常重要。 本文主要介绍AWS亚马逊云中&#xff0c;关于弹性架构设计中存储设备的一些讲解。 本文的部分内容适用于…

哈希切割 + 位图 + 布隆过滤器 —— 海量数据面试题

目录 题目一&#xff1a;给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址&#xff1f; 哈希切割 题目二&#xff1a;给定100亿个整数&#xff0c;设计算法找到只出现一次的整数&#xff1f; 解法一&#xff1a;哈希切割 解法二&#xf…

【论文精读】Guided-MVS

今天读的是一篇发表在IROS2022上的MVS文章&#xff1a;Multi-View Guided Multi-View Stereo&#xff0c;作者是来自于意大利University of Bologna的Matteo Poggi。 论文链接&#xff1a;arxiv 代码链接&#xff1a;https://github.com/andreaconti/multi-view-guided-multi-v…

SpringCloud Netflix复习之OpenFeign

文章目录写作背景Feign核心组件介绍Encoder和DecoderLoggerContractFeign.Builder上手实战开启FeignClient调用请求日志给FeignClient注入自定义拦截器Feign支持文件上传配置Feign开启Gzip压缩Feign配置超时时间Feign整合Ribbon支持负载均衡核心源码部分FeignClient注入到Sprin…

【Qt】通过创建ui界面类成员变量的方式显示窗体

【Qt】通过创建ui界面类成员变量的方式显示窗体1、背景2、实例3、验证1、背景 将.ui 文件转化为.h 头文件参考&#xff1a; 【Qt】将QtDesigner生成的.ui文件转化为.h头文件 https://jn10010537.blog.csdn.net/article/details/128589666其生成的.h头文件的显示&#xff0c;如…

HQChart实战教程56-限制指标周期

HQChart实战教程56-限制指标周期 指标周期范围效果图增加周期限制步骤1. 创建系统指标Condition.PeriodCONDITION_PERIOD 枚举说明提示信息提示信息配色实例源码指标周期范围 有些指标我们需要限制它的周期, 如指标A它只能对日线周期有效, 分时周期时无效的, 所有在切换到分…

Apache Hive 使用

Apache Hive 使用使用beeline 连接Apache Hive查看数据库使用或进入数据库创建表查看数据表上传数据数据操纵语言&#xff08;DML&#xff09;查询语句函数数学函数条件函数) 使用beeline 连接Apache Hive /export/server/apache-hive-3.1.2-bin/bin/beelinebeeline> ! co…

C51单片机基础之4G模块

一、4G模块初识EC03-DNC是亿佰特公司推出的 LTE CAT1 数传模块产品&#xff0c; 该产品软件功能完善&#xff0c; 覆盖绝大多数常规应用场景&#xff0c; EC03-DNC 是为实现串口设备与网络服务&#xff0c;通过网络相互传输数据而开发的产品 &#xff0c; 该产品是一款带分集接…

Linux 计算机网络 从 ping 来初窥计算机网络

Linux 计算机网络 从 ping 来初窥计算机网络 在上一章节《计算机网络从零到一》我们重点讲解了整个网络的形成&#xff0c;以及物理层、数据链路层、网络层这三层的形成以及他们所解决的问题&#xff0c;而本章节主要讲解 ping 命令在 Linux 中到底发生了一些什么。 ping 简介…

学习open62541 --- [73] 数据源造成无法监测变量的问题解决

本人最近遇到一个问题&#xff1a;给一个变量添加数据源后&#xff0c;使用监测项去监测变量变化&#xff0c;如果采样时间为0&#xff0c;会发现无法监测到变量的变化。 本文讲述这种情况的发生原因以及解决办法。 一 Server例子 首先准备server例子&#xff0c;如下&#x…

WSL 下载服务器加速

网络下载加速&#xff0c;这里使用修改 hosts 文件 &#xff0c;地址映射 方法&#xff0c;所有网址适用&#xff0c;这里以 WSL 下载服务器为例子 命令 wsl -l -o 访问的地址&#xff1a; https://raw.githubusercontent.com/microsoft/WSL/master/distributions/Distributi…

Java设计模式中组合模式是什么/树形结构怎么组合或显示存储,编程怎么实现树形结构

继续整理记录这段时间来的收获&#xff0c;详细代码可在我的Gitee仓库SpringBoot克隆下载学习使用&#xff01; 5.7 组合模式 5.7.1 概述 又名整体模式&#xff0c;是用于把一组相似的对象当作一个单一的对象依据树形结构来组合对象&#xff0c;用来表示部分以及整体层次属于…

基于asp.net+vbscript+wsc编写网站

1、前言 asp大家应该都比较熟悉&#xff0c;就是一个动态服务器页面&#xff0c;有点类似于jsp。只是不同的是asp可以在IIS服务器上创建&#xff0c;并且如果配置了.net环境的话&#xff0c;那么就可以在asp里面<%%>写vbscript。vbscript是一种脚本语言&#xff0c;其实就…