七种Linux设备驱动模型之——Device

2023-11-16

前言

Linux将所有的设备统一抽象为struct device结构, 同时将所有的驱动统一抽象为struct device_driver结构。这样设计之后就方便驱动开发工程师编写驱动,只需要将具体的设备包含struct device结构,具体的驱动包含struct device_driver结构。最终会调用device_register和driver_register将驱动和设备注册到系统,表现出来就是在sys目录的device和driver目录下。本小节先分析device结构,以及相关API,以及如何注册到系统中,以及提供给上层的sys接口。

数据结构

Linux将所有的设备统一抽象为struct device结构。定义在<linux/device.h>

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 this
					   device */
	void		*platform_data;	/* Platform specific data, device
					   core doesn't touch it */
	void		*driver_data;	/* Driver data, set and get with
					   dev_set/get_drvdata */
	struct dev_pm_info	power;
	struct dev_pm_domain	*pm_domain;

#ifdef CONFIG_PINCTRL
	struct dev_pin_info	*pins;
#endif

#ifdef CONFIG_NUMA
	int		numa_node;	/* NUMA node this device is close to */
#endif
	u64		*dma_mask;	/* dma mask (if dma'able device) */
	u64		coherent_dma_mask;/* Like dma_mask, but for
					     alloc_coherent mappings as
					     not all hardware supports
					     64 bit addresses for consistent
					     allocations 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 mem
					     override */
#ifdef CONFIG_DMA_CMA
	struct cma *cma_area;		/* contiguous memory area for dma
					   allocations */
