Linux设备驱动——模块的构造、运行与设计 与众不同的hello world与点灯

news2024/9/20 9:45:07

编写一个Linux下的设备驱动,首先要准备好对应内核版本的内核源码树文件。

该系列的全部文章都以嵌入式系统的ARM-Linux的环境进行阐述,并以交叉编译的方式在主机Ubuntu20.04系统上编译和ARM开发板上跑测 (E2000Q、H616、或者IMX6ULL看情况交叉着使用)  关于交叉编译的配置,以往的文章中有作介绍,这里不再赘述。

驱动程序一般要执行两类任务

1.模块中的某些函数作为系统调用的一部分而执行

2.某些函数则负责中断处理

hello world模块

很多高级语言的学习,第一个程序都是打印一句“hello world”来打开编程世界。内核模块的编写虽然不是应用类的程序,但是也可以有入门级的模块程序,如下hello world模块

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>

static int __init hello_init(void)
{

	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
    return 0;
}


static void __exit hello_exit(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");

需要强调驱动程序,和应用程序不同,它不是生成可执行程序,而是生成目标代码的可执行模块

因此驱动程序的编写,不会有入口函数(main函数),但是它的生效需要module__前缀的函数来驱动。

MODULE_LICENSE("GPL")是为了告诉内核,装载驱动模块采用公共许可证;module__init()和module__exit()会调用内核的特殊宏来表示函数所扮演的角色,一个是模块入口(负责初始化和注册设备),一个是模块出口(负责关闭和清理工作)

注意:内核有自己单独的打印输出函数printk,因为它在运行时不能依赖于C库;

模块能调用printk是因为在insmod函数装载模块后,模块就连接到内核,可以访问内核的公共函数和变量。

编译并装载

注意:这里的驱动程序源文件命名需要和目标文件相同,即 hello_drv.c 配 hello_drv.o

编写Makefile文件

KERN_DIR =  /home/usr/phytium-linux-buildroot/output/build/linux-kernel-5.10_v2.1/

all:
	make -C $(KERN_DIR) M=`pwd` modules
	
clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order


obj-m += hello_drv.o

Ubuntu主机上编译,并上传到ARM开发板装载(装载也可以使用 modprobe (它可以附带地装载一些必要模块))

#host
make

#arm-Linux
sudo insmod hello_drv.ko

卸载

sudo rmmod hello_drv

列出当前装载到内核中的所有模块,并提供了其他一些信息  (通过读取 /proc/modules虚拟文件来获得这些信息)

lsmod

模块与应用程序的异同

还是那句话,要为某个特定的内核版本编译模块,则需要该特定版本对应的构造系统源代码树(KERN_DIR变量)

开发过程中的经验:如果打算编写一个驱动程序用于一般性的发布,要考虑好如何支持可能的不同处理器变种,以源代码形式及一组用于编译的脚本发布自己的驱动程序是最好的办法。

不同点

1.大多数小规模及中规模应用程序是从头到尾执行单个任务,而模块却只是预先注册自己以便服务于将来的某个请求(事件驱动的编程方式)。

2.应用程序在退出时,可以不管资源的释放或者其他的清理工作(内核会清理),但是模块的退出函数却必须仔细撤销初始化函数所做的一切,否则,在系统重新引导之前某些东西就会残留在系统中

3.应用程序可以调用它并未定义的函数,因为链接过程能够解析外部引用从而使用合适的函数库,而模块仅仅被链接到内核(没有任何函数库会和模块链接,内核模块只能使用作为内核一部分的函数)。

4.内核错误即使不会影响整个系统,但是也至少会杀死当前进程,而应用程序开发的段错误基本是没有什么危害的,而且总能跟踪到BUG。

5.应用程序运行在所谓的用户空间中,模块运行在所谓的内核空间中。(执行系统调用,会将CPU的执行模式切换到内核空间,执行系统调用的内核代码运行在进程上下文中)

这里说明一下,处理硬件中断的内核代码和进程是异步的。

6.应用程序在虚拟内存中布局,并具有一块很大的栈空间,而内核具有非常小的栈(内核是一个大的,独立的程序)

初始化与销毁的细节

对于放入module__init的称之为初始化函数

模块的初始化函数负责注册模块所提供的任何设施(模块可以注册许多不同类型的设施,对每种设施,对应有具体的内核函数用来完成注册),其中__init表明该函数仅在初始化期间使用,模块装载完成后,将该函数占用的内存释放。

设施能被注册,是因为它们能够以某种方式集成到驱动程序功能当中,大部分注册函数名字带有register__前缀。

对于放入module__exit的称之为销毁函数

模块的销毁函数在模块被移除前注销接口并向系统中返回所有资源,其中__exit修饰词表示该函数仅用于模块卸载(编译器将把该函数放在特殊特殊的ELF段中),只能在模块被卸载或者系统关闭时被调用,其他的任何用法都是错误的。

驱动开发的注意点:模块代码必须始终检查返回值,并确保所请求的操作已真正成功。模块的初始化出现错误之后,模块必须自行撤销已注册的设施(使用goto来处理)。

每次检查时,返回合适的错误编码,因为用户程序可以通过perror函数将它转换为有意义的字符串。销毁函数必须在撤销每项设施的注册之前检查它的状态。

Linux驱动也能来点灯

“点灯”,这是许多单片机教学入门的第一个程序。Linux驱动也可以点灯,以一个稍微复杂点的实现底层点灯驱动的代码来阐述这一过程。(个人概括,单片机属于用户空间驱动程序,而Linux下也可以实现用户空间驱动程序,但是那是伪驱动;我将实现内核空间驱动程序,两者各有优缺点及区别;其中比较显著的区别就是响应时间区别)

这里以NXP公司的IMX6ULL开发板 和 瑞芯微 的 ROC-RK3399-PC  开发板为例子

注意!!!!!

后面几个框架目录,更多是提供思想框架的介绍,实际的驱动开发中,更多的是使用内核子系统(内核的接口);因此,在直接操作寄存器地址,才有进行内存的映射,可以真真正正地点灯,其他均以内核打印,来模拟操作哈(不涉及真正的物理内存和虚拟内存的映射)

直接操作寄存器地址

led_drv.c  (驱动程序)

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/mutex.h>
#include <linux/wait.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <asm/io.h>

static int major;
static struct class *led_class;

/* registers */
// IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 地址:0x02290000 + 0x14
static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;

// GPIO5_GDIR 地址:0x020AC004
static volatile unsigned int *GPIO5_GDIR;

//GPIO5_DR 地址:0x020AC000
static volatile unsigned int *GPIO5_DR;

static ssize_t led_write(struct file *filp, const char __user *buf,
			 size_t count, loff_t *ppos)
{
	char val;
	int ret;
	
	/* copy_from_user : get data from app */
	ret = copy_from_user(&val, buf, 1);

	/* to set gpio register: out 1/0 */
	if (val)
	{
		/* set gpio to let led on */
		*GPIO5_DR &= ~(1<<3);
	}
	else
	{

		/* set gpio to let led off */
		*GPIO5_DR |= (1<<3);
	}
	return 1;
}

static int led_open(struct inode *inode, struct file *filp)
{
	/* enable gpio5
	 * configure gpio5_io3 as gpio
	 * configure gpio5_io3 as output 
	 */
	*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 &= ~0xf;
	*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 |= 0x5;

	*GPIO5_GDIR |= (1<<3);
	
	return 0;
}

static struct file_operations led_fops = {
	.owner		= THIS_MODULE,
	.write		= led_write,
	.open		= led_open,
};


static int __init led_init(void)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	major = register_chrdev(0, "led", &led_fops);

	/* ioremap */
	// IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 地址:0x02290000 + 0x14
	IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = ioremap(0x02290000 + 0x14, 4);
	
	// GPIO5_GDIR 地址:0x020AC004
	GPIO5_GDIR = ioremap(0x020AC004, 4);
	
	//GPIO5_DR 地址:0x020AC000
	GPIO5_DR  = ioremap(0x020AC000, 4);

	led_class = class_create(THIS_MODULE, "myled");
	device_create(led_class, NULL, MKDEV(major, 0), NULL, "myled"); /* /dev/myled */
	
	return 0;
}

