驱动开发的引入

1.引入

Linux内核的整体架构本就非常庞大,其包含的组件也非常多。而我们怎样把需要的部分都包含在内核中呢?
一种方法是把所有需要的功能都编译到Linux内核中。这会导致两个问题,一是生成的内核会很大,二是如果我们要在现有的内核中新增或删除功能,将不得不重新编译内核。有没有另一种机制可使得编译出的内核本身并不需要包含所有功能,而在这些功能需要被使用的时候,其对应的代码被动态地加载到内核中呢?
Linux 提供了这样的机制,这种机制被称为模块(Module)。模块具有这样的特点,模块本身不被编译人内核映像,从而控制了内核的大小。模块一旦被加载,它就和内核中的其他部分完全一样。

Linux驱动开发就是编写上面提到的模块。

应用程序和 VFS 之间的接口是系统调用,而 VFS 与文件系统以及设备文件之间的接口是 file_operations结构体成员函数,这个结构体包含对文件进行打开、关闭、读写、控制的系列成员函数,关系如图5.2所示。

由于字符设备的上层没有类似于磁盘的ext2等文件系统,所以字符设备的file_operations成员函数就直接由设备驱动提供了file_operations正是字符设备驱动的核心。

块设备有两种访问方法:

一种方法是不通过文件系统直接访问裸设备,在 Linux内核实现了统一的 def blk_fops这一file_operations,它的源代码位于fs/block_dev.c,所以当我们运行类似于“dd if=/dev/sdbl of-sdbl.img”的命令把整个 /dev/sdb1 裸分区复制到 sdbl.img的时候,内核走的是def blk_fops这个file_operations;

另外一种方法是通过文件系统来访问块设备,file_operations的实现则位于文件系统内,文件系统会把针对文件的读写转换为针对块设备原始扇区的读写。ext2、fat、Btrfs等文件系统中会实现针对VFS的file operations成员函数,设备驱动层将看不到file_operations的存在。

!外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2.struct file、struct inode

在设备驱动程序的设计中,一般面言,会关心fileinode这两个结构体
file结构体代表一个打开的文件,系统中每个打开的文件在内核空间都有一个关联的struct file。它由内核在打开文件时创建,并传递给在文件上进行操作的任何函数。在文件的在内核和驱动源代码中,struct file的指针通常所有实例都关闭后,内核释放这个数据结构。被命名为file或filp(即 file pointer)。

struct file {union {struct llist_node	fu_llist;struct rcu_head 	fu_rcuhead;} f_u;struct path			f_path;struct inode		*f_inode;	/* cached value */const struct file_operations	*f_op;/** Protects f_ep_links, f_flags.* Must not be taken from IRQ context.*/spinlock_t		    	f_lock;atomic_long_t			f_count;unsigned int 			f_flags;fmode_t			    	f_mode;struct mutex			f_pos_lock;loff_t					f_pos;struct fown_struct		f_owner;const struct cred		*f_cred;struct file_ra_state	f_ra;u64			f_version;
#ifdef CONFIG_SECURITYvoid				*f_security;
#endif/* needed for tty driver, and maybe others */void				*private_data;#ifdef CONFIG_EPOLL/* Used by fs/eventpoll.c to link all the hooks to this file */struct list_head		f_ep_links;struct list_head		f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */struct address_space	*f_mapping;
} __attribute__((aligned(4)));	/* lest something weird decides that 2 is OK */

文件读/写模式mode、标志flags 都是设备驱动关心的内容,而私有数据指针 private_data在设备驱动中被广泛应用,大多被指向设备驱动自定义以用于描述设备的结构体。

struct inode包含文件访问权限、属主、组、大小、生成时间、访问时间、最后修改时间等信息。它是Linux管理文件系统的最基本单位,也是文件系统连接任何子目录、文件的桥梁

