Linux设备模型(十一) - platform设备

一,platform device概述

在Linux2.6以后的设备驱动模型中,需关心总线、设备和驱动这3个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,

会寻找与之匹配的驱动;相反的,在系统每注册一个设备的时候,会寻找与之匹配的设备,而匹配由总线完成。

一个现实的Linux设备和驱动通常都需要挂接在一种总线上,而对于本身依附于PCI、USB、I2C、SPI等的设备而言,这自然不是问题,

但是在嵌入式系统里面,在SoC系统中集成的独立外设控制器、挂接在SoC内存空间的外设等却不依附于此类总线。基于这一背景,Linux

发明了一种虚拟总线,称为platform总线,相应的设备称为platform_device,而驱动称为platform_driver。

所谓的platform_device并不是与字符设备、块设备和网络设备并存的概念,而是linux系统提供的一种附加手段,例如,我们通常把在

SoC内部集成的I2C、RTC、LCD、看门狗等控制器都归纳为platform_device,而他们本身就是字符设备。这些设备有一个基本的特征:可以通过CPU bus直接寻址(例如在嵌入式系统常见的“寄存器”)。

二,platform模块的软件架构

内核中Platform设备有关的实现位于include/linux/platform_device.h和drivers/base/platform.c两个文件中,它的软件架构如下:

由图片可知,Platform设备在内核中的实现主要包括三个部分:

Platform Bus,基于底层bus模块,抽象出一个虚拟的Platform bus,用于挂载Platform设备;

Platform Device,基于底层device模块,抽象出Platform Device,用于表示Platform设备;

Platform Driver,基于底层device_driver模块,抽象出Platform Driver,用于驱动Platform设备。

其中Platform Device和Platform Driver会给其它Driver提供封装好的API,具体可参考后面的描述。

三,platform bus/platform device/platform driver结构体

1,platform_driver

// msm_kernel\include\linux\platform_device.h
struct platform_driver {
    int (*probe)(struct platform_device *);
    int (*remove)(struct platform_device *);
    void (*shutdown)(struct platform_device *);
    int (*suspend)(struct platform_device *, pm_message_t state);
    int (*resume)(struct platform_device *);
    struct device_driver driver;
    const struct platform_device_id *id_table; /* 另外这里有一个id_table的指针,该指针和of_match_table、acpi_match_table的功能类似:提供其它方式的设备probe */
    bool prevent_deferred_probe;

    ANDROID_KABI_RESERVE(1);
};

2,platform_device

// msm_kernel\include\linux\platform_device.h
struct platform_device {
    const char    *name; /* 设备的名称,和struct device结构中的init_name意义相同。实际上,该名称在设备注册时,会拷贝到dev.init_name中 */
    int        id; /* 用于标识该设备的ID。内核允许存在多个名称相同的设备。而设备驱动的probe,依赖于名称,Linux采取的策略是:在bus的设备链表中查找device,和对应的device_driver比对name,如果相同,则查看该设备是否已经绑定了driver(查看其dev->driver指针是否为空),如果已绑定,则不会执行probe动作,如果没有绑定,则以该device的指针为参数,调用driver的probe接口。
因此,在driver的probe接口中,通过判断设备的ID,可以知道此次驱动的设备是哪个*/
    bool        id_auto; /* 指示在注册设备时,是否自动赋予ID值 */
    struct device    dev; /* 真正的设备(Platform设备只是一个特殊的设备,因此其核心逻辑还是由底层的模块实现) */
    u64        platform_dma_mask;
    struct device_dma_parameters dma_parms;
    u32        num_resources;
    struct resource    *resource; /* 该设备的资源描述,由struct resource(include/linux/ioport.h)结构抽象 */

    const struct platform_device_id    *id_entry;
    char *driver_override; /* Driver name to force a match */

    /* MFD cell pointer */
    struct mfd_cell *mfd_cell;

    /* arch specific additions */
    struct pdev_archdata    archdata;

    ANDROID_KABI_RESERVE(1);
    ANDROID_KABI_RESERVE(2);
};

3,platform bus

// msm_kernel\drivers\base\platform.c
struct bus_type platform_bus_type = {
    .name        = "platform",
    .dev_groups    = platform_dev_groups,
    .match        = platform_match,
    .uevent        = platform_uevent,
    .dma_configure    = platform_dma_configure,
    .pm        = &platform_dev_pm_ops,
};
EXPORT_SYMBOL_GPL(platform_bus_type);

四,platform bus/platform device/platform driver注册