#endif
	/* arch specific additions */
	struct dev_archdata	archdata;

	struct device_node	*of_node; /* associated device tree node */
	struct acpi_dev_node	acpi_node; /* associated ACPI 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;

	bool			offline_disabled:1;
	bool			offline:1;
};

device结构体比较复杂,同时其中还包含了dma, numa, pintrl, pm相关的知识,本小节不做过多谈论。不过该结构体的注释是相当的详细,可以参考。

parent: 代表设备的parent节点,通常parent是bus或者controller。 如果此parent为NULL,则此设备就是顶层设备。

p: 代表device的私有数据的指针,指针类型为device_private。

kobj: kobject结构,用于层级关系。

init_name: 设备对象的名称,出现在sys目录下。

type: 指向device_type结构,代表了设备的特殊的信息。

mutex: 同步操作。

bus: 设备所属的总线。

driver: 设备所对应的驱动。

platfrorm_data: 设备所对应的平台数据。

driver_data: 保存设备所对于驱动的数据,一般使用get/set函数。

power/pm_domain: 电源管理相关的内容。

of_node: 该设备对应的设备树结构。

devt: 设备号,由主设备号和次设备号组成。

id: 设备索引号。

devres_head: 设备资源管理的链表。用于将设备使用的资源用链表管理。

class: 设备所属的class。

group: 设备默认的属性。

设备相关函数

  • devices_init

此函数主要是初始化devices_kset,以及初始化所有的dev_kset

int __init devices_init(void)
{
	devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);   
	if (!devices_kset)
		return -ENOMEM;
	dev_kobj = kobject_create_and_add("dev", NULL);
	if (!dev_kobj)
		goto dev_kobj_err;
	sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
	if (!sysfs_dev_block_kobj)
		goto block_kobj_err;
	sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
	if (!sysfs_dev_char_kobj)
		goto char_kobj_err;

	return 0;

 char_kobj_err:
	kobject_put(sysfs_dev_block_kobj);
 block_kobj_err:
	kobject_put(dev_kobj);
 dev_kobj_err:
	kset_unregister(devices_kset);
	return -ENOMEM;
}

内核中每一个设备都是struct device结构,同时内核将所有的设备使用devices_kset管理。同时为了统一方便管理又将设备分为block和char设备,生成的内核对象分别为sysfs_dev_block_kobj和sysfs_dev_char_kobj。当然这两个内核模块都是在dev内核模块之下的。

这个函数的操作实现最后表现到sys文件目录下,分别为/sys/devices, /sys/dev, /sys/dev/char, /sys/dev/block。

  • device_initialize(用于初始化一个device)
void device_initialize(struct device *dev)
{
	dev->kobj.kset = devices_kset;
	kobject_init(&dev->kobj, &device_ktype);
	INIT_LIST_HEAD(&dev->dma_pools);
	mutex_init(&dev->mutex);
	lockdep_set_novalidate_class(&dev->mutex);
	spin_lock_init(&dev->devres_lock);
	INIT_LIST_HEAD(&dev->devres_head);
	device_pm_init(dev);
	set_dev_node(dev, -1);
}

主要是设置设备所属的kset,设置设备所属kset的ktype,初始化设备资源管理的链表,以及设备电源管理初始化。

  • device_add(添加一个设备到系统中)
int device_add(struct device *dev)
{
	struct device *parent = NULL;
	struct kobject *kobj;
	struct class_interface *class_intf;
	int error = -EINVAL;

	dev = get_device(dev);                                  //增加此设备的引用计数
	if (!dev)
		goto done;

	if (!dev->p) {
		error = device_private_init(dev);            ---------------A
		if (error)
			goto done;
	}

	/*
	 * for statically allocated devices, which should all be converted
	 * some day, we need to initialize the name. We prevent reading back
	 * the name, and force the use of dev_name()
	 */
	if (dev->init_name) {
		dev_set_name(dev, "%s", dev->init_name);
		dev->init_name = NULL;
	}

	/* subsystems can specify simple device enumeration */
	if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
		dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);

	if (!dev_name(dev)) {                                                  //设置device的name域,如果设置失败退出。
		error = -EINVAL;                                                   
		goto name_error;
	}                                              

	pr_debug("device: '%s': %s\n", dev_name(dev), __func__);

	parent = get_device(dev->parent);                                      //增加设备parent的引用计数
	kobj = get_device_parent(dev, parent);                                 //设置设备parent的内核对象,建立设备在sys下的层次结构。
	if (kobj)
		dev->kobj.parent = kobj;

	/* use parent numa_node */
	if (parent)
		set_dev_node(dev, dev_to_node(parent));

	/* first, register with generic layer. */
	/* we require the name to be set before, and pass NULL */
	error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);             //调用kobject_add将设备添加到sys中
	if (error)
		goto Error;

	/* notify platform of device entry */
	if (platform_notify)                                                 
		platform_notify(dev); 

	error = device_create_file(dev, &dev_attr_uevent);                 //创建设备的uevent属性
	if (error)
		goto attrError;

	if (MAJOR(dev->devt)) {
		error = device_create_file(dev, &dev_attr_dev);             //如果主设备号存在,创建dev属性
		if (error)
			goto ueventattrError;

		error = device_create_sys_dev_entry(dev);
		if (error)
			goto devtattrError;

		devtmpfs_create_node(dev);                                  //调用devtmpfs,自动创建设备节点
	}

	error = device_add_class_symlinks(dev);                             //创建链接文件
	if (error)
		goto SymlinkError;
	error = device_add_attrs(dev);                                      //添加设备的属性
	if (error)
		goto AttrsError;
	error = bus_add_device(dev);                                        //将此设备添加到一条总线上
	if (error)
		goto BusError;
	error = dpm_sysfs_add(dev);
	if (error)
		goto DPMError;
	device_pm_add(dev);                                                  //将设备添加到pm core链表中,会在suspend中使用到

	/* Notify clients of device addition.  This call must come
	 * after dpm_sysfs_add() and before kobject_uevent().
	 */
	if (dev->bus)
		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,    //使用通知链同时有新设备添加到bus中
					     BUS_NOTIFY_ADD_DEVICE, dev);

	kobject_uevent(&dev->kobj, KOBJ_ADD);                     //使用uevent通知上层,这时候就会使用到device_uevent_ops中的函数
	bus_probe_device(dev);                                    //匹配bus下的设备与驱动
	if (parent)
		klist_add_tail(&dev->p->knode_parent,             //如果parent存在,将dev添加到parent链表中
			       &parent->p->klist_children);

	if (dev->class) {                                         //如果class存在,则将dev添加到class链表中
		mutex_lock(&dev->class->p->mutex);
		/* tie the class to the device */
		klist_add_tail(&dev->knode_class,
			       &dev->class->p->klist_devices);

		/* notify any interfaces that the device is here */
		list_for_each_entry(class_intf,
				    &dev->class->p->interfaces, node)
			if (class_intf->add_dev)
				class_intf->add_dev(dev, class_intf);
		mutex_unlock(&dev->class->p->mutex);
	}
}