static void __exit led_exit(void)
{
	iounmap(IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3);
	iounmap(GPIO5_GDIR);
	iounmap(GPIO5_DR);
	
	device_destroy(led_class, MKDEV(major, 0));
	class_destroy(led_class);
	
	unregister_chrdev(major, "led");
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

 led_test.c (应用程序)

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>


// ledtest /dev/myled on
// ledtest /dev/myled off

int main(int argc, char **argv)
{
	int fd;
	char status = 0;
	
	if (argc != 3)
	{
		printf("Usage: %s <dev> <on|off>\n", argv[0]);
		printf("  eg: %s /dev/myled on\n", argv[0]);
		printf("  eg: %s /dev/myled off\n", argv[0]);
		return -1;
	}

	// open
	fd = open(argv[1], O_RDWR);
	if (fd < 0)
	{
		printf("can not open %s\n", argv[0]);
		return -1;
	}

	// write
	if (strcmp(argv[2], "on") == 0)
	{
		status = 1;
	}

	write(fd, &status, 1);
	return 0;	
}

 Makefile文件

KERN_DIR = /home/usr/100ask_imx6ull-sdk/Linux-4.9.88

all:
	make -C $(KERN_DIR) M=`pwd` modules 
	$(CROSS_COMPILE)gcc -o led_test led_test.c 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order
	rm -f led_test

obj-m	+= led_drv.o

使用字符设备驱动相关结构体

led_drv.c(驱动相关源程序)

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

#include "led_opr.h"

#define LED_NUM 2


static int major = 0;
static struct class *led_class;
struct led_operations *p_led_opr;


#define MIN(a, b) (a < b ? a : b)


static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

/* write(fd, &val, 1); */
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	int err;
	char status;
	struct inode *inode = file_inode(file);
	int minor = iminor(inode);
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	err = copy_from_user(&status, buf, 1);

	/* 根据次设备号和status控制LED */
	p_led_opr->ctl(minor, status);
	
	return 1;
}

static int led_drv_open (struct inode *node, struct file *file)
{
	int minor = iminor(node);
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	/* 根据次设备号初始化LED */
	p_led_opr->init(minor);
	
	return 0;
}

static int led_drv_close (struct inode *node, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}


static struct file_operations led_drv = {
	.owner	 = THIS_MODULE,
	.open    = led_drv_open,
	.read    = led_drv_read,
	.write   = led_drv_write,
	.release = led_drv_close,
};


