Linux驱动编程 - platform平台设备驱动总线

news2024/9/19 20:34:31

目录

简介:

一、初识platform平台设备驱动

1、platform_driver驱动代码框架

2、platform_device设备代码框架

3、测试结果

3.1 Makefile编译

3.2 加载驱动

二、platform框架分析

1、注册platform总线

1.1 创建platform平台总线函数调用流程

1.2 platform_bus_init() 函数

2、注册platform_driver

2.1 platform_driver_register函数流程

2.2 匹配过程

2.3 调用probe函数

3、注册platform_device

3.1 platform_device_register函数流程

三、dts设备树

1、设备树的匹配过程

2、设备树与platform_driver匹配示例

2.1 dts设备树文件

​编辑

2.2 platform_driver 驱动代码


简介:


        Linux系统为了驱动的可重用性,提出驱动的分离与分层的软件思路,为了保持设备驱动的统一性,platform平台设备驱动模型就此诞生。相对于USB、PCI、I2C、SPI等物理总线来说,platform总线是虚拟、抽象出来的总线,实际中并不存在这样的总线。

说明:本文基于Linux版本为4.1.15

一、初识platform平台设备驱动


        platform平台总线下有驱动链表设备链表,当调用 platform_driver_register() 注册platform驱动,或调用 platform_device_register() 注册platform设备时,都会执行match匹配函数。当链表中有platform驱动和platform设备匹配上,就会调用platform驱动probe() 函数。

图解platform_driver和platform_device匹配过程:

比较 platform_driver 的 driver->name 与 platform_device 的 name 相同都为 "myled",就会执行 platform_driver 的 probe函数,这里为 led_probe。led_probe函数由我们自己定义实现注册字符设备等功能。

platform 总线设备驱动大概有以下步骤:

  • platform_device_register() 注册平台设备
  • platform_driver_register() 注册平台驱动
  • platform总线自动匹配name,匹配上就调用 driver 的 .probe
  • probe中注册字符设备等操作

1、platform_driver驱动代码框架


#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/io.h>

/* 当驱动driver->name和设备name相同, 则调用驱动的.probe函数, .probe函数可以做任何事情 */
static int led_probe(struct platform_device *pdev)
{
	/* 注册字符设备等操作 */
	printk("platform_driver probe run!\n");
	return 0;
}

static int led_remove(struct platform_device *pdev)
{
	/* 卸载字符设备等操作 */
	printk("platform_driver remove run!\n");
	return 0;
}

/* 定义平台drv,通过.name来比较dev */
struct platform_driver led_drv = {
	.probe		= led_probe,
	.remove		= led_remove,
	.driver		= {
		.name	= "myled",
	}
};

static int led_drv_init(void)
{
	platform_driver_register(&led_drv);			//注册驱动,最终调用driver_register()
	return 0;
}

static void led_drv_exit(void)
{
	platform_driver_unregister(&led_drv);
}