struct inode {umode_t				i_mode;unsigned short		i_opflags;kuid_t				i_uid;kgid_t				i_gid;unsigned int		i_flags;#ifdef CONFIG_FS_POSIX_ACLstruct posix_acl	*i_acl;struct posix_acl	*i_default_acl;
#endifconst struct inode_operations	*i_op;struct super_block	*i_sb;struct address_space	*i_mapping;#ifdef CONFIG_SECURITYvoid			*i_security;
#endif/* Stat data, not accessed from path walking */unsigned long		i_ino;/** Filesystems may only read i_nlink directly.  They shall use the* following functions for modification:**    (set|clear|inc|drop)_nlink*    inode_(inc|dec)_link_count*/union {const unsigned int i_nlink;unsigned int __i_nlink;};dev_t			i_rdev;loff_t			i_size;struct timespec		i_atime;struct timespec		i_mtime;struct timespec		i_ctime;spinlock_t		i_lock;	/* i_blocks, i_bytes, maybe i_size */unsigned short      i_bytes;unsigned int		i_blkbits;blkcnt_t			i_blocks;#ifdef __NEED_I_SIZE_ORDEREDseqcount_t		i_size_seqcount;
#endif/* Misc */unsigned long		i_state;struct rw_semaphore	i_rwsem;unsigned long		dirtied_when;	/* jiffies of first dirtying */unsigned long		dirtied_time_when;struct hlist_node	i_hash;struct list_head	i_io_list;		/* backing dev IO list */
#ifdef CONFIG_CGROUP_WRITEBACKstruct bdi_writeback	*i_wb;		/* the associated cgroup wb *//* foreign inode detection, see wbc_detach_inode() */int			i_wb_frn_winner;u16			i_wb_frn_avg_time;u16			i_wb_frn_history;
#endifstruct list_head	i_lru;		/* inode LRU list */struct list_head	i_sb_list;struct list_head	i_wb_list;	/* backing dev writeback list */union {struct hlist_head	i_dentry;struct rcu_head		i_rcu;};u64				i_version;atomic_t		i_count;atomic_t		i_dio_count;atomic_t		i_writecount;
#ifdef CONFIG_IMAatomic_t		i_readcount; /* struct files open RO */
#endifconst struct file_operations	*i_fop;	/* former ->i_op->default_file_ops */struct file_lock_context	*i_flctx;struct address_space	i_data;struct list_head	i_devices;union {struct pipe_inode_info	*i_pipe;struct block_device	*i_bdev;struct cdev		*i_cdev;char			*i_link;unsigned		i_dir_seq;};__u32			i_generation;#ifdef CONFIG_FSNOTIFY__u32			i_fsnotify_mask; /* all events this inode cares about */struct hlist_head	i_fsnotify_marks;
#endif#if IS_ENABLED(CONFIG_FS_ENCRYPTION)struct fscrypt_info	*i_crypt_info;
#endifvoid			*i_private; /* fs or device private pointer */
};

对于表示设备文件的inode结构,struct cdev *i_cdev;字段包含设备编号。Linux内核设备编号分为主设备编号和次设备编号,前者为devt的高12位.后者为 devt的低 20位。

3.udev

**devfs(设备文件系统)**是由Linux2.4内核引入的,引入时被许多工程师给予了高度评价它的出现使得设备驱动程序能自主地管理自己的设备文件。具体来说,devs具有如下优点

  • 可以通过程序在设备初始化时在/dev目录下创建设备文件,卸载设备时将它删除
  • 设备驱动程序可以指定设备名、所有者和权限位,用户空间程序仍可以修改所有者和权限位。
  • 不再需要为设备驱动程序分配主设备号以及处理次设备号,在程序中可以直接给register chrdev()传递0主设备号以获得可用的主设备号,并在devfsregister()中指定次设备号。

尽管 devfs有这样和那样的优点,但是,在Linux2.6内核中,devfs被认为是过时的方法,并最终被抛弃了,udev取代了它。

在嵌人式系统中,也可以用 udev 的轻量级版本 mdev,mdev 集成于 busybox 中。

Linux设计中强调的一个基本观点是机制和策略的分离。机制是做某样事情的固定步骤、方法,而策略就是每一个步骤所采取的不同方式。机制是相对固定的,而每个步骤采用的策略是不固定的。机制是稳定的,而策略则是灵活的,因此,在Linux内核中,不应该实现策略。

