Linux第68步_旧字符设备驱动的一般模板

file_operations结构体中的函数就是我们要实现的具体操作函数。

注意:

register_chrdev()和 unregister_chrdev()这两个函数是老版本驱动使用的。现在新字符设备驱动已经不再使用这两个函数,而是使用Linux内核推荐的新字符设备驱动API函数

1、创建CharDeviceXXX.c

输入“cd /home/zgq/linux/Linux_Drivers/回车”,切换到“/home/zgq/linux/Linux_Drivers/”目录

输入“ls回车”,查看“/home/zgq/linux/Linux_Drivers/”目录

输入“mkdir CharDeviceXXX回车”,创建“CharDeviceXXX”目录

输入“ls回车”,查看“/home/zgq/linux/Linux_Drivers/”目录

输入“cd CharDeviceXXX回车”,切换到“/home/zgq/linux/Linux_Drivers/ CharDeviceXXX/”目录

输入“vi CharDeviceXXX.c回车”,打开“CharDeviceXXX.c

CharDeviceXXX.c文件如下:

#include <linux/types.h>

//数据类型重命名

//使能bool,u8,u16,u32,u64, uint8_t, uint16_t, uint32_t, uint64_t

//使能s8,s16,s32,s64,int8_t,int16_t,int32_t,int64_t

#include <linux/kernel.h>     //必须要包含的头文件

#include <linux/delay.h>

#include <linux/ide.h>

#include <linux/init.h>

#include <linux/module.h>     //必须要包含的头文件

#include <linux/string.h>     //下面要用到字符串,显然也要包含

//#include <linux/device.h>     //必须要包含的头文件

//#include <linux/fs.h>         //使能结构体"file_operations"

#define CharDeviceXXX_MAJOR   200

//定义主设备号

//静态分配设备号:在串口输入“cat/proc/devices”查询当前已用的主设备号

//然后使用一个“没有被使用的设备号”作为该设备的的主设备号

#define CharDeviceXXX_NAME   "CharDeviceXXXName"  //定义设备的名字

static char CharDeviceXXX_readbuf[100]; //读缓冲区

static char CharDeviceXXX_writebuf[100]; //写缓冲区

static char My_DataBuffer[] = {"My Data!"};

/* 打开设备 */

static int CharDeviceXXX_open(struct inode *inode, struct file *filp)

{

  /* 用户实现具体功能 */

  printk("CharDeviceXXX_open!\r\n");

  return 0;

}

/* 从设备读取数据,保存到首地址为buf的数据块中,长度为cnt个字节 */

//file结构指针变量flip表示要打开的设备文件

//buf表示用户数据块的首地址

//cnt表示用户数据的长度,单位为字节

//loff_t结构指针变量offt表示“相对于文件首地址的偏移”

static ssize_t CharDeviceXXX_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)

{

  int ret = 0;

  memcpy(CharDeviceXXX_readbuf, My_DataBuffer,sizeof(My_DataBuffer));

  //将My_DataBuffer[]中的所有数据拷贝到CharDeviceXXX_readbuf[]

  ret = copy_to_user( buf, CharDeviceXXX_readbuf, cnt );

  //将CharDeviceXXX_readbuf[]中的前cnt个字节拷贝到buf[]中

  if(ret==0) printk("Send the data to the user, and the result is ok!\r\n");

  else printk("Send the data to the user, and the result is failed!\r\n");

  return 0;

}

/* 向设备写数据,将数据块首地址为buf的数据,长度为cnt个字节,发送给用户 */

//file结构指针变量flip表示要打开的设备文件

//buf表示用户数据块的首地址

//cnt表示用户数据的长度,单位为字节

//loff_t结构指针变量offt表示“相对于文件首地址的偏移”

static ssize_t CharDeviceXXX_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)

{

  int ret = 0;

  ret = copy_from_user(CharDeviceXXX_writebuf, buf, cnt);

  //将buf[]中的前cnt个字节拷贝到CharDeviceXXX_writebuf[]中

  if(ret==0) printk("Receive the data form user , and the result is ok!\r\n");

  else printk("Receive the data form user , and the result is failed!\r\n");

  return 0;

}

