Uboot Driver Model-程序员宅基地

技术标签: uboot  dm  

1.Driver Model Introduction

  Why driver model? 源自Driver Model Conversion

  U-Boot originally had a pretty ad-hoc device driver system. While some subsystems have their own framework (e.g. mmc, block devices, stdio), others are implemented as bare function calls to be provided by whichever driver shows up. This limits us to one driver of a particular type (e.g. you cannot have USB2 and USB3 ports at the same time as they use different drivers).

  Driver model aims to provide a consistent model for drivers and the platform data / device tree information needed by drivers to instantiate devices. In particular:

  • Hierarchical, allowing a device to have children and parents
  • Replaces #define CONFIGs with board-supplied platform data or device tree
  • Devices organized by class, e.g. I2C, SPI, LCD
  • Efficient implementation, suitable in principle for pre-relocation and SPL
  • Devices are known very early but are only activated (‘probed’) when used
  • More scalable as the U-Boot code base grows

  Uboot引入驱动模型(driver model),这种驱动模型为驱动的定义和访问接口提供了统一的方法,提高了驱动之间的兼容性以及访问的标准型,Uboot驱动模型和linux kernel的设备驱动模型相类似,但是又有所区别。

  uboot driver model设计目标包括:

  • 提供统一设备驱动框架,降低设备驱动的开发复杂度
  • 提供设备树视图
  • 支持设备组
  • 支持设备lazy init
  • 支持设备驱动沙盒测试
  • 较小的系统开销(内存和CPU)

1.1.The modular concept

  The driver core design is done with modularity in mind. The long-term plan is to extend this modularity to allow loading not only drivers, but various other objects into U-Boot at runtime – like commands, support for other boards etc.

1.2.Driver core initialization stages

  The drivers have to be initialized in two stages, since the U-Boot bootloader runs in two stages itself.

  • The first stage is the one which is executed before the bootloader itself is relocated.
  • The second stage then happens after relocation.

1.2.1.First stage

  The first stage runs after the bootloader did very basic hardware init. This means the stack pointer was configured, caches disabled and that’s about it. The problem with this part is the memory management isn’t running at all. To make things even worse, at this point, the RAM is still likely uninitialized and therefore unavailable.

1.2.2.Second stage

  At this stage, the bootloader has initialized RAM and is running from it’s final location. Dynamic memory allocations are working at this point. Most of the driver initialization is executed here.

2.Uboot DM Enable

1>.add the config

configs/xxx_defconfig:
+CONFIG_DM=y //drivers/core/Kconfig

2>.add the uclass driver config

  DM和uclass是息息相关的,如果希望在某个模块引入DM,那么就需要使用相应模块的uclass driver来代替旧版的通用driver。 以serial为例:

configs/xxx_defconfig:
+CONFIG_DM_SERIAL=y

Mafile定义:

driver/serial/Makefile:
ifdef CONFIG_DM_SERIAL
obj-y += serial-uclass.o          //引入DM serial core驱动
else
obj-y += serial.o                // 通用的serial core驱动
endif

3>.对应设备驱动也要引入dm的功能

  设备驱动主要是实现和底层交互,为uclass层提供相应的接口函数。

  233 U_BOOT_DRIVER(serial_msm) = {
  234     .name   = "serial_msm",
  235     .id = UCLASS_SERIAL,
  236     .of_match = msm_serial_ids,
  237     .ofdata_to_platdata = msm_serial_ofdata_to_platdata,
  238     .priv_auto_alloc_size = sizeof(struct msm_serial_data),
  239     .probe = msm_serial_probe,
  240     .ops    = &msm_serial_ops,
  241 }; 

3.DM模型

  DM模型包含:uclass,udevice,driver,udevice和driver绑定,设备层级关系。

3.1.设计对象分析

  对象的设计出于设计模块化的考虑,分为静态形式和运行态形式。

  • 静态表达形式的对象是离散的,和系统的其他对象隔离开,减小对象的复杂度,利于模块化设计,遵循人类表达习惯。
  • 运行态形式的对象是把所有对象组合成层次视图,有着清晰的数据关联视图,方便系统运行时数据的流动。