热插拔:

udev完全在用户态工作利用设备加人或移除时内核所发送的热插拔事件(Hotplug),在热插拔时,设备的详细信息会由内核通过netlink套接字发送出来,发出(Event)来工作的事情叫 uevent。udev的设备命名策略、权限控制和事件处理都是在用户态下完成的,它利用从内核收到的信息来进行创建设备文件节点等工作

冷插拔:

udev就是采用这种方式接收netlink消息,并根据它的内容和用户设置给udev的规则做匹配来进行工作的。这里有一个问题,就是冷插拔的设备怎么办?冷插拔的设备在开机时就存在,在udev启动前已经被插入了。对于冷插拔的设备,Linux内核提供了sysfs下面一个uevent节点,可以往该节点写一个“add”,导致内核重新发送netlink,之后udev就可以收到冷插拔的netlink 消息了。

udev自动加载驱动模块的原理
#设备被发现时发送事件:当硬件设备被插入系统时,内核会检测到该设备,并通过kobject_uevent函数向用户空间发送一个uevent事件。这个事件包含了设备的相关信息,如设备的Vendor ID、Product ID等。
#udev监听事件并处理:udev守护进程在用户空间运行,监听这些uevent事件。udev根据事件中的设备信息,查找/lib/modules/uname-r/modules.alias文件,确定需要加载的驱动模块。
#加载驱动模块:udev通过调用modprobe命令加载对应的驱动模块。modprobe会根据模块的依赖关系,自动加载所有必要的依赖模块。#注意:/lib/modules/uname-r/modules.alias文件的内容是在内核模块编译和安装时由make modules_install命令生成的。它包含了模块的别名映射,用于帮助udev或其他工具根据设备的硬件信息找到并加载正确的驱动模块。模块编译进内核时不会出现在modules.alias文件中,只有作为可加载模块编译时才会被包含在内。insmod命令用于手动加载模块,但不会修改modules.alias文件。

**udev的设计者认为inux应该在设备被发现的时候加载驱动模块,而不是当它被访问的时候。**udev的设计者认为devs所提供的打开/dev节点时自动加载驱动的功能对一个配置正确的计算机来说是多余的,系统中所有的设备都应该产生热插拔事件并加载恰当的驱动,而udev能注意到这点并且为它创建对应的设备节点。

4.sysfs文件系统与Linux设备模型

Linux 2.6以后的内核引人了sysfs 文件系统,sys被看成是与 proc、devfs和 devpty 同类别的文件系统,该文件系统是一个虚拟的文件系统,它可以产生一个包括所有系统硬件的层级视图,与提供进程和状态信息的proc文件系统十分类似。

sysfs把连接在系统上的设备和总线组织成为一个分级的文件,它们可以由用户空间存取,向用户空间导出内核数据结构以及它们的属性。sysfs的一个目的就是展示设备驱动模型中各组件的层次关系,其顶级目录包括 block、bus、dev、devices、class、fs、kernel、power和 frmware 等。

block目录包含所有的块设备;devices目录包含系统所有的设备,并根据设备挂接的总线类型组织成层次结构;bus日录包含系统中所有的总线类型;class目录包含系统中的设备类型(如网卡设备、声卡设备、输人设备等)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在 /sys/bus的 pci等子目录下,又会再分出 drivers和devices目录,而devices 目录中的文件是对/sys/devices目录中文件的符号链接。同样地,/sys/class目录下也包含许多对/sys/devices下文件的链接。如图5.3所示,Linux设备模型与设备、驱动、总线和类的现实状况是直接对应的,也正符合Linux2.6以后内核的设备模型。

大多数情况下,Linux2.6以后的内核中的设备驱动核心层代码作为“幕后大佬”可处理好这些关系,内核中的总线和其他内核子系统会完成与设备模型的交互,这使得驱动工程师在编写底层驱动的时候几乎不需要关心设备模型,只需要按照每个框架的要求,"填鸭式地填充xxx driver里面的各种回调函数,xxx是总线的名字,在 Linux 内核中,分别使用 bus typedevice driverdevice 来描述总线、驱动和设备,这3个结构体定义于include/linux/device.h头文件中,其定义如代码清单5.7所示。