/* 关闭/释放设备 */

static int CharDeviceXXX_release(struct inode *inode, struct file *filp)

{

  /* 用户实现具体功能 */

  printk("CharDeviceXXX_release!\r\n");

  return 0;

}

/*声明file_operations结构变量MyCharDevice_fops*/

/*它是指向设备的操作函数集合变量*/

const struct file_operations CharDeviceXXX_fops = {

  .owner = THIS_MODULE,

  .open = CharDeviceXXX_open,

  .read = CharDeviceXXX_read,

  .write = CharDeviceXXX_write,

  .release = CharDeviceXXX_release,

};

/*驱动入口函数 */

static int  __init CharDeviceXXX_init(void)

{

  int ret;

  ret = register_chrdev(CharDeviceXXX_MAJOR, CharDeviceXXX_NAME, &CharDeviceXXX_fops);

  //注册字符设备驱动

  //CharDeviceXXX_MAJOR为主设备号,采用宏CharDeviceXXX_NAME定义设备名字

  //CharDeviceXXX_fops是设备的操作函数集合,它是file_operations结构变量

  if (ret < 0)

  {

    pr_err("CharDeviceXXX_init is failed!!!\r\n");

    return ret;

  }

  else pr_err("CharDeviceXXX_init is ok!!!\r\n");

  return 0;

}

/*驱动出口函数 */

static void __exit CharDeviceXXX_exit(void)

{/*出口函数具体内容 */

  unregister_chrdev(CharDeviceXXX_MAJOR, CharDeviceXXX_NAME);

  //注销字符设备驱动

  //CharDeviceXXX_MAJOR为主设备号,采用宏CharDeviceXXX_NAME定义设备名字

}

module_init(CharDeviceXXX_init);

//指定CharDeviceXXX_init()为驱动入口函数

module_exit(CharDeviceXXX_exit);

//指定CharDeviceXXX_exit()为驱动出口函数

MODULE_AUTHOR("Zhanggong");//添加作者名字

MODULE_LICENSE("GPL");//LICENSE采用“GPL协议”

MODULE_INFO(intree,"Y");

//去除显示“loading out-of-tree module taints kernel.”

2、Makefile文件的一般模板

输入“vi Makefile回车

KERNELDIR := /home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31

#使用“:=”将其后面的字符串赋值给KERNELDIR

CURRENT_PATH := $(shell pwd)

#采用“shell pwd”获取当前打开的路径

#使用“$(变量名)”引用“变量的值”

obj-m := CharDeviceXXX.o

#生成“obj-m”需要依赖“CharDeviceXXX.o

build: kernel_modules

#生成“build”需要依赖“kernel_modules”

@echo $(KERNELDIR)

#输出KERNELDIR的值为“/home/zgq/linux/atk-mp1/linux/linux-5.4.31”

@echo $(CURRENT_PATH)

#输出CURRENT_PATH的值为/home/zgq/linux/Linux_Drivers/CharDeviceXXX

@echo $(MAKE)

#输出MAKE的值为make

kernel_modules:

$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

#后面的"modules"表示编译成模块

#“KERNELDIR”上面定义为“/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31”,即“指定的工作目录”

#“CURRENT_PATH”上面定义为“当前的工作目录”

#“-C $(KERNELDIR) M=$(CURRENT_PATH) ”表示将“当前的工作目录”切换到“指定的目录”中

#即切换到“/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31”。

#M表示模块源码目录

#在“make和modules”之间加入“M=$(CURRENT_PATH)”,表示切换到由“CURRENT_PATH”指定的目录中读取源码,同时将其编>译为.ko 文件

clean:

$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

#“KERNELDIR”上面定义为“/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31”,即“指定的工作目录”

#“CURRENT_PATH”上面定义为“当前的工作目录

3、创建“c_cpp_properties.json

打开VSCode,按下“Ctrl+Shift+P”,打开VSCode控制台,然后输入“C/C++:Edit Configurations(JSON)”,见下图:

打开以后会自动在“.vscode ”目录下生成一个名为“c_cpp_properties.json” 的文件,此文件默认内容如下所示:

{

    "configurations": [

        {

            "name": "Linux",

            "includePath": [

                "${workspaceFolder}/**"

            ],

            "defines": [],

            "compilerPath": "/usr/bin/gcc",

            "cStandard": "gnu11",

            "cppStandard": "gnu++14",

            "intelliSenseMode": "gcc-x64"

        }

    ],

    "version": 4

}

修改“includePath”后,“c_cpp_properties.json”文件如下:

{

    "configurations": [

        {

            "name": "Linux",

            "includePath": [

                "${workspaceFolder}/**",

"/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31",

"/home/zgq/linux/Linux_Drivers/CharDeviceXXX_1",

"/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31/arch/arm/include",

"/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31/include",

"/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31/arch/arm/include/generated"

],

            "defines": [],

            "compilerPath": "/usr/bin/gcc",

            "cStandard": "gnu11",

            "cppStandard": "gnu++14",

            "intelliSenseMode": "gcc-x64"

        }

    ],

    "version": 4

}

4、了解APP程序需要用到的相关函数

编写Linux应用程序,需要用到C库里面“和文件操作有关”的函数。

1)、open()函数

int open(const char *pathname, int flags)

指针变量pathname表示要打开的设备文件名;

flags表示“文件打开模式”:O_RDONLY表示只读模式;

O_WRONLY表示只写模式;

O_RDWR表示读写模式;

其他可选模式:O_APPEND表示每次写操作都写入文件的尾部;O_CREAT表示如果指定文件不存在,则创建这个文件;

O_EXCEL表示如果要创建的文件已经存在,则返回-1,并修改errno的值;O_TRUNC表示如果文件存在,并且以“只写或读写”方式打开,则清空文件全部内容;O_NOCTTY表示如果路径名指向终端设备,不要把这个设备用作控制终端;O_NONBLOCK表示如果路径名指向FIFO/块文字/字符文件,则把文件的打开和后继I/O设置为非阻塞;O_DSYNC表示等待物理I/O结束后再write。在不影响读取新写入的数据的前提下,不等待文件属性更新;O_RSYNC表示read等待所有写入同一区域的写操作完成后再进行;O_SYNC表示等待物理I/O结束后再write,包括更新文件属性的IO;

返回值:如果文件打开成功,则返回“文件描述符”。

在终端输入“man 2 open”可以查询open()这个函数

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

需要包含上面的头文件,才可以使用open()函数

2)、read()函数

ssize_t read(int fd, void *buf, size_t count)

fd表示要进行读操作的“文件描述符”

buf表示将读到的数据保存到“以buf为首地址的数据块”

count表示读取到的“数据长度”,单位为字节

返回值:

大于0表示读取到的字节数

0表示读到了文件的尾部

小于0表示读取失败

在终端输入“man 2 read”可以查询read()这个函数

#include <unistd.h>

需要包含上面这个头文件,才可以使用read()函数

3)、write()函数

ssize_t write(int fd, const void *buf, size_t count)

fd表示要进行写操作的“文件描述符”

buf和count表示将“以buf为首地址的数据块”,长度为count个字节,发送给用户

返回值:

大于0表示写入的字节数

0表示没有写入任何数据

小于0表示写入失败

在终端输入“man 2 write”可以查询write()这个函数

#include <unistd.h>

需要包含上面这个头文件,才可以使用write()函数

4)、close()函数

int close(int fd)

fd表示要关闭的“文件描述符”

返回值:

0表示关闭成功

小于0表示关闭失败

在终端输入“man 2 close”可以查询close()这个函数

#include <unistd.h>

需要包含上面这个头文件,才可以使用close()函数

在终端输入“man 3 memcpy”在第3节中可以查询memcpy()这个函数

#include <string.h>

需要包含上面这个头文件,才可以使用memcpy()函数

linux之man命令 (baidu.com)

man后面的数字代表的内容

1:用户在shell环境可操作的命令或执行文件;如输入“man 1 ls”就可以查询ls命令

2:系统内核可调用的函数与工具等;如输入“man 2 read”就可以查询read()函数

3:一些常用的函数(function)与函数库(library),大部分为C的函数库(libc);

;如输入“man 3 strstr”就可以查询strstr()函数