3.1.1.静态表达形式

  • device: FDT 或者静态数据结构U_BOOT_DEVICE(以数据段形式组织)
  • driver: 静态数据结构U_BOOT_DRIVER(以数据段形式组织)

3.1.2.运行态形式

  • uclass_driver: 设备组公共行为对象,作为uclass的一个属性
  • uclass:设备组公共属性对象(以链表形式组织),外部顶层对象,作为udevice的一个属性
  • udevice: 设备对象(以链表形式组织)
  • driver: 驱动对象。作为udevice的一个属性

3.2.uclass、udevice

框架图如下所示:
在这里插入图片描述
3.2.1.uclass

  uclass代表一个类,同一类设备属于同一个uclass,拥有相同的uclass ID。以RTC为例,市面上RTC芯片很多,由不同的厂家生产,其内存寄存器定义甚至访问接口都不一样,所以RTC的driver肯定是不一样的,但是从功能的角度来说,它们都是用来记录时间的,所以它们都属于rtc-class。

  • 从驱动角度来看,uclass driver实现通用的处理逻辑。
  • 从设备角度来看,同一类的设备拥有相同的uclass ID,并全部挂在该uclass下。
  • 从层级结构来看,uclass 起到非常好的承上启下的作用,它既能屏蔽具体设备个体间的差异性,向用户提供统一的接口,又能为同一类的设备定义统一的处理函数,具体的设备驱动只需要实现这些处理函数即可,从而简化的设备驱动的开发。

3.2.2.udevice

  device拥有设备资源,提供三种方式进行bind:

  • 静态编译方式
  • 设备树方式(推荐)
  • 传参方式

3.2.2.1.静态编译方式(Binding with Platform Data)

  platform data is the old way of doing things. It is basically a C structure which is passed to drivers to tell them about platform-specific settings like the address of its registers, bus speed, etc. Device tree is now the preferred way of handling this.Unless you have a good reason not to use device tree (the main one being you need serial support in SPL and don’t have enough SRAM for the cut-down device tree and libfdt libraries) you should stay away from platform data.

  Platform data is like Linux platform data, if you are familiar with that. It provides the board-specific information to start up a device.

  在代码中调用U_BOOT_DEVICE宏来定义设备资源,即是一个设备实例。以tegra gpio为例:

arch/arm/mach-at91/arm926ejs/at91sam9260_devices.c:
/* Platform data for the GPIOs */
  static const struct at91_port_platdata at91sam9260_plat[] = { 
      { ATMEL_BASE_PIOA, "PA" },
      { ATMEL_BASE_PIOB, "PB" },
      { ATMEL_BASE_PIOC, "PC" },
  };
  
  U_BOOT_DEVICES(at91sam9260_gpios) = { 
      { "gpio_at91", &at91sam9260_plat[0] },
      { "gpio_at91", &at91sam9260_plat[1] },
      { "gpio_at91", &at91sam9260_plat[2] },
  };

该定义使用struct driver_info进行定义,除了特殊的情况,这种方式基本不用。

  /**
   * struct driver_info - Information required to instantiate a device
   *
   * NOTE: Avoid using this except in extreme circumstances, where device tree
   * is not feasible (e.g. serial driver in SPL where <8KB of SRAM is                                        
   * available). U-Boot's driver model uses device tree for configuration.
   */
  struct driver_info {
      const char *name;
      const void *platdata;
  #if CONFIG_IS_ENABLED(OF_PLATDATA)
      uint platdata_size;
  #endif
  };

3.2.2.2.设备数方式(Binding with Device Tree)
在这里插入图片描述
  将设备描述信息写在对应的DTS文件中,DTS文件被编译成DTB文件,然后跟uboot 二进制文件合并在一起,uboot启动的时候,会解析DTB文件,将所有的设备描述信息解析出来,该方式定义设备资源信息是目前比较流行的方案。

  了解这两种方式请参考- ./doc/driver-model/of-plat.txt.

3.2.2.3.参数的方式

  通过命令行或者接口将设备资源信息传递进来,非常灵活。

3.3.DMSoftware Framework
在这里插入图片描述

