当前位置: 首页 > news >正文

tslib(触摸屏输入设备的轻量级库)的学习、编译及测试记录

目录

  • tslib的简介
  • tslib的源码和make及make install后得到的文件下载
  • tslib的主要功能
  • tslib的工作原理
  • tslib的核心组成部分
  • tslib的框架和核心函数分析
    • tslib的框架
    • tslib的核心函数`ts_setup()`的分析(对如何获取设备名和数据处理流程的分析)
      • 函数`ts_setup()`自身的主要代码
      • `ts_setup()`对`ts_open()`的调用
      • `ts_setup()`对`ts_config()`的调用
    • tslib的核心函数`ts_read()`的分析
  • tslib的交叉编译
  • 上板测试

tslib的简介

tslib 是一个用于 Linux 系统中触摸屏输入设备的轻量级库,特别常用于嵌入式开发。它主要提供了一组工具和 API,用于对触摸屏输入事件进行校准、过滤和处理,使得触摸屏可以更精确、更稳定地与系统交互。
应用场景如下:

  • 嵌入式 Linux 设备(如工业控制、车载系统、POS 终端)。
  • 需要轻量级触摸屏处理库的开发环境。
  • 与 Qt、SDL 等图形界面结合使用。

tslib的源码和make及make install后得到的文件下载

tslib-1.21的源码下载地址:
https://pan.baidu.com/s/1Yc1IiRqECn6SyJHI9-6Ksg?pwd=t4zj

附:tslib-1.21make及make install后得出的输入文件下载地址:
https://pan.baidu.com/s/1U6_JUJEiJLAdniZW5wLEoQ?pwd=x4w6

tslib的主要功能

  1. 校准 (Calibration)
    提供 ts_calibrate 工具,可以对触摸屏进行几何校准,解决触摸位置和显示坐标不匹配的问题。

  2. 事件过滤 (Filtering)
    提供了一系列的过滤器,比如去抖动、线性化等,保证输入事件的平滑性和准确性。

  3. 事件捕获 (Event Capture)
    捕获触摸屏输入设备(如 /dev/input/eventX/dev/tsX)的触摸事件,并转化为标准格式供上层应用使用。

  4. 兼容性强
    支持多种触摸屏控制器(例如 eGalax、Goodix 等)和多种 Linux 输入子系统。

tslib的工作原理

  1. 应用程序通过调用 tslib 的 API 读取触摸事件。
  2. tslib 内部从触摸屏设备获取原始事件。
  3. 通过过滤器链对原始事件进行处理(如校准、去抖动)。
  4. 输出处理后的事件数据供应用程序使用。

tslib的核心组成部分

  • 库文件
    提供了核心的触摸屏数据处理和 API,比如 ts_read()ts_config()

  • 工具程序

    • ts_calibrate: 校准工具,用于生成校准数据。
    • ts_test: 用于测试触摸屏事件和显示触摸点。
    • ts_print: 打印触摸事件的工具。
  • 配置文件
    通常是 /etc/ts.conf,可以配置过滤器、设备路径等。

tslib的框架和核心函数分析

tslib的框架

在这里插入图片描述
从上面的框架可以看出,tslib有三个核心函数,分别为ts_setup、ts_read、ts_read_mt。其中ts_setup依靠两个核心函数分别为ts_open和ts_config来实现。

tslib的核心函数ts_setup()的分析(对如何获取设备名和数据处理流程的分析)

函数ts_setup()自身的主要代码

我们打开源码\tslib-1.21\tests\ts_test.c,转到主函数,发现第134有如下代码:

	ts = ts_setup(NULL, 0);

我们转到函数ts_setup()的定义,如下:
位置:\tslib-1.21\src\ts_setup.c

struct tsdev *ts_setup(const char *dev_name, int nonblock)
{const char * const *defname;struct tsdev *ts = NULL;
#if defined (__linux__)char *fname = NULL;
#endif /* __linux__ */dev_name = dev_name ? dev_name : getenv("TSLIB_TSDEVICE");if (dev_name != NULL) {ts = ts_open(dev_name, nonblock);} else {defname = &ts_name_default[0];while (*defname != NULL) {ts = ts_open(*defname, nonblock);if (ts != NULL)break;++defname;}}#if defined (__linux__)if (!ts) {fname = scan_devices();if (!fname)return NULL;ts = ts_open(fname, nonblock);free(fname);}
#endif /* __linux__ *//* if detected try to configure it */if (ts && ts_config(ts) != 0) {ts_error("ts_config: %s\n", strerror(errno));ts_close(ts);return NULL;}return ts;
}