module_init(led_drv_init);
module_exit(led_drv_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("dongao");

2、platform_device设备代码框架


#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>

static void led_release(struct device * dev)		//构造一个release函数,防止报错
{
}

/* platform_device结构体.name与platform_driver的driver->name比较 */
static struct platform_device led_dev = {
    .name         = "myled",
    .id       = -1,
    //.num_resources    = ARRAY_SIZE(led_resource), //可以将device的资源传给driver使用
    //.resource     = led_resource,
    .dev = { 
    	.release = led_release, 
	},
};

static int led_dev_init(void)
{
	platform_device_register(&led_dev);				//注册1个平台设备,最终调用device_add()
	return 0;
}

static void led_dev_exit(void)
{
	platform_device_unregister(&led_dev);			//卸载1个平台设备
}

module_init(led_dev_init);
module_exit(led_dev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("dongao");

3、测试结果


3.1 Makefile编译

KERN_DIR = /home/linux-imx-rel_imx_4.1.15_2.1.0
 
all:
	make -C $(KERN_DIR) M=`pwd` modules
 
clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order
 
obj-m   += led_drv.o
obj-m   += led_dev.o

3.2 加载驱动


加载编译出来的2个ko文件。

$ insmod led_dev.ko               # 加载 platform_device 驱动

$ insmod led_drv.ko                # 加载 platform_driver 驱动

结果: 

结果,运行probe 打印 "platform_driver remove run!"

查看在系统中的驱动:

$ ls  /sys/bus/platform/devices 

$ ls  /sys/bus/platform/drivers/

二、platform框架分析


        Linux的这种platform driver机制和传统的device_driver机制相比,一个十分明显的优势在于platform机制将本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platform_device提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性。

1、注册platform总线


1.1 创建platform平台总线函数调用流程

start_kernel()			//启动linux内核,路径:init/main.c
--->rest_init()		    //init/main.c
--->--->kernel_init()		    //1号进程
--->--->--->kernel_init_freeable()
--->--->--->--->do_basic_setup()
--->--->--->--->--->driver_init()    //初始化driver模块,路径:drivers\base\init.c
--->--->--->--->--->--->platform_bus_init()
                            // 注册平台总线,路径:drivers\base\platform.c
                            ->bus_register(&platform_bus_type)

bus_register(&platform_bus_type) 注册了platform总线platform_bus_type,对应sysfs下的 /sys/bus/platform 目录。

1.2 platform_bus_init() 函数

/* 总线结构体为 struct bus_type */
struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_groups	= platform_dev_groups,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.pm		= &platform_dev_pm_ops,
};
EXPORT_SYMBOL_GPL(platform_bus_type);    //platform_bus_type 平台总线

int __init platform_bus_init(void)
{
	int error;

	early_platform_cleanup();

	error = device_register(&platform_bus);
	if (error)
		return error;
	error =  bus_register(&platform_bus_type);    // 注册平台总线
	if (error)
		device_unregister(&platform_bus);
	of_platform_register_reconfig_notifier();
	return error;
}

 总线注册:

 bus_register      //注册一条总线,路径:drivers\base\bus.c
 bus_unregister    //注销总线

bus_register(&platform_bus_type) 注册了平台总线 platform_bus_type ,之后在注册platform_driverplatform_device时会与platform_bus_type 建立联系。

2、注册platform_driver


platform_driver_register() 源码路径:include/linux/platform_device.h

#define platform_driver_register(drv) \
	__platform_driver_register(drv, THIS_MODULE)

drv->driver.bus = &platform_bus_type; 与 platform_bus_type 建立联系,并设置了 probe、remove、shutdown函数。

int __platform_driver_register(struct platform_driver *drv,
				struct module *owner)
{
	drv->driver.owner = owner;
	drv->driver.bus = &platform_bus_type;
	drv->driver.probe = platform_drv_probe;
	drv->driver.remove = platform_drv_remove;
	drv->driver.shutdown = platform_drv_shutdown;

	return driver_register(&drv->driver);
}

2.1 platform_driver_register函数流程

platform_driver_register(drv)
--->__platform_driver_register(drv, THIS_MODULE)    // drivers/base/platform.c
--->--->driver_register(&drv->driver)               // drivers/base/driver.c
--->--->--->bus_add_driver(drv)                     // drivers/base/bus.c
--->--->--->--->driver_attach(drv)                  // drivers/base/dd.c
--->--->--->--->--->bus_for_each_dev(drv->bus, NULL, drv, __driver_attach) // drivers/base/bus.c
--->--->--->--->--->--->__driver_attach()           // drivers/base/dd.c
--->--->--->--->--->--->--->driver_match_device(drv, dev)    // dev 和 drv 匹配
--->--->--->--->--->--->--->driver_probe_device(drv, dev)
--->--->--->--->--->--->--->--->really_probe(dev, drv)
                                    dev->driver = drv;    // dev 和 drv 建立联系
                                    drv->probe(dev)

bus_for_each_dev 会轮询每个dev,并调用__driver_attach。__driver_attach 非常关键,它主要匹配dev和drv,匹配上就会调用drv的probe()。

static int __driver_attach(struct device *dev, void *data)
{
	struct device_driver *drv = data;

	if (!driver_match_device(drv, dev))    // drv 和 dev 匹配
		return 0;

	if (dev->parent)	/* Needed for USB */
		device_lock(dev->parent);
	device_lock(dev);
	if (!dev->driver)
		driver_probe_device(drv, dev);    // 调用probe
	device_unlock(dev);
	if (dev->parent)
		device_unlock(dev->parent);

	return 0;
}

2.2 匹配过程


driver_match_device() 函数将drv和dev做匹配,返回匹配结果。

// drivers\base\base.h
static inline int driver_match_device(struct device_driver *drv,
				      struct device *dev)
{
	return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}

最终调用 platform_bus_type 总线的match函数, platform_match() 如下:

// drivers\base\platform.c
static int platform_match(struct device *dev, struct device_driver *drv)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct platform_driver *pdrv = to_platform_driver(drv);

	/* When driver_override is set, only bind to the matching driver */
	if (pdev->driver_override)
		return !strcmp(pdev->driver_override, drv->name);

	/* Attempt an OF style match first */
	if (of_driver_match_device(dev, drv))
		return 1;

	/* Then try ACPI style match */
	if (acpi_driver_match_device(dev, drv))
		return 1;

	/* Then try to match against the id table */
	if (pdrv->id_table)
		return platform_match_id(pdrv->id_table, pdev) != NULL;

	/* fall-back to driver name match */
	return (strcmp(pdev->name, drv->name) == 0);
}