4:设备文件说明,通常在/dev下的文件

5:配置文件或某些文件格式

6:游戏(games)

7:惯例与协议等,如Linux文件系统,网络协议,ASCII code等说明

8:系统管理员可用的管理命令

9:跟kernel有关的文件。

5、编写CharDeviceXXX_APP.c

#include "stdio.h"

#include "unistd.h"

#include "sys/types.h"

#include "sys/stat.h"

#include "fcntl.h"

#include "stdlib.h"

#include "string.h"

static char usrdataBuffer[] = {"usr data!"};

//例如当argv[]是指向输入参数“./CharDeviceXXX /dev/chrdevbase 1”

/*

参数argc: argv[]数组元素个数

参数argv[]:是一个指针数组

返回值: 0 成功;其他 失败

*/

int main(int argc, char *argv[])

{

  int fd, retvalue;

  char *filename;

  char readbuf[100], writebuf[100];

  if(argc != 3)

  {

    printf("Error Usage!\r\n");

    return -1;

  }

  //argv[]是指向输入参数“./CharDeviceXXXApp” “/dev/CharDeviceXXX” “1”

  filename = argv[1];

  //argv[1]指向字符串“/dev/CharDeviceXXX”

  fd = open(filename, O_RDWR);

  //以“读写方式”打开“/dev/CharDeviceXXX”文件,若成功则fd为“文件描述符”

  //fd=0表示标准输入流; fd=1表示标准输出流;fd=2表示错误输出流;

  if(fd < 0)

  {

    printf("Can't open file %s\r\n", filename);

    return -1;

  }

  else printf("open file: %s\r\n", filename);

  if(atoi(argv[2]) == 1)

  {

    retvalue = read(fd, readbuf, 50);

    //fd表示要进行读操作的“文件描述符”

    //readbuf表示将读到的数据保存到“以readbuf为首地址的数据块”

    //50表示读取到的“数据长度”,单位为字节

    //返回值大于0表示读取到的字节数

    //返回值为0表示读到了文件的尾部

    //返回值小于0表示读取文件失败

    if(retvalue < 0)//读取文件失败

    {

      printf("read file %s failed!\r\n", filename);

    }

    else//读取文件成功

    {

      printf("read data:%s\r\n",readbuf);

    }

  }

  if(atoi(argv[2]) == 2)

  {

memcpy(writebuf, usrdataBuffer, sizeof(usrdataBuffer));

//将usrdataBuffer[]中所有数据拷贝到writebuf[]

    retvalue = write(fd, writebuf, 50);

    //fd=2表示要进行写操作的“文件描述符”

    //将writebuf[]中前50个字节发送给用户

    //返回值大于0表示写入的字节数;

    //返回值等于0表示没有写入任何数据;

    //返回值小于0表示写入失败

    if(retvalue < 0)

    {

      printf("write file %s failed!\r\n", filename);

    }

  }

  /* 关闭设备 */

  retvalue = close(fd);

  //fd表示要关闭的“文件描述符”

  //返回值等于0表示关闭成功

  //返回值小于0表示关闭失败

  if(retvalue < 0)

  {

    printf("Can't close file %s\r\n", filename);

    return -1;

  }

  return 0;

}

6、编写脚本文件

CharDeviceXXX_APP.sh文件内容如下:

#!/bin/sh

file="CharDeviceXXX_APP"

if [ -s $file ]

#"-s $file"检测文件是否为空(文件大小是否大于0),不为空返回 true

then

rm CharDeviceXXX_APP

echo clear CharDeviceXXX_APP

else

arm-none-linux-gnueabihf-gcc CharDeviceXXX_APP.c -o CharDeviceXXX_APP

echo CharDeviceXXX_APP

fi

7、测试

输入“make回车”编译生成“CharDeviceXXX.ko

输入“chmod 777 CharDeviceXXX_APP.sh回车

将“CharDeviceXXX_APP.sh”赋可执行权限

输入“./CharDeviceXXX_APP.sh回车”,编译生成“CharDeviceXXX_APP”文件

输入“sudo cp CharDeviceXXX.ko CharDeviceXXX_APP /home/zgq/linux/nfs/rootfs/lib/modules/5.4.31/ -f回车