A: 如果设备的device_private不存在,就重现分配一个

int device_private_init(struct device *dev)
{
	dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL);                         //分配一个device_private结构
	if (!dev->p)
		return -ENOMEM;
	dev->p->device = dev;                                                  //设置device_private的device结构
	klist_init(&dev->p->klist_children, klist_children_get,         
		   klist_children_put);                                       //初始化设备下所有children链表
	INIT_LIST_HEAD(&dev->p->deferred_probe);
	return 0;
}

B: 主要分析下如何将设备添加到一条总线上,这是核心。

int bus_add_device(struct device *dev)
{
	struct bus_type *bus = bus_get(dev->bus);
	int error = 0;

	if (bus) {
		pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));
		error = device_add_attrs(bus, dev);
		if (error)
			goto out_put;
		error = device_add_groups(dev, bus->dev_groups);
		if (error)
			goto out_groups;
		error = sysfs_create_link(&bus->p->devices_kset->kobj,
						&dev->kobj, dev_name(dev));
		if (error)
			goto out_id;
		error = sysfs_create_link(&dev->kobj,
				&dev->bus->p->subsys.kobj, "subsystem");
		if (error)
			goto out_subsys;
		klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
	}
	return 0;
}

如果此设备不属于任何总线,直接就返回,否则添加设备属性,创建链接文件,将此bus下的设备添加到klist_devices链表中。

C: 关于总线先设备与驱动的匹配是设备驱动模型的核心

void bus_probe_device(struct device *dev)
{
	struct bus_type *bus = dev->bus;
	struct subsys_interface *sif;
	int ret;

	if (!bus)                               //如果不存在总线,直接返回
		return;

	if (bus->p->drivers_autoprobe) {       //是否总线支持自动匹配
		ret = device_attach(dev);
		WARN_ON(ret < 0);
	}

	mutex_lock(&bus->p->mutex);           
	list_for_each_entry(sif, &bus->p->interfaces, node)
		if (sif->add_dev)
			sif->add_dev(dev, sif);            
	mutex_unlock(&bus->p->mutex);
}

一般总线都会支持自动匹配,当然可以修改driver_autoprobe的值,然后调用device_attach进行匹配。

int device_attach(struct device *dev)
{
	int ret = 0;

	device_lock(dev);                      //因为此操作只能运行一个dev进行,需要互斥保护。
	if (dev->driver) {                     //如果设备存在对应的driver,说明已经进行了匹配,只需要调用device_bind_driver在sys中建立关系。
		if (klist_node_attached(&dev->p->knode_driver)) {
			ret = 1;
			goto out_unlock;
		}
		ret = device_bind_driver(dev);
		if (ret == 0)
			ret = 1;
		else {
			dev->driver = NULL;
			ret = 0;
		}
	} else {
		ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);      //如果driver不存在,则需要遍历dev所在bus下的所有driver
		pm_request_idle(dev);
	}
out_unlock:
	device_unlock(dev);
	return ret;
}

当遍历driver下过程中,最终会调用到__device_attach函数。

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);
}

函数match最终使用的匹配是

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

根据bus是否存在match函数,如果存在调用bus的match函数,如果不存在,直接返回1。如果匹配成功之后就会调用driver_probe_device进行绑定,最终会调用really_probe函数。