platform_match() 中有4种匹配方式:

(1)通过of_driver_match_device:设备树方式匹配,device_driver结构体里面的of_match_table变量保存着驱动的compatible的属性字符串表。设备树中的每个节点的compatible属性会of_match_table 表中的所有成员比较,相同则匹配,设备和驱动匹配成功以后 probe 函数就会执行;

(2)ACPI 匹配方式;

(3)id_table 匹配。每个 platform_driver 结构体有一个 id_table成员变量,它保存了很多 id,这些 id 信息存放着这个 platformd 驱动所支持的驱动类型;

(4)如果第三种匹配方式的 id_table 不存在的话就直接比较驱动和设备的 name 字段,看看是不是相等,如果相等的话就匹配成功。

 上面例子实际上用第四种匹配方式

return (strcmp(pdev->name, drv->name) == 0);

2.3 调用probe函数


platform_match() 匹配OK后,执行driver_probe_device(),最终执行really_probe(),really_probe实现了probe()函数的调用。

int driver_probe_device(struct device_driver *drv, struct device *dev)
{
	int ret = 0;

	if (!device_is_registered(dev))
		return -ENODEV;

	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
		 drv->bus->name, __func__, dev_name(dev), drv->name);

	pm_runtime_barrier(dev);
	ret = really_probe(dev, drv);
	pm_request_idle(dev);

	return ret;
}

static int really_probe(struct device *dev, struct device_driver *drv)
{
    ... ...
	if (dev->bus->probe) {
		ret = dev->bus->probe(dev);
		if (ret)
			goto probe_failed;
	} else if (drv->probe) {
		ret = drv->probe(dev);
		if (ret)
			goto probe_failed;
	}
    ... ...
}

drv->probe(dev) 就是在 __platform_driver_register 中注册的函数,它指向 platform_drv_probe()

static int platform_drv_probe(struct device *_dev)
{
    //从 struct device_driver 倒推 struct platform_driver 结构体
	struct platform_driver *drv = to_platform_driver(_dev->driver);
	struct platform_device *dev = to_platform_device(_dev);
	
    ... ...
	if (drv->probe) {
		ret = drv->probe(dev);        //执行 platform_driver 的probe()函数
		if (ret)
			dev_pm_domain_detach(_dev, true);
	}
    ... ...
}

to_platform_driver调用container_of从成员struct device_driver倒推宿主结构体 platform_driver。并执行我们自己定义的 platform_driver驱动 中的 probe() 函数。

3、注册platform_device


注册platform_device其实跟注册platform_driver极其相似。主要也是匹配和调用platform_driver的probe()函数。

3.1 platform_device_register函数流程

platform_device_register() 源码路径:drivers\base\platform.c