启动开发板,从网络下载程序

输入“root

输入“cd /lib/modules/5.4.31/回车

切换到“/lib/modules/5.4.31/”目录

注意:“lib/modules/5.4.31/在虚拟机中是位于“/home/zgq/linux/nfs/rootfs/”目录下,但在开发板中,却是位于根目录中

输入“ls”查看“CharDeviceXXX.ko和CharDeviceXXXApp”是否存在

输入“depmod”,驱动在第一次执行时,需要运行“depmod”

输入“modprobe CharDeviceXXX.ko”,加载“CharDeviceXXX.ko”模块

输入“lsmod”查看有哪些驱动在工作

输入“mknod /dev/CharDeviceXXX c 200 0回车

//“mknod”是创建节点命令

//“/dev/CharDeviceXXX”表示节点文件

//“c”表示CharDeviceXXX是个字符设备

//“200”表示设备的主设备号

//“0”表示设备的次设备号

输入“ls /dev/CharDeviceXXX  -l回车”,发现节点文件“/dev/CharDeviceXXX

输入“./CharDeviceXXX_APP /dev/CharDeviceXXX 1回车”执行读操作

输入“./CharDeviceXXX_APP /dev/CharDeviceXXX 2回车”执行写操作

输入“cat /proc/devices回车”查询设备号

操作完成,则执行卸载模块:

输入“rmmod CharDeviceXXX.ko”,卸载“CharDeviceXXX.ko”模块

注意:输入“rmmod CharDeviceXXX”也可以卸载“CharDeviceXXX.ko”模块

输入“lsmod”查看有哪些驱动在工作。

至此,CharDeviceXXX设备的整个驱动就验证完成了,驱动工作正常。

8、修改Makefile文件如下:

KERNELDIR := /home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31

#使用“:=”将其后面的字符串赋值给KERNELDIR

CURRENT_PATH := $(shell pwd)

#采用“shell pwd”获取当前打开的路径

#使用“$(变量名)”引用“变量的值”

obj-m := CharDeviceXXX.o

#生成“obj-m”需要依赖“CharDeviceXXX.o”

ko: kernel_modules

#生成“build”需要依赖“kernel_modules”

@echo $(KERNELDIR)

#输出KERNELDIR的值为“/home/zgq/linux/atk-mp1/linux/linux-5.4.31”

@echo $(CURRENT_PATH)

#输出CURRENT_PATH的值为/home/zgq/linux/Linux_Drivers/CharDeviceXXX”

@echo $(MAKE)

#输出MAKE的值为make

kernel_modules:

$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

#后面的"modules"表示编译成模块

#“KERNELDIR”上面定义为“/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31”,即“指定的工作目录”

#“CURRENT_PATH”上面定义为“当前的工作目录”

#“-C $(KERNELDIR) M=$(CURRENT_PATH) ”表示将“当前的工作目录”切换到“指定的目录”中

#即切换到“/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31”。

#M表示模块源码目录

#在“make和modules”之间加入“M=$(CURRENT_PATH)”,表示切换到由“CURRENT_PATH”指定的目录中读取源码,同时将其编>译为.ko 文件

clean_ko:

$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

#“KERNELDIR”上面定义为“/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31”,即“指定的工作目录”

#“CURRENT_PATH”上面定义为“当前的工作目录

app:

arm-none-linux-gnueabihf-gcc CharDeviceXXX_APP.c -o CharDeviceXXX_APP

clean_app:

rm CharDeviceXXX_APP

9、使用Makefile编译

输入“rm CharDeviceXXX_APP.sh回车

输入“make clean_ko回车”,清除CharDeviceXXX.*

输入“make ko回车”,编译生成CharDeviceXXX.ko

输入“make clean_app回车”,清除CharDeviceXXX_APP

输入“make app回车”,编译生成CharDeviceXXX_APP

输入“ls -l回车

输入“sudo cp CharDeviceXXX.ko CharDeviceXXX_APP /home/zgq/linux/nfs/rootfs/lib/modules/5.4.31/ -f回车

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

zerotier局域网组建 笔记