static int really_probe(struct device *dev, struct device_driver *drv)
{
	int ret = 0;
	int local_trigger_count = atomic_read(&deferred_trigger_count);

	atomic_inc(&probe_count);
	pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
		 drv->bus->name, __func__, drv->name, dev_name(dev));
	WARN_ON(!list_empty(&dev->devres_head));

	dev->driver = drv;                                                 //设置设备的driver变量

	/* If using pinctrl, bind pins now before probing */
	ret = pinctrl_bind_pins(dev);
	if (ret)
		goto probe_failed;

	if (driver_sysfs_add(dev)) {                                       
		printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
			__func__, dev_name(dev));
		goto probe_failed;
	}

	if (dev->bus->probe) {
		ret = dev->bus->probe(dev);                    //如果bus的probe存在,则调用bus的probe函数
		if (ret)
			goto probe_failed;
	} else if (drv->probe) {                               //否则调用驱动的probe,这下知道驱动的probe函数是如何调用的。
		ret = drv->probe(dev);
		if (ret)
			goto probe_failed;
	}

	driver_bound(dev);                                     //将设备所属的驱动加入到驱动链表中,使用通知链发出BOUND信息。
	ret = 1;
	pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
		 drv->bus->name, __func__, dev_name(dev), drv->name);
	goto done;

probe_failed:               //probe失败之后,就会清空设备资源,设备driver设置为NULL,从sys中移除dev建立的driver链接。
	devres_release_all(dev);
	driver_sysfs_remove(dev);
	dev->driver = NULL;
	dev_set_drvdata(dev, NULL);

	if (ret == -EPROBE_DEFER) {
		/* Driver requested deferred probing */
		dev_info(dev, "Driver %s requests probe deferral\n", drv->name);
		driver_deferred_probe_add(dev);
		/* Did a trigger occur while probing? Need to re-trigger if yes */
		if (local_trigger_count != atomic_read(&deferred_trigger_count))
			driver_deferred_probe_trigger();
	} else if (ret != -ENODEV && ret != -ENXIO) {
		/* driver matched but the probe failed */
		printk(KERN_WARNING
		       "%s: probe of %s failed with error %d\n",
		       drv->name, dev_name(dev), ret);
	} else {
		pr_debug("%s: probe of %s rejects match %d\n",
		       drv->name, dev_name(dev), ret);
	}
	/*
	 * Ignore errors returned by ->probe so that the next driver can try
	 * its luck.
	 */
	ret = 0;
}
  • device_register(注册一个设备到系统中是上述两个函数的结合体)
  • device_unregister(将一个设备从系统中注销)
void device_unregister(struct device *dev)
{
	pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
	device_del(dev);
	put_device(dev);
}

此函数分为两步走,第一步从系统中移除此设备,第二步将设备的引用计数减去1。主要的注销操作在device_del中实现,也就是register的反操作。

void device_del(struct device *dev)
{
	struct device *parent = dev->parent;
	struct class_interface *class_intf;

	/* Notify clients of device removal.  This call must come
	 * before dpm_sysfs_remove().
	 */
	if (dev->bus)      //如果bus总线存在,调用通知链删除设备
		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
					     BUS_NOTIFY_DEL_DEVICE, dev);
	dpm_sysfs_remove(dev); //电源管理函数,关掉设备电源
	if (parent)            //如果有父设备,将当前设备从父设备所属链表删除
		klist_del(&dev->p->knode_parent);
	if (MAJOR(dev->devt)) {     //如果主设备号存在, 动态删除设备节点,移除设备的属性
		devtmpfs_delete_node(dev);
		device_remove_sys_dev_entry(dev);
		device_remove_file(dev, &dev_attr_dev);
	}
	if (dev->class) {   //如果设备所属的class存在,移除设备的链接,调用remove_dev做移除,从class链表中删除该设备
		device_remove_class_symlinks(dev);

		mutex_lock(&dev->class->p->mutex);
		/* notify any interfaces that the device is now gone */
		list_for_each_entry(class_intf,
				    &dev->class->p->interfaces, node)
			if (class_intf->remove_dev)
				class_intf->remove_dev(dev, class_intf);
		/* remove the device from the class list */
		klist_del(&dev->knode_class);
		mutex_unlock(&dev->class->p->mutex);
	}
	device_remove_file(dev, &dev_attr_uevent);    //移除设备的uevent属性
	device_remove_attrs(dev);            
	bus_remove_device(dev);                       //从总线中移除设备,以及电源管理等。
	device_pm_remove(dev);
	driver_deferred_probe_del(dev);

	/* Notify the platform of the removal, in case they
	 * need to do anything...
	 */
	if (platform_notify_remove)
		platform_notify_remove(dev);
	if (dev->bus)
		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
					     BUS_NOTIFY_REMOVED_DEVICE, dev);
	kobject_uevent(&dev->kobj, KOBJ_REMOVE);
	cleanup_device_parent(dev);
	kobject_del(&dev->kobj);              //删除设备的内核模块
	put_device(parent);
}