1,platform bus注册

struct device platform_bus = {
    .init_name    = "platform",
};
EXPORT_SYMBOL_GPL(platform_bus);

int __init platform_bus_init(void)
{
    int error;

    early_platform_cleanup(); /* 清除所有和Early device/driver相关的代码。因为执行到这里的时候,证明系统已经完成了Early阶段的启动,转而进行正常的设备初始化、启动操作,所以不再需要Early Platform相关的东西。 */

    error = device_register(&platform_bus); /* 在sysfs中创建/sys/devices/platform目录,所有的platform设备都会包含在此目录下 */
    if (error) {
        put_device(&platform_bus);
        return error;
    }
    error =  bus_register(&platform_bus_type); /* 在sysfs中创建/sys/bus/platform目录并在此目录中创建如下attributes和devices目录,drivers目录 */
    /*
    lynkco:/sys/bus/platform # ls
    devices  drivers  drivers_autoprobe  drivers_probe  uevent
    */
    if (error)
        device_unregister(&platform_bus);
    of_platform_register_reconfig_notifier();
    return error;
}

2,platform device注册

platform_device_register - add a platform device to device hierarchy

// msm_kernel\drivers\base\platform.c
platform_device_register(struct platform_device *pdev)
----device_initialize(&pdev->dev);
----platform_device_add(pdev);
--------pdev->dev.parent = &platform_bus; //该设备的sysfs目录/sys/devices/platform/xxx_device
--------pdev->dev.bus = &platform_bus_type; //该设备的bus type定义为platform_bus_type
--------dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id); //对于多个同名的设备,可以使用ID区分,在这里将实际名称修改为“name.id”的形式
--------insert_resource(p, r); /* 调用resource模块的insert_resource接口,将该设备需要使用的resource统一管理起来(我们知道,在这之前,只是声明了本设备需要使用哪些resource,但resource模块并不知情,也就无从管理,因此需要告知)。 */
--------device_add(&pdev->dev); // 将内嵌的struct device变量添加到内核中
            ......

3,platform driver注册

platform_driver_register - register a driver for platform-level devices

// msm_kernel\drivers\base\platform.c
platform_driver_register(drv)
----__platform_driver_register(drv, THIS_MODULE)
--------drv->driver.bus = &platform_bus_type; //该driver的bus type设置为platform_bus_type
------------drv->driver.probe = platform_drv_probe; /* 如果该platform driver提供了probe、remove、shutdown等回调函数,将该它内嵌的struct driver变量的probe、remove、shutdown等指针,设置为platform模块提供函数,包括platform_drv_probe、platform_drv_remove和platform_drv_shutdown。因为probe等动作会从struct driver变量开始,经过platform_drv_xxx等接口的转接就可以到达platform diver自身的回调函数中。*/
------------drv->driver.remove = platform_drv_remove;
------------drv->driver.shutdown = platform_drv_shutdown;
--------driver_register(&drv->driver); //将内嵌的struct driver变量添加到内核中
            ......

五,platform device/platform driver提供的API

1,platform driver提供的API

extern int platform_driver_register(struct platform_driver *);
extern void platform_driver_unregister(struct platform_driver *); //platform driver的注册、注销接口

extern int platform_driver_probe(struct platform_driver *driver,
                 int (*probe)(struct platform_device *)); //主动执行probe动作

static inline void *platform_get_drvdata(const struct platform_device *pdev);
static inline void platform_set_drvdata(struct platform_device *pdev,
                                         void *data); //设置或者获取driver保存在device变量中的私有数据

2,platform device提供的API

extern int platform_device_register(struct platform_device *);
extern void platform_device_unregister(struct platform_device *); //Platform设备的注册/注销接口,和底层的device_register等接口类似

extern struct resource *platform_get_resource(struct platform_device *,
                                               unsigned int, unsigned int);
extern int platform_get_irq(struct platform_device *, unsigned int);
extern struct resource *platform_get_resource_byname(struct platform_device *,
                                                      unsigned int,
                                                      const char *);
extern int platform_get_irq_byname(struct platform_device *, const char *); //通过这些接口,可以获取platform_device变量中的resource信息,以及直接获取IRQ的number等等

extern int platform_device_add_resources(struct platform_device *pdev,
                                          const struct resource *res,
                                          unsigned int num); //向platform device中增加资源描述
                                          
extern int platform_device_add_data(struct platform_device *pdev,
                                     const void *data, size_t size); //向platform device中添加自定义的数据(保存在pdev->dev.platform_data指针中)

