网站域名备案与不备案的区别/nba球队排名
frameworks 之getEvent指令
- 指令解析
- 源码追溯
- 源码解析
- 1.解析参数
- 2.初始化ufds数组
- 3.添加到poll 并做对应处理
通过 getEvent 可以识别按键基本命令和里面的关键信息
涉及到的类如下
- system/core/toolbox/toolbox.c
- system/core/toolbox/tools.h
- system/core/toolbox/getevent.c
指令解析
通过 getEvent -h 可打印出相关的指令帮助,对应的指令注释如下。输出的数字都为16进制。
houji:/ $ getevent -help
Usage: getevent [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-d] [-p] [-i] [-l] [-q] [-c count] [-r] [device]-t: show time stamps // 打印时间戳-n: don't print newlines // 不换行-s: print switch states for given bits // 打印指定位的开关状态-S: print all switch states // 打印所有开关状态-v: verbosity mask (errs=1, dev=2, name=4, info=8, vers=16, pos. events=32, props=64) // 根据 mask 值显示相关信息,执行后会一直显示上报数据,详细掩码(错误=1,dev=2,名称=4,信息=8,错误=16,位置事件=32,道具=64)默认显示 dev| name| info| vers = 30-d: show HID descriptor, if available // 如果设备可用,显示设备隐藏的描述信息-p: show possible events (errs, dev, name, pos. events) // 显示设备支持的事件类型和编码方式-i: show all device info and possible events // 显示设备的所有信息和支持的事件,比 -p 显示更多信息-l: label event types and names in plain text // 以文本形式输出事件类型和名称,比 -t 更清楚直观-q: quiet (clear verbosity mask) // 以文本形式输出事件类型和名称,比 -t 更清楚直观-c: print given number of events then exit // 打印固定数量的事件并退出-r: print rate events are received // 显示事件上报速率
其中 -v 打印输入设备的详细信息,默认为 默认显示 dev| name| info| vers = 30。
emulator_x86_64:/ # getevent -v
add device 1: /dev/input/event0bus: 0019vendor 0000product 0001version 0000name: "Power Button"location: "LNXPWRBN/button/input0"id: ""version: 1.0.1
-l 打印的信息 分别对应 type code value。
emulator_x86_64:/ # getevent -l
/dev/input/event3: EV_ABS ABS_MT_TRACKING_ID 00000000
/dev/input/event3: EV_ABS ABS_MT_POSITION_X 000031af
/dev/input/event3: EV_ABS ABS_MT_POSITION_Y 00004447
参数可以组合使用,一次性查看需要的触摸屏信息,常用的命令组合为 getevent -ltr
emulator_x86_64:/ # getevent -ltr
[ 1384.965200] /dev/input/event3: EV_ABS ABS_MT_TRACKING_ID 00000000
源码追溯
可以执行, 则表示 system/bin 有对应的脚本。所以可以通过执行对应的打印 查看文件。通过命令 ls -l | grep event 过滤对应的关键字可以看到 该事件通过 软链接(前面开头为l开头即为软链接)。链接到 toolbox。
emulator_x86_64:/system/bin # ls -l | grep event
lrwxr-xr-x 1 root shell 7 2024-08-17 22:51 getevent -> toolbox
-rwxr-xr-x 1 root shell 3925 2024-03-18 00:26 mm_events
lrwxr-xr-x 1 root shell 6 2024-08-17 22:51 sendevent -> toybox
lrwxr-xr-x 1 root shell 4 2024-08-17 22:51 ueventd -> init
回到源码文件,一般工具类 在 system 目录下,可以先通过该目录查找。执行 find -name toolbox。可以看到在该**./core/toolbox** 文件夹下。
跳转到该文件夹下,查看对应的文件打印,查看对应的Bp文件。包含以下文件,可以看出入口为
toolbox.c 文件夹。
cc_defaults {...srcs: ["toolbox.c","getevent.c","getprop.cpp","modprobe.cpp","setprop.cpp","start.cpp",],...
}
对应的 main 入口如下,该方法通过读取参数,遍历数组 ,然后判断名称和命令是否相等,相等就执行对应的 func 方法。对应 tools[] 数组 在文件前面定义,定义了名词和执行的方法。
会根据 name 拼接对应的执行方法。如 getEvent 拼接完为 getevent_main。相关数组定义在 tools.h 头文件里面。
// system/core/toolbox/tools.c
#define TOOL(name) int name##_main(int, char**); // 拼接对应的执行方法名称
#include "tools.h"
#undef TOOL
// 定义对应的数组,数组内容来自 tools.h定义
static struct {const char* name;int (*func)(int, char**);
} tools[] = {
#define TOOL(name) { #name, name##_main },
#include "tools.h"
#undef TOOL{ 0, 0 },
};int main(int argc, char** argv) {// Let's assume that none of this code handles broken pipes. At least ls,// ps, and top were broken (though I'd previously added this fix locally// to top). We exit rather than use SIG_IGN because tools like top will// just keep on writing to nowhere forever if we don't stop them.signal(SIGPIPE, SIGPIPE_handler);// 获取第一个参数,遍历数组,判断是否和名称相等,相等执行对应的方法。char* cmd = strrchr(argv[0], '/');char* name = cmd ? (cmd + 1) : argv[0];for (size_t i = 0; tools[i].name; i++) {if (!strcmp(tools[i].name, name)) {return tools[i].func(argc, argv);}}printf("%s: no such tool\n", argv[0]);return 127;
}
头文件定义
// system/core/toolbox/tools.h
TOOL(getevent)
TOOL(getprop)
TOOL(modprobe)
TOOL(setprop)
TOOL(start)
TOOL(stop)
TOOL(toolbox)
所以运行脚本 执行的方法 在 getevent.c 下的 getevent_main方法下。
源码解析
1.解析参数
查看对应的方法 main 方法,可以看到 第一步 通过 getopt 解析对应的参数
部分参数通过 或 记录到 print_flags 中。
opterr = 0;do {c = getopt(argc, argv, "tns:Sv::dpilqc:rh"); // 解析对应的参数if (c == EOF)break;switch (c) {case 't':get_time = 1;break;case 'n':newline = "";break;case 's':get_switch = strtoul(optarg, NULL, 0);if(dont_block == -1)dont_block = 1;break;case 'S':get_switch = ~0;if(dont_block == -1)dont_block = 1;break;case 'v':if(optarg)print_flags |= strtoul(optarg, NULL, 0);elseprint_flags |= PRINT_DEVICE | PRINT_DEVICE_NAME | PRINT_DEVICE_INFO | PRINT_VERSION;print_flags_set = 1;break;case 'd':print_flags |= PRINT_HID_DESCRIPTOR;break;case 'p':print_flags |= PRINT_DEVICE_ERRORS | PRINT_DEVICE| PRINT_DEVICE_NAME | PRINT_POSSIBLE_EVENTS | PRINT_INPUT_PROPS;print_flags_set = 1;if(dont_block == -1)dont_block = 1;break;case 'i':print_flags |= PRINT_ALL_INFO;print_flags_set = 1;if(dont_block == -1)dont_block = 1;break;case 'l':print_flags |= PRINT_LABELS;break;case 'q':print_flags_set = 1;break;case 'c':event_count = atoi(optarg); //解析对应参数值dont_block = 0;break;case 'r':sync_rate = 1;break;case '?':fprintf(stderr, "%s: invalid option -%c\n",argv[0], optopt);case 'h':usage(argv[0]); // 打印帮助exit(1);}} while (1);if(dont_block == -1)dont_block = 0;if (optind + 1 == argc) {device = argv[optind];optind++;}if (optind != argc) {usage(argv[0]);exit(1);}
2.初始化ufds数组
第二步,会通过 device 判断是否 观察单个或者全部 。如如果我们 执行的命令后面加了对应的设备,则只会观察对应的设备 如 getevent -ltr /dev/input/event1。其中 inotify_init 获取了 dev/input 文件夹对应的监听。 然后通过 inotify_add_watch。进行删除和添加的监听。
nfds = 1;ufds = calloc(1, sizeof(ufds[0]));ufds[0].fd = inotify_init();ufds[0].events = POLLIN;// 打印单个设备对应信息if(device) {if(!print_flags_set)print_flags |= PRINT_DEVICE_ERRORS;// 通过 open 方法打开对应的驱动,并通过 ioctl 获取对应的信息,并放到对应的 ufds 数组res = open_device(device, print_flags);if(res < 0) {return 1;}} else { // 打印全部if(!print_flags_set)print_flags |= PRINT_DEVICE_ERRORS | PRINT_DEVICE | PRINT_DEVICE_NAME;print_device = 1;// 添加删除和添加对应文件夹的监听res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE);if(res < 0) {fprintf(stderr, "could not add watch for %s, %s\n", device_path, strerror(errno));return 1;}// 遍历该文件夹 dev/input 文件夹下所有的res = scan_dir(device_path, print_flags);if(res < 0) {fprintf(stderr, "scan dir failed for %s\n", device_path);return 1;}}
没指定对应的设备的会 将所有添加到数组中,通过 scan_dir 方法。遍历文件夹下。先通过 opendir,打开文件夹,然后 通过 readdir进行遍历。遍历到符合的 在通过 open_device 将fd 添加到 数组中。
static int scan_dir(const char *dirname, int print_flags)
{char devname[PATH_MAX];char *filename;DIR *dir;struct dirent *de;// // 打开对应的文件夹dir = opendir(dirname);if(dir == NULL)return -1;strcpy(devname, dirname);filename = devname + strlen(devname);*filename++ = '/';while((de = readdir(dir))) {if(de->d_name[0] == '.' &&(de->d_name[1] == '\0' ||(de->d_name[1] == '.' && de->d_name[2] == '\0')))continue;strcpy(filename, de->d_name);// 将fd 添加到 数组open_device(devname, print_flags);}closedir(dir);return 0;
}
最终都是通过 open_device 添加到数组,查看对应的方法,可以看到该方法通过 open 打开对应的节点,在通过 ioctl 获取对应的信息,并根据 设置的 print_flags 按需打印对应信息。最终在存放到 ufds 数组中。
static int open_device(const char *device, int print_flags)
{int version;int fd;int clkid = CLOCK_MONOTONIC;struct pollfd *new_ufds;char **new_device_names;char name[80];char location[80];char idstr[80];struct input_id id;// 打开这个节点fd = open(device, O_RDONLY | O_CLOEXEC);if(fd < 0) {if(print_flags & PRINT_DEVICE_ERRORS)fprintf(stderr, "could not open %s, %s\n", device, strerror(errno));return -1;}// 获取相关的信息if(ioctl(fd, EVIOCGVERSION, &version)) {if(print_flags & PRINT_DEVICE_ERRORS)fprintf(stderr, "could not get driver version for %s, %s\n", device, strerror(errno));return -1;}if(ioctl(fd, EVIOCGID, &id)) {if(print_flags & PRINT_DEVICE_ERRORS)fprintf(stderr, "could not get driver id for %s, %s\n", device, strerror(errno));return -1;}name[sizeof(name) - 1] = '\0';location[sizeof(location) - 1] = '\0';idstr[sizeof(idstr) - 1] = '\0';if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {//fprintf(stderr, "could not get device name for %s, %s\n", device, strerror(errno));name[0] = '\0';}if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) {//fprintf(stderr, "could not get location for %s, %s\n", device, strerror(errno));location[0] = '\0';}if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) {//fprintf(stderr, "could not get idstring for %s, %s\n", device, strerror(errno));idstr[0] = '\0';}if (ioctl(fd, EVIOCSCLOCKID, &clkid) != 0) {fprintf(stderr, "Can't enable monotonic clock reporting: %s\n", strerror(errno));// a non-fatal error}new_ufds = realloc(ufds, sizeof(ufds[0]) * (nfds + 1));if(new_ufds == NULL) {fprintf(stderr, "out of memory\n");return -1;}ufds = new_ufds;new_device_names = realloc(device_names, sizeof(device_names[0]) * (nfds + 1));if(new_device_names == NULL) {fprintf(stderr, "out of memory\n");return -1;}device_names = new_device_names;if(print_flags & PRINT_DEVICE)printf("add device %d: %s\n", nfds, device);if(print_flags & PRINT_DEVICE_INFO)printf(" bus: %04x\n"" vendor %04x\n"" product %04x\n"" version %04x\n",id.bustype, id.vendor, id.product, id.version);if(print_flags & PRINT_DEVICE_NAME)printf(" name: \"%s\"\n", name);if(print_flags & PRINT_DEVICE_INFO)printf(" location: \"%s\"\n"" id: \"%s\"\n", location, idstr);if(print_flags & PRINT_VERSION)printf(" version: %d.%d.%d\n",version >> 16, (version >> 8) & 0xff, version & 0xff);if(print_flags & PRINT_POSSIBLE_EVENTS) {print_possible_events(fd, print_flags);}if(print_flags & PRINT_INPUT_PROPS) {print_input_props(fd);}if(print_flags & PRINT_HID_DESCRIPTOR) {print_hid_descriptor(id.bustype, id.vendor, id.product);}// 将 fd 存放到ufds 数组中 ufds[nfds].fd = fd;ufds[nfds].events = POLLIN;// 放到 device_names 数组中device_names[nfds] = strdup(device);nfds++;return 0;
}
3.添加到poll 并做对应处理
添加完数组后,通过 poll 等待消息的监听。poll 和 epoll 是一样的。但是 epoll 更加高效。因为 epoll 是监听到消息 塞到 events 数组并返回 数量。后续遍历变化的即可。但是 poll 是无论如何都要遍历全部数组 找到对应的变化,没 epoll 高效。
当有新消息来后,则做对应的处理。数组第一个为文件夹 ,所以对文件夹做单独的处理。
通过 read_notify 方法。添加对应添加和删除,添加则调用 open_device,删除则调用 close_device 移除对应的监听。
因为第0个已处理,所以下面的遍历从下标1开始。遍历到变化时候, 通过 read 读取对应信息到 变量 event 中(input_event),最后根据设置的 falg 打印相对于的信息格式。
while(1) {//int pollres =// 通过 poll 等待消息的监听poll(ufds, nfds, -1);//printf("poll %d, returned %d\n", nfds, pollres);// 第1个是文件夹的变化,对文件夹内容的监听做单独的处理if(ufds[0].revents & POLLIN) {read_notify(device_path, ufds[0].fd, print_flags);}// 因为第0个已处理,所以下面的遍历从下标1开始for(i = 1; i < nfds; i++) {if(ufds[i].revents) {// 判断到有变化if(ufds[i].revents & POLLIN) {// 读取对应的内容到变量 event中,为 input_event 结构体res = read(ufds[i].fd, &event, sizeof(event));if(res < (int)sizeof(event)) {fprintf(stderr, "could not get event\n");return 1;}if(get_time) {printf("[%8ld.%06ld] ", event.time.tv_sec, event.time.tv_usec);}if(print_device)printf("%s: ", device_names[i]);// 传进去 值,根据 对应的 print_flags 做打印print_event(event.type, event.code, event.value, print_flags);if(sync_rate && event.type == 0 && event.code == 0) {int64_t now = event.time.tv_sec * 1000000LL + event.time.tv_usec;if(last_sync_time)printf(" rate %lld", 1000000LL / (now - last_sync_time));last_sync_time = now;}printf("%s", newline);if(event_count && --event_count == 0)return 0;}}}}
相关文章:

frameworks 之getEvent指令
frameworks 之getEvent指令 指令解析源码追溯源码解析1.解析参数2.初始化ufds数组3.添加到poll 并做对应处理 通过 getEvent 可以识别按键基本命令和里面的关键信息 涉及到的类如下 system/core/toolbox/toolbox.csystem/core/toolbox/tools.hsystem/core/toolbox/getevent.c …...

tensorboard显示一片空白解决方案
OK艾瑞巴蒂 不知道看这个视频几个小土堆过来的,今天已经发了一篇博文探讨快速下载tensorboard了 下面用的时候叒出现问题了 from torch.utils.tensorboard import SummaryWriter writer SummaryWriter("logs")# writer.add_image() # Yx for i in range…...

C#编程中,如何实现一个高效的数据排序算法?
在C#编程中,可以使用内置的排序方法来实现高效的数据排序。最常用的方法是使用Array.Sort()和List<T>.Sort()。这些方法内部使用了快速排序算法(Quick Sort),它是一种非常高效的排序算法,平均时间复杂度为O(n lo…...

LookupError: Resource averaged_perceptron_tagger not found.解决方案
大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…...

Leetcode JAVA刷刷站(39)组合总和
一、题目概述 二、思路方向 为了解决这个问题,我们可以使用回溯算法来找到所有可能的组合,使得组合中的数字之和等于目标数 target。因为数组中的元素可以无限制地重复选择,所以在回溯过程中,我们不需要跳过已经选择的元素&#x…...

Spring中AbstractAutowireCapableBeanFactory
AbstractAutowireCapableBeanFactory 是 Spring 框架中的一个抽象类,位于 org.springframework.beans.factory.support 包中。它实现了 AutowireCapableBeanFactory 接口,提供了一些通用的方法和逻辑,以支持 Spring 中的自动装配功能。 主要…...

PostgreSQL的walwriter和archiver进程区别
PostgreSQL的walwriter和archiver进程区别 在PostgreSQL中,walwriter和archiver进程是两个重要的后台进程,它们负责管理WAL(Write-Ahead Logging,预写日志)文件,但它们的职责和功能有所区别。理解这两个进…...

前端字体没有授权,字体版权检测(是否为方正字体)
1.截图系统中的首页和登录页面,主要是有字体的地方 2.在线字体版权检测地址:字体版权自动检测-求字体网 3.上传照片,开始对图片进行检测,每个账号有三次免费次数 4.检测完,直接查看检测报告即可, 报告中…...

在 SOCKS 和 HTTP 代理之间如何选择?
在 SOCKS 和 HTTP 代理之间进行选择需要彻底了解每种代理的工作原理以及它们传达的配置。只有这样,您才能轻松地在不同类型的代理之间进行选择。 本文概述了 HTTP 和 SOCKS 代理是什么、它们如何运作以及它们各自带来的好处。此外,我们将比较这两种代理类…...

C++适配windows和linux下网络编程TCP简单案例
C网络编程 网络协议是计算机网络中通信双方必须遵循的一套规则和约定,用于实现数据的传输、处理和控制。这些规则包括了数据格式、数据交换顺序、数据处理方式、错误检测和纠正等。网络协议是使不同类型的计算机和网络设备能够相互通信的基础,是网络通信…...

OpenDDS的GUID是如何构造的?
1、GUID、RepoID、GUID_t概念和关系 GUID(Global Unique IDentifiers)是RTPS规范约定的DDS对象的唯一性ID;RepoId(Repository IDentifiers)是Repo服务约定的DDS对象的唯一性ID;GUID和RepoId,都是基于GUID_t结构体定义,名称不同,但实质上是一样的。题外话: 无论是GUID还…...

初识MySQL(安装与配置环境)
嗨!今天我们进入一个新的领域---数据库。 首先来个小小铺垫。 我们平时存储东西的时候,一般用到文件。为什么有文件了,还继续要这个数据库呢? 很明显,文件有一些不好的地方,需要数据库来进行补充。 文件…...

druid+logback打印sql执行日志
druid 的LogFilter 为我们准备了四种logger类型,对应打印 datasource相关、connection相关、statement相关、resultset相关的日志。 druid.sql.Datasource:打印数据源相关的字段。 druid.sql.Connection:打印应用程序获得数据库连接的过程。…...

C++编程:无锁环形队列 (LockFreeRingQueue)的简单实现、测试和分析
文章目录 0. 概述1. 无锁环形队列概述1.1 无锁环形队列的特点1.2 无锁环形队列的优势与局限 2. LockFreeRingQueue 实现2.1 Enqueue 操作流程图2.2 Dequeue 操作流程图 3. 核心实现细节3.1 环形队列的大小调整3.2 索引计算3.3 原子操作与CAS3.4 线程让步 4. 测试示例程序4.1 实…...

植物生长时为什么会扭动?科学家解开令查尔斯·达尔文困惑的千古之谜
在一项新的研究中,来自美国和以色列的物理学家可能已经弄清了植物生长过程中的一种古怪行为–也是查尔斯-达尔文本人在其生命的最后几十年里所好奇的一个谜:对于许多人类来说,植物可能看起来静止不动,甚至有点无趣。但实际上&…...

SAP LE学习笔记02 - WM和库存管理(IM)之间的关系,保管Lot(Quant)
上一章学习了LE的基础知识。 1,LE的概述,LE里面包含下面3个大的模块 - LE-WM 仓库管理 / - LE-SHP 发货/ - LE-TRA 运输 2,仓库的结构 - 仓库番号 / -保管域Type(存储区域)/ - 保管区画(存储区)/ - 棚番(Storage Bin 仓位&…...

Span<T> 是 C# 7.2 引入的重要类型
Span<T> 是 C# 7.2 引入的一个非常重要的类型,它提供了一种低开销、类型安全的方式来操作连续的内存区域。Span<T> 本质上是一个结构体,它封装了一个内存段的引用(通过指针)以及该内存段的长度。由于它直接操作内存&a…...

Python办公自动化:初识 `openpyxl`
1.1 什么是 openpyxl? openpyxl 是一个用于读写 Excel 2010 xlsx/xlsm/xltx/xltm 文件的 Python 库。它允许我们通过 Python 脚本自动化处理 Excel 文件,包括创建新的工作簿、修改现有的工作簿、格式化单元格、处理公式和图表等功能。这对于办公自动化、…...

Pocketbase实战体验:内置数据库与实时功能如何超越传统MySQL
Pocketbase 是一个开源的实时后端服务器,内置了数据库、实时订阅、用户认证、RESTful API 等功能,而 MySQL 是一个广泛使用的关系数据库管理系统。以下是 Pocketbase 相对于 MySQL 的一些潜在优点: 完整的后端解决方案 一体化:P…...

ChatGPT 3.5/4.0 新手使用手册(详细版)
1. 什么是 ChatGPT? ChatGPT是由 OpenAI 开发的先进人工智能语言模型,能够理解并生成自然语言文本。它可以帮助你进行写作、回答问题、提供建议,甚至参与对话。ChatGPT 3.5 和 4.0 是两个不同版本,它们都拥有强大的语言处理能力&…...

【Java学习】Stream流详解
所属专栏:Java学习 Stream流是JDK 8引入的一个概念,它提供了一种高效且表达力强的方式来处理数据集合(如List、Set等)或数组。Stream API可以以声明性方式(指定做什么)来处理数据序列。流操作可以被分为两大…...

Oracle(69)什么是表压缩(Table Compression)?
表压缩(Table Compression)是一种数据库优化技术,用于减少表数据的存储空间和提高I/O性能。通过压缩表数据,可以显著减少存储需求,并在某些情况下提高查询性能,特别是对于只读或主要是读取操作的表。表压缩…...

java JUC编程
Java并发工具包(JUC),全称Java Util Concurrent,是Java提供的一个用于构建多线程应用程序的工具包,位于java.util.concurrent包及其子包中。 并发编程主要解决以下三个经典问题: 1. **原子性问题…...

vue3+element-plus表格分页选中加默认回显选中
1.需求 某个表单需要选择多条数据,点击选择按钮,弹框出来一个分页列表,选择多条数据,外面表单中显示选中的数据,可以删除数据,再次点击按钮,回显当前选中的数据。 2.解决办法 1.el-table加ro…...

Erupt 项目搭建
创建Spring Boot项目 Maven依赖 Spring Boot版本为 2.7.10,erupt版本为 1.12.14 erupt版本要与Spring Boot版本适配,3.x.x版本Spring Boot暂不适用说是 <properties><erupt.version>1.12.14</erupt.version></properties> <…...

HarmonyOS Next 系列之列表下拉刷新和触底加载更多数据实现(十一)
系列文章目录 HarmonyOS Next 系列之省市区弹窗选择器实现(一) HarmonyOS Next 系列之验证码输入组件实现(二) HarmonyOS Next 系列之底部标签栏TabBar实现(三) HarmonyOS Next 系列之HTTP请求封装和Token…...

比特位的计算
给你一个整数 n ,对于 0 < i < n 中的每个 i ,计算其二进制表示中 1 的个数 ,返回一个长度为 n 1 的数组 ans 作为答案。 示例 1: 输入:n 2 输出:[0,1,1] 解释: 0 --> 0 1 --> …...

SQLAlchemy 学习笔记
通信类型:AF_INET 协议家族一般是表示TCP通信的SOC_STREAM和UDP通信的SOCK_DGRAM。对于TCP通信,建立socket连接,: s socket.socket(socket.AF_INET, socket.SOCK_STREAM)连接socket, s.connect((host,port))socket通信…...

Linux内核分析(调度类和调度实体)
文章目录 前言一、调度类1. stop_sched_class2. dl_sched_class3. rt_sched_class4. fair_sched_class5. idle_sched_class总结 二、调度类中的操作函数三、调度实体 前言 调度是操作系统内核的一个关键职责,它涉及到如何合理分配CPU时间给不同的进程或线程。在Lin…...

用输入输出流(I/O)流,递归复制和删除多级文件
一、(I/O)流递归复制一个文件 第一种: else if语句过多,看起来冗余,优点:多级文件一次性复制完整 import java.io.*;//数据源:src/main/java/day15_8_13/haha //目标;src/main/java/LaJi pub…...