设备属性

linux中使用device_attribute结构体表示一个设备的属性

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);
};

通常使用to_dev_attr宏定义得到device_attribute对象

#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)

同时为了方便定义设备的属性,内核提供了一系列相关的宏定义,用于初始化设备的属性。

#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)

关于设备属性的调用过程,最终会调用到设备的show和store函数中,具体的流程分析可见Linux设备驱动模型-Ktype

static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,
			     char *buf)
{
	struct device_attribute *dev_attr = to_dev_attr(attr);
	struct device *dev = kobj_to_dev(kobj);
	ssize_t ret = -EIO;

	if (dev_attr->show)
		ret = dev_attr->show(dev, dev_attr, buf);
	if (ret >= (ssize_t)PAGE_SIZE) {
		print_symbol("dev_attr_show: %s returned bad count\n",
				(unsigned long)dev_attr->show);
	}
	return ret;
}

static ssize_t dev_attr_store(struct kobject *kobj, struct attribute *attr,
			      const char *buf, size_t count)
{
	struct device_attribute *dev_attr = to_dev_attr(attr);
	struct device *dev = kobj_to_dev(kobj);
	ssize_t ret = -EIO;

	if (dev_attr->store)
		ret = dev_attr->store(dev, dev_attr, buf, count);
	return ret;
}

static const struct sysfs_ops dev_sysfs_ops = {
	.show	= dev_attr_show,
	.store	= dev_attr_store,
};

设备类型

在include/linux/device.h文件存在这样的结构体:

struct device_type {
	const char *name;
	const struct attribute_group **groups;
	int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
	char *(*devnode)(struct device *dev, umode_t *mode,
			 kuid_t *uid, kgid_t *gid);
	void (*release)(struct device *dev);

	const struct dev_pm_ops *pm;
};

此结构代表设备类型。通常一个Bus下会存在各种设备的,比如:disks, mouse, event等。而此结构就表明此设备是何种类型的设备。

name: 代表设备的名称,在上报event的时候,会通过DEVTYPE设置设备的类型名称。

groups: 代码设备的属性,在添加设备的属性的时候,如果存在设备类型,也会添加设备类型的属性。

uevent: 在新增一个设备时,通过该函数上报设备类型的uevent。

devnode: 在创建一个设备节点的时候会使用到,获取设备的信息。

release: 在设备释放时候,如果存在设备类型会调用到。

原文地址:Linux设备驱动模型-Device - 阅读清单 - 腾讯云开发者社区-腾讯云(版权归原文作者所有,侵权留言联系删除)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