static int __init led_init(void)
{
	int err;
	int i;
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	major = register_chrdev(0, "my_led", &led_drv);


	led_class = class_create(THIS_MODULE, "my_led_class");
	err = PTR_ERR(led_class);
	if (IS_ERR(led_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "my_led");
		return -1;
	}

	for (i = 0; i < LED_NUM; i++)
		device_create(led_class, NULL, MKDEV(major, i), NULL, "my_led%d", i); /* /dev/my_led0,1,... */

	p_led_opr = get_board_led_opr();
	
	return 0;
}


static void __exit led_exit(void)
{
	int i;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	for (i = 0; i < LED_NUM; i++)
		device_destroy(led_class, MKDEV(major, i)); /* /dev/my_led0,1,... */

	device_destroy(led_class, MKDEV(major, 0));
	class_destroy(led_class);
	unregister_chrdev(major, "my_led");
}

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");

 board_demo.c (驱动相关源程序)

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>


#include "led_opr.h"

static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */	   
{
	
	printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which);
	return 0;
}

static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
{
	printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off");
	return 0;
}

static struct led_operations board_demo_led_opr = {
	.init = board_demo_led_init,
	.ctl  = board_demo_led_ctl,
};

struct led_operations *get_board_led_opr(void)
{
	return &board_demo_led_opr;
}

led_opr.h (自定义结构体头文件,关联board_demo)

#ifndef _LED_OPR_H
#define _LED_OPR_H

struct led_operations {
	int (*init) (int which); /* 初始化LED, which-哪个LED */       
	int (*ctl) (int which, char status); /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
};

struct led_operations *get_board_led_opr(void);


#endif

 led_test.c (应用程序)

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

/*
 * ./led_test /dev/my_led0 on
 * ./led_test /dev/my_led0 off
 */
int main(int argc, char **argv)
{
	int fd;
	char status;
	
	/* 1. 判断参数 */
	if (argc != 3) 
	{
		printf("Usage: %s <dev> <on | off>\n", argv[0]);
		return -1;
	}

	/* 2. 打开文件 */
	fd = open(argv[1], O_RDWR);
	if (fd == -1)
	{
		printf("can not open file %s\n", argv[1]);
		return -1;
	}

	/* 3. 写文件 */
	if (0 == strcmp(argv[2], "on"))
	{
		status = 1;
		write(fd, &status, 1);
	}
	else
	{
		status = 0;
		write(fd, &status, 1);
	}
	
	close(fd);
	
	return 0;
}

Makefile文件

KERN_DIR = /home/usr/100ask_roc-rk3399-pc/linux-4.4

all:
	make -C $(KERN_DIR) M=`pwd` modules 
	$(CROSS_COMPILE)gcc -o led_test led_test.c 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order
	rm -f led_test

# 参考内核源码drivers/char/ipmi/Makefile
# 要想把a.c, b.c编译成ab.ko, 可以这样指定:
# ab-y := a.o b.o
# obj-m += ab.o


my_led-y := led_drv.o board_demo.o
obj-m	+= my_led.o

使用资源分离结构

开发板资源

board_A_led.c   与   led_resource.h

#include "led_resource.h"

static struct led_resource board_A_led = {
	.pin = GROUP_PIN(3,1),
};

struct led_resource *get_led_resouce(void)
{
	return &board_A_led;
}
#ifndef _LED_RESOURCE_H
#define _LED_RESOURCE_H

/* GPIO3_0 */
/* bit[31:16] = group */
/* bit[15:0]  = which pin */
#define GROUP(x) (x>>16)
#define PIN(x)   (x&0xFFFF)
#define GROUP_PIN(g,p) ((g<<16) | (p))

struct led_resource {
	int pin;
};

struct led_resource *get_led_resouce(void);

#endif

开发板驱动

chip_demo_gpio.c   与   led_opr.h

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

#include "led_opr.h"
#include "led_resource.h"

static struct led_resource *led_rsc;
static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */	   
{	
	//printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which);
	if (!led_rsc)
	{
		led_rsc = get_led_resouce();
	}
	
	printk("init gpio: group %d, pin %d\n", GROUP(led_rsc->pin), PIN(led_rsc->pin));
	switch(GROUP(led_rsc->pin))
	{
		case 0:
		{
			printk("init pin of group 0 ...\n");
			break;
		}
		case 1:
		{
			printk("init pin of group 1 ...\n");
			break;
		}
		case 2:
		{
			printk("init pin of group 2 ...\n");
			break;
		}
		case 3:
		{
			printk("init pin of group 3 ...\n");
			break;
		}
	}
	
	return 0;
}

static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
{
	//printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off");
	printk("set led %s: group %d, pin %d\n", status ? "on" : "off", GROUP(led_rsc->pin), PIN(led_rsc->pin));

	switch(GROUP(led_rsc->pin))
	{
		case 0:
		{
			printk("set pin of group 0 ...\n");
			break;
		}
		case 1:
		{
			printk("set pin of group 1 ...\n");
			break;
		}
		case 2:
		{
			printk("set pin of group 2 ...\n");
			break;
		}
		case 3:
		{
			printk("set pin of group 3 ...\n");
			break;
		}
	}

	return 0;
}

static struct led_operations board_demo_led_opr = {
	.init = board_demo_led_init,
	.ctl  = board_demo_led_ctl,
};

struct led_operations *get_board_led_opr(void)
{
	return &board_demo_led_opr;
}
#ifndef _LED_OPR_H
#define _LED_OPR_H

struct led_operations {
	int (*init) (int which); /* 初始化LED, which-哪个LED */       
	int (*ctl) (int which, char status); /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
};

