首页 > 其他分享 >驱动 ioctl使用

驱动 ioctl使用

时间:2024-07-16 21:10:51浏览次数:15  
标签:return int CMD ioctl 使用 驱动 include my

 

驱动程序部分

  1. 创建一个简单的字符设备驱动程序

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h>

#define DEVICE_NAME "mychardev"
#define CLASS_NAME "mycharclass"
#define IOCTL_GET_VALUE _IOR('a', 1, int32_t *)
#define IOCTL_SET_VALUE _IOW('a', 2, int32_t *)

static int major_number;
static int32_t value = 0;
static struct class* char_class = NULL;
static struct device* char_device = NULL;

static long my_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {
int32_t temp;
switch(cmd) {
case IOCTL_GET_VALUE:
if (copy_to_user((int32_t *)arg, &value, sizeof(value))) {
return -EACCES;
}
break;
case IOCTL_SET_VALUE:
if (copy_from_user(&temp, (int32_t *)arg, sizeof(temp))) {
return -EACCES;
}
value = temp;
break;
default:
return -EINVAL;
}
return 0;
}

static int my_open(struct inode *inode, struct file *file) {
return 0;
}

static int my_release(struct inode *inode, struct file *file) {
return 0;
}

static struct file_operations fops = {
.open = my_open,
.release = my_release,
.unlocked_ioctl = my_ioctl,
};

static int __init mychar_init(void) {
major_number = register_chrdev(0, DEVICE_NAME, &fops);
if (major_number < 0) {
printk(KERN_ALERT "Failed to register a major number\n");
return major_number;
}

char_class = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(char_class)) {
unregister_chrdev(major_number, DEVICE_NAME);
printk(KERN_ALERT "Failed to register device class\n");
return PTR_ERR(char_class);
}

char_device = device_create(char_class, NULL, MKDEV(major_number, 0), NULL, DEVICE_NAME);
if (IS_ERR(char_device)) {
class_destroy(char_class);
unregister_chrdev(major_number, DEVICE_NAME);
printk(KERN_ALERT "Failed to create the device\n");
return PTR_ERR(char_device);
}

printk(KERN_INFO "MyChar: device class created correctly\n");
return 0;
}

static void __exit mychar_exit(void) {
device_destroy(char_class, MKDEV(major_number, 0));
class_unregister(char_class);
class_destroy(char_class);
unregister_chrdev(major_number, DEVICE_NAME);
printk(KERN_INFO "MyChar: Goodbye from the LKM!\n");
}