struct bus_type {const char						*name;const char						*dev_name;struct device					*dev_root;struct device_attribute			*dev_attrs;	/* use dev_groups instead */const struct attribute_group 	**bus_groups;const struct attribute_group 	**dev_groups;const struct attribute_group 	**drv_groups;int (*match)(struct device *dev, struct device_driver *drv);int (*uevent)(struct device *dev, struct kobj_uevent_env *env);int (*probe)(struct device *dev);int (*remove)(struct device *dev);void (*shutdown)(struct device *dev);int (*online)(struct device *dev);int (*offline)(struct device *dev);int (*suspend)(struct device *dev, pm_message_t state);int (*resume)(struct device *dev);const struct dev_pm_ops *pm;const struct iommu_ops *iommu_ops;struct subsys_private *p;struct lock_class_key lock_key;
};
struct device_driver {const char		*name;  	// 驱动程序的名称struct bus_type		*bus;   // 驱动程序所属的总线类型(如 platform_bus_type、i2c_bus_type 等)// 内核通过总线类型将驱动程序与设备匹配struct module		*owner; // 指向拥有该驱动程序的模块(通常使用 THIS_MODULE)const char		*mod_name;	// 用于内置模块的名称(通常用于模块卸载时)bool suppress_bind_attrs;	/* disables bind/unbind via sysfs */// 如果为 true,则禁用通过 sysfs 进行驱动程序的绑定和解绑操作enum probe_type probe_type;	// 设备探测类型,用于控制探测行为(如同步探测或异步探测)const struct of_device_id	*of_match_table;	// 设备树匹配表,用于支持设备树(Device Tree)的设备匹配const struct acpi_device_id	*acpi_match_table;	// ACPI 匹配表,用于支持 ACPI 设备的匹配int (*probe) (struct device *dev);		// 设备探测函数,当驱动程序与设备匹配成功时调用int (*remove) (struct device *dev);		// 设备移除函数,当设备从系统中移除时调用void (*shutdown) (struct device *dev); 	// 设备关闭函数,当系统关闭时调用int (*suspend) (struct device *dev, pm_message_t state);// 设备挂起函数,当设备进入休眠状态时调用int (*resume) (struct device *dev);		// 设备恢复函数,当设备从休眠状态恢复时调用const struct attribute_group **groups;	// 驱动程序的属性组const struct dev_pm_ops *pm;// 电源管理操作集,用于定义设备的电源管理操作struct driver_private *p; 	// 驱动程序私有数据
};
struct device {struct device			*parent;struct device_private	*p;struct kobject kobj;const char		*init_name; 	/* initial name of the device */const struct device_type *type;struct mutex		mutex;		/* mutex to synchronize calls to* its driver.*/struct bus_type	*bus;			/* type of bus device is on */struct device_driver *driver;	/* which driver has allocated thisdevice */void		*platform_data;		/* Platform specific data, devicecore doesn't touch it */void		*driver_data;		/* Driver data, set and get withdev_set/get_drvdata */struct dev_links_info	links;struct dev_pm_info	power;struct dev_pm_domain	*pm_domain;#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAINstruct irq_domain	*msi_domain;
#endif
#ifdef CONFIG_PINCTRLstruct dev_pin_info	*pins;
#endif
#ifdef CONFIG_GENERIC_MSI_IRQstruct list_head	msi_list;
#endif#ifdef CONFIG_NUMAint		numa_node;	/* NUMA node this device is close to */
#endifu64		*dma_mask;	/* dma mask (if dma'able device) */u64		coherent_dma_mask;/* Like dma_mask, but foralloc_coherent mappings asnot all hardware supports64 bit addresses for consistentallocations such descriptors. */unsigned long	dma_pfn_offset;struct device_dma_parameters *dma_parms;struct list_head	dma_pools;		/* dma pools (if dma'ble) */struct dma_coherent_mem	*dma_mem; 	/* internal for coherent memoverride */
#ifdef CONFIG_DMA_CMAstruct cma *cma_area;				/* contiguous memory area for dmaallocations */
#endif/* arch specific additions */struct dev_archdata	archdata;struct device_node		*of_node; 	/* associated device tree node */struct fwnode_handle	*fwnode; 	/* firmware device node */dev_t			devt;				/* dev_t, creates the sysfs "dev" */u32				id;					/* device instance */spinlock_t			devres_lock;struct list_head	devres_head;struct klist_node	knode_class;struct class		*class;const struct attribute_group **groups;	/* optional groups */void	(*release)(struct device *dev);struct iommu_group	*iommu_group;struct iommu_fwspec	*iommu_fwspec;bool			offline_disabled:1;bool			offline:1;
};