struct led_operations *get_board_led_opr(void);


#endif

led_drv.c   (驱动程序)  

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

#include "led_opr.h"

#define LED_NUM 2


static int major = 0;
static struct class *led_class;
struct led_operations *p_led_opr;


#define MIN(a, b) (a < b ? a : b)


static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

/* write(fd, &val, 1); */
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	int err;
	char status;
	struct inode *inode = file_inode(file);
	int minor = iminor(inode);
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	err = copy_from_user(&status, buf, 1);

	/* 根据次设备号和status控制LED */
	p_led_opr->ctl(minor, status);
	
	return 1;
}

static int led_drv_open (struct inode *node, struct file *file)
{
	int minor = iminor(node);
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	/* 根据次设备号初始化LED */
	p_led_opr->init(minor);
	
	return 0;
}

static int led_drv_close (struct inode *node, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}


static struct file_operations led_drv = {
	.owner	 = THIS_MODULE,
	.open    = led_drv_open,
	.read    = led_drv_read,
	.write   = led_drv_write,
	.release = led_drv_close,
};


static int __init led_init(void)
{
	int err;
	int i;
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	major = register_chrdev(0, "my_led", &led_drv);


	led_class = class_create(THIS_MODULE, "my_led_class");
	err = PTR_ERR(led_class);
	if (IS_ERR(led_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "my_led");
		return -1;
	}

	for (i = 0; i < LED_NUM; i++)
		device_create(led_class, NULL, MKDEV(major, i), NULL, "my_led%d", i); /* /dev/my_led0,1,... */
	
	p_led_opr = get_board_led_opr();
	
	return 0;
}


static void __exit led_exit(void)
{
	int i;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	for (i = 0; i < LED_NUM; i++)
		device_destroy(led_class, MKDEV(major, i)); /* /dev/my_led0,1,... */

	device_destroy(led_class, MKDEV(major, 0));
	class_destroy(led_class);
	unregister_chrdev(major, "my_led");
}


module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");

led_test.c  (应用程序)

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

/*
 * ./led_test /dev/my_led0 on
 * ./led_test /dev/my_led0 off
 */
int main(int argc, char **argv)
{
	int fd;
	char status;
	
	/* 1. 判断参数 */
	if (argc != 3) 
	{
		printf("Usage: %s <dev> <on | off>\n", argv[0]);
		return -1;
	}

	/* 2. 打开文件 */
	fd = open(argv[1], O_RDWR);
	if (fd == -1)
	{
		printf("can not open file %s\n", argv[1]);
		return -1;
	}

	/* 3. 写文件 */
	if (0 == strcmp(argv[2], "on"))
	{
		status = 1;
		write(fd, &status, 1);
	}
	else
	{
		status = 0;
		write(fd, &status, 1);
	}
	
	close(fd);
	
	return 0;
}

Makefile文件

KERN_DIR = /home/book/100ask_roc-rk3399-pc/linux-4.4

all:
	make -C $(KERN_DIR) M=`pwd` modules 
	$(CROSS_COMPILE)gcc -o led_test led_test.c 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order
	rm -f led_test

# 参考内核源码drivers/char/ipmi/Makefile
# 要想把a.c, b.c编译成ab.ko, 可以这样指定:
# ab-y := a.o b.o
# obj-m += ab.o


my_led-y := led_drv.o chip_demo_gpio.o board_A_led.o
obj-m	+= my_led.o

使用设备和驱动总线模型

设备

board_A_led.c  与  led_resource.h

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/platform_device.h>

#include "led_resource.h"


static void led_dev_release(struct device *dev)
{
}

static struct resource resources[] = {
        {
                .start = GROUP_PIN(3,1),
                .flags = IORESOURCE_IRQ,
                .name = "my_led_pin",
        },
        {
                .start = GROUP_PIN(5,8),
                .flags = IORESOURCE_IRQ,
                .name = "my_led_pin",
        },
};


static struct platform_device board_A_led_dev = {
        .name = "my_led",
        .num_resources = ARRAY_SIZE(resources),
        .resource = resources,
        .dev = {
                .release = led_dev_release,
         },
};

static int __init led_dev_init(void)
{
    int err;
    
    err = platform_device_register(&board_A_led_dev);   
    
    return 0;
}

static void __exit led_dev_exit(void)
{
    platform_device_unregister(&board_A_led_dev);
}

module_init(led_dev_init);
module_exit(led_dev_exit);

MODULE_LICENSE("GPL");
#ifndef _LED_RESOURCE_H
#define _LED_RESOURCE_H

/* GPIO3_0 */
/* bit[31:16] = group */
/* bit[15:0]  = which pin */
#define GROUP(x) (x>>16)
#define PIN(x)   (x&0xFFFF)
#define GROUP_PIN(g,p) ((g<<16) | (p))


#endif

驱动

chip_demo_gpio.c  与 led_opr.h  与  led_drv.h

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/platform_device.h>

#include "led_opr.h"
#include "led_drv.h"
#include "led_resource.h"

static int g_ledpins[100];
static int g_ledcnt = 0;

static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */       
{   
    //printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which);
    
    printk("init gpio: group %d, pin %d\n", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));
    switch(GROUP(g_ledpins[which]))
    {
        case 0:
        {
            printk("init pin of group 0 ...\n");
            break;
        }
        case 1:
        {
            printk("init pin of group 1 ...\n");
            break;
        }
        case 2:
        {
            printk("init pin of group 2 ...\n");
            break;
        }
        case 3:
        {
            printk("init pin of group 3 ...\n");
            break;
        }
    }
    
    return 0;
}