platform_device_register(struct platform_device *dev)  //路径:drivers\base\platform.c
--->platform_device_add(pdev);
--->--->device_add(&pdev->dev);         //drivers\base\core.c
--->--->bus_probe_device(dev);      //drivers\base\bus.c
--->--->--->device_attach(dev);    //drivers\base\dd.c
--->--->--->--->bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
--->--->--->--->--->__device_attach
--->--->--->--->--->--->driver_match_device(drv, dev);// 匹配成功才向下执行probe!
--->--->--->--->--->--->--->driver_probe_device(drv, dev);
--->--->--->--->--->--->--->--->really_probe(dev, drv);
--->--->--->--->--->--->--->--->--->dev->bus->probe(dev);
                                    或者 drv->probe(dev);

bus_for_each_drv 会轮询每个drv,调用到 __device_attach 核心代码

// drivers\base\dd.c
static int __device_attach(struct device_driver *drv, void *data)
{
	struct device *dev = data;

	if (!driver_match_device(drv, dev))
		return 0;

	return driver_probe_device(drv, dev);
}

只有当 driver_match_device() 匹配成功才会调用 driver_probe_device() 函数,driver_probe_device() 最终执行了 platform_drviver 的probe()函数,这些上面都已经介绍了,不再赘述。

三、dts设备树


        在Linux 2.6及之前,大量板级信息被硬编码到内核里,十分庞大,大量冗余代码,此背景下,引入dts设备树。

        在没有设备树的 Linux 内核下,我们需要分别编写并注册 platform_device 和 platform_driver,分别代表设备和驱动。使用设备树时,设备的描述被放到了设备树中,因此 platform_device 就不需要我们去编写了,我们只需要实现 platform_driver ,与设备树中的platform_device 匹配即可。

1、设备树的匹配过程


platform_match() 中有4种匹配方式,设备树和platform_driver匹配函数为 of_driver_match_device()

// 匹配函数
static inline int of_driver_match_device(struct device *dev,
					 const struct device_driver *drv)
{
	return of_match_device(drv->of_match_table, dev) != NULL;
}

// drivers/of/device.c
const struct of_device_id *of_match_device(const struct of_device_id *matches,
					   const struct device *dev)
{
	if ((!matches) || (!dev->of_node))
		return NULL;
	return of_match_node(matches, dev->of_node);
}
EXPORT_SYMBOL(of_match_device);

// drivers/of/base.c
const struct of_device_id *of_match_node(const struct of_device_id *matches,
					 const struct device_node *node)
{
	const struct of_device_id *match;
	unsigned long flags;

	raw_spin_lock_irqsave(&devtree_lock, flags);
	match = __of_match_node(matches, node);
	raw_spin_unlock_irqrestore(&devtree_lock, flags);
	return match;
}
EXPORT_SYMBOL(of_match_node);

static
const struct of_device_id *__of_match_node(const struct of_device_id *matches,
					   const struct device_node *node)
{
	const struct of_device_id *best_match = NULL;
	int score, best_score = 0;

	if (!matches)
		return NULL;

	for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++) {
		score = __of_device_is_compatible(node, matches->compatible,
						  matches->type, matches->name);
		if (score > best_score) {
			best_match = matches;
			best_score = score;
		}
	}

	return best_match;
}

__of_device_is_compatible() 最终会比较设备树子节点的 "compatible" 与 驱动中 of_match_table->compatible,判断是否匹配。

static int __of_device_is_compatible(const struct device_node *device,
				     const char *compat, const char *type, const char *name)
{
	struct property *prop;
	const char *cp;
	int index = 0, score = 0;

	/* Compatible match has highest priority */
	if (compat && compat[0]) {
		prop = __of_find_property(device, "compatible", NULL);
		for (cp = of_prop_next_string(prop, NULL); cp;
		     cp = of_prop_next_string(prop, cp), index++) {
			if (of_compat_cmp(cp, compat, strlen(compat)) == 0) {
				score = INT_MAX/2 - (index << 2);
				break;
			}
		}
		if (!score)
			return 0;
	}

	/* Matching type is better than matching name */
	if (type && type[0]) {
		if (!device->type || of_node_cmp(type, device->type))
			return 0;
		score += 2;
	}

	/* Matching name is a bit better than not */
	if (name && name[0]) {
		if (!device->name || of_node_cmp(name, device->name))
			return 0;
		score++;
	}

	return score;
}