七种Linux设备驱动模型之——Device 的相关文章

  • Nasm 打印到下一行

    我用 nasm Assembly 编写了以下程序 section text global start start Input variables mov edx inLen mov ecx inMsg mov ebx 1 mov eax 4
  • Mono 和 WebRequest 速度 - 测试

    在 mono 4 6 2 linux 中 我注意到 wget 下载文件的速度与webclient DownloadString 所以我做了一个小测试来调查 为什么 wget 明显比 C 快 根据我自己的实验 使用 wget 下载 手动读取文
  • 无法为 Python 3.4 创建工作虚拟环境

    I 安装Python 3 4 2 https docs python org 3 using unix html building python和我的 Linux Mint 17 1 中的 Virtualenv 12 0 5 然后我尝试创建
  • 我在哪里可以学习如何使 C++ 程序与操作系统 (Linux) 交互

    我是一个 C 初学者 我想创建与操作系统交互的小程序 使用 Kubuntu Linux 到目前为止 我还没有找到任何教程或手册来让 C 与操作系统交互 在 PHP 中 我可以使用命令 exec 或反引号运算符来启动通常在控制台中执行的命令
  • 对 sf:: 的未定义引用

    我想用 C 制作 GUI 应用程序 发现 SFML 是一个不错的选择 幸运的是 我使用的是 Linux 所以 SFML 2 4 已经安装在我的系统上 所以我开始搜索一些教程并找到了一个制作简单窗口的教程 但是当我运行代码时 出现错误 提示未
  • 使用 sed 将 old-link-url 替换为 new-link-url

    我正在 bash 中编写一个脚本 将 old link url 替换为 new link url 我的问题是 sed 由于斜杠而无法替换 url 如果我只输入一些文字就可以了 my code sed e s old link new lin
  • 变量作为 bash 数组索引?

    bin bash set x array counter 0 array value 1 array 0 0 0 for number in array do array array counter array value array co
  • 如何使用 bash 脚本关闭所有终端,在每个终端中有效地按 Ctrl+Shift+Q

    我经常打开许多终端 其中一些正在运行重要的进程 例如服务器 而另一些则没有运行任何东西并且可以关闭 如果您按 重要 则会弹出确认提示Cntrl Shift Q在其中 如下所示 我想要一个 bash 脚本 它可以关闭所有终端 但将 重要 终端
  • ARM 系统调用的接口是什么?它在 Linux 内核中的何处定义?

    我读过有关 Linux 中的系统调用的内容 并且到处都给出了有关 x86 架构的描述 0x80中断和SYSENTER 但我无法追踪 ARM 架构中系统调用的文件和进程 任何人都可以帮忙吗 我知道的几个相关文件是 arch arm kerne
  • 在中断时获取 current->pid

    我正在Linux调度程序上写一些东西 我需要知道在我的中断到来之前哪个进程正在运行 当前的结构可用吗 如果我在中断处理程序中执行 current gt pid 我是否可以获得我中断的进程的 pid 你可以 current gt pid存在并
  • 如何阅读shell命令的源代码?

    我想阅读编写linux命令的实际源代码 我已经获得了一些使用它们的经验 现在我认为是时候与我的机器进行更深层次的交互了 我在这里找到了一些命令http directory fsf org wiki GNU http directory fs
  • 如何从“git log”中查看 Git 中的特定版本?

    My git log显示为 enter code here git trial git log commit 4c5bc66ae50780cf8dcaf032da98422aea6e2cf7 Author king lt email pro
  • 通过 SSH 将变量传递给远程脚本

    我正在通过 SSH 从本地服务器在远程服务器上运行脚本 首先使用 SCP 复制该脚本 然后在传递一些参数时调用该脚本 如下所示 scp path to script server example org another path ssh s
  • 如何在 Linux 中使用单行命令获取 Java 版本

    我想通过单个命令获取 Linux 中的 Java 版本 我是 awk 的新手 所以我正在尝试类似的事情 java version awk print 3 但这不会返回版本 我将如何获取1 6 0 21从下面的Java版本输出 java ve
  • UDP 广播发送失败:在 Linux 2.6.30 上“网络无法访问”

    我用udp广播写了一个程序 代码段如下 struct sockaddr in broadcast addr socklen t sock len sizeof broadcast addr bzero broadcast addr sock
  • Xenomai 中的周期性线程实时失败

    我正在创建一个周期性线程 它在模拟输出上输出方波信号 我正在使用 Xenomai API 中的 Posix Skin 和 Analogy 我使用示波器测试了代码的实时性能 并查看了方波信号 频率为 1kHz 的延迟 我应该实现 250us
  • 使用命令行将 MediaWiki 维基文本格式转换为 HTML

    我倾向于编写大量文档 因此 MediaWiki 格式对我来说很容易理解 而且比编写传统 HTML 节省了我很多时间 然而 我也写了一篇博客 发现一直从键盘切换到鼠标来输入正确的 HTML 标签会增加很多时间 我希望能够使用 Mediawik
  • 是否有可能在linux中找到包含特定文本的文件?

    考虑这种情况 我在文件夹 Example 下有很多文件 如果我需要找到一个包含特定短语 如 Class Example 的文件 我该如何使用 Linux shell 来做到这一点 linux中有类似 定位 的函数可以做到这一点吗 Thank
  • 无关的库链接

    我有一个可能有点愚蠢的问题 因为我很确定我可能已经知道答案了 假设你有静态库A 动态共享库B和你的linux下的程序C 假设库 A 调用库 B 中的函数 并且您的程序调用库 A 中的函数 现在假设 C 在 A 中调用的所有函数都不使用 B
  • 为 Linux 安装 R 包时出错

    我试图在 R 3 3 上安装一个名为 rgeos 的包 但是当我输入 install packages rgeos 但它返回给我以下错误 其他包也会发生同样的情况 但不是所有包 gt installing source package rg