static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
{
    //printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off");
    printk("set led %s: group %d, pin %d\n", status ? "on" : "off", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));

    switch(GROUP(g_ledpins[which]))
    {
        case 0:
        {
            printk("set pin of group 0 ...\n");
            break;
        }
        case 1:
        {
            printk("set pin of group 1 ...\n");
            break;
        }
        case 2:
        {
            printk("set pin of group 2 ...\n");
            break;
        }
        case 3:
        {
            printk("set pin of group 3 ...\n");
            break;
        }
    }

    return 0;
}

static struct led_operations board_demo_led_opr = {
    .init = board_demo_led_init,
    .ctl  = board_demo_led_ctl,
};

struct led_operations *get_board_led_opr(void)
{
    return &board_demo_led_opr;
}

static int chip_demo_gpio_probe(struct platform_device *pdev)
{
    struct resource *res;
    int i = 0;

    while (1)
    {
        res = platform_get_resource(pdev, IORESOURCE_IRQ, i++);
        if (!res)
            break;
        
        g_ledpins[g_ledcnt] = res->start;
        led_class_create_device(g_ledcnt);
        g_ledcnt++;
    }
    return 0;
    
}

static int chip_demo_gpio_remove(struct platform_device *pdev)
{
    struct resource *res;
    int i = 0;

    while (1)
    {
        res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
        if (!res)
            break;
        
        led_class_destroy_device(i);
        i++;
        g_ledcnt--;
    }
    return 0;
}


static struct platform_driver chip_demo_gpio_driver = {
    .probe      = chip_demo_gpio_probe,
    .remove     = chip_demo_gpio_remove,
    .driver     = {
        .name   = "my_led",
    },
};

static int __init chip_demo_gpio_drv_init(void)
{
    int err;
    
    err = platform_driver_register(&chip_demo_gpio_driver); 
    register_led_operations(&board_demo_led_opr);
    
    return 0;
}

static void __exit lchip_demo_gpio_drv_exit(void)
{
    platform_driver_unregister(&chip_demo_gpio_driver);
}

module_init(chip_demo_gpio_drv_init);
module_exit(lchip_demo_gpio_drv_exit);

MODULE_LICENSE("GPL");
#ifndef _LED_OPR_H
#define _LED_OPR_H

struct led_operations {
	int (*init) (int which); /* 初始化LED, which-哪个LED */       
	int (*ctl) (int which, char status); /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
};

struct led_operations *get_board_led_opr(void);


#endif
#ifndef _LED_DRV_H
#define _LED_DRV_H

#include "led_opr.h"

void led_class_create_device(int minor);
void led_class_destroy_device(int minor);
void register_led_operations(struct led_operations *opr);

#endif /* _LED_DRV_H */

led_drv.c  ( 驱动程序 )

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

#include "led_drv.h"



static int major = 0;
static struct class *led_class;
struct led_operations *p_led_opr;


#define MIN(a, b) (a < b ? a : b)


void led_class_create_device(int minor)
{
	device_create(led_class, NULL, MKDEV(major, minor), NULL, "my_led%d", minor); /* /dev/my_led0,1,... */
}
void led_class_destroy_device(int minor)
{
	device_destroy(led_class, MKDEV(major, minor));
}
void register_led_operations(struct led_operations *opr)
{
	p_led_opr = opr;
}

EXPORT_SYMBOL(led_class_create_device);
EXPORT_SYMBOL(led_class_destroy_device);
EXPORT_SYMBOL(register_led_operations);




static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

/* write(fd, &val, 1); */
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	int err;
	char status;
	struct inode *inode = file_inode(file);
	int minor = iminor(inode);
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	err = copy_from_user(&status, buf, 1);

	/* 根据次设备号和status控制LED */
	p_led_opr->ctl(minor, status);
	
	return 1;
}

static int led_drv_open (struct inode *node, struct file *file)
{
	int minor = iminor(node);
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	/* 根据次设备号初始化LED */
	p_led_opr->init(minor);
	
	return 0;
}

static int led_drv_close (struct inode *node, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}


static struct file_operations led_drv = {
	.owner	 = THIS_MODULE,
	.open    = led_drv_open,
	.read    = led_drv_read,
	.write   = led_drv_write,
	.release = led_drv_close,
};


static int __init led_init(void)
{
	int err;
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	major = register_chrdev(0, "my_led", &led_drv); 


	led_class = class_create(THIS_MODULE, "my_led_class");
	err = PTR_ERR(led_class);
	if (IS_ERR(led_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "my_led");
		return -1;
	}
	
	return 0;
}


static void __exit led_exit(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	class_destroy(led_class);
	unregister_chrdev(major, "my_led");
}


module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");

Makefile文件

KERN_DIR = /home/usr/100ask_roc-rk3399-pc/linux-4.4

all:
	make -C $(KERN_DIR) M=`pwd` modules 
	$(CROSS_COMPILE)gcc -o led_test led_test.c 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order
	rm -f led_test


obj-m += led_drv.o chip_demo_gpio.o board_A_led.o

(注意,此时编译的可执行模块,一共有三个,最后都需要装载,才能正常让硬件被控制(三个模块各自完成部分功能))

使用设备树模型

设备树

my_led.dts    (表示在根树上创建设备节点)