六,platform device resource/platform data的定义与获取

1,struct resource结构体介绍

在platform device结构体的定义中关于device resource的定义如下

    u32        num_resources;
    struct resource    *resource;

它们描述了platform_device的资源,资源本身由resource结构体描述

/*
* Resources are tree-like, allowing
* nesting etc..
*/
struct resource {
    resource_size_t start;
    resource_size_t end;
    const char *name;
    unsigned long flags;
    unsigned long desc;
    struct resource *parent, *sibling, *child;

    ANDROID_KABI_RESERVE(1);
    ANDROID_KABI_RESERVE(2);
    ANDROID_KABI_RESERVE(3);
    ANDROID_KABI_RESERVE(4);
};

我们通常关心start、end和flags这3个字段,它们分别标明了资源的开始值、结束值和类型,flags可以为IORESOURCE_IO、IORESOURCE_MEM、IORESOURCE_IRQ、IORESOURCE_DMA等。

start、end的含义会随着flags而变更,如当flags为IORESOURCE_MEM时,start、end分别表示该platform_device占据的内存的开始地址和结束地址;

当flags为IORESOURCE_IRQ时,start、end分别表示该platform_device使用的中断号的开始值和结束值,如果只使用了一个中断号,开始值和结束值相同。

对于同种类型的资源而言,可以有多份,例如说某设备占据了两个内存区域,则可以定义两个IORESOURCE_MEM资源。

系统中所有的platform_device,都可以在/sys/devices/platform/路径下查看。另外,系统中所有的platform_device,有来自设备树的,也有来自.c文件中注册的。那么,我们怎么知道哪些platform_device是来自设备树,哪些是来自.c文件中注册的?

可以查看该platform_device的相关目录下,是否有of_node,如果有of_node,那么这个platform_device就来自于设备树;否则,来自.c文件。

2,来自.c文件中注册的platform_device

2.1 example
// msm_kernel\arch\arm\mach-ep93xx\core.c

static struct usb_ohci_pdata ep93xx_ohci_pdata = {
    .power_on    = ep93xx_ohci_power_on,
    .power_off    = ep93xx_ohci_power_off,
    .power_suspend    = ep93xx_ohci_power_off,
};

static struct resource ep93xx_ohci_resources[] = {
    DEFINE_RES_MEM(EP93XX_USB_PHYS_BASE, 0x1000),
    DEFINE_RES_IRQ(IRQ_EP93XX_USB),
};

static struct platform_device ep93xx_ohci_device = {
    .name        = "ohci-platform",
    .id        = -1,
    .num_resources    = ARRAY_SIZE(ep93xx_ohci_resources),
    .resource    = ep93xx_ohci_resources,
    .dev        = {
        .dma_mask        = &ep93xx_ohci_dma_mask,
        .coherent_dma_mask    = DMA_BIT_MASK(32),
        .platform_data        = &ep93xx_ohci_pdata,
    },
};
platform_device_register(&ep93xx_ohci_device);

设备除了可以在BSP中定义资源以外,还可以附加一些数据信息,因为对设备的硬件描述除了中断、内存等标准资源以外,还可能有一些配置信息,而这些配置信息也依赖于板,不适宜直接放置在设备驱动上。因此platform也提供了platform_data的支持,platform_data的形式是由每个驱动自定义的,如对于usb ohci设备而言,platform_data为一个usb_ohci_pdata结构体,完成定义后将可以将PM operation相关的接口信息放入platform_data中。

在usb ohci驱动msm-kernel/drivers/usb/host/ohci-platform.c的probe()函数中,通过如下方式就拿到了platform_data:

static int ohci_platform_probe(struct platform_device *dev)    
{
    struct usb_hcd *hcd;
    struct resource *res_mem;
    struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev);
    struct ohci_platform_priv *priv;
    ... ...
}
2.2 memory resource资源的定义
#define DEFINE_RES_MEM(_start, _size)                    \
    DEFINE_RES_MEM_NAMED((_start), (_size), NULL)

#define DEFINE_RES_MEM_NAMED(_start, _size, _name)            \
    DEFINE_RES_NAMED((_start), (_size), (_name), IORESOURCE_MEM)

/* helpers to define resources */
#define DEFINE_RES_NAMED(_start, _size, _name, _flags)            \
    {                                \
        .start = (_start),                    \
        .end = (_start) + (_size) - 1,                \
        .name = (_name),                    \
        .flags = (_flags),                    \
        .desc = IORES_DESC_NONE,                \
    }