背景 家里的windows电脑&#xff1a;home-win10-pc 家里的windows电脑上vmware运行的ubuntu虚拟机&#xff1a;home-ubuntu-vm 公司的mac电脑&#xff1a;company-mac-pc 由于xxx需求&#xff0c;需要组建一个局域网&#xff0c;前东家都是用的zerotier&#xff0c;出于路径依…

代码学习记录10

随想录日记part10 t i m e &#xff1a; time&#xff1a; time&#xff1a; 2024.03.03 主要内容&#xff1a;今天的主要内容是深入了解数据结构中栈和队列&#xff0c;并通过三个 l e e t c o d e leetcode leetcode 题目深化认识。 20. 有效的括号1047. 删除字符串中的所有…

FL Studio怎么分轨导出音频文件 FL Studio轨道怎么合并 音乐编曲软件推荐 FL Studio下载

对于现在的编曲人来说&#xff0c;熟练掌握各类编曲软件已经是硬性要求了。掌握编曲软件的使用方法需要我们付出一些学习时间&#xff0c;例如编曲软件中各个轨道的拆分与合并等等&#xff0c;这些都是非常实用的编曲软件使用技巧。今天我就以FL Studio举例&#xff0c;向大家展…

轻薄蓝牙工牌室内人员定位应用

在现代化企业管理的背景下&#xff0c;轻薄蓝牙工牌人员定位应用逐渐崭露头角&#xff0c;成为提升企业效率和安全性的重要工具。本文将从轻薄蓝牙工牌的定义、特点、应用场景以及未来发展趋势等方面&#xff0c;对其进行全面深入的探讨。 一、轻薄蓝牙工牌的定义与特点 轻薄…

今日arXiv最热大模型论文:哈工深新研究发现!无需额外资源,SelectIT方法助力大语言模型精准调优

在当今的人工智能领域&#xff0c;大语言模型&#xff08;LLMs&#xff09;已经成为了研究的热点&#xff0c;它们在理解指令和解决复杂问题方面展现出了令人印象深刻的能力。然而&#xff0c;要想进一步提升这些模型的性能&#xff0c;指令调优&#xff08;Instruction Tuning…

Material UI 5 学习01-按钮组件

Material UI 5 学习01-按钮组件 一、安装Material UI二、 组件1、Button组件1、基础按钮2、variant属性3、禁用按钮4、可跳转的按钮5、disableElevation属性6、按钮的点击事件onClick 2、Button按钮的颜色和尺寸1、Button按钮的颜色2、按钮自定义颜色3、Button按钮的尺寸 3、图…

量化交易日记 基础概念篇