注点:总线、驱动和设备最终都会落实为sysfs中的1个目录,因为进一步追踪代码会发现,它们实际上都可以认为是 kobject的派生类,kobject可看作是所有总线、设备和驱动的抽象基类,1个kobject对应sys中的1个目录.

总线设备和驱动中的各个attribute 直接落实为sysfs中的一个文件,attribute 会伴随着show()store()这两个函数。分别用于读写该attribute 对应的sysfs文件。

下面给出了 attribute、bus attribute、driver attribute 和 device attribute 这几个结构体的定义。

struct attribute {const char		*name;umode_t			mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOCbool			ignore_lockdep:1;struct lock_class_key	*key;struct lock_class_key	skey;
#endif
};struct bus_attribute {struct attribute	attr;ssize_t (*show)(struct bus_type *bus, char *buf);ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
};struct device_attribute {struct attribute	attr;ssize_t (*show)(struct device *dev, struct device_attribute *attr,char *buf);ssize_t (*store)(struct device *dev, struct device_attribute *attr,const char *buf, size_t count);
};struct driver_attribute {struct attribute attr;ssize_t (*show)(struct device_driver *driver, char *buf);ssize_t (*store)(struct device_driver *driver, const char *buf,size_t count);
};

事实上sysfs中的目录来源于 bus type、device_driver、device,而目录中的文件则来源attribute.

Linux内核中也定义了一些快捷方式以方便attribute的创建工作:

#define BUS_ATTR(_name, _mode, _show, _store)	\struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)
#define BUS_ATTR_RW(_name) \struct bus_attribute bus_attr_##_name = __ATTR_RW(_name)
#define BUS_ATTR_RO(_name) \struct bus_attribute bus_attr_##_name = __ATTR_RO(_name)#define DRIVER_ATTR(_name, _mode, _show, _store) \struct driver_attribute driver_attr_##_name = __ATTR(_name, _mode, _show, _store)
#define DRIVER_ATTR_RW(_name) \struct driver_attribute driver_attr_##_name = __ATTR_RW(_name)
#define DRIVER_ATTR_RO(_name) \struct driver_attribute driver_attr_##_name = __ATTR_RO(_name)
#define DRIVER_ATTR_WO(_name) \struct driver_attribute driver_attr_##_name = __ATTR_WO(_name)#define DEVICE_ATTR(_name, _mode, _show, _store) \struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
#define DEVICE_ATTR_RW(_name) \struct device_attribute dev_attr_##_name = __ATTR_RW(_name)
#define DEVICE_ATTR_RO(_name) \struct device_attribute dev_attr_##_name = __ATTR_RO(_name)
#define DEVICE_ATTR_WO(_name) \struct device_attribute dev_attr_##_name = __ATTR_WO(_name)
#define DEVICE_ULONG_ATTR(_name, _mode, _var) \struct dev_ext_attribute dev_attr_##_name = \{ __ATTR(_name, _mode, device_show_ulong, device_store_ulong), &(_var) }
#define DEVICE_INT_ATTR(_name, _mode, _var) \struct dev_ext_attribute dev_attr_##_name = \{ __ATTR(_name, _mode, device_show_int, device_store_int), &(_var) }
#define DEVICE_BOOL_ATTR(_name, _mode, _var) \struct dev_ext_attribute dev_attr_##_name = \{ __ATTR(_name, _mode, device_show_bool, device_store_bool), &(_var) }
#define DEVICE_ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store) \struct device_attribute dev_attr_##_name =		\__ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store)