如上图所示:

  • uclass 理解为一些具有相同属性的udevice对外操作的接口,uclass的驱动是uclass_driver,主要为上层提供接口。
  • udevice 具体设备的抽象,对应驱动是driver,driver主要负责和硬件通信,为uclass提供实际的操作集。
  • udevice如何匹配uclass:udevice对应的driver的id和uclass对应的uclass_driver的id是否匹配。
  • bind关系
    • udevice和uclass
    • driver和udevice
    • uclass_driver和uclass

3.3.1.GD中和DM相关的部分

//include/asm-generic/global_data.h
 typedef struct global_data {
#ifdef CONFIG_DM
    struct udevice  *dm_root;   /* Root instance for Driver Model */  // DM中的根设备,也是uboot中第一个创建的udevice,也就对应了dts里的根节点。
    struct udevice  *dm_root_f; /* Pre-relocation root instance */ // 在relocation之前DM中的根设备
    struct list_head uclass_root;   /* Head of core tree */          // uclass链表,所有被udevice匹配的uclass都会被挂载到这个链表上
#endif
} gd_t;

4.DM四个主要组成部分

4.1.uclass id

  每一种uclass都有唯一的ID number,在uclass_driver中进行定义,该值必须与udevice的driver中的uclass id一一对应, 这个id也是用来获取uclass的标志。uclass id定义如下:

include/dm/uclass-id.h: 
enum uclass_id {
    /* These are used internally by driver model */
    UCLASS_ROOT = 0,
    UCLASS_DEMO,
    UCLASS_CLK,         /* Clock source, e.g. used by peripherals */
    UCLASS_PINCTRL,     /* Pinctrl (pin muxing/configuration) device */
    UCLASS_SERIAL,      /* Serial UART */
    ...
}

4.2. struct uclass

1>.结构体:

struct uclass {
    void *priv;                      // uclass的私有数据指针
    struct uclass_driver *uc_drv; // 对应的uclass driver
    struct list_head dev_head; // 链表头,连接所属的所有udevice
    struct list_head sibling_node; // 链表节点,用于把uclass连接到uclass_root链表上
};

2>.获取uclass

int uclass_get(enum uclass_id key, struct uclass **ucp);

  直接遍历链表gd->uclass_root链表,并且根据uclass id来获取到相应的uclass。 详细实现如下所示:

struct uclass *uclass_find(enum uclass_id key)
{
      struct uclass *uc;
  
     if (!gd->dm_root)
         return NULL;

     list_for_each_entry(uc, &gd->uclass_root, sibling_node) {
        if (uc->uc_drv->id == key)
              return uc;
      }
  
     return NULL;
}   

2>.如何定义

  uclass是uboot自动生成,并且不是所有uclass都会生成,有对应uclass driver并且有被udevice匹配到的uclass才会生成。

3>.存放位置
  所有生成的uclass都会被挂载gd->uclass_root链表上。

4.3.uclass_driver

1>.结构体:

include/dm/uclass.h:
struct uclass_driver {
    const char *name; // 该uclass_driver的命令
    enum uclass_id id; // 对应的uclass id
/* 以下函数指针主要是调用时机的区别 */
    int (*post_bind)(struct udevice *dev); // 在udevice被绑定到该uclass之后调用
    int (*pre_unbind)(struct udevice *dev); // 在udevice被解绑出该uclass之前调用
    int (*pre_probe)(struct udevice *dev); // 在该uclass的一个udevice进行probe之前调用
    int (*post_probe)(struct udevice *dev); // 在该uclass的一个udevice进行probe之后调用
    int (*pre_remove)(struct udevice *dev);// 在该uclass的一个udevice进行remove之前调用
    int (*child_post_bind)(struct udevice *dev); // 在该uclass的一个udevice的一个子设备被绑定到该udevice之后调用
    int (*child_pre_probe)(struct udevice *dev); // 在该uclass的一个udevice的一个子设备进行probe之前调用
    int (*init)(struct uclass *class); // 安装该uclass的时候调用
    int (*destroy)(struct uclass *class); // 销毁该uclass的时候调用
    int priv_auto_alloc_size; // 需要为对应的uclass分配多少私有数据
    int per_device_auto_alloc_size; //
    int per_device_platdata_auto_alloc_size; //
    int per_child_auto_alloc_size; //
    int per_child_platdata_auto_alloc_size;  //
    const void *ops; //操作集合
    uint32_t flags;   // 标识为
};