随机推荐

  • 获取Android设备唯一标识码

    概述 有时需要对用户设备进行标识 所以希望能够得到一个稳定可靠并且唯一的识别码 虽然Android系统中提供了这样设备识别码 但是由于Android系统版本 厂商定制系统中的Bug等限制 稳定性和唯一性并不理想 而通过其他硬件信息标识也因为
  • Simulink代码生成(二)——代码生成时模型的配置方法及操作流程

    Simulink代码生成 二 代码生成时模型的配置方法及操作流程 文章目录 Simulink代码生成 二 代码生成时模型的配置方法及操作流程 一 模型 二 代码生成设置 1 步长选择 2 系统目标文件设置 3 生成代码打开测试报告 4 保存
  • 为什么如今这么多人讨论网络安全?

    网络安全如今备受讨论 跟各种经济政治的关系是分不开的 并且变得更加复杂多变 网络安全的发展前景更可观 很多安全企业也开始积极寻求各类网络风险的防范方案和数据隐私保护技术 当今世界形势的变化 以及各种因素的不断影响 全球网络安全问题再不断提升
  • Qt扫盲-QSS概述

    QSS概述 一 概述 二 详细 一 概述 QSS 其实是Qt样式表 Qt样式表是Qt界面的一种强大的机制 除了通过继承QStyle已经可以实现的功能外 它还允许您自定义窗口组件的外观 Qt样式表的概念 术语和语法很大程度上受到HTML层叠样
  • sql server 提取汉字、数字和字母的sql server方法

    sql server 提取汉字 数字 字母的方法 提取数字 if object id dbo get number2 is not null drop function dbo get number2 go create function
  • QT学习(五)——从子窗口传来多个信号(带参数的自定义信号)

    同样是两个窗口 主窗口与副窗口 给副窗口自定义两个重载的信号 传给主窗口处理 void mySignals 信号可以重载 void mySignals int QString 由被关联的按钮发送消息 并送出两条消息给主窗口 emit myS
  • 常用C语言文件操作

    1 fopen 使用fopen需要引用头文件stdio h 函数声明如下 FILE fopen const char pathname const char mode 这里要多多关心的是第二个参数mode 关系到我们对文件操作的权限 这里做
  • 提升KNN的运行效率

    20221005 引言 KNN算法是一种 懒惰 算法 在模型训练过程 仅仅是将数据存储到快速查询的数据结构中 在测试阶段会通过进行距离计算来输出结果 那么当数据集比较大的时候 一方面内存要求会提升 另一方面在计算的时间也会增大 之前的时候
  • vue实现文字水印效果

    vue文件代码
  • Java自学路线(超全超详细)—初学者零基础版Ⅰ

    Java 对于第一次见到它的人来说 不知道它是什么东西 可能看起来是个单词 可是通过网络翻译却没办法给它一个中文定义 但是 在计算机领域中 它是一门面向对象的编程语言 那么问题来了 有人对于 面向对象的编程语言 这个词组并不理解 在此 作出
  • 出现'MySQL Daemon failed to start‘解决方法

    方法千万条 备份第一条 运行 service mysqld start 重启数据库总是会出现如下提示 MySQL Daemon failed to start Starting mysqld FAILED 的提示 如果直接输入 mysql
  • Ubuntu/linux c开发(6)内存泄露

    写好个服务程序 短期测试没啥问题 准备跑长时间的 结果 前两天正常 第三天突然涨了100多M 这感觉 爽飞了 这里说下Ubuntu中内存泄露检测工具 Valgrind 安装和使用连接如下 链接 Valgrind安装使用 这里大概说下统计结果
  • 神经网络量化

    前言 神经网络在图像 语音识别等领域使用越来越广泛 大部分实时性要求不高的服务都可以部署在云上 然而还是有不少模型需要在计算能力有限的可移动设备上快速运行 如人脸解锁 拍照视频的实时处理等 一般训练的模型采用的都是32位浮点数 考虑到大部分
  • 第四讲 赋予网页样式

    文科编程系列课程 Web开发 第四讲 赋予网页样式 目录 引言 1 大小 1 1 长度单位 1 1 1 px 像素 1 1 2 百分比 1 2 宽高 1 1 1 宽度 1 1 2 高度 1 1 3 边框 2 颜色 2 1 颜色的三种表示形式
  • 毕业设计:自主开发的害虫识别系统--文档附源码

    基于yolov5多目标检测算法的农业害虫识别查询系统 设计文档 目标问题与意义价值 研究意义 本项目能够及时准确地识别农业害虫的种类 是害虫准确测报和合理防治的前提 传统的害虫识别方法主要依赖个人的专业经验进行辨别 或参考书本 网络上的文字
  • Qt5(一)编写Qt多窗口程序

    本文作者 小嗷 微信公众号 aoxiaoji 吹比QQ群 736854977 链接 https f600lt github io archives 摘要 这篇开始将从基础知识点开始一步一步QT到QT项目 原因就是读者说 QT类很多看不懂 这
  • 如何抵御ddos攻击-免费防御方法分享

    网站遭受DDOS攻击不要怕 今天来跟大家聊聊防御ddos攻击 顺便分享一些防御ddos攻击比较使用的方法 如果你是学生也不要紧 这里可以给你分享免费的防御方法 针对ddos攻击 我们升级服务器带宽配置是不起作用的 因为大部分攻击都是来自海外
  • 文心千帆为你而来

    1 前言 3月16号百度率先发布了国内第一个人工智能大语言模型 文心一言 文心一言的发布在业界引起了不小的震动 而文心一言的企业服务则由文心千帆大模型平台提供 文心千帆大模型平台是百度智能云打造出来的一站式大模型开发与应用平台 提供包括文心
  • 【面试题】说一下promise的理解

    一 什么是Promise ES6 异步编程的一种解决方案 比传统的方案 回调函数和事件 更加的合理和强大 大家都知道传统解决异步编程用的是回调函数套回调函数 简称回调地域 以前用JQuery的朋友应该是相当熟悉了 维护起来很难搞 回调地域
  • 七种Linux设备驱动模型之——Device

    前言 Linux将所有的设备统一抽象为struct device结构 同时将所有的驱动统一抽象为struct device driver结构 这样设计之后就方便驱动开发工程师编写驱动 只需要将具体的设备包含struct device结构 具