module_init(mychar_init);
module_exit(mychar_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Linux char driver with ioctl");
MODULE_VERSION("0.1");

 

 

用户空间:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>

#include <errno.h>

#define DEVICE_PATH "/dev/mychardev"
#define IOCTL_GET_VALUE _IOR('a', 1, int32_t *)
#define IOCTL_SET_VALUE _IOW('a', 2, int32_t *)

int main() {
int fd;
int32_t value;

fd = open(DEVICE_PATH, O_RDWR);
if (fd < 0) {
perror("Failed to open the device");
return errno;
}

printf("Reading the value from the device...\n");
if (ioctl(fd, IOCTL_GET_VALUE, &value) == -1) {
perror("Failed to get the value");
close(fd);
return errno;
}
printf("The current value is: %d\n", value);

printf("Setting the value to 42...\n");
value = 42;
if (ioctl(fd, IOCTL_SET_VALUE, &value) == -1) {
perror("Failed to set the value");
close(fd);
return errno;
}

printf("Reading the value again from the device...\n");
if (ioctl(fd, IOCTL_GET_VALUE, &value) == -1) {
perror("Failed to get the value");
close(fd);
return errno;
}
printf("The new value is: %d\n", value);

close(fd);
return 0;
}

 

使用步骤

  1. 编译和加载驱动模块

    bash make sudo insmod mychardev.ko sudo mknod /dev/mychardev c <major_number> 0 # Replace <major_number> with the major number assigned by the kernel sudo chmod 666 /dev/mychardev
  2. 编译并运行用户空间应用

    bash gcc -o user_app user_app.c ./user_app
  3. 卸载驱动模块

    bash sudo rmmod mychardev sudo rm /dev/mychardev

以上示例展示了如何使用 ioctl 实现用户空间应用与内核驱动程序之间的通信。通过这个示例,你可以了解到如何在内核模块中处理 ioctl 请求,并在用户空间应用中使用 ioctl 系统调用与设备驱动程序交互。

 

 

 

makefile:

obj-m += mychardev.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

 

 

=========================================

https://www.cnblogs.com/TaXueWuYun/p/15315594.html

ioctl简介

kernel3.0之前,叫ioctl,之后改名为unlocked_ioctl。功能和接口基本相同,名字发生了变化

ioctl既可以往内核读也可以写,read/write在执行大数据量读/写时比较有优势。

在应用层调用ioctl函数时,内核会调用对应驱动中的ublocked_ioctl函数,向内核读写数据。

驱动内的unlocked_ioctl函数

unlocked_ioctl函数属于file_operations文件操作集的一个成员,结构体内函数的定义为:

long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
//参数:句柄;接口命令;从应用层传入的据
//返回值:返回给应用层的数据

应用层的ioctl函数

定义在头文件<sys/ioctl.h>中,是个可变参数的函数

int ioctl(int fd, ind cmd, …);
//参数和返回值,和文件操作集里的unlocked_ioctl函数一致
//传给内核的参数可以省略

unlocked_ioctl接口命令规则

命令是一个整型参数(32位)

第一个分区:0-7,命令的编号,范围是0-255
第二个分区:8-15,命令的幻数
第三个分区:16-29,表示传递的数据的大小
第四分区:30-31,代表读写的方向


第一和第二分区,主要用来区分命令,不能有两个完全一样的“一+二”
第四分区:00:没有数据传递;10:用户从驱动读数据;01:用户向驱动写数据;11:先写数据到驱动再把数据读出来

接口命令相关的宏

合成宏,返回一个接口命令,可传入ioctl函数

_IO(type,nr)   没有数据传递的命令
_IOR(type, nr, size)  从驱动中读取数据的命令
_IOW(type, nr, size)  向驱动中写入数据的命令
_IOWR(type, nr, size) 交换数据的命令
//type表示数据的幻数,8-15位
//nr命令的编号,0-7位
//size参数传递的大小,传递的是数据类型:如果传递4字节,可以写成int

分解宏,将接口命令解析出需要的部分

_IOC_DIR(nr)   方向
_IOC_TYPE(nr)  幻数
_IOC_NR(nr)    编号
_IOC_SIZE(nr)   大小

//nr要分解的命令

示例代码

以杂项设备为例

驱动部分

#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>

#define CMD_TEST_0 _IO('A', 0)       //不需要读写的命令
#define CMD_TEST_1 _IOR('A', 1, int) //从内核读取一个int的命令
#define CMD_TEST_2 _IOW('A', 2, int) //向内核写入一个int的命令
#define CMD_TEST_3 _IOWR('A', 3, int) //读写一个int的命令

int misc_open(struct inode *a,struct file *b){
    printk("misc open \n");
    return 0;
}

int misc_release (struct inode * a, struct file * b){
    printk("misc file release\n");
    return 0;
}

long misc_ioctl(struct file *fd, unsigned int cmd, unsigned long b){
    /*将命令按内容分解,打印出来*/
    printk("cmd type=%c\t nr=%d\t dir=%d\t size=%d\n", _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_DIR(cmd), _IOC_SIZE(cmd));

    switch(cmd){
        case CMD_TEST_0:
            printk("CMD_TEST_0\n");
            break;
        case CMD_TEST_1:
            printk("CMD_TEST_1\n");
            return 1;
            break;
        case CMD_TEST_2:
            printk("CMD_TEST_2 date=%d\n",b);
            break;
        case CMD_TEST_3:
            printk("CMD_TEST_3 date=%d\n",b);
            return b+1;
            break;
    }

    return 0;
}

//文件操作集
struct file_operations misc_fops = {
    .owner = THIS_MODULE,
    .open = misc_open,
    .release = misc_release,
    .unlocked_ioctl = misc_ioctl
};

struct miscdevice misc_dev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "test_ioctrl",   //设备节点名
    .fops = &misc_fops
};