我们可以看到它有两个输入参数,一个是dev_name,另一个是nonblock
dev_name显然是表示设备名。
nonblock表示读取数据的时候是阻塞方式还是非阻塞方式,当nonblock的值为1(或非 0)时,表示非阻塞模式,当nonblock的值为0时,为阻塞方式,所以在文件ts_test.c中,是以阻塞方式打开触摸屏设置的,因为给的参数值为0嘛,这意味着从触摸屏读取数据时,如果数据尚未准备好,进程会阻塞,直到数据可用。延伸阅读:Linux系统的阻塞方式和非阻塞方式是什么意思?

在这里,参数dev_name的值传递为NULL,即空指针,那么在函数ts_setup()里有下在这一行:

dev_name = dev_name ? dev_name : getenv("TSLIB_TSDEVICE");

可见,如果有值,则用它本身的值,如果值为NULL,那么 getenv("TSLIB_TSDEVICE") 的结果将作为 dev_name 的值。
所以源码\tslib-1.21\tests\ts_test.c里会去取 getenv("TSLIB_TSDEVICE") 的结果作为设备名。函数getenv()的意思是从环境变量里获取设备名,比如这里会从环境变量TSLIB_TSDEVICE里获取设备名。

那如果既没有传递设备名,也没有设置环境变,函数ts_setup()它会像下面这样做:

static const char * const ts_name_default[] = {"/dev/input/ts","/dev/input/touchscreen","/dev/touchscreen/ucb1x00",NULL
};
	if (dev_name != NULL) {ts = ts_open(dev_name, nonblock);} else {defname = &ts_name_default[0];while (*defname != NULL) {ts = ts_open(*defname, nonblock);if (ts != NULL)break;++defname;}}

即,它会去遍历字符串数组ts_name_default中的各个成员,发现第一个不为NULL值的成员并且能够打开成功的话,即用这个成员作为设备名。

如果到这里还没有成功打开设备,在定义了宏__linux__的情况下,它会去扫描系统中的设备,代码如下:

#if defined (__linux__)if (!ts) {fname = scan_devices();if (!fname)return NULL;ts = ts_open(fname, nonblock);free(fname);}
#endif /* __linux__ */

scan_devices()函数中有下面的代码:

ndev = scandir(DEV_INPUT_EVENT, &namelist, is_event_device, alphasort);

DEV_INPUT_EVENT的宏定义如下:

#define DEV_INPUT_EVENT "/dev/input"

即它会去目录/dev/input下去扫描设备。扫描到的设备列表存放在ndev中,然后一个个去判断是设是触摸类型的设备,通过下面这句代码来判断:

		if ((ioctl(fd, EVIOCGPROP(sizeof(propbit)), propbit) < 0) ||!(propbit[BIT_WORD(INPUT_PROP_DIRECT)] &BIT_MASK(INPUT_PROP_DIRECT)))

ioctl()函数会获取到设备信息,这段代码通过查询设备属性位 INPUT_PROP_DIRECT,判断设备是否为触摸屏类型设备。如果设备支持 INPUT_PROP_DIRECT 属性,则认为是直接输入设备(如触摸屏)。否则,它可能是间接输入设备或其他设备。具体的这段代码的详解这里就不展开了。

如果扫描到某个设备是触摸型输入设备,它就会去在tslib库的层面上利用函数ts_open()去打开它,具体的代码是下面的代码:

ts_setup()ts_open()的调用

ts = ts_open(fname, nonblock);

可见,核心函数ts_setup()是依靠ts_open()函数实现的。另外,核心函数ts_setup()还要依靠函数ts_config(),那我们来看下是怎么依靠函数ts_config()的。

ts_setup()ts_config()的调用

接下来的代码:

	/* if detected try to configure it */if (ts && ts_config(ts) != 0) {ts_error("ts_config: %s\n", strerror(errno));ts_close(ts);return NULL;}

可见,设备打开之后,就开始配置,函数ts_config()的定义如下:

int ts_config(struct tsdev *ts)
{return __ts_config(ts, NULL, NULL, NULL);
}

可见,它以设备打开获得的结构体ts为输入参数,并对这个结构体ts进行配置。具体的配置是在函数__ts_config()中进行的。函数__ts_config()以“__”开头,说明它是一个内部函数,供库或框架的内部代码调用。

函数__ts_config()的定义如下:

static int __ts_config(struct tsdev *ts, char **conffile_modules,char **conffile_params, int *raw)
{char buf[BUF_SIZE], *p;FILE *f;int line = 0;int ret = 0;short strdup_allocated = 0;char *conffile;if ((conffile = getenv("TSLIB_CONFFILE")) == NULL) {conffile = strdup(TS_CONF);if (conffile) {strdup_allocated = 1;} else {ts_error("Couldn't find tslib config file: %s\n",strerror(errno));return -1;}}f = fopen(conffile, "r");if (!f) {if (strdup_allocated)free(conffile);ts_error("Couldn't open tslib config file %s: %s\n",conffile, strerror(errno));return -1;}buf[BUF_SIZE - 2] = '\0';while ((p = fgets(buf, BUF_SIZE, f)) != NULL) {char *e;char *tok;char *module_name;line++;/* Chomp */e = strchr(p, '\n');if (e)*e = '\0';/* Did we read a whole line? */if (buf[BUF_SIZE - 2] != '\0') {ts_error("%s: line %d too long\n", conffile, line);break;}#if !defined HAVE_STRSEPtok = ts_strsep(&p, " \t");#elsetok = strsep(&p, " \t");#endifdiscard_null_tokens(&p, &tok);/* Ignore comments or blank lines.* Note: strsep modifies p (see man strsep)*/if (p == NULL || *tok == '#')continue;/* Search for the option. */if (strcasecmp(tok, "module") == 0) {#if !defined HAVE_STRSEPmodule_name = ts_strsep(&p, " \t");#elsemodule_name = strsep(&p, " \t");#endifdiscard_null_tokens(&p, &module_name);if (!conffile_modules) {ret = ts_load_module(ts, module_name, p);} else {#ifdef DEBUGprintf("TSLIB_CONFFILE: module %s %s\n",module_name, p);#endifsprintf(conffile_modules[line], "%s", module_name);if (conffile_params)sprintf(conffile_params[line], "%s", p);}} else if (strcasecmp(tok, "module_raw") == 0) {#if !defined HAVE_STRSEPmodule_name = ts_strsep(&p, " \t");#elsemodule_name = strsep(&p, " \t");#endifdiscard_null_tokens(&p, &module_name);if (!conffile_modules) {ret = ts_load_module_raw(ts, module_name, p);} else {#ifdef DEBUGprintf("TSLIB_CONFFILE: module_raw %s %s\n",module_name, p);#endifsprintf(conffile_modules[line], "%s", module_name);if (conffile_params)sprintf(conffile_params[line], "%s", p);if (raw)raw[line] = 1;}} else {ts_error("%s: Unrecognised option %s:%d:%s\n",conffile, line, tok);break;}if (ret != 0) {ts_error("Couldn't load module %s\n", module_name);break;}}if (ts->list_raw == NULL) {ts_error("No raw modules loaded.\n");ret = -1;}fclose(f);if (strdup_allocated)free(conffile);return ret;
}

从这个代码中我们可以看出,我们可以使用环境变量TSLIB_CONFFILE来确定使用哪个配置文件,相关代码如下:

if ((conffile = getenv("TSLIB_CONFFILE")) == NULL) {

如果环境变量TSLIB_CONFFILE为空,那么就会使用 strdup(TS_CONF)的返回值作为配置文件,这个配置文件其实就是/etc/ts.conf
这里,我们打开tslib源码里配的\tslib-1.21\etc\ts.conf"文件,看一下:
打开它后,把被注释的去掉,没有注释的提取出来,如下:

module_raw input
module pthres pmin=1
module dejitter delta=100
module linear

其中,input、pthres、dejitter、linear都是模块名。pthres会处理input得到的数据、 dejitter会处理pthres得到的数据,linear会处理dejitter得到的数据。具体的逻辑关系可以通过分析函数__ts_config()下面的代码得到:
在这里插入图片描述
从这个关系我们可以看出,当模块类型为module_raw时,调用函数ts_load_module_raw(),当模块类型为module时,调用函数ts_load_module(),这两个被调用的函数分别如下:

int ts_load_module(struct tsdev *ts, const char *module, const char *params)
{return __ts_load_module(ts, module, params, 0);
}int ts_load_module_raw(struct tsdev *ts, const char *module, const char *params)
{return __ts_load_module(ts, module, params, 1);
}

可见,它们其实都是调用的__ts_load_module(),只是最后一个参数不一样,一个最后为0,一个最后为1,再接着看__ts_load_module(),它与这个问题相关的核心代码在:

	if (raw)ret = __ts_attach_raw(ts, info);elseret = __ts_attach(ts, info);

这里面raw的值就是其最后一个参数的值,info表示模块信息。
所以我们需要看去函数__ts_attach_raw()__ts_attach()

int __ts_attach(struct tsdev *ts, struct tslib_module_info *info)
{info->dev = ts;info->next = ts->list;ts->list = info;return 0;
}int __ts_attach_raw(struct tsdev *ts, struct tslib_module_info *info)
{struct tslib_module_info *next, *prev, *prev_list = ts->list_raw;info->dev = ts;info->next = prev_list;ts->list_raw = info;/** ensure the last item in the normal list now points to the* top of the raw list.*/if (ts->list == NULL || ts->list == prev_list) {/* main list is empty, ensure it points here */ts->list = info;return 0;}for (next = ts->list, prev = next;next != NULL && next != prev_list;next = prev->next, prev = next);prev->next = info;return 0;
}

函数__ts_attach_raw()__ts_attach()的输入参数都有一个关键结构体 tsdev

struct tsdev {int fd;char *eventpath;struct tslib_module_info *list;/* points to position in 'list' where raw reads* come from.  default is the position of the* ts_read_raw module.*/struct tslib_module_info *list_raw;unsigned int res_x;unsigned int res_y;int rotation;
};

可见,这个结构体有两个与模块信息有关的成员,分别为list和list_raw。
当处理第一个模块时,即语句:

module_raw input

时,__ts_attach_raw()会把ts->list_raw的值设为input模块,同时把ts->list的第一个值也设为input模块,然后后面的几个模块配置语句:

module pthres pmin=1
module dejitter delta=100
module linear

由于不是raw类型,所以__ts_attach()去执行相应的链接操作,说白了就是把这些模块按顺序连接起来,形成一个链表,具体来说,在ts->list_raw中存储了module_raw模块的信,而在ts->list则把所有的模块以列表的形式存储起来,示意图如下:
在这里插入图片描述
通过这个示意图可以看出,数据是一层一层地被处理和过滤,这就是tslib对数据的核心处理流程,即通过配置文件去配置对数据的处理流程。

tslib的核心函数ts_read()的分析

我们打开源码\tslib-1.21\tests\ts_test.c,转到主函数,发现第174有如下代码:

		ret = ts_read(ts, &samp, 1);

我们进入函数ts_read(),来看下它的流程:

int ts_read(struct tsdev *ts, struct ts_sample *samp, int nr)
{int result;
#ifdef DEBUGint i;
#endifresult = ts->list->ops->read(ts->list, samp, nr);
#ifdef DEBUGfor (i = 0; i < result; i++) {fprintf(stderr, "TS_READ----> x = %d, y = %d, pressure = %d\n",samp->x, samp->y, samp->pressure);samp++;}
#endifreturn result;}

关键语如下:

result = ts->list->ops->read(ts->list, samp, nr);

我们可以看到,它是调取linear模块的ops成员的read()成员函数
在这里插入图片描述
然后我们再看下ts->list->ops->read()函数的定义:
这个read()函数实际上是在文件\tslib-1.21\plugins\linear.c中定义的函数linear_read(),然后它作为结构体ops的成员函数,如下面的代码所示:

static const struct tslib_ops linear_ops = {.read		= linear_read,.read_mt	= linear_read_mt,.fini		= linear_fini,
};

在文件\tslib-1.21\plugins\linear.c的第55行,有对函数linear_read()定义的代码如下:

ret = info->next->ops->read(info->next, samp, nr_samples);

我们看到,它实际上是去调用下一个模块中成员ops中的read()函数,实际上就是dejitter模块中的read()函数,以此类推,最终是去调用模块input中的read()函数。
可以推测,模块input中的read()函数是直接去操作设备读取信息了,我们看下是不是这样,打开源文件:\tslib-1.21\plugins\input-raw.c,然后去看里面的函数ts_input_read(),发现关键代码如下:

ret = read(ts->fd, &ev, sizeof(struct input_event));

这个显然就是系统里的符合POSIX标准read()函数了, 关于这个函数的介绍,请参见我的另一篇博文 https://blog.csdn.net/wenhao_ir/article/details/144645178

tslib的交叉编译

首先把tslib的源码放到目录/home/book/usedlib下:
在这里插入图片描述
然后解压:

tar xjf tslib-1.21.tar

在这里插入图片描述
进入目录:

cd /home/book/usedlib/tslib-1.21

配置编译:

./configure --host=arm-buildroot-linux-gnueabihf --prefix=/

注意:这里一定要根据自己系统中的交叉编译器的前缀来填,可用下面的命令来测试自己的交叉编译器的前缀:

arm-buildroot-linux-gnueabihf-gcc -v

如果有结果返回,则这里--host的值该为arm-buildroot-linux-gnueabihf

在这里插入图片描述
执行make命令:

make

在这里插入图片描述
在这里插入图片描述
安装到当前目录下的子目录tmp中

make install DESTDIR=$PWD/tmp

运行之后就在tmp目录下生成了如下目录:
在这里插入图片描述
其中bin目录中是生成的可执行程序,主要是一些测试实例:
在这里插入图片描述
etc目录里的文件自然就是配置文件了,因为我的前缀设置为根目录(--prefix=/),这个得复制到开发板上的linux系统下的etc目录下,因为根据上面的分析,函数ts_config()在运行时需要使用到这个文件。
在这里插入图片描述

inlude目录里是头文件,如果你在开发别的项目时用到了这个库,那得在你的工程的include目录中放上这个头文件:
在这里插入图片描述
lib目录里就是库文件了,如果你在开发别的项目时用到了这个库,那链接器就需要用到这个库里面的相关文件。
在这里插入图片描述
在这里,我们首先要测试我们生成的二进制可执行文件是否是适用于ARM架构的,因为如果你前边的编译器前缀填错了,它并不会报错,而会去用系统中其它的编译器,比如×86架构的编译器。
运行下面的命令:

file /home/book/usedlib/tslib-1.21/tmp/bin/ts_test_mt

运行结果如下:

/home/book/usedlib/tslib-1.21/tmp/bin/ts_test_mt: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 4.9.0, not stripped

这就说明下面这条命令中,参数 --host的设置是正确的。:

./configure --host=arm-buildroot-linux-gnueabihf --prefix=/

在这里,我们把它复制到目录/usr/local/lib中,并重命为tslib,这样编译器编译别的工程时就能用到这个库了,这里得能命令复制,因为目录/usr/local/lib不是能随便写文件的,运行下面的命令:

sudo cp -rfd /home/book/usedlib/tslib-1.21/tmp/lib /usr/local/lib/tslib

在这里插入图片描述

附:tslib-1.21make及make install后得出的输入文件下载地址:
https://pan.baidu.com/s/1U6_JUJEiJLAdniZW5wLEoQ?pwd=x4w6

上板测试

把文件
/home/book/usedlib/tslib-1.21/tmp/etc/ts.conf
/home/book/usedlib/tslib-1.21/tmp/bin/ts_print_mt
复制到NFS文件中:
在这里插入图片描述

打开开发板的串口终端→打开开发板→挂载网络文件:

mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt

复制ts.conf到开发板的ect目录:

cp /mnt/ts.conf /etc/

复制文件ts_print_mt到开发板的/bin目录下:

cp /mnt/ts_print_mt /bin

在这里插入图片描述
为文件ts_print_mt添加执行权限:

chmod +x ts_print_mt

由于ts_print_mt这个文件需要动态库支持,所以我们还需要把生成的动态库复制到对应目录下:

按下面的操作复制库,先把整个make install命令生成的目录tmp复制到NFS文件中:
在这里插入图片描述
然后按下面的命令复制:

cp /mnt/tmp/lib/ts -rfd /lib
cp /mnt/tmp/lib/*so* -fd /lib

其中的参数r表示递归复制,f表示强制覆盖,d表示保留符号链接,而不是把符号链接所指向的文件进行复制。

复制完了运行可执行文件:

/bin/ts_print_mt 

在这里插入图片描述
用手指在触摸屏上划一下:
在这里插入图片描述
这样就完美运行了~

相关文章:

tslib(触摸屏输入设备的轻量级库)的学习、编译及测试记录

目录 tslib的简介tslib的源码和make及make install后得到的文件下载tslib的主要功能tslib的工作原理tslib的核心组成部分tslib的框架和核心函数分析tslib的框架tslib的核心函数ts_setup()的分析(对如何获取设备名和数据处理流程的分析)函数ts_setup()自身的主要代码ts_setup()对…...

Ubuntu vi(vim)编辑器配置一键补全main函数

1.打开对应的配置文件 vi ~/.vim/snippets/c.snippets 2.按G将光标定位到文件末尾 3.按i进入插入模式 以tab键开头插入下的内容&#xff0c;空行也要加 tab键 4.:wq保存退出 5.再打开任意一个新的 .c文件后&#xff0c;插入模式输入 main 然后按tal键就能补全了...

验证码机制

偶然间看到了验证码机制&#xff0c;顺便总结一下&#xff1a; 首先&#xff0c;验证码是从后端生成的&#xff0c;随机生成&#xff1b; 【后端永远认为前端有可能会被伪造】 1.后端调用相关的绘图第三方类库&#xff0c;或是&#xff08;平台PHP、.NET、java&#xff09;系…...

【CVE-2024-56145】PHP 漏洞导致 Craft CMS 出现 RCE

大多数开发人员都同意,与 15 年前相比,PHP 是一种更加理智、更加安全和可靠的语言。PHP5早期的不良设计已让位于更好的开发生态系统,其中包括类、自动加载、更严格的类型、更理智的语法以及一大堆其他改进。安全性也没有被忽视。 register_globals一些老读者可能还记得和的…...

使用FakeSMTP创建本地SMTP服务器接收邮件具体实现。

以下代码来自Let’s Go further节选。具体说明均为作者本人理解。 编辑邮件模版 主要包含三个template: subject&#xff1a;主题plainBody&#xff1a; 纯文本正文htmlBody&#xff1a;超文本语言正文 {{define "subject"}}Welcome to Greenlight!{{end}} {{def…...

【网络安全】逆向工程 练习示例

1. 逆向工程简介 逆向工程 (RE) 是将某物分解以了解其功能的过程。在网络安全中&#xff0c;逆向工程用于分析应用程序&#xff08;二进制文件&#xff09;的运行方式。这可用于确定应用程序是否是恶意的或是否存在任何安全漏洞。 例如&#xff0c;网络安全分析师对攻击者分发…...

Oracle Database 21c Express Edition数据库 和 Sqlplus客户端安装配置

目录 一. 前置条件二. Win10安装配置Oracle数据库2.1 数据库获取2.2 数据库安装2.3 数据库配置确认2.4 数据库访问 三. Win10配置Oracle数据库可对外访问3.1 打开文件和打印机共享3.2 开放1521端口 四. 端口与地址确认4.1 查看监听器的状态4.2 Win10查看1521端口是否被监听4.3 …...

arcgisPro将面要素转成CAD多段线

1、说明&#xff1a;正常使用【导出为CAD】工具&#xff0c;则导出的是CAD三维多线段&#xff0c;无法进行编辑操作、读取面积等。这是因为要素面中包含Z值&#xff0c;导出则为三维多线段数据。需要利用【复制要素】工具禁用M值和Z值&#xff0c;再导出为CAD&#xff0c;则得到…...

相机内外参知识

已知相机的内外参数矩阵&#xff0c;可以求得相机在世界坐标系下的原点坐标。这里需要理解几个概念&#xff1a; 内参数矩阵&#xff08;Intrinsic Matrix&#xff09;: 描述相机本身的属性&#xff0c;比如焦距、主点位置等。外参数矩阵&#xff08;Extrinsic Matrix&#xf…...

从代币角度介绍solana账户体系

1、solana 的账户概念介绍 Solana的账户体系是其区块链的核心组成部分&#xff0c;它允许数据和价值在链上存储和转移。以下是Solana账户体系的一些关键特点&#xff1a; • 账户模型&#xff1a; • 在Solana上&#xff0c;所有数据都存储在所谓的“账户”中&#xff0c;类似…...

前端引入字体文件

1. 字体下载 阿里矢量图图标库地址 https://www.iconfont.cn/&#xff0c;页面打开后选中&#xff0c;素材库 > 字体库 左侧两个标签页可以切换&#xff0c;右侧放大镜图标可以搜索自己需要的字体 字体预览区域可以自行调整进行字体预览 右上角点击字体包下载&#xff0c;下…...

qemu启动后网络怎么设置?配合qemu-system-riscv64的命令设置

QEMU启动的时候&#xff0c;可以选择组网方式&#xff0c;一般有两种选择&#xff0c;user模式和tap模式 user模式就是用NAT&#xff0c;tap模式就是用bridge网桥模式。以前也有过一次实践&#xff1a;FreeBSD RISCV 在QEME中实践-网络配置_pkg.txz: not found-CSDN博客 user…...

如何测量分辨率

一、什么是分辨率&#xff1f; 分辨率指的是分清物体细节的能力。分辨率是一个成像系统还原空间频率的能力。一些人只是简单的用分辨率去描述极限分辨率&#xff0c;但是相机在在不同的对比度的情况下还原低&#xff0c;中和高频率的能力&#xff0c;也可以显示全面综合的信息。…...

汇总贴:cocos creator

1 cocoscreator-doc-TS:目录-CSDN博客 访问节点和组件 常用节点和组件接口 创建和销毁节点 加载和切换场景 获取和设置资源 监听和发射事件 节点系统事件 缓动系统&#xff08;cc.tween&#xff09; 使用计时器 使用对象池 使用 TypeScript 脚本 模块化脚本 脚本执行顺序 全局…...

[N1CTF 2018]eating_cms

[N1CTF 2018]eating_cms 知识点 文件上传 解题 这个题感觉还好&#xff0c;知识点真心不难&#xff0c;就是全混在一起。 思路差不多挺离谱 首先看到&#xff0c;有一个登录界面&#xff0c;然后猜测有注册界面 admin注册不了&#xff0c;随便注册一个账号。 注册之后&…...

重拾设计模式--建造者模式

文章目录 建造者模式&#xff08;Builder Pattern&#xff09;概述建造者模式UML图作用&#xff1a;建造者模式的结构产品&#xff08;Product&#xff09;&#xff1a;抽象建造者&#xff08;Builder&#xff09;&#xff1a;具体建造者&#xff08;Concrete Builder&#xff…...

【机器学习】以机器学习为翼,翱翔网络安全创新苍穹

我的个人主页 我的领域&#xff1a;人工智能篇&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;&#x1f44d;点赞 收藏❤ 在数字化浪潮汹涌澎湃的当下&#xff0c;网络安全如同守护数字世界的坚固堡垒&#xff0c;其重要性不言而喻。而机器学习技术的蓬勃…...

人工智能在VR展览中扮演什么角色?

人工智能&#xff08;AI&#xff09;在VR展览中扮演着多重关键角色&#xff0c;这些角色不仅增强了用户体验&#xff0c;还为展览的组织者提供了强大的工具。 接下来&#xff0c;由专业从事VR展览制作的圆桌3D云展厅平台为大家介绍AI在VR展览中的一些主要作用&#xff1a; 个性…...

mysql,创建数据库和用户授权核心语句

一.库操作1.创建库create database if not exists 库名 default 字符集 default 校对规则2.删除库drop database if exists 库名3.修改库的,字符集,校对规则alter databse 库名 default 字符集 default 校对规则4.查看当前使用的库seclect databse();5.查看库show databases;…...

日期区间选择器插件的操作流程

我们知道&#xff0c;在开发过程中&#xff0c;为了能够在规定时间内完成项目&#xff0c;有时候我们都会使用插件来大大提高我们的开发效率&#xff0c;有些插件是可以直接拿来用&#xff0c;但是有些插件拿过来之后是需要进行修改&#xff0c;在使用插件的时候还有很多的注意…...

【WRF教程第3.2期】预处理系统 WPS详解:以4.5版本为例

预处理系统 WPS 详解&#xff1a;以4.5版本为例 WPS 嵌套域&#xff08;WPS Nested Domains&#xff09;USGS 和 MODIS 土地利用重力波拖拽方案静态数据&#xff08;Gravity Wave Drag Scheme Static Data&#xff09;1. 什么是重力波拖拽方案&#xff08;GWDO&#xff09;静态…...

深度学习的DataLoader是什么数据类型,为什么不可用来索引

在 Python 中&#xff0c;DataLoader是torch.utils.data.DataLoader类的实例对象&#xff0c;用于加载数据&#xff0c;它本身不是一种基本数据类型&#xff0c;而是一种特殊的迭代器类型&#xff0c;主要用于按批次加载数据&#xff0c;以下是其通常不可索引的原因&#xff1a…...

物理信息神经网络(PINN)八课时教案

物理信息神经网络&#xff08;PINN&#xff09;八课时教案 第一课&#xff1a;物理信息神经网络概述 1.1 PINN的定义与背景 物理信息神经网络&#xff08;Physics-Informed Neural Networks&#xff0c;简称PINN&#xff09;是一种将物理定律融入神经网络训练过程中的先进方…...

Linux setfacl 命令详解

文章目录 Linux setfacl 命令详解一、ACL 和 setfacl 简介二、基本语法三、常用操作1. 查看 ACL2. 为用户设置权限3. 为组设置权限4. 删除 ACL 条目5. 设置默认 ACL6. 递归设置 ACL 四、示例操作1. 创建示例目录和文件2. 设置 ACL3. 验证 ACL 五、注意事项六、总结 Linux setfa…...

电商环境下的财务ERP系统架构

先介绍一下自己的工作经历&#xff0c;2002年开始进入ERP实施行业&#xff0c;专注于O记EBS系统&#xff0c;正好赶上中国经济和信息化高度发展的阶段&#xff0c;先后实施过很多大国企和民企的大型ERP项目&#xff0c;在实施过程中逐渐对ERP系统的架构、模块设计有更深入的认识…...

Linux相关概念和易错知识点(25)(信号原理、操作系统的原理、volatile)

目录 1.信号的产生 &#xff08;1&#xff09;kill &#xff08;2&#xff09;raise、abort 2.对block、pending、handler表的管理 &#xff08;1&#xff09;信号集&#xff08;sigset_t&#xff09; &#xff08;2&#xff09;block表的管理 ①操作相关的函数 ②sigpr…...

线上问题——频繁 Full GC 问题的排查思路

文章目录 一、查看 GC 日志二、分析内存泄漏三、检查对象生命周期四、优化代码五、调整垃圾回收策略六、使用监控工具 一、查看 GC 日志 启用 GC 日志 在 Java 应用中&#xff0c;需要在启动参数中添加适当的参数来启用 GC 日志记录。可以使用-XX:PrintGCDetails、-XX:PrintGCD…...

《探秘 Qt Creator Manual 4.11.1》

《探秘 Qt Creator Manual 4.11.1》 一、Qt Creator 4.11.1 概述二、功能特性全解析&#xff08;一&#xff09;跨平台能力展示&#xff08;二&#xff09;代码编辑优势&#xff08;三&#xff09;版本控制集成&#xff08;四&#xff09;特定 Qt 功能呈现&#xff08;五&#…...

level2逐笔委托查询接口

沪深逐笔委托队列查询 前置步骤 分配数据库服务器 查询模板 以下是沪深委托队列查询的请求模板&#xff1a; http://<数据库服务器>/sql?modeorder_book&code<股票代码>&offset<offset>&token<token>查询参数说明 参数名类型说明mo…...

在Linux系统安装配置 MySQL 和 hive,hive配置为远程模式

前提&#xff1a;已安装配置好了Hadoop环境&#xff0c;因为hive的底层是Hadoop 1 Mysql安装 搜索Centos7自带的mariadb rpm -qa|grep mariadb 卸载mariadb rpm -e mariadb-libs-5.5.64-1.el7.x86_64 --nodeps 再搜索一次看看是否还存在 rpm -qa|grep mariadb 安装mysql 创…...