至于dts设备树如何解析生成 struct device_node的?之后会专门写一篇博文介绍。

2、设备树与platform_driver匹配示例


2.1 dts设备树文件

/ {
  gpioled {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "donga-gpioled"; /* platform 总线通过 compatible 属性值来匹配驱动 */
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_led>;    /* 设置 LED 灯所使用的 PIN 对应的 pinctrl 节点 */
		led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;	/* 指定了 LED 灯所使用的 GPIO,在这里就是 GPIO1 的 IO03,低电平有效 */
		status = "okay";
	};
}

查看设备树中gpioled是否生效

$ ls /proc/device-tree/                                        # 查看设备树

$ cat /proc/device-tree/gpioled/compatible        # 查看gpioled的compatible

2.2 platform_driver 驱动代码

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define LEDDEV_CNT		1				/* 设备号长度 	*/
#define LEDDEV_NAME		"dtsplatled"	/* 设备名字 	*/

static dev_t devid;				/* 设备号	*/
static int major;				/* 主设备号	*/
static struct cdev cdev;		/* cdev		*/
static struct class *class;		/* 类 		*/
static struct device *device;	/* 设备		*/

static int led_open(struct inode *inode, struct file *filp)
{
	printk("led_open run!\n");
	return 0;
}

static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    printk("led_write run!\n");
	return 0;
}

/* 设备操作函数 */
static struct file_operations led_fops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.write = led_write,
};

/* probe函数,当驱动与设备匹配以后此函数就会执行 */
static int led_probe(struct platform_device *dev)
{	
	printk("platform_driver probe run!\n");
	/* 1、设置设备号 */
	if (major) {
		devid = MKDEV(major, 0);
		register_chrdev_region(devid, LEDDEV_CNT, LEDDEV_NAME);
	} else {
		alloc_chrdev_region(&devid, 0, LEDDEV_CNT, LEDDEV_NAME);
		major = MAJOR(devid);
	}

	/* 2、注册设备      */
	cdev_init(&cdev, &led_fops);
	cdev_add(&cdev, devid, LEDDEV_CNT);

	/* 3、创建类      */
	class = class_create(THIS_MODULE, LEDDEV_NAME);
	if (IS_ERR(class)) {
		return PTR_ERR(class);
	}

	/* 4、创建设备 */
	device = device_create(class, NULL, devid, NULL, LEDDEV_NAME);
	if (IS_ERR(device)) {
		return PTR_ERR(device);
	}

  /* of函数获取dts中的资源,gpio等操作,略 */
  
	return 0;
}

/* platform驱动的remove函数,移除platform驱动的时候此函数会执行 */
static int led_remove(struct platform_device *dev)
{
    printk("platform_driver remove run!\n");
	cdev_del(&cdev);				            /*  删除cdev */
	unregister_chrdev_region(devid, LEDDEV_CNT); /* 注销设备号 */
	device_destroy(class, devid);
	class_destroy(class);
	return 0;
}

/* 匹配列表 */
static const struct of_device_id led_of_match[] = {
	{ .compatible = "donga-gpioled" },		/* 此参数要与设备树匹配 */
	{ /* Sentinel */ }
};

/* platform驱动结构体 */
static struct platform_driver led_driver = {
	.driver		= {
		.name	= "led_drv",			/* 驱动名字,用于和设备匹配 */
		.of_match_table	= led_of_match, /* 设备树匹配表 */
	},
	.probe		= led_probe,
	.remove		= led_remove,
};
		
/* 驱动模块加载函数 */
static int __init leddriver_init(void)
{
	return platform_driver_register(&led_driver);
}

/* 驱动模块卸载函数 */
static void __exit leddriver_exit(void)
{
	platform_driver_unregister(&led_driver);
}