2.3 irq resource资源的定义
#define DEFINE_RES_IRQ(_irq)                        \
    DEFINE_RES_IRQ_NAMED((_irq), NULL)

#define DEFINE_RES_IRQ_NAMED(_irq, _name)                \
    DEFINE_RES_NAMED((_irq), 1, (_name), IORESOURCE_IRQ)
2.4 IO resources flags
/*
* IO resources have these defined flags.
*
* PCI devices expose these flags to userspace in the "resource" sysfs file,
* so don't move them.
*/
#define IORESOURCE_BITS        0x000000ff    /* Bus-specific bits */

#define IORESOURCE_TYPE_BITS    0x00001f00    /* Resource type */
#define IORESOURCE_IO        0x00000100    /* PCI/ISA I/O ports */
#define IORESOURCE_MEM        0x00000200
#define IORESOURCE_REG        0x00000300    /* Register offsets */
#define IORESOURCE_IRQ        0x00000400
#define IORESOURCE_DMA        0x00000800
#define IORESOURCE_BUS        0x00001000

3,来自设备树的platform_device

在Linux内核启动时,内核通过 of_platform_populate() 函数,将dts中的device node创建成platform device。为后续和各类驱动的platform driver匹配做准备。

of_platform_device_create_pdata
----of_device_alloc
--------of_address_to_resource(np, num_reg, &temp_res)
--------num_irq = of_irq_count(np);
--------dev->num_resources = num_reg + num_irq;
--------dev->resource = res;
--------of_address_to_resource(np, i, res);
--------of_irq_to_resource_table(np, res, num_irq); //将dts中的address和irq等信息转化到resource结构体中
--------dev->dev.bus = &platform_bus_type;
----dev->dev.platform_data = platform_data;
----of_device_add(dev)
--------device_add(&ofdev->dev);

具体解析转化过程会在后续的dts章节中详细分析。

4,get resource API实现及使用

4.1 get resource API实现

platform_get_resource_byname:

/**
* platform_get_resource_byname - get a resource for a device by name
* @dev: platform device
* @type: resource type
* @name: resource name
*/
struct resource *platform_get_resource_byname(struct platform_device *dev,
                          unsigned int type,
                          const char *name)
{
    u32 i;

    for (i = 0; i < dev->num_resources; i++) {
        struct resource *r = &dev->resource[i];

        if (unlikely(!r->name))
            continue;

        if (type == resource_type(r) && !strcmp(r->name, name)) //匹配type和name
            return r;
    }
    return NULL;
}
EXPORT_SYMBOL_GPL(platform_get_resource_byname);

platform_get_resource:

/**
* platform_get_resource - get a resource for a device
* @dev: platform device
* @type: resource type
* @num: resource index
*
* Return: a pointer to the resource or NULL on failure.
*/
struct resource *platform_get_resource(struct platform_device *dev,
                       unsigned int type, unsigned int num)
{
    u32 i;

    for (i = 0; i < dev->num_resources; i++) {
        struct resource *r = &dev->resource[i];

        if (type == resource_type(r) && num-- == 0) //匹配type和index
            return r;
    }
    return NULL;
}
EXPORT_SYMBOL_GPL(platform_get_resource);
4.2 get resource API使用示例

get memory resource使用示例:

struct resource *res;

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
    dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
    return -ENODEV;
}

adata->acp3x_base = devm_ioremap(&pdev->dev, res->start,
                 resource_size(res));
if (!adata->acp3x_base)
    return -ENOMEM;

get irq resource 使用示例:

res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
    dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n");
    return -ENODEV;
}
adata->i2s_irq = res->start;

status = devm_request_irq(&pdev->dev, adata->i2s_irq, i2s_irq_handler,
              irqflags, "ACP3x_I2S_IRQ", adata);
if (status) {
    dev_err(&pdev->dev, "ACP3x I2S IRQ request failed\n");
    return -ENODEV;
}

get resource by name 使用示例:

struct resource *r;

/* card: irq assigned to the card itself. */
r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "card");
sock->card_irq = r ? r->start : 0;

/* stschg: irq which trigger on card status change (optional) */
r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "stschg");
sock->stschg_irq = r ? r->start : -1;

/* 36bit PCMCIA Memory area address */
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-mem");
if (!r) {
    printk(KERN_ERR "pcmcia%d has no 'pseudo-mem' resource!\n",
        sock->nr);
    goto out0;
}
sock->phys_mem = r->start;