static int hello_init(void){
    int ret;

    ret = misc_register(&misc_dev);  //注册杂项设备
    if(ret < 0){
        printk("misc regist failed\n");
        return -1;
    }

    printk("misc regist succeed\n");
    return 0;
}

static void hello_exit(void){
    misc_deregister(&misc_dev);
}

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("TAXUE");

module_init(hello_init); 
module_exit(hello_exit);  

应用层部分

#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<string.h>
#include<unistd.h>
#include<sys/ioctl.h>

#define CMD_TEST_0 _IO('A', 0)
#define CMD_TEST_1 _IOR('A', 1, int)
#define CMD_TEST_2 _IOW('A', 2, int)
#define CMD_TEST_3 _IOWR('A', 3, int)

int main(int argc, char *argv[]){
    int fd=0;
    int revData=0;

    fd = open("/dev/test_ioctrl", O_RDWR);
    if(fd < 0){
        printf("open failed\n");
        exit(1);
    }
    printf("open success\n");

    /*依次调用四个命令*/
    ioctl( fd, CMD_TEST_0);
    
    revData = ioctl( fd, CMD_TEST_1);
    printf("receive 1 data=%d\n", revData);

    ioctl( fd, CMD_TEST_2, 99);

    revData = ioctl( fd, CMD_TEST_3, 101);
    printf("receive 3 data=%d\n", revData);

    close(fd);
    return 0;
}
======================================================================================

ioctl
在用户空间, ioctl 系统调用有下面的原型:

int ioctl(int fd, unsigned long cmd, ...);
1
ioctl 驱动方法有和用户空间版本不同的原型:

int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
1
inode 和 filp 指针是对应应用程序传递的文件描述符 fd 的值, 和传递给 open 方法的相同参数. cmd 参数从用户那里不改变地传下来, 并且可选的参数 arg 参数以一个 unsigned long 的形式传递, 不管它是否由用户给定为一个整数或一个指针. 如果调用程序不传递第 3 个参数, 被驱动操作收到的 arg 值是无定义的. 因为类型检查在这个额外参数上被关闭, 编译器不能警告你如果一个无效的参数被传递给 ioctl, 并且任何关联的错误将难以查找.

ioctl 命令数字应当在这个系统是唯一的, 为了阻止向错误的设备发出正确的命令而引起的错误. 为帮助程序员创建唯一的 ioctl 命令代码, 这些编码已被划分为几个位段.

定义 ioctl 命令号的正确方法使用 了4 个位段, 它们有下列的含义.
type
幻数(魔数). 占了8位,可以用数字或者字符来标记类型。
number
序(顺序)号. 它是 8位宽,代表是该类型命令下的第几个命令.
direction
数据传送的方向,占2位。如果这个特殊的命令涉及数据传送. 可能的值是 _IOC_NONE(没有数据传输), _IOC_READ, _IOC_WRITE, 和 _IOC_READ|_IOC_WRITE (数据在2个方向被传送). 数据传送是从应用程序的观点来看待的; _IOC_READ 意思是从设备读, 因此设备必须写到用户空间. 注意这个成员是一个位掩码, 因此 _IOC_READ 和 _IOC_WRITE 可使用一个逻辑 AND 操作来抽取.
size
涉及到的用户数据的大小. 这个成员的宽度是依赖体系的, 但是常常是 13 或者 14 位. 你可为你的特定体系在宏 _IOC_SIZEBITS 中找到它的值. 你使用这个 size 成员不是强制的 - 内核不检查它 – 但是它是一个好主意. 正确使用这个成员可帮助检测用户空间程序的错误并使你实现向后兼容, 如果你曾需要改变相关数据项的大小. 如果你需要更大的数据结构, 但是, 你可忽略这个 size 成员. 我们很快见到如何使用这个成员.