#define UCLASS_DRIVER(__name)                       \
    ll_entry_declare(struct uclass_driver, __name, uclass)

#define ll_entry_declare(_type, _name, _list)               \
    _type _u_boot_list_2_##_list##_2_##_name __aligned(4)       \
            __attribute__((unused,              \
            section(".u_boot_list_2_"#_list"_2_"#_name)))

2>.定义以serial-uclass为例:

UCLASS_DRIVER(serial) = {
    .id        = UCLASS_SERIAL,
    .name        = "serial",
    .flags        = DM_UC_FLAG_SEQ_ALIAS,   
    .post_probe    = serial_post_probe,
    .pre_remove    = serial_pre_remove,
    .per_device_auto_alloc_size = sizeof(struct serial_dev_priv),
};

  UCLASS_DRIVER展开最终得到如下结构体(存放在.u_boot_list_2_uclass_2_serial段):

struct uclass_driver  _u_boot_list_2_uclass_2_serial = {
    .id        = UCLASS_SERIAL,   
    .name        = "serial",
    .flags        = DM_UC_FLAG_SEQ_ALIAS,   
    .post_probe    = serial_post_probe,
    .pre_remove    = serial_pre_remove,
    .per_device_auto_alloc_size = sizeof(struct serial_dev_priv),
}

查看生成的u-boot.map:

.u_boot_list_2_uclass_2_serial
                0x23e36970       0x48 drivers/serial/built-in.o
                0x23e36970                _u_boot_list_2_uclass_2_serial // serial uclass driver的符号
.u_boot_list_2_uclass_2_simple_bus
                0x23e369b8       0x48 drivers/built-in.o
                0x23e369b8                _u_boot_list_2_uclass_2_simple_bus

  最终,所有uclass driver结构体以列表的形式被放在.u_boot_list_2_uclass_1和.u_boot_list_2_uclass_3的区间中。

3>.获取API

  想要获取uclass_driver需要先获取uclass_driver table,通过以下宏来获取

    struct uclass_driver *uclass =
        ll_entry_start(struct uclass_driver, uclass); 
// 会根据.u_boot_list_2_uclass_1的段地址来得到uclass_driver table的地址

    const int n_ents = ll_entry_count(struct uclass_driver, uclass);
// 获得uclass_driver table的长度

  接着通过遍历这个uclass_driver table,得到相应的uclass_driver。 有如下API:

struct uclass_driver *lists_uclass_lookup(enum uclass_id id)
// 从uclass_driver table中获取uclass id为id的uclass_driver。

4.4.udevice

  uboot 通过两种方法来添加设备

  • 直接定义平台设备(这种方式被抛弃)
  • 使用device tree

4.4.1直接定义平台设备

struct driver_info {
      const char *name;
      const void *platdata;
  #if CONFIG_IS_ENABLED(OF_PLATDATA)
      uint platdata_size;
  #endif
};

#define U_BOOT_DEVICE(__name)                       \
    ll_entry_declare(struct driver_info, __name, driver_info)

/* Declare a list of devices. The argument is a driver_info[] array */
#define U_BOOT_DEVICES(__name)                      \
    ll_entry_declare_list(struct driver_info, __name, driver_info)

除了根设备使用外,其他基本不使用:

drivers/core/root.c :
 static const struct driver_info root_info = {
      .name       = "root_driver",
  };

 /* This is the root driver - all drivers are children of this */
  U_BOOT_DRIVER(root_driver) = { 
      .name   = "root_driver",
      .id = UCLASS_ROOT,
  };
  
  /* This is the root uclass */
  UCLASS_DRIVER(root) = { 
      .name   = "root",
      .id = UCLASS_ROOT,
  };

4.4.2.Device tree

  While platdata is useful, a more flexible way of providing device data is by using device tree. In U-Boot you should use this where possible. Avoid sending patches which make use of the U_BOOT_DEVICE() macro unless strictly necessary.

1>.数据结构:

include/dm/device.h
struct udevice {
    const struct driver *driver; // 该udevice对应的driver
    const char *name;           // 设备名
    void *platdata;                  // 该udevice的平台数据
    void *parent_platdata;    // 提供给父设备使用的平台数据
    void *uclass_platdata;    // 提供给所属uclass使用的平台数据
    int of_offset;           // 该udevice的dtb节点偏移,代表了dtb里面的这个节点node
    ulong driver_data;       // 驱动数据
    struct udevice *parent; // 父设备
    void *priv;             // 私有数据的指针
    struct uclass *uclass; // 所属uclass
    void *uclass_priv; // 提供给所属uclass使用的私有数据指针
    void *parent_priv; // 提供给其父设备使用的私有数据指针
    struct list_head uclass_node; // 用于连接到其所属uclass的链表上
    struct list_head child_head; // 链表头,连接其子设备
    struct list_head sibling_node; // 用于连接到其父设备的链表上
    uint32_t flags; // 标识
    int req_seq;
    int seq;
#ifdef CONFIG_DEVRES
    struct list_head devres_head;
#endif
};

2>.定义

  在dtb存在的情况下,由uboot解析dtb后动态生成。

3>.存放位置

  • 连接到对应uclass中
    也就是会连接到uclass->dev_head中
  • 连接到父设备的子设备链表中
    也就是会连接到udevice->child_head中,并且最终的根设备是gd->dm_root这个根设备。

4>.获取API

  从uclass中获取udevice 遍历uclass->dev_head,获取对应的udevice。有如下API:

#define uclass_foreach_dev(pos, uc) \
    list_for_each_entry(pos, &uc->dev_head, uclass_node)

#define uclass_foreach_dev_safe(pos, next, uc)  \
    list_for_each_entry_safe(pos, next, &uc->dev_head, uclass_node)

int uclass_get_device(enum uclass_id id, int index, struct udevice **devp); // 通过索引从uclass中获取udevice
int uclass_get_device_by_name(enum uclass_id id, const char *name, // 通过设备名从uclass中获取udevice
                  struct udevice **devp);
int uclass_get_device_by_seq(enum uclass_id id, int seq, struct udevice **devp);
int uclass_get_device_by_of_offset(enum uclass_id id, int node,
                   struct udevice **devp);
int uclass_get_device_by_phandle(enum uclass_id id, struct udevice *parent,
                 const char *name, struct udevice **devp);
int uclass_first_device(enum uclass_id id, struct udevice **devp);
int uclass_first_device_err(enum uclass_id id, struct udevice **devp);
int uclass_next_device(struct udevice **devp);
int uclass_resolve_seq(struct udevice *dev);

4.5.driver

1>.数据结构

include/dm/device.h:
  struct driver {
      char *name;
      enum uclass_id id;
      const struct udevice_id *of_match;
      int (*bind)(struct udevice *dev);
      int (*probe)(struct udevice *dev);
      int (*remove)(struct udevice *dev);
      int (*unbind)(struct udevice *dev);
      int (*ofdata_to_platdata)(struct udevice *dev);
      int (*child_post_bind)(struct udevice *dev);
      int (*child_pre_probe)(struct udevice *dev);
      int (*child_post_remove)(struct udevice *dev);
      int priv_auto_alloc_size;
      int platdata_auto_alloc_size;
      int per_child_auto_alloc_size;
      int per_child_platdata_auto_alloc_size;
      const void *ops;    /* driver-specific operations */
      uint32_t flags;
  };

2>.如何定义

以s5pv210为例:

driver/serial/serial_s5p.c
U_BOOT_DRIVER(serial_s5p) = {
    .name    = "serial_s5p",
    .id    = UCLASS_SERIAL,
    .of_match = s5p_serial_ids,
    .ofdata_to_platdata = s5p_serial_ofdata_to_platdata,
    .platdata_auto_alloc_size = sizeof(struct s5p_serial_platdata),
    .probe = s5p_serial_probe,
    .ops    = &s5p_serial_ops,
    .flags = DM_FLAG_PRE_RELOC,
};

  The driver will contain a structure located in a separate section, which will allow linker to create a list of compiled-in drivers at compile time. Let’s call this list “driver_list”. You can refer to the symobl in the u-boot.map after compilation.

  #define ll_entry_declare(_type, _name, _list)               \
      _type _u_boot_list_2_##_list##_2_##_name __aligned(4)       \
              __attribute__((unused,              \
              section(".u_boot_list_2_"#_list"_2_"#_name)))

  /* Declare a new U-Boot driver */
  #define U_BOOT_DRIVER(__name)                       \                                                    
      ll_entry_declare(struct driver, __name, driver)

最终得到结构体:(存放在.u_boot_list_2_driver_2_serial_s5p段)

struct driver _u_boot_list_2_driver_2_serial_s5p= {
    .name    = "serial_s5p",
    .id    = UCLASS_SERIAL,
    .of_match = s5p_serial_ids,
    .ofdata_to_platdata = s5p_serial_ofdata_to_platdata,
    .platdata_auto_alloc_size = sizeof(struct s5p_serial_platdata),
    .probe = s5p_serial_probe,
    .ops    = &s5p_serial_ops,
    .flags = DM_FLAG_PRE_RELOC,
};

3>.获取API
  想要获取driver需要先获取driver table。 可以通过以下宏来获取driver table

   struct driver *drv =
        ll_entry_start(struct driver, driver);
// 会根据.u_boot_list_2_driver_1的段地址来得到uclass_driver table的地址

    const int n_ents = ll_entry_count(struct driver, driver);
// 获得driver table的长度

接着通过遍历这个driver table,得到相应的driver。

struct driver *lists_driver_lookup_name(const char *name)
// 从driver table中获取名字为name的driver。

代码参考:
doc/driver-model/README.txt
include/dm/device.h
include/dm/uclass.h
include/dm/uclass-id.h

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_41028621/article/details/90643550

智能推荐

c# 调用c++ lib静态库_c#调用lib-程序员宅基地

文章浏览阅读2w次,点赞7次,收藏51次。四个步骤1.创建C++ Win32项目动态库dll 2.在Win32项目动态库中添加 外部依赖项 lib头文件和lib库3.导出C接口4.c#调用c++动态库开始你的表演...①创建一个空白的解决方案,在解决方案中添加 Visual C++ , Win32 项目空白解决方案的创建:添加Visual C++ , Win32 项目这......_c#调用lib

deepin/ubuntu安装苹方字体-程序员宅基地

文章浏览阅读4.6k次。苹方字体是苹果系统上的黑体,挺好看的。注重颜值的网站都会使用,例如知乎:font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, W..._ubuntu pingfang

html表单常见操作汇总_html表单的处理程序有那些-程序员宅基地

文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些

PHP设置谷歌验证器(Google Authenticator)实现操作二步验证_php otp 验证器-程序员宅基地

文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器

【Python】matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距-程序员宅基地

文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距

docker — 容器存储_docker 保存容器-程序员宅基地

文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器

随便推点

网络拓扑结构_网络拓扑csdn-程序员宅基地

文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn

JS重写Date函数,兼容IOS系统_date.prototype 将所有 ios-程序员宅基地

文章浏览阅读1.8k次,点赞5次,收藏8次。IOS系统Date的坑要创建一个指定时间的new Date对象时,通常的做法是:new Date("2020-09-21 11:11:00")这行代码在 PC 端和安卓端都是正常的,而在 iOS 端则会提示 Invalid Date 无效日期。在IOS年月日中间的横岗许换成斜杠,也就是new Date("2020/09/21 11:11:00")通常为了兼容IOS的这个坑,需要做一些额外的特殊处理,笔者在开发的时候经常会忘了兼容IOS系统。所以就想试着重写Date函数,一劳永逸,避免每次ne_date.prototype 将所有 ios

如何将EXCEL表导入plsql数据库中-程序员宅基地

文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql

Git常用命令速查手册-程序员宅基地

文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...

分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120-程序员宅基地

文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120

【C++缺省函数】 空类默认产生的6个类成员函数_空类默认产生哪些类成员函数-程序员宅基地

文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数

推荐文章

热门文章

相关标签