#define GROUP_PIN(g,p) ((g<<16) | (p))

/ {
	my_led@0 {
		compatible = "my,led_drv";
		pin = <GROUP_PIN(3, 1)>;
	};

	my_led@1 {
		compatible = "my,led_drv";
		pin = <GROUP_PIN(5, 8)>;
	};

};

 设备

  board_A_led.c  与  led_resource.h

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/platform_device.h>

#include "led_resource.h"


static void led_dev_release(struct device *dev)
{
}

static struct resource resources[] = {
        {
                .start = GROUP_PIN(3,1),
                .flags = IORESOURCE_IRQ,
                .name = "my_led_pin",
        },
		{
				.start = GROUP_PIN(5,8),
				.flags = IORESOURCE_IRQ,
				.name = "my_led_pin",
		},
};


static struct platform_device board_A_led_dev = {
		.name = "my_led",
		.num_resources = ARRAY_SIZE(resources),
		.resource = resources,
		.dev = {
				.release = led_dev_release,
		 },
};

static int __init led_dev_init(void)
{
	int err;
	
	err = platform_device_register(&board_A_led_dev);	
	
	return 0;
}

static void __exit led_dev_exit(void)
{
	platform_device_unregister(&board_A_led_dev);
}

module_init(led_dev_init);
module_exit(led_dev_exit);

MODULE_LICENSE("GPL");
#ifndef _LED_RESOURCE_H
#define _LED_RESOURCE_H

/* GPIO3_0 */
/* bit[31:16] = group */
/* bit[15:0]  = which pin */
#define GROUP(x) (x>>16)
#define PIN(x)   (x&0xFFFF)
#define GROUP_PIN(g,p) ((g<<16) | (p))


#endif

驱动

chip_demo_gpio.c  与  led_opr.h   与  led_drv.h

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/platform_device.h>
#include <linux/of.h>

#include "led_opr.h"
#include "leddrv.h"
#include "led_resource.h"

static int g_ledpins[100];
static int g_ledcnt = 0;

static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */       
{   
    //printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which);
    
    printk("init gpio: group %d, pin %d\n", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));
    switch(GROUP(g_ledpins[which]))
    {
        case 0:
        {
            printk("init pin of group 0 ...\n");
            break;
        }
        case 1:
        {
            printk("init pin of group 1 ...\n");
            break;
        }
        case 2:
        {
            printk("init pin of group 2 ...\n");
            break;
        }
        case 3:
        {
            printk("init pin of group 3 ...\n");
            break;
        }
    }
    
    return 0;
}

static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
{
    //printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off");
    printk("set led %s: group %d, pin %d\n", status ? "on" : "off", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));

    switch(GROUP(g_ledpins[which]))
    {
        case 0:
        {
            printk("set pin of group 0 ...\n");
            break;
        }
        case 1:
        {
            printk("set pin of group 1 ...\n");
            break;
        }
        case 2:
        {
            printk("set pin of group 2 ...\n");
            break;
        }
        case 3:
        {
            printk("set pin of group 3 ...\n");
            break;
        }
    }

    return 0;
}

static struct led_operations board_demo_led_opr = {
    .init = board_demo_led_init,
    .ctl  = board_demo_led_ctl,
};

struct led_operations *get_board_led_opr(void)
{
    return &board_demo_led_opr;
}

static int chip_demo_gpio_probe(struct platform_device *pdev)
{
    struct device_node *np;
    int err = 0;
    int led_pin;

    np = pdev->dev.of_node;
    if (!np)
        return -1;

    err = of_property_read_u32(np, "pin", &led_pin);
    
    g_ledpins[g_ledcnt] = led_pin;
    led_class_create_device(g_ledcnt);
    g_ledcnt++;
        
    return 0;
    
}

static int chip_demo_gpio_remove(struct platform_device *pdev)
{
    int i = 0;
    int err;
    struct device_node *np;
    int led_pin;

    np = pdev->dev.of_node;
    if (!np)
        return -1;

    err = of_property_read_u32(np, "pin", &led_pin);

    for (i = 0; i < g_ledcnt; i++)
    {
        if (g_ledpins[i] == led_pin)
        {
            led_class_destroy_device(i);
            g_ledpins[i] = -1;
            break;
        };
    }

    for (i = 0; i < g_ledcnt; i++)
    {
        if (g_ledpins[i] != -1)
            break;
    }

    if (i == g_ledcnt)
        g_ledcnt = 0;
    
    return 0;
}

static const struct of_device_id ask100_leds[] = {
    { .compatible = "my,led_drv" },
    { },
};

static struct platform_driver chip_demo_gpio_driver = {
    .probe      = chip_demo_gpio_probe,
    .remove     = chip_demo_gpio_remove,
    .driver     = {
        .name   = "my_led",
        .of_match_table = my_leds,
    },
};

static int __init chip_demo_gpio_drv_init(void)
{
    int err;
    
    err = platform_driver_register(&chip_demo_gpio_driver); 
    register_led_operations(&board_demo_led_opr);
    
    return 0;
}

static void __exit lchip_demo_gpio_drv_exit(void)
{
    platform_driver_unregister(&chip_demo_gpio_driver);
}

module_init(chip_demo_gpio_drv_init);
module_exit(lchip_demo_gpio_drv_exit);

MODULE_LICENSE("GPL");
#ifndef _LED_OPR_H
#define _LED_OPR_H