定义宏来帮助建立命令号, 如下:
_IO(type,nr)(给没有参数的命令),
_IOR(type, nre, datatype)(给从驱动中读数据的),
_IOW(type,nr,datatype)(给写数据),
_IOWR(type,nr,datatype)(给双向传送).
type 和 number 成员作为参数被传递, 并且 size 成员通过应用 sizeof 到 datatype 参数而得到.

驱动中来解码这个号: _IOC_DIR(cmd), _IOC_TYPE(cmd), _IOC_NR(cmd), 和 _IOC_SIZE(cmd).

使用 ioctl 参数
我们需要涉及的另一点是如何使用这个额外的参数. 如果它是一个整数, 就容易,它可以直接使用. 但是如果它是一个指针, 必须小心些.
当用一个指针引用用户空间, 我们必须确保用户地址是有效的. 试图存取一个没验证过的用户提供的指针可能导致不正确的行为, 一个内核 oops, 系统崩溃, 或者安全问题. 它是驱动的责任来对每个它使用的用户空间地址进行正确的检查, 并且返回一个错误如果它是无效的.
copy_from_user 和 copy_to_user 函数, 它们可用来安全地移动数据到和从用户空间. 这些函数也可用在 ioctl 方法中, 但是 ioctl 调用常常包含小数据项, 可通过其他方法更有效地操作. 开始, 地址校验(不传送数据)由函数 access_ok 实现, 它定义在 :
int access_ok(int type, const void *addr, unsigned long size);
在较新一点得内核中是
int access_ok(const void *addr, unsigned long size);
第一个参数应当是 VERIFY_READ 或者 VERIFY_WRITE, 依据这个要进行的动作是否是读用户空间内存区或者写它. addr 参数持有一个用户空间地址, size 是一个字节量. 例如, 如果 ioctl 需要从用户空间读一个整数, size 是 sizeof(int). 如果你需要读和写给定地址, 使用 VERIFY_WRITE, 因为它是 VERIRY_READ 的超集.
不象大部分的内核函数, access_ok 返回一个布尔值: 1 是成功(存取没问题)和 0 是失败(存取有问题). 如果它返回假, 驱动应当返回 -EFAULT 给调用者.

示例代码:
mioctl.c

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/ioctl.h>
#include <linux/uaccess.h>
#include "cmd.h"

#define DEV_NAME "ioctl_test"
#define DEV_NUM (1)


static int kernel_num = 10;

static struct my_st
{
int major;
int minor;
dev_t dev_num;
struct cdev cdev;
struct class *class;
struct device *device;
}*my;

static int ioctl_open(struct inode *inode,struct file *flip)
{
printk("open()\r\n");
return 0;
}

static int ioctl_release(struct inode *inode,struct file *flip)
{
printk("close()\r\n");
return 0;
}