比如,我们在 drivers/base/bus.c文件中可以找到这样的代码

static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store);static BUS_ATTR(drivers_probe, S_IWUSR, NULL, store_drivers_probe);
static BUS_ATTR(drivers_autoprobe, S_IWUSR | S_IRUGO,show_drivers_autoprobe, store_drivers_autoprobe);

而在 /sys/bus/platform 等里面就可以找到对应的文件

[root@100ask:/sys/bus/platform]# ls
devices  drivers  drivers_autoprobe  drivers_probe  uevent

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

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

相关文章

AI日报 - 2025年3月24日

🌟 今日概览(60秒速览) ▎🤖 AGI突破 | Lyra生物序列建模架构效率惊人 在100生物任务中达最优,推理速度提升高达12万倍 ▎💼 商业动向 | OpenAI用户破4亿,Meta与Reliance探讨AI合作 生态扩展与全…

VMware上对CentOS7虚拟机进行磁盘扩容、缩容

在VMware 17 Pro上对CentOS 7虚拟机进行磁盘扩容,同时保证原先部署的软件正常使用,可以按照以下步骤进行操作: 一、扩容 步骤一:关闭虚拟机并在VMware中扩展磁盘容量 关闭虚拟机:在VMware Workstation 17 Pro中&…

.gitignore使用指南

.gitignore使用指南 目录 什么是.gitignore为什么需要.gitignore如何创建.gitignore文件.gitignore文件的语法规则 忽略单个文件忽略目录忽略特定类型的文件不忽略特定文件或目录递归匹配 示例.gitignore文件注意事项更多特殊场景匹配规则 忽略多个特定后缀的文件忽略特定目录…