struct led_operations {
	int (*init) (int which); /* 初始化LED, which-哪个LED */       
	int (*ctl) (int which, char status); /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
};

struct led_operations *get_board_led_opr(void);


#endif
#ifndef _LED_DRV_H
#define _LED_DRV_H

#include "led_opr.h"

void led_class_create_device(int minor);
void led_class_destroy_device(int minor);
void register_led_operations(struct led_operations *opr);

#endif /* _LED_DRV_H */

led_drv.c (驱动程序)

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

#include "led_drv.h"


static int major = 0;
static struct class *led_class;
struct led_operations *p_led_opr;


#define MIN(a, b) (a < b ? a : b)


void led_class_create_device(int minor)
{
	device_create(led_class, NULL, MKDEV(major, minor), NULL, "my_led%d", minor); /* /dev/my_led0,1,... */
}
void led_class_destroy_device(int minor)
{
	device_destroy(led_class, MKDEV(major, minor));
}
void register_led_operations(struct led_operations *opr)
{
	p_led_opr = opr;
}

EXPORT_SYMBOL(led_class_create_device);
EXPORT_SYMBOL(led_class_destroy_device);
EXPORT_SYMBOL(register_led_operations);


static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

/* write(fd, &val, 1); */
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	int err;
	char status;
	struct inode *inode = file_inode(file);
	int minor = iminor(inode);
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	err = copy_from_user(&status, buf, 1);

	/* 根据次设备号和status控制LED */
	p_led_opr->ctl(minor, status);
	
	return 1;
}

static int led_drv_open (struct inode *node, struct file *file)
{
	int minor = iminor(node);
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	/* 根据次设备号初始化LED */
	p_led_opr->init(minor);
	
	return 0;
}

static int led_drv_close (struct inode *node, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}


static struct file_operations led_drv = {
	.owner	 = THIS_MODULE,
	.open    = led_drv_open,
	.read    = led_drv_read,
	.write   = led_drv_write,
	.release = led_drv_close,
};


static int __init led_init(void)
{
	int err;
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	major = register_chrdev(0, "my_led", &led_drv);  


	led_class = class_create(THIS_MODULE, "my_led_class");
	err = PTR_ERR(led_class);
	if (IS_ERR(led_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "my_led");
		return -1;
	}
	
	return 0;
}


static void __exit led_exit(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	class_destroy(led_class);
	unregister_chrdev(major, "my_led");
}

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");

Makefile文件

KERN_DIR = /home/usr/100ask_roc-rk3399-pc/linux-4.4

all:
	make -C $(KERN_DIR) M=`pwd` modules 
	$(CROSS_COMPILE)gcc -o led_test led_test.c 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order
	rm -f led_test


obj-m += led_drv.o chip_demo_gpio.o

(此时,只需要编译驱动程序驱动组件可执行模块,设备树完成了设备模块的解析与转换)

这里面涉及了关于字符设备驱动的一些知识及Linux设备模型的一些知识,未来将在本专栏其他文章中进行详细地介绍哈。

完结撒花!!!!!!!!!

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

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

相关文章

MySQL基础练习题44-只出现一次的最大数字

目录 题目 情况一 准备数据 分析数据 情况二 准备数据 实现一 题目 单一数字 是在 MyNumbers 表中只出现一次的数字。 找出最大的 单一数字 。如果不存在 单一数字 &#xff0c;则返回 null 。 情况一 准备数据 ## 创建库 create database db; use db;## 创建表 Cre…

腾讯云短信正文模板每个变量取值最多支持6个字符出现的问题及应对方法

目录 一、参考链接二、应对方法 一、参考链接 对于长期未使用的账号及 2024 年 1 月 25 日后开通的新账号&#xff0c;腾讯云对短信正文模板的变量进行了限制&#xff1a; 验证码短信&#xff1a;每个变量取值最多支持6位纯数字。 非验证码短信&#xff1a;每个变量取值最多支…

MySQL架构设计

MySQL架构设计 查询语句&#xff1a; select * from users;返回结果为&#xff1a; 问题&#xff1a; 一条SQL查询语句是如何执行的&#xff1f; Server层 MySQL架构可以分为server层和Engine层两部分&#xff1a; 连接器&#xff08;connector&#xff09; 1. 连接管理 …

关于python中的get,set方法

一般在python面向对象中&#xff0c;每个类内都会有其所对应的属性。 而在定义属性值的时候&#xff0c;可能还得会对这些属性值进行修改和获取。为了确保代码的安全性&#xff0c;封装性和可操作性&#xff0c;我们可以使用get&#xff0c;set方式去操控。 get方法用于获取属…

贝莱德与摩根大通的最新季度持仓分析

近期&#xff0c;华尔街的两大投资巨头贝莱德和摩根大通公布了其2024年第二季度的13F报告&#xff0c;揭示了他们在投资组合上的最新动向。通过分析这些持仓数据&#xff0c;我们可以更清楚地了解这些顶级投资机构的投资策略和市场偏好。 贝莱德的科技巨头与能源投资 根据贝莱…

Trilium Notes:你的个人知识库!【送源码】

简介 Trilium Notes是一款功能全面的层次化笔记应用&#xff0c;通过树形结构构建个人知识库&#xff0c;支持Markdown编辑、网页内容剪切、笔记搜索与映射&#xff0c;并特别提供了基于画布的自由涂画功能&#xff0c;极大地提升了笔记的灵活性和创造性&#xff0c;是知识管理…

移动UI:把握好这9点,轻松设计积分兑换页面。