static long ioctl_ioctl(struct file *flip,unsigned int cmd,unsigned long arg)
{
int err;
void __user *argp = (void __user *)arg;

if(_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(argp,_IOC_SIZE(cmd));
else if(_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(argp,_IOC_SIZE(cmd));
if(err)
return -EFAULT;

switch(cmd)
{
case DEV_CMD:
printk("cmd\r\n");
break;
case DEV_GET_CMD:
copy_to_user(argp,&kernel_num,_IOC_SIZE(cmd));
printk("get cmd\r\n");
break;
case DEV_SET_CMD:
copy_from_user(&kernel_num,argp,_IOC_SIZE(cmd));
printk("data = %d\r\n",kernel_num);
printk("set cmd\r\n");
break;
default:
printk("error");
return -EINVAL;
}

return 0;
}

 

static ssize_t ioctl_write(struct file *flip,const char __user *buf,size_t count,loff_t *offset)
{
return 0;
}


static ssize_t ioctl_read(struct file *flip,char __user *buf,size_t count,loff_t *offset)
{
return 0;
}

static const struct file_operations fops ={
.owner = THIS_MODULE,
.open = ioctl_open,
.read = ioctl_read,
.write = ioctl_write,
.unlocked_ioctl = ioctl_ioctl,
.release = ioctl_release,
};


static int __init mioctl_init(void)
{
my = kzalloc(sizeof(struct my_st),GFP_KERNEL);
if(my == NULL)
return -ENOMEM;

int ret = alloc_chrdev_region(&my->dev_num,DEV_NUM,0,DEV_NAME);
if(ret < 0)
goto alloc_chrdev_failed;

my->major = MAJOR(my->dev_num);
my->minor = MINOR(my->dev_num);

cdev_init(&my->cdev,&fops);
my->cdev.owner = THIS_MODULE;
ret = cdev_add(&my->cdev,my->dev_num,DEV_NUM);
if(ret < 0)
goto cdev_add_failed;

my->class = class_create(THIS_MODULE,DEV_NAME);
if(IS_ERR(my->class))
{
ret = PTR_ERR(my->class);
goto class_create_failed;
}

my->device = device_create(my->class,NULL,my->dev_num,NULL,DEV_NAME);
if(IS_ERR(my->device))
{
ret = PTR_ERR(my->device);
goto device_create_failed;
}

printk("ioctl device is ok !\r\n");
printk("主设备号:%d\r\n",my->major);
return 0;

device_create_failed:
class_destroy(my->class);
class_create_failed:
cdev_del(&my->cdev);
cdev_add_failed:
unregister_chrdev_region(my->dev_num,DEV_NUM);
alloc_chrdev_failed:
kfree(my);
return ret;
}

static void __exit mioctl_exit(void)
{
device_destroy(my->class,my->dev_num);
class_destroy(my->class);
cdev_del(&my->cdev);
unregister_chrdev_region(my->dev_num,DEV_NUM);
kfree(my);
printk("ioctl device was remove\r\n");
}
module_init(mioctl_init);
module_exit(mioctl_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Chen");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
Makefile

ifneq ($(KERNELRELEASE),)
obj-m:=mioctl.o
else
CURRENT_DIR:=$(shell pwd)
KERNEL_DIR:=/lib/modules/$(shell uname -r)/build
ccflags-y:=-std=gnu99 -Wno-declaration-after-statement
all:
$(MAKE) -C $(KERNEL_DIR) M=$(CURRENT_DIR) modules
clean:
$(MAKE) -C $(KERNEL_DIR) M=$(CURRENT_DIR) clean
endif
1
2
3
4
5
6
7
8
9
10
11
cmd.h

#ifndef __CMD_H
#define __CMD_H

#define DEV_TYPE 'c'
#define DEV_CMD _IO(DEV_TYPE,0)
#define DEV_GET_CMD _IOR(DEV_TYPE,1,int)
#define DEV_SET_CMD _IOW(DEV_TYPE,2,int)

#endif
1
2
3
4
5
6
7
8
9
测试代码app.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "cmd.h"

int main()
{
int arg = 30;
int fd = open("/dev/ioctl_test",O_RDWR);
if(fd < 0)
{
perror("");
exit(1);
}

int ret = ioctl(fd,DEV_GET_CMD,&arg);
if(ret < 0)
{
perror("");
exit(1);
}

int ret = ioctl(fd,DEV_GET_CMD,&arg);
if(ret < 0)
{
perror("");
exit(1);
}

printf("arg = %d\r\n",arg);

arg = 100;

ret = ioctl(fd,DEV_SET_CMD,&arg);
if(ret < 0)
{
perror("");
exit(1);
}

close(fd);
exit(1);
}
————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/weixin_45619852/article/details/120833505






参考:
https://www.cnblogs.com/TaXueWuYun/p/15315594.html

标签:return,int,CMD,ioctl,使用,驱动,include,my
From: https://www.cnblogs.com/rebrobot/p/18306100

相关文章

  • 使用 GitHub 跟踪博文数据,并自动同步到 cnblogs
    导读开通了博客园的VIP发现了会员服务中的GitHub跟踪和同步博文数据的功能,测试发现习惯使用GitHub的话,这个还是一个挺不错的功能。本文主要分两部分,一是介绍说明如何使用该功能,并做相关个人的经验分享,二是作为个人GitHub同步cnblogs的文章的索引教程:如何使用适合人......
  • Django项目中使用markdown语法
    要让Django项目支持Markdown语言,你可以使用第三方库markdown来转换Markdown文本到HTML。以下是如何在Django项目中集成Markdown支持的步骤:1.安装Markdown库首先,安装Markdown库:pipinstallmarkdown2.在视图中使用Markdown在你的视图函数中,使用Markdown库将Markdown文本转......
  • 阿贝云永久免费云服务器1核1G5M服务器使用评测
    最近关注到了阿贝云,申请了一台,做个评测,供大家参考使用。永久免费服务器规格(CPU1核,内存1G,公网带宽5M,SSD数据盘10G),网络还是防御200G的高防BGP,还提供了两个快照服务,作为免费服务器来说还是非常不错的。使用 i-abc/Speedtest 三网测速大陆三网+教育网IPv4单线程测速-------......
  • 第八天笔记(项目测试工具悟道使用)
    禅道一、禅道的介绍(1)定义禅道是一个项目管理工具,也是一个bug管理工具,还是一个用例管理工具。(2)作用:为了解决众多企业在管理中出现混乱,无序的现象,开发出来(3)来源:禅道属易软天川公司(4)禅道是集于产品管理,项目管理,测试管理于一身,同时包含事务管理,组织管理8众多功能,是中小企业管理......
  • 使用四元数解决欧拉角万向锁问题(二)
    使用四元数规避欧拉角万向锁问题(二)一、背景二、具体应用公式1.单位四元数对应旋转作用于向量2.轴角表示转四元数三、代码及实验1.python2.实验结果以及分析四、验证五、存在问题六、参考资料一、背景在使用四元数解决欧拉角万向锁问题(一)一文中已经实现了基于固......
  • sqldumper.exe 是 SQL Server 的一个重要组件,专门用于捕获和处理关键错误,帮助管理员和
     sqldumper.exe的起源可以追溯到MicrosoftSQLServer的开发和运行时管理中。它作为SQLServer的一部分,主要用于处理数据库引擎遇到的关键错误和异常情况。具体来说,sqldumper.exe主要功能包括:异常处理和故障捕获:当SQLServer数据库引擎遇到严重错误时,如内存访问错误......
  • 【RAII机制】的使用方法及优势
            RAII(ResourceAcquisitionIsInitialization)机制是C++中一种非常流行的资源管理策略,其核心思想是利用对象的生命周期来自动管理资源。当对象被创建时,它自动获取所需的资源;当对象被销毁时(例如,离开其作用域时),它自动释放这些资源。这种机制有助于减少资源泄露,增......
  • postman使用小技巧:如何快速构建一个页面的测试
    postman使用小技巧:如何快速构建一个页面的测试首先打开一个页面的开发者模式,找到network中的一个name的url。右击点击copyascURL,复制其中内容,然后打开postman直接粘贴在里面。直接粘贴在这个框框中就可以了,然后postman就会自动解析,把各种信息填好。......
  • bundletool工具使用(Android aab包安装)
    ......
  • windows无法绑定端口,但是使用netstat无法查到占用进程
    windows无法绑定端口,但是使用netstat无法查到占用进程内容来源https://stackoverflow.com/questions/58216537/what-is-administered-port-exclusions-in-windows-10本文章只对该链接内容做简单的提取,总结。如果无法解决建议翻阅原文。windows保留端口查看保留端口。如果你......