联系方式 17710158550 NBEATS (Neural Basis Expansion Analysis for Time Series)、NHiTS (Neural Hierarchical Interpolation for Time Series Forecasting)、LSTNet (Long Short-Term Memory Network)、TCN (Temporal Convolutional Network)、Transformer、DeepAR (DeepAR…

TikTok(字节跳动)的新人工智能Boximator

AI 视频生成器最近占据了科技头条新闻&#xff0c;特别是在 OpenAI 宣布推出Sora之后&#xff0c;Sora 是他们的第一个视频模型&#xff0c;可以通过简单的文本提示生成令人惊叹的 AI 视频。 如今&#xff0c;制作 TikTok 的公司字节跳动也加入了这一行动。他们创建了Boximato…

FPGA AXI4总线操作教程

AXI&#xff08;Advanced Extensible Interface&#xff09;总线是一种高性能、低延迟的片上系统&#xff08;SoC&#xff09;接口标准&#xff0c;广泛应用于现代数字系统设计中。它允许不同的硬件组件以高效、可靠的方式进行数据传输和控制。本教程将介绍AXI总线的基本操作和…

卧室装修干货|榻榻米的4种类型及优缺点。福州中宅装饰,福州装修

卧室想要做榻榻米设计&#xff0c;不知道如何下手&#xff0c;这篇文章一定要看&#xff1a;常见榻榻米的类型有哪些&#xff1f;这些类型分别有哪些优缺点呢? 榻榻米是一种传统的日本床铺设计&#xff0c;近年来在现代室内设计中越来越受欢迎。它以低矮的床垫和简洁的线条为…

004-执行上下文事件循环

执行上下文&事件循环 1、执行上下文2、执行上下文类型3、执行上下文的生命周期4、示例说明5、事件循环机制6、宏任务7、微任务8、同步任务、宏任务、微任务9、代码执行顺序 - 示例 &#x1f4a1; Tips&#xff1a;用于说明 浏览器 对 JavaScript 执行顺序&#xff0c;涉及知…

Unity UGUI之Scrollbar基本了解

Unity的Scrollbar组件是用于在UI中创建滚动条的组件之一。滚动条通常与其他可滚动的UI元素&#xff08;如滚动视图或列表&#xff09;一起使用&#xff0c;以便用户可以在内容超出可见区域时滚动内容。 以下是Scrollbar的基本信息和用法: 1、创建 在Unity的Hierarchy视图中右…

Debian篇——系统安装在SD卡上如何调整系统分区大小

背景&#xff1a;我的SD卡是128G的&#xff0c;开发商安装好系统后&#xff0c;我发现SD的系统分区才8.9G空间&#xff08;剩下的108G未分区&#xff09;&#xff0c;不够使用&#xff0c;于是需要调整系统分区的大小。 1.查看系统盘挂载情况 df -h 2.查看系统盘在哪个分区 …

解决java: 无法访问javax.servlet.ServletException

问题 在对历往项目工具类总结和归纳更新过程中&#xff0c;common模块在compile编译过程中遇到了“Error java: 无法访问javax.servlet.ServletException 找不到javax.servlet.ServletException的类文件”这个报错问题。 IDE使用的是idea2021。 解决方法 pom中增加如下依赖&…

十七、IO流

IO 目录 一、IO流的概述IO流的分类 二、基本流2.1字节流2.2 字节输出流写出数据的三种方式2.3 换行和续写2.4 字节输入流2.5 文件拷贝2.6 IO流中不同JDK版本捕获异常的方式2.7 字符集详解2.7.1 ASCII字符集2.7.2 GBK字符集2.7.3 Unicode字符集 2.8 为什么会有乱码2.9 Java中的编…

python之海龟绘图

海龟绘图&#xff08;turtle&#xff09;是一个Python内置的绘图库&#xff0c;也被称为“Turtle Graphics”或简称“Turtles”。它采用了一种有趣的绘图方式&#xff0c;模拟一只小海龟在屏幕上爬行&#xff0c;而小海龟爬行的路径就形成了绘制的图形。这种绘图方式最初源自20…

某资产管理系统打点过程中的免杀经历

上周&#xff0c;被扔过来单位内部的一个链接&#xff0c;让渗透一下&#xff0c;本以为三下五除二很快就能测完&#xff0c;没想到在对抗杀软时费了一番功夫&#xff0c;再加上杂七杂八的事儿&#xff0c;经过了一个星期才测完(&#xff03;&#xffe3;&#xff5e;&#xff…

API(接口) | 软件组件之间信息交互的“桥梁”

Hi&#xff0c;大家好&#xff0c;我是半亩花海。本文主要从 API 的定义、包含、用途和其他方面来简单地介绍 API&#xff08;接口&#xff09; ——软件组件之间信息交互的“桥梁”。 目录 一、什么是 API&#xff1f; 二、 API 中所包含哪些&#xff1f; 补充 三、API 可…

SQL server内存问题排查方案

前言 由于昨晚线上服务器数据库突然访问数据缓慢&#xff0c;任务管理里面SQL server进程爆满等等&#xff0c;重大事故的排查拟写解决方案。 整体思路 查询数据库请求连接&#xff1a;排查连接池是否占满查询数据库请求量&#xff1a;排查数据是否存在反复查询查询数据库阻…

Mysql 学习(十五)redo 日志

redo 日志 什么是redo日志&#xff1f;在说这个之前我们先来想一个场景&#xff0c;在访问磁盘的页面之前&#xff0c;我们会先把页面缓存到Buffer Pool之后&#xff0c;才会访问。写页面的时候也会先将buffer pool中的页面修改之后&#xff0c;然后在某个时机才会刷新到磁盘中…