设计移动UI的积分兑换页面需要考虑用户体验和交互设计&#xff0c;以下是一些设计建议&#xff1a; 1. 清晰的积分信息展示&#xff1a; 在页面顶部或者中间位置展示用户当前的积分数量&#xff0c;让用户清晰地了解自己的积分情况。 2. 商品列表展示&#xff1a; 以列表或者…

[数据集][目标检测]违规撑伞检测数据集VOC+YOLO格式341张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;341 标注数量(xml文件个数)&#xff1a;341 标注数量(txt文件个数)&#xff1a;341 标注类别…

浅看MySQL数据库

有这么一句话&#xff1a;“一个不会数据库的程序员不是合格的程序员”。有点夸张&#xff0c;但是确是如此。透彻学习数据库是要学习好多知识&#xff0c;需要学的东西也是偏难的。我们今天来看数据库MySQL的一些简单基础东西&#xff0c;跟着小编一起来看一下吧。 什么是数据…

什么是树的先/中/后序遍历

前言 很久没有写c教程&#xff08;有几个月&#xff09;了&#xff0c;今天来讲讲树 为什么之前不讲树因为我不会但我现在能写出一些树的选择题了 首先我们画一棵树&#xff1a; 有些潦草 遍历是什么 我们要研究先/中/后序遍历&#xff0c;我们应该先知道什么叫遍历 遍历…

论文精要:《对静态分析缺陷报告进行聚类,以降低维护成本》

1. 前言 周末找到一篇《对静态分析缺陷报告进行聚类&#xff0c;以降低维护成本》&#xff0c;读了之后受到不少启发&#xff0c;特此将笔记整理出来。 论文出处&#xff1a; Published in: 2013 20th Working Conference on Reverse Engineering (WCRE)Date of Conference:…

疫情期间高校人员管理

TOC springboot322疫情期间高校人员管理 绪论 1.1 选题背景 当人们发现随着生产规模的不断扩大&#xff0c;人为计算方面才是一个巨大的短板&#xff0c;所以发明了各种计算设备&#xff0c;从结绳记事&#xff0c;到算筹&#xff0c;以及算盘&#xff0c;到如今的计算机&a…

C语言家教记录(六)

导语 本次授课的内容如下&#xff1a;指针&#xff0c;指针和数组 辅助教材为 《C语言程序设计现代方法&#xff08;第2版&#xff09;》 指针 指针变量 计算机按字节划分地址&#xff0c;每个地址访问一个字节 指针变量指向变量的地址&#xff0c;指的是变量第一个字节的…

MySQL InnoDB中一个update语句从执行到提交的全过程(1)

目录 一、开启事务 二、sql解析、查询计划生成 三、查询要修改的数据 1、读buffer pool的过程 buffer pool的结构组成 1&#xff09;Free List (空闲链表) 2&#xff09;LRU List (最近最少使用链表) 3&#xff09;Flush List (刷新链表) 三条链表之间的关系 2、怎么…

[Linux][OS][信号的保存和处理]

目录 信号的处理 1. 在内核中的表示 2. 相关概念 3. 信号集操作函数 4.sigprocmask 5.sigpending 信号的捕捉 重谈地址空间 信号的处理 1. 在内核中的表示 普通信号&#xff0c;多次产生只会记录一次 信号范围 [1,31]&#xff0c;每一种信号都要有自己的一种处理方式…

Java Spring|day3.SpringBoot

Spring Boot 定义 从本质上来说&#xff0c;Spring Boot就是Spring&#xff0c;它做了那些没有它你自己也会去做的Spring Bean配置。Spring Boot使用“习惯优于配置”的理念让你的项目快速地运行起来&#xff0c;使用Spring Boot很容易创建一个能独立运行、准生产级别、基于S…

SpringBoot + Hadoop + HDFS + Vue 实现一个简单的文件管理系统

1. 安装前的准备工作 1.1 更新系统并安装必要的工具 在终端中运行以下命令&#xff1a; sudo apt-get update sudo apt-get install -y ssh rsync curl1.2 安装 Java 如果系统中没有安装 Java&#xff0c;可以通过以下命令安装 OpenJDK&#xff1a; sudo apt-get install …

软件安全测试的必要性,第三方软件测试机构进行安全测试好处简析

在当前信息技术迅猛发展的时代&#xff0c;软件的安全性显得尤为重要。随着越来越多的企业依赖软件进行日常运营&#xff0c;软件漏洞和安全隐患所带来的风险也逐渐上升。因此&#xff0c;软件安全测试不再是可有可无的选择&#xff0c;而是每个企业必须考虑的关键环节。 一、…

Node之npm常用命令与package.json文件

新书速览|Vue.jsNode.js全栈开发实战-CSDN博客 《Vue.jsNode.js全栈开发实战&#xff08;第2版&#xff09;&#xff08;Web前端技术丛书&#xff09;》(王金柱)【摘要 书评 试读】- 京东图书 (jd.com) npm常用命令 npm默认与Node.js一起安装&#xff0c;可以在命令行中输入…

达梦数据库系列—49.审计功能

目录 1、打开审计 2、审计级别 系统级审计 语句级审计 对象级审计 3、审计文件管理 删除审计文件 查看审计信息 4、审计分析 审计分析工具Analyzer 审计分析工具dmaudtool 1、打开审计 0&#xff1a;关闭审计1&#xff1a;打开普通审计2&#xff1a;打开普通审计和…