Linux 驱动开发笔记--1.驱动开发的引入

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.rhkb.cn/news/36846.html

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

相关文章

跨域问题确认及处理

背景如下: 近期在做的项目中,有个奇怪的需求,需要在JSP项目中嵌套一个VUE项目,原因是:JSP项目是在运且不大方便重构的一个项目,新需求又想为了未来着想做一套单独的项目,无奈只能嵌套。 当项目开…

Qwen2.5-VL 开源视觉大模型,模型体验、下载、推理、微调、部署实战

一、Qwen2.5-VL 简介 Qwen2.5-VL,Qwen 模型家族的旗舰视觉语言模型,比 Qwen2-VL 实现了巨大的飞跃。 欢迎访问 Qwen Chat (Qwen Chat)并选择 Qwen2.5-VL-72B-Instruct 进行体验。 1. 主要增强功能 1)直观地理解事物&…

实时监控、数据分析!Web-Check构建你的网站健康检测系统实操方案

文章目录 前言1.关于Web-Check2.功能特点3.安装Docker4.创建并启动Web-Check容器5.本地访问测试6.公网远程访问本地Web-Check7.内网穿透工具安装8.创建远程连接公网地址9.使用固定公网地址远程访问 前言 在数字化运维领域,网站稳定性保障始终是开发者和运维团队的核…

为什么在外置容器时要保证打包方式是war包?

目录 1. 符合Java EE标准 2. 打包结构清晰 3. 便于部署 4. 支持热部署 5. 与Spring Boot的对比 示例:将Spring Boot应用打包为WAR文件 在传统的Java Web应用开发中,当使用外置容器(如Tomcat、Jetty等)部署应用时&#xff0c…

【大语言模型_8】vllm启动的模型通过fastapi封装增加api-key验证

背景: vllm推理框架启动模型不具备api-key验证。需借助fastapi可以实现该功能 代码实现: rom fastapi import FastAPI, Header, HTTPException, Request,Response import httpx import logging# 创建 FastAPI 应用 app FastAPI() logging.basicConfig(…

【Linux】快速上手Makeflie CMake

🦄个人主页:修修修也 🎏所属专栏:Linux ⚙️操作环境:Xshell (操作系统:Ubuntu 22.04 server 64bit) 目录 快速上手Makefile 基本结构 变量 自动变量 常用目标 快速上手CMake CMake与Makefile的关系 CMake的使用步骤 常用命令 (1) 基本配置 (2) 变量与选…

智能蔬菜收获移动平台设计(大纲)

智能蔬菜收获移动平台设计 基于视觉识别与机械臂协同的自动化采摘系统 第一章 绪论 1.1 研究背景与意义 农业自动化需求: 人力成本高、采摘效率低(尤其在温室、大棚等复杂环境)传统机械采摘易造成蔬菜损伤,缺乏柔性化能力 技…

Java 实现排序算法 TopK 问题

1. 低级排序 &#xff08;1&#xff09;冒泡排序&#xff08;Bubble Sort&#xff09; 思路&#xff1a; 每次从左到右冒泡&#xff0c;把最大的数推到最后。 public class BubbleSort {public static void bubbleSort(int[] arr) {int n arr.length;for (int i 0; i <…

函数的介绍

1.函数的概念 在C语言中也有函数的概念&#xff0c;有些翻译为&#xff1a;子程序&#xff0c;这种翻译更为准确。C语言的函数就是一个完成某项特定的任务的一小段代码。这段代码是有特殊的写法和调用方法的。 C语言的程序其实是有无数个小的函数组合而成的&#xff0c;也可以…

MES汽车零部件制造生产监控看板大屏

废话不多说&#xff0c;直接上效果 预览效果请在大的显示器查看&#xff0c;笔记本可能有点变形 MES汽车零部件制造生产监控看板大屏 纯html写的项目结构如下 主要代码分享 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UT…

JS—原型与原型链:2分钟掌握原型链