module_init(leddriver_init);
module_exit(leddriver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("donga");

insmod加载驱动后打印 "platform_driver probe run!"

同时probe中创建了字符设备,字符设备相关介绍可以看下这篇文章:

https://blog.csdn.net/hinewcc/article/details/140672331


其他博主画的图非常好,参考文章:

https://blog.csdn.net/qq_16504163/article/details/118562670


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

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

相关文章

鸿蒙开发之ArkTS 基础三 数组

数组可以存储多个数据 语法为: let 数组名字:数组类型[] [数据一,数据二 ,数据三 ,数据四 ,数据5⃣️] 例如:学生类数组 let students:string[] [小美,小红,小张,小西] console.log("students",students) 输出 小美,小红,小张,小西 这里不需要遍历就能输出内容…

C Primer Plus 第5章习题

你该逆袭了 红色标注的是&#xff1a;错误的答案 蓝色标注的是&#xff1a;正确的答案 绿色标注的是&#xff1a;做题时有疑问的地方 橙色标注的是&#xff1a;答案中需要着重注意的地方 练习题 一、复习题1、2、3、4、错误答案&#xff1a;正确答案&#xff1a; 5、我的答案&a…

十三,Spring Boot 中注入 Servlet,Filter,Listener

十三&#xff0c;Spring Boot 中注入 Servlet&#xff0c;Filter&#xff0c;Listener 文章目录 十三&#xff0c;Spring Boot 中注入 Servlet&#xff0c;Filter&#xff0c;Listener1. 基本介绍2. 第一种方式&#xff1a;使用注解方式注入&#xff1a;Servlet&#xff0c;Fil…

Cobbler 搭建方法

统信服务器操作系统行业版V20-1000c【Cobbler 搭建】手册 统信服务器操作系统行业版 V20版本上Cobbler 搭建方法 文章目录 功能概述一、使用范围二、cobbler工作流程1. Server 端2. Client 端三、 环境准备1. 测试环境告知,以提供配置时参考:2. 关闭防火墙、selinux:3. 注意…

C#学习笔记(三)Visual Studio安装与使用

博主刚开始接触C#&#xff0c;本系列为学习记录&#xff0c;如有错误欢迎各位大佬指正&#xff01;期待互相交流&#xff01; 上一篇文章中安装了Visual Studio Code来编写调试C#程序&#xff0c;但是博主的目标是编写带窗口的应用程序&#xff0c;了解之后发现需要安装Visual …

python-素数对

题目描述 定义两个相差为 2 的素数称为素数对&#xff0c;如 5 和 7,17 和 19 等&#xff0c;要求找出所有两个数均不大于 n 的素数对。输入 一个正整数 n。1≤n≤10000。输出 所有小于等于 n 的素数对。每对素数对输出一行&#xff0c;中间用单个空格隔开。若没有找到任何素数…

VS2019配置TIFF

1.下载 Index of /libtiff/ (osgeo.org) 2.配置 3.使用 4.测试程序 #include <iostream> #include <cstdint> // 包含 stdint.h 头文件 #include "tiffio.h"int main() {std::cout << "Hello World!\n";// 打开一个 TIFF 文件const ch…

06_Python数据类型_元组

Python的基础数据类型 数值类型&#xff1a;整数、浮点数、复数、布尔字符串容器类型&#xff1a;列表、元祖、字典、集合 元组 元组&#xff08;Tuple&#xff09;是一种不可变的序列类型&#xff0c;与列表类似&#xff0c;但有一些关键的区别。本质&#xff1a;只读的列表…

java程序崩了不会看怎么办,那就用jconsole试试

性能监控工具 jconsole JConsole工具是JDK自带的图形化性能监控工具。并通过JConsole工具&#xff0c; 可以查看Java应用程序的运行概况&#xff0c; 监控堆信息、 元空间使用情况及类的加载情况等。 JConsole程序在%JAVA_HOM E%/bin目录下 或者你可以直接在命令行对他进行…

排序算法-交换排序

目录 基本思想 一、冒泡排序 二、快速排序分析 1. hoare版本 2. 挖坑法 3. 前后指针版本 4. 快速排序的优化 三、代码示例 1. hoare版本 2. 挖坑法 3. 前后指针版本 四、快速排序&#xff08;三路划分) 五、总结 基本思想 基本思想&#xff1a;所谓交换&#xff0…

VS Code终端命令执行后老是出现 __vsc_prompt_cmd_original: command not found

VS Code终端命令执行后老是出现 __vsc_prompt_cmd_original: command not found。 如下图&#xff08;vscode终端中&#xff09;&#xff1a; 解决方案&#xff1a; 1、vim ~/.bashrc 2、在~/.bashrc里面加入命令&#xff1a;unset PROMPT_COMMAND 3、source ~/.bashrc

【AI大模型】Kimi API大模型接口实现

一、Kimi大模型概述 Kimi&#xff0c;月之暗面旗下国产大模型。是北京月之暗面科技有限公司&#xff08;Moonshot AI&#xff09;于2023年10月9日推出的一款智能助手&#xff0c;主要应用场景为专业学术论文的翻译和理解、辅助分析法律问题、快速理解API开发文档等&#xff0c…

关于http的206状态码和416状态码的意义、断点续传以及CORS使用Access-Control-Allow-Origin来允许跨域请求

一、关于http的206状态码和416状态码的意义及断点续传 HTTP 2xx范围内的状态码表明客户端发送的请求已经被服务器接受并且被成功处理了,HTTP/1.1 206状态码表示客户端通过发送范围请求头Range抓取到了资源的部分数据&#xff0c;一般用来解决大文件下载问题&#xff0c;一般CDN…

直接的强化学习与间接的强化学习

强化学习是一种机器学习方法&#xff0c;用于让智能体在与环境的交互中学习最优策略&#xff0c;以获得最大的奖励。根据强化学习的方式&#xff0c;可以分为直接强化学习和间接强化学习。直接强化学习注重直接从奖励信号中学习最优策略&#xff0c;而间接强化学习则通过学习环…

【吊打面试官系列-MySQL面试题】MySQL 中有哪几种锁?

大家好&#xff0c;我是锋哥。今天分享关于【MySQL 中有哪几种锁&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; MySQL 中有哪几种锁&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 1、表级锁&#xff1a;开销小&#xff0c;加锁快&…

2024.9.16 day 1 pytorch安装及环境配置

一、配置pytorch环境&#xff0c;安装pytorch 1.查看python版本 python --version 2.在anaconda命令中创建pytorch环境 conda create -n pytorch python3.12(python版本&#xff09; 3.pytorch安装 pytorch首页 PyTorchhttps://pytorch.org/ os为windows推荐package选择…

在jenkins作业中如何增加git fetch的超时时间

在jenkins作业中如何增加git fetch的超时时间 可以通过以下几种方式来增加 Jenkins 中 git fetch 的超时时间: 1.在 Jenkins 的构建配置中设置超时时间: 在 Jenkins 的构建配置页面,找到 "Git" 部分,在 "Additional Behaviours" 中选择 "Advanced c…

Sui与3DOS合作推动3D打印网络的去中心化

制造业创新者3DOS宣布将其庞大的3D打印网络与Sui集成。这一集成使用户、3D打印机和制造商能够连接到全球可访问的去中心化网络。 解锁去中心化3D打印的全部潜力依赖于精确的实时协调。Sui作为通用协调层&#xff0c;将用户、3D打印机和制造商同步到一个高效、统一的网络中。通…

注册登录案列

案列需求&#xff1a; 在主测页面中输入用户数据&#xff0c;点击注册按钮完成用户注册 实现步骤&#xff1a; 1.创建数据库表&#xff0c;Mysql代码如下&#xff1a; CREATE TABLE tb_user( id int primary key auto_increment, username VARCHAR(32), password VARCHAR(3…

【题解】【枚举】—— [USACO1.5] 回文质数 Prime Palindromes

【题解】【枚举】—— [USACO1.5] 回文质数 Prime Palindromes [USACO1.5] 回文质数 Prime Palindromes题目描述输入格式输出格式输入输出样例输入 #1输出 #1 提示 思路1.素数筛法1.1.思路解析1.2.参考代码 解法1.打表1.1.思路解析1.2.AC代码 解法2.构造回文数2.1.思路解析2.2.…