由以上分析可知,在设备驱动中引入platform的概念至少有如下好处:

1)使得设备被挂接在一个总线上,符合Linux2.6以后内核的设备模型。其结果是使配套的sysfs节点、设备电源管理都成为可能。

2)隔离BSP和驱动。在BSP中定义platform设备和设备使用的资源、设备的具体配置信息,而在驱动中,只需要通过通用API去获取资源和数据,做到了板相关代码和驱动代码的分离,使得驱动具有更好的可扩展性和跨平台性。

3)让一个驱动支持多个设备实例。

参考链接:

Linux设备模型(8)_platform设备

dts展开为platform_device结构过程分析-腾讯云开发者社区-腾讯云

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

开发者38万+,鸿蒙开发岗为何却无人敢应聘?

鸿蒙校园公开课已走进135家高校&#xff0c;305所高校学生参与鸿蒙活动&#xff0c;286家企业参加鸿蒙生态学堂&#xff0c;38万开发者通过鸿蒙认证。 居上华为官方是说有通过鸿蒙开发者认证的已有38万。具体有多少开发者并没有明确表示。除此之外还有200家头部应用加速鸿蒙原…

【Golang切片】

切片 切片的引入内存分析切片的定义切片的遍历切片注意事项 切片的引入 【1】切片&#xff08;slice&#xff09;是golang中一种特有的数据类型 【2】数组有特定的用处&#xff0c;但是却有一些呆板&#xff08;数组长度固定不可变&#xff09;&#xff0c;所以在Go语言的代码…

面试题VUE篇

文章目录 Vue 的核心是什么/请简述你对 vue 的理解请简述 vue 的单向数据流槽口请简述Vue 常用的修饰符有哪些1. 普通修饰符2. 事件修饰符3. 键盘修饰符4. 系统修饰符 v-text 与{{}}与 v-html 区别v-on 可以绑定多个方法吗Vue 循环的 key 作用什么是计算属性Vue 单页面的优缺点…

最新版阿里云Linux CentOS7 ecs-user用户安装Mysql8详细教程(超简单)

经过两天的踩坑后&#xff0c;终于成功安装&#xff0c;并找到了最快捷的安装方式。接下来就由我来给大家介绍不踩坑安装大法&#xff01; 一、下载Mysql 首先前往Mysql官网下载 MySQL官方下载地址 第一步&#xff0c;选择安装包&#xff0c;这是最关键的一步&#xff0c;选错安…

进程与线程:通过实际生活来解析计算机的基本运作单位

进程与线程 进程与线程&#xff1a;详细解析计算机的基本运作单位1. 进程&#xff1a;独立的执行环境1.1 进程的特点&#xff1a; 2. 线程&#xff1a;轻量级的执行单元2.1 线程的特点&#xff1a; 3. 区别和联系4. 表格 进程与线程&#xff1a;详细解析计算机的基本运作单位 在…

AsConvSR | NTIRE2023-RTSR-Track1冠军方案

编辑 | Happy 首发 | AIWalker 链接 | https://mp.weixin.qq.com/s/p9u6RYkd37MmN12rUCMCuQ 前段时间&#xff0c;NTIRE2023各个竞赛落下帷幕&#xff0c;近期各个冠亚军方案提出者也在逐步公开方案细节。今天给大家概要介绍一下"RTSR-Track1"赛道冠军方案&#xff…

【Leetcode每日一题】前缀和(难度⭐)(25)

1. 题目解析 题目链接&#xff1a;DP34 【模板】前缀和 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 核心在于计算题目所给区间数组元素和返回即可。 2. 算法原理 为了提高计算效率&#xff0c;我们可以预先计算出一个「前缀…

【NDK系列】Android tombstone文件分析

文件位置 data/tombstone/tombstone_xx.txt 获取tombstone文件命令&#xff1a; adb shell cp /data/tombstones ./tombstones 触发时机 NDK程序在发生崩溃时&#xff0c;它会在路径/data/tombstones/下产生导致程序crash的文件tombstone_xx&#xff0c;记录了死亡了进程的…

Appium移动端自动化测试-(Java)

目录 环境搭建ADB调试工具adb构成adb工作原理adb常用命令电脑连接多个设备跟模拟器使用adb包名与界面名的概念如何获取包名和界面名文件传输获取app启动时间获取手机日志其他命令 Appium全自动化测试框架&#xff08;python&#xff09;冲错了序言 环境搭建Appium客户端安装App…

【洛谷学习自留】p5707 上学迟到

解题思路&#xff1a; 1.先用给出的时间和速度&#xff08;如果无法整除&#xff0c;则时间加一&#xff09;&#xff0c;计算出时间&#xff08;分&#xff09;&#xff0c;然后将时间加上10分钟。 2.创建一个计时器&#xff0c;设置一个日期&#xff0c;保证时分秒部分&#…

【简说八股】Redisson的守护线程是怎么实现的

Redisson Redisson 是一个 Java 语言实现的 Redis SDK 客户端&#xff0c;在使用分布式锁时&#xff0c;它就采用了「自动续期」的方案来避免锁过期&#xff0c;这个守护线程我们一般也把它叫做「看门狗」线程。 Redission是一个在Java环境中使用的开源的分布式缓存和分布式锁实…

经典的算法面试题(1)

题目&#xff1a; 给定一个整数数组 nums&#xff0c;编写一个算法将所有的0移到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 示例: 输入: [0,1,0,3,12] 输出: [1,3,12,0,0] 注意&#xff1a;必须在原数组上操作&#xff0c;不能拷贝额外的数组。尽量减少操作次数。 这…

最长上升子序列(LIS)简介及其例题分析

一.最长上升子序列&#xff08;LIS&#xff09;的相关知识 1.最长上升子序列&#xff08;Longest Increasing Subsequence&#xff09;&#xff0c;简称LIS&#xff0c;也有些情况求的是最长非降序子序列&#xff0c;二者区别就是序列中是否可以有相等的数。假设我们有一个序…

10.轮廓系数-机器学习模型性能的常用的评估指标

轮廓系数&#xff08;Silhouette Coefficient&#xff09;是评估聚类算法效果的常用指标之一。它结合了聚类的凝聚度&#xff08;Cohesion&#xff09;和分离度&#xff08;Separation&#xff09;&#xff0c;能够量化聚类结果的紧密度和分离度。 背景 1.聚类分析的背景 在…

操作系统(1)——学习导论(Ⅱ)

目录 小程一言专栏链接: [link](http://t.csdnimg.cn/6grrU) 学习导论&#xff08;Ⅱ&#xff09;操作系统-赏前人佳作大型操作系统大型操作系统的一些特点和功能举例 服务器操作系统服务器操作系统特点和功能举例 多处理器操作系统举例 个人计算机操作系统举例 掌上计算机操作…

Jmeter接口测试---随机数、加密、cookie鉴权、断言、CSV参数化

随机数 第一步&#xff1a;选择工具-函数助手对话框 第二步&#xff1a;选择random&#xff0c;设置最大值最小值&#xff0c;复制函数字符串到指定位置 加密接口 类型&#xff1a;AES、DES、Base64、RSA&#xff08;可以解密&#xff09; | MD5、SHA、HmacSHA&#xff08;不…

【Linux系统化学习】线程概念

目录 线程的概念 线程的引出 什么是线程 理解线程比进程更加的轻量化 线程的优点 现成的缺点 线程异常 线程用途 Linux进程VS线程 线程的简单现象 线程的概念 有关操作系统的书籍或者课本都会这样描述线程&#xff1a; 线程是比进程轻量化的一种执行流线程是进程内部…

CogCaliperTool

关于visionpro工具的博客偏少&#xff0c;以下是本人自己查阅visionpro官方文档完成的&#xff08;注意标红的计分函数模式&#xff09;: 本主题介绍Caliper工具&#xff0c;这是一种视觉工具&#xff0c;可在图像的定义明确的区域内提供快速准确的图案检测和定位。 卡尺工具…

GPU 硬件与 CUDA 程序开发工具

GPU 硬件简介 从十多年前起&#xff0c;GPU 的浮点数运算峰值就比同时期的 CPU 高一个量级&#xff1b;GPU 的内存带宽峰值也比同时期的 CPU 高一个量级。 CPU 和 GPU 的显著区别是&#xff1a;一个典型的 CPU 拥有少数几个快速的计算核心&#xff0c;而一个典型的 GPU 拥有几…

考研复试类比社团招新,无所谓“公平”,导师选谁都是他的权力

这篇文章是抖音和b站上上传的同名视频的原文稿件&#xff0c;感兴趣的csdn用户可以关注我的抖音和b站账号&#xff08;GeekPower极客力量&#xff09;。同时这篇文章也为视频观众提供方便&#xff0c;可以更加冷静地分析和思考。文章同时在知乎发表。 我考研一战的时候计算机考…