OpenCV旋转估计(3)帮助构建一个最大生成树(Maximum Spanning Tree)函数findMaxSpanningTree()

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 cv::detail::findMaxSpanningTree 是 OpenCV 中用于图像拼接工作流的一个函数,它帮助构建一个最大生成树(Maximum Spanni…

Android在kts中简单使用AIDL

Android在kts中简单使用AIDL AIDL相信做Android都有所了解,跨进程通信会经常使用,这里就不展开讲解原理跨进程通信的方式了,最近项目换成kts的方式,于是把aidl也换成了统一的方式,其中遇到了很多问题,这里…

论文阅读:2024-NAACL Semstamp、2024-ACL (Findings) k-SemStamp

总目录 大模型安全相关研究:https://blog.csdn.net/WhiffeYF/article/details/142132328 Semstamp: A semantic watermark with paraphrastic robustness for text generation https://aclanthology.org/2024.naacl-long.226/ k-SemStamp: A Clustering-Based Semantic Wate…

物化视图详解:数据库性能优化的利器

物化视图(Materialized View)作为数据库性能优化的核心手段,通过预计算和存储查询结果,显著提升了复杂查询的效率。本文将深入剖析物化视图的工作原理、应用场景及最佳实践,帮助企业在合适的场景中充分发挥其性能优势。…

快速入手:Nacos融合SpringCloud成为注册配置中心

快速入手:Nacos融合SpringCloud成为注册配置中心 前言安装Nacos项目搭建添加配置启动类添加注解运行项目服务调用RestTemplate 模式FeignClient 模式 Gateway 网关 前言 Spring Cloud是一系列框架的集合,提供了微服务架构下的各种解决方案,如…

2025年2月-3月后端go开发找工作感悟

整体感悟 目标 找工作首先要有一个目标,这个目标尽可能的明确,比如我要字节、拼多多之类的公司,还是要去百度、滴滴这样的,或者目标是创业公司。但是这个目标是会动态调整的,有可能我们的心态发生了变化,一…

Python | 如何在Pandas中删除常量列

在数据分析中,经常会遇到数据集中始终具有常量值的列(即,该列中的所有行包含相同的值)。这样的常量列不提供有意义的信息,可以安全地删除而不影响分析。 如: 在本文中,我们将探索如何使用Pyth…

5.高频加热的原理与常用集成电路介绍

一、高频加热的类型 利用高频电源加热通常由两种方法:电介质加热(被加热物体绝缘)与感应加热(被加热物体导电),详细解释如下: 电介质加热(利用高频电压的高频电场导致物体自身分子摩…

串口通信与Modbus通信的区别和联系

一、定义与定位 1‌、串口通信‌ 是物理层的硬件接口标准,用于实现设备间的‌串行数据传输‌,常见类型包括RS-232、RS-485和RS-422‌35。其功能是完成并行数据与串行信号的转换,并定义电气特性(如电平、传输速率)‌。…

Linux生产者消费者模型

Linux生产者消费者模型 Linux生产者消费者模型详解生产者消费者模型生产者消费者模型的概念生产者消费者模型的特点生产者消费者模型优点 基于BlockingQueue的生产者消费者模型基于阻塞队列的生产者消费者模型模拟实现基于阻塞队列的生产消费模型基础实现生产者消费者步调调整条…

【中文翻译】第9章-The Algorithmic Foundations of Differential Privacy

由于GitHub项目仅翻译到前5章,我们从第6章开始通过大语言模型翻译,并导出markdown格式。 大模型难免存在错漏,请读者指正。 教材原文地址:https://www.cis.upenn.edu/~aaroth/Papers/privacybook.pdf 9 差分隐私与计算复杂度 到目…

【AI大模型】搭建本地大模型GPT-NeoX:详细步骤及常见问题处理

搭建本地大模型GPT-NeoX:详细步骤及常见问题处理 GPT-NeoX是一个开源的大型语言模型框架,由EleutherAI开发,可用于训练和部署类似GPT-3的大型语言模型。本指南将详细介绍如何在本地环境中搭建GPT-NeoX,并解决过程中可能遇到的常见问题。 1. 系统要求 1.1 硬件要求 1.2 软…

Unity跨平台构建快速回顾

知识点来源:人间自有韬哥在,豆包 目录 一、发布应用程序1. 修改发布必备设置1.1 打开设置面板1.2 修改公司名、游戏项目名、版本号和默认图标1.3 修改 Package Name 和 Minimum API Level 2. 发布应用程序2.1 配置 Build Settings2.2 选择发布选项2.3 构…

低配电脑畅玩《怪物猎人:荒野》,ToDesk云电脑优化从30帧到144帧?

《怪物猎人:荒野(Monster Hunter Wilds)》自2025年正式发售以来已取得相当亮眼的成绩,仅用三天时间便轻松突破800万销量,目前顺利蝉联周榜冠军;凭借着开放世界的宏大场景和丰富的狩猎玩法,该游戏…

Flink基础简介和安装部署

文章目录 一、Flink基础简介1、什么是Flink2、Flink流处理特性3、Flink四大基石4、Flink中的角色 二、Flink集群搭建1、Local模式①上传Flink安装包②启动交互窗口③提交任务测试④访问WebUI页面查看④退出停止集群 一、Flink基础简介 1、什么是Flink Flink是⼀个分布式&#…

【2025】基于ssm+jsp的二手商城系统设计与实现(源码、万字文档、图文修改、调试答疑)

基于SSMJSP的二手商城系统设计与实现系统功能结构图: 课题背景 随着经济的发展和人们生活水平的提高,二手交易市场日益活跃。人们对于闲置物品的处理方式逐渐从传统的废品回收转变为通过二手交易平台进行再利用。这种交易模式不仅能够帮助用户节省开支&a…

幻影星空亮相CAAPA北京展 引领文旅产业升级转型

3月19日,中国游艺机游乐园协会(CAAPA)主办的2025中国(北京)国际游乐设施设备博览会及2025北京国际旅游休闲娱乐产业博览会在北京盛大启幕。在这场行业盛会上,广州卓远旗下的“幻影星空”品牌以创新性的虚拟…