个人博客&#xff1a;haichenyi.com。感谢关注 一. 目录 一–目录二–原型三–原型链 二. 原型 什么是原型&#xff1f; 每个JavaScript对象都有一个原型&#xff0c;这个原型也是一个对象。比方说 function Person(name) {this.name name; } let person new Person(&quo…

TCP 协议

文章目录 TCP 协议简介数据包格式TCP的特性连接机制确认与重传缓冲机制全双工通信流量控制差错控制拥塞控制 端口号三次握手数据传输四次挥手抓包参考 本文为笔者学习以太网对网上资料归纳整理所做的笔记&#xff0c;文末均附有参考链接&#xff0c;如侵权&#xff0c;请联系删…

二分查找的应用

什么时候用二分查找&#xff1f; 数据具有二段性的时候 第一题&#xff1a; 题解代码&#xff1a; class Solution { public:int search(vector<int>& nums, int target) {int left 0,right nums.size()-1;while(left<right){int mid left (right-left)/2;//中…

cmake 之 CMakeLists.txt 中的函数是从哪里来的

我们都知道&#xff0c;cmake会解释执行 CMakeLists.txt 以及其他 *.cmake 脚本&#xff0c; 这里先给出一个“先验” 的知识点&#xff1a; 任何一个独立脚本或脚本函数命令的执行&#xff0c;都是通过 CPP 函数 RunListFile(...) 调用的 void cmMakefile::RunListFile(cmL…

QT 实现信号源实时采集功能支持频谱图,瀑布图显示

利用QT实现信号源实时采集功能&#xff0c;先看效果 支持双光标显示 &#xff0c;功率测量&#xff0c;带宽测量&#xff0c;载噪比测量&#xff0c;波形框选&#xff0c;水平移动等功能&#xff0c;下载链接 https://download.csdn.net/download/ZuoYueXian/90501632 实现方…

【Kafka】深入了解Kafka

集群的成员关系 Kafka使用Zookeeper维护集群的成员信息。 每一个broker都有一个唯一的标识&#xff0c;这个标识可以在配置文件中指定&#xff0c;也可以自动生成。当broker在启动时通过创建Zookeeper的临时节点把自己的ID注册到Zookeeper中。broker、控制器和其他一些动态系…

神聖的綫性代數速成例題10. N維矢量綫性運算、矢量由矢量組綫性表示、N個N維矢量相關性質

N 維矢量綫性運算&#xff1a; 設&#xff0c;是維矢量&#xff0c;是數。加法&#xff1a;。數乘&#xff1a;。 矢量由矢量組綫性表示&#xff1a; 設是n維矢量&#xff0c;若存在一組數&#xff0c;使得&#xff0c;則稱矢量可由矢量組綫性表示。 N 個 N 維矢量相關性質&…

在CentOS 7.6中安装openGauss 5.1.0 (Preview)数据库并使用Navicat进行远程连接的过程记录

部署环境 华为云Flexus应用服务器 操作系统&#xff1a;CentOS 7.6 openGauss版本&#xff1a;openGauss 5.1.0 (Preview) 参考文档 官方安装文档&#xff1a; https://docs.opengauss.org/zh/docs/5.1.0/docs/InstallationGuide/%E4%BA%86%E8%A7%A3%E5%AE%89%E8%A3%85%E6%B…

SysOM 可观测体系建设(一):万字长文解读低开销、高精度性能剖析工具livetrace

可观测性是一种通过分析系统输出结果并推断和衡量系统内部状态的能力。谈及可观测性一般包含几大功能&#xff1a;监控指标、链路追踪、告警日志&#xff0c;及 Continues Profiling 持续剖析能力。对于操作系统可观测&#xff0c;监控指标可以帮助查看各个子系统&#xff08;I…

Shell脚本学习笔记:从入门到变量(一)

前言 最近在看 Shell 脚本相关的内容&#xff0c;以下是我从入门到变量部分的整理笔记&#xff0c;内容有点多&#xff0c;但都是干货。 先从基础开始&#xff0c;再逐步深入。 一、Shell 脚本入门 1. Linux 如何控制硬件&#xff1f; Linux 靠内核操作硬件&#xff08;CP…