linux kernel内存泄漏检测工具之slub debug

一、背景

slub debug 是一个debug集,聚焦于kmem_cache 分配机制的slub内存(比如kmalloc),这部分内存在内核中使用最频繁,slub debug其中有相当部分是用来处理内存踩踏,内存use after free 等异常的,由于这部分的检测效果不如kasan(调试时slub前后填充不同的flag,在分配和释放时做检查,存在发现问题不及时的问题), 本文就不介绍了,本文关注slub debug当中的内存泄漏定位方法。

注意:本文中slub和slab名称有些混用,目前linux版本中实际默认都是使用slub,由于内核代码复用的缘故,有很多的函数名,结构体等还是slab命名,是slub还是slab还是以内核config是打开的CONFIG_SLUB还是CONFIG_SLAB来区分,本文所有实验和分析都是基于CONFIG_SLUB=y;

二、SLUB_DEBUG配置及调试工具

2.1 内核中相关配置

CONFIG_SLUB=y
CONFIG_SLUB_DEBUG=y
CONFIG_SLUB_DEBUG_ON=y
CONFIG_SLUB_STATS=y
#save the stack
CONFIG_STACKDEPOT=y

2.2 slub_debug命令行参数控制

这个参考 linux-6.6.1/Documentation/mm/slub.rst 描述即可
slub debug相关控制可以通过命令行方式进行:
slub_debug=<Debug-Options>
	打开debug选项,应用到所有的slub类型(具体类型可以参考/proc/slabinfo)

slub_debug=<Debug-Options>,<slab name1>,<slab name2>,...
	打开debug选项,可以选择指定的 slub name, 通过逗号隔开

debug option 当前可以选择调试类型,及开关控制字符

	F		Sanity checks on (SLAB_DEBUG_CONSISTENCY_CHECKS)
	Z		Red zoning(SLAB_RED_ZONE)
	P		Poisoning (object and padding) SLAB_POISON
	U		User tracking (free and alloc) SLAB_STORE_USER
	T		Trace (please only use on single slabs) SLAB_TRACE
	A		Enable failslab filter mark for the cache
	O		Switch debugging off for caches that would have
			caused higher minimum slab orders
	-		关闭所有slub 调试 (useful if the kernel is
			configured with CONFIG_SLUB_DEBUG_ON)

例子: slub 调试只开 sanity checks and red zoning:

	slub_debug=FZ

例子:开启调试,仅针对dentry cache

	slub_debug=,dentry

例子:调试 kmalloc-XXXX和dentry相关slub调试:

	slub_debug=P,kmalloc-*,dentry

例子: 关闭slub debug相关调试(开了CONFIG_SLUB_DEBUG_ON=y才需要)

	slub_debug=-

The state of each debug option for a slab can be found in the respective files
under::

	/sys/kernel/slab/<slab name>/

If the file contains 1, the option is enabled, 0 means disabled. 

slabinfo小工具,内核自带工具,能够方便快速的确认泄漏类型

aarch64-none-linux-gnu-gcc -o slabinfo tools/mm/slabinfo.c

slabinfo 有两个参数指令比较重要:
slabinfo -S      //按slub size占用大小排序slub类型及object个数信息等
slabinfo -T      //打印slub的统计信息

2.3 调试节点

查看slab 使用状态
/proc/slabinfo 
查看slab debug信息,统计状态等
/sys/kernel/slab/*
调试内存泄漏,踩踏等信息
/sys/kernel/debug/slab/*

三、slubdebug原理

3.1 slub 分配基本流程

创建kmem_cache
create_cache    //mm/slab-common.c
   -->struct kmem_cache* s = kmem_cache_zalloc(kmem_cache, GFP_KERNEL);
   -->__kmem_cache_create(s, flags);
      -->set_cpu_partial(s);
      -->init_kmem_cache_nodes(s)
      -->alloc_kmem_cache_cpus(s)
   -->list_add(&s->list, &slab_caches); //所有的kmem_cache都会加到slab_caches列表中

分配slub
kmem_cache_alloc  mm/slub.c
  -->__kmem_cache_alloc_lru  mm/slub.c
     -->slab_alloc   mm/slub.c
        -->slab_alloc_node mm/slub.c
           -->__slab_alloc_node
               -->object = c->freelist      //(1)如果freelist上有空闲,直接使用本cpu上cpu_slab->slab的
               -->object = __slab_alloc     //否则重新寻找
                   -->slub_percpu_partial   //(2)从cpu_slab->partial上取
                   -->freelist = get_partial(s, node, &pc);  //(3)从node上取         
                   -->slab = new_slab(s, gfpflags, node);    //(4)如果还获取不到,则从buddy中申请内存
                   -->set_track //开启slub debug时启动存储调用栈
  • 获取指定kemem_cache
  • 从percpu变量(struct kmem_cache_cpu*)cpu_slab上获取,从当前cpu cache(cpu_slab->slab)上freelist取
  • 如果cache上用完,则从cpu_slab的partial 列表上提取,并且将该slab 从partial列表删除,并添加到cpu cache上
  • 如果percpu上的cpu_slab上partial列表中用完,则从kmem_cache_node的partial上提取
  • 从page(buddy system)中分配一个struct slab (之前老版本是struct page,当前已经通过filo机制转换)

3.2 slub debug memleak 检测方法

  • 分配slub object时记录trace信息,会根据调用栈生成hash
  • 在每个slub object对应的tack区域存储hash值,记录pid,分配时间等信息
  • 提取分配信息时,扫描所有已分配object的track区域,根据hash值出现次数排序,并利用hash寻找对应的完整调用栈信息,最终排序打印出来

四、测试验证

4.1 测试代码:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/workqueue.h>
#include <linux/jiffies.h>
#include <asm/page.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>

enum sample_kmemleak_test_case{
    SLAB_LEAK = 0,
    PAGE_LEAK = 1,
    VMALLOC_LEAK = 2,
    PCPU_LEAK = 3,
    SLAB_ALLOC_FREE = 4,
};

static noinline void kmalloc_leak(size_t size, int cnt, bool bfree)
{
    char *ptr;

    int i = 0;
    for (; i < cnt; i++)
    {
        ptr = kmalloc(size, GFP_KERNEL);
        if(bfree)
            kfree(ptr);
    }
}

static noinline void pagealloc_leak(size_t order)
{
    struct page *pages;
    char *ptr;

    pages = alloc_pages(GFP_KERNEL, order);
    ptr = page_address(pages);

    pr_info("%s page addr %llx, page_to_virt %llx\n", __func__, (unsigned long long)pages, (unsigned long long)ptr);
}

static noinline void vmalloc_leak(size_t size)
{
    char *v_ptr;

    v_ptr = vmalloc(size);

    OPTIMIZER_HIDE_VAR(v_ptr);

    pr_info("%s %llx", __func__, (unsigned long long)v_ptr);
    v_ptr[0] = 0;

}

static noinline void sample_kmemleak_test_case(int type, int param)
{
    switch(type) {
        case SLAB_LEAK:
            kmalloc_leak(128, param, false); //alloc 128 byte and repeat param times 
            break;

        case PAGE_LEAK: 
            pagealloc_leak(param);
            break;

        case VMALLOC_LEAK:
            vmalloc_leak(2048);
            break;

        case PCPU_LEAK:
            break;

        case SLAB_ALLOC_FREE:
            kmalloc_leak(128, param, true); //alloc 128 byte and free repeat param times 
            break;

        default :
            pr_info("undef error type %d\n", type);
            break;
    }
    pr_info("%s type %d\n", __func__, type);
}

static noinline ssize_t sample_kmemleak_testcase_write(struct file *filp, const char __user *buf,
                   size_t len, loff_t *off)
{
    char kbuf[64] = {0};
    int ntcase;
    int nparam;
    int ret = 0;
    if(len > 64) {
        len = 64;
    }
    
    if (copy_from_user(kbuf, buf, len) != 0) {
        pr_info("copy the buff failed \n");
        goto done;
    }

    ret = sscanf(kbuf, "%d %d", &ntcase, &nparam);

    if (ret <= 0) {
        pr_err("should enter 2 param, first is test case type, second is param\n");
        goto done;
    }

    sample_kmemleak_test_case(ntcase, nparam);
done:
    return len;
}

static struct file_operations sample_kmemleak_fops = {
    .owner  =   THIS_MODULE,
    .write  =   sample_kmemleak_testcase_write,
    .llseek =   noop_llseek,
};

static struct miscdevice sample_kmemleak_misc = {
    .minor  = MISC_DYNAMIC_MINOR,
    .name   = "sample_kmemleak_test",
    .fops   = &sample_kmemleak_fops,
};

static int __init sample_kmemleak_start(void) 
{
    int ret;

    ret = misc_register(&sample_kmemleak_misc);
    if (ret < 0) {
        printk(KERN_EMERG " sample_kmemleak test register failed %d\n", ret);
        return ret;
    }

    printk(KERN_INFO "sample_kmemleak test register\n");
    return 0;
}

static void __exit sample_kmemleak_end(void) 
{ 
    misc_deregister(&sample_kmemleak_misc);
} 

MODULE_LICENSE("GPL");
MODULE_AUTHOR("geek");
MODULE_DESCRIPTION("A simple kmemleak test driver!");
MODULE_VERSION("0.1");
 
module_init(sample_kmemleak_start);
module_exit(sample_kmemleak_end);

4.2 定位泄漏

加载内存泄漏测试程序
/test # insmod kmemleak_driver.ko 

触发kmalloc 128 泄漏 100000次
/test # echo 0 100000 > /dev/sample_kmemleak_test 


利用工具slabinfo按的size排序查看 slub object,可以查看 space 总占用大小,及objects个数判断泄漏点 为kmalloc-128;
/test # ./slabinfo -S
Name                   Objects Objsize           Space Slabs/Part/Cpu  O/S O %Fr %Ef Flg
kmalloc-128             100331     128           25.6M       3136/1/0   32 1   0  49 U
inode_cache               6649     616            4.7M        290/1/0   23 2   0  86 aU
kernfs_node_cache        23966     128            4.6M       1142/1/0   21 0   0  65 U
dentry                    4041     192            1.0M        127/1/0   32 1   0  74 aU
kmalloc-1k                 384    1024          786.4K         24/0/0   16 3   0  50 U
kmalloc-512                417     512          458.7K         14/2/0   32 3  14  46 U
task_struct                102    3776          425.9K         13/2/0    8 3  15  90 U
kmalloc-2k                  93    2048          393.2K         12/1/0    8 3   8  48 U
kmalloc-4k                  46    4096          393.2K         12/1/0    4 3   8  47 U
radix_tree_node            596     576          393.2K         24/1/0   25 2   4  87 aU
kmalloc-192               1398     192          385.0K         47/1/0   30 1   2  69 U
kmalloc-256                472     256          245.7K         15/1/0   32 2   6  49 U
pool_workqueue             282     512          229.3K         14/1/0   21 2   7  62 U
sighand_cache              102    2080          229.3K          7/2/0   15 3  28  92 AU
biovec-max                  40    4096          196.6K          6/1/0    7 3  16  83 AU

......

如果没有工具,直接通过/proc/slabinfo节点来确认
/test # cat /proc/slabinfo 
slabinfo - version: 2.1
# name            <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>
......
kmalloc-4k            49     50  12288    2    8 : tunables    0    0    0 : slabdata     25     25      0
kmalloc-2k            94     95   6144    5    8 : tunables    0    0    0 : slabdata     19     19      0
kmalloc-1k           383    390   3072   10    8 : tunables    0    0    0 : slabdata     39     39      0
kmalloc-512          417    441   1536   21    8 : tunables    0    0    0 : slabdata     21     21      0
kmalloc-256          472    483    768   21    4 : tunables    0    0    0 : slabdata     23     23      0
kmalloc-192         1400   1404    296   27    2 : tunables    0    0    0 : slabdata     52     52      0
kmalloc-128       100331 100352    256   32    2 : tunables    0    0    0 : slabdata   3136   3136      0
//这里也可以通过num_objs * objsize的大小排序来确认是kmalloc-128出现泄漏
kmalloc-96           496    540    200   20    1 : tunables    0    0    0 : slabdata     27     27      0
kmalloc-64           877    928    256   32    2 : tunables    0    0    0 : slabdata     29     29      0
kmalloc-32           725    750    160   25    1 : tunables    0    0    0 : slabdata     30     30      0
kmalloc-16           927    928    128   32    1 : tunables    0    0    0 : slabdata     29     29      0
kmalloc-8           1614   1620    112   36    1 : tunables    0    0    0 : slabdata     45     45      0
kmem_cache_node      211    224    256   32    2 : tunables    0    0    0 : slabdata      7      7      0
kmem_cache           211    231    384   21    2 : tunables    0    0    0 : slabdata     11     11      0
......

查看具体的泄漏点
/test # cat /sys/kernel/debug/slab/kmalloc-128/alloc_traces
 100000 kmalloc_leak.constprop.0+0x54/0x80 [kmemleak_driver] age=12920/12945/12972 pid=95 cpus=3
        __kmem_cache_alloc_node+0xf4/0x2a4
        kmalloc_trace+0x20/0x2c
        kmalloc_leak.constprop.0+0x54/0x80 [kmemleak_driver]
        sample_kmemleak_test_case+0x9c/0xa8 [kmemleak_driver]
        sample_kmemleak_testcase_write+0xb0/0x12c [kmemleak_driver]
        vfs_write+0xc8/0x300
        ksys_write+0x74/0x10c
        __arm64_sys_write+0x1c/0x28
        invoke_syscall+0x48/0x110
        el0_svc_common.constprop.0+0x40/0xe0
        do_el0_svc+0x1c/0x28
        el0_svc+0x40/0xe4
        el0t_64_sync_handler+0x120/0x12c
        el0t_64_sync+0x190/0x194

     88 set_kthread_struct+0x38/0xc4 waste=1408/16 age=13270/17638/17797 pid=2 cpus=0-2
        __kmem_cache_alloc_node+0xf4/0x2a4
        kmalloc_trace+0x20/0x2c
        set_kthread_struct+0x38/0xc4
        copy_process+0x7a0/0x145c
        kernel_clone+0x68/0x368
        kernel_thread+0x80/0xb4
        kthreadd+0x144/0x1c4
        ret_from_fork+0x10/0x20
......

五、小结

5.1、性能分析

内存损耗比较大,从slabinfo也可以看到打开slub debug相关的config之后(/proc/slabinfo)下objsize大小变化,未开启时kmalloc-128 objsize就是128,开启debug后变成了384;

5.2 一个标准的slub泄漏定位方法

1、先确认slub存在内存泄漏

启动时记录/proc/meminfo 中SUnreclaim size 大小, 假设此时初始值为A ,然后每半小时或1小时检测SUnreclaim size 大小,假设为B, 如果B - A 增量超过600M 表示存在SUnreclaim 内存泄漏, 这个600M 可以根据实际项目,内存大小来综合设定

2、细化内存泄漏类型

如果版本存在SLUB内存泄漏, 抓取/proc/slabinfo或者使用slabinfo 工具来确认泄漏的slub 类型;

可以使用slabinfo -S 指令,或者直接通过/proc/slabinfo 中<num_objs> <objsize> 这两列信息做一个乘积,在excel 排序一下即可 (注意:这里的size 统计是不区分unreclaim的)

3、开启指定slub 类型泄漏的debug

例子:调试 kmalloc-XXXX和dentry相关slub leak调试:

slub_debug=U,kmalloc-*,dentry

或者全开:slub_debug=U

4、确认泄漏调用栈

等待内存泄漏一段时间后,执行下面即可确认slub泄漏调用栈及泄漏次数(乘上object的size即泄漏大小)

cat /sys/kernel/debug/slab/XXX/alloc_traces

5.3 优化和改进

商用版本上默认可以打开:CONFIG_SLUB_DEBUG=y, 不开CONFIG_SLUB_DEBUG_ON=y(或者参数传递slub_debug开启)对性能是无影响的;

缺陷: 无法开始时默认关闭,出现问题后动态打开,当前只能在发现版本存在slub内存泄漏后可以通过修改启动参数来控制调试开关,再来定位泄漏点;

如果问题复现概率极低,商用版本一直打开slub泄漏检测的话对用户的内存还是有较大影响,一般来说size占用是未开slub debug的2~4倍 。

不能动态开启的原因主要是 kmalloc的object 及size(是否有调试的metadata)在初始化时就确立了,后面无法修改object 和size,或者控制flag开关。

优化方案:当前android厂商针对这种情况也做了一些改进方案;利用vendor hook机制,在一个外部驱动中通过vendor hook机制动态修改kmalloc_caches,及获取 alloc trace即可完成针对kmalloc的动态调试;其核心思想是构建一套开启debug的kmem_cache, 然后替换原始的kmalloc_caches, 后续kmalloc就会走开启debug的kmem_cache,然后就可以定位问题

关键代码(从android kernel 5.15 拷贝,非android的只能仿照下自己加一下注册函数了):
mm/slab_common.c
//kmalloc分配时会根据size大小及flag类型去选择用kmalloc_caches中的哪一个
struct kmem_cache *kmalloc_slab(size_t size, gfp_t flags)
{
    unsigned int index;
    struct kmem_cache *s = NULL;

    if (size <= 192) {
        if (!size)
            return ZERO_SIZE_PTR;

        index = size_index[size_index_elem(size)];
    } else {
        if (WARN_ON_ONCE(size > KMALLOC_MAX_CACHE_SIZE))
            return NULL;
        index = fls(size - 1);
    }    

    trace_android_vh_kmalloc_slab(index, flags, &s); //1.注册这里vendor hook可以替换掉后面的kmalloc_cache
    if (s)
        return s;

    return kmalloc_caches[kmalloc_type(flags)][index];
}



mm/slub.c中
static void set_track(struct kmem_cache *s, void *object,
            enum track_item alloc, unsigned long addr)
{
    struct track *p = get_track(s, object, alloc);

    if (addr) {
#ifdef CONFIG_STACKTRACE
        unsigned int nr_entries;

        metadata_access_enable();
        nr_entries = stack_trace_save(kasan_reset_tag(p->addrs),
                          TRACK_ADDRS_COUNT, 3);
        metadata_access_disable();

        if (nr_entries < TRACK_ADDRS_COUNT)
            p->addrs[nr_entries] = 0; 
#endif
        p->addr = addr;
        p->cpu = smp_processor_id();
        p->pid = current->pid;
        p->when = jiffies;
        trace_android_vh_save_track_hash(alloc == TRACK_ALLOC, p);  //2.注册这里外部的ko即可获取到调用栈
    } else {
        memset(p, 0, sizeof(struct track));
    }    
}

ko中的实现基本就是将slub.c,slab_common.c中的依赖的结构体,函数等重写一下,注册好上面的两个hook函数,一个替换掉kmalloc_caches, 一个存储slub分配的调用栈,剩下的就是处理好编译报错,将/sys/kernel/debug/slab/XXX/alloc_traces 的实现用在新增的ko中,增加调试节点,即可完成动态开启slubdebug的功能。

参考

极致Linux内核:Linux内存:块分配器slab、slob和slub

SLUB DEBUG原理

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/include/linux/slub_def.h?h=v6.6.23&id=bb192ed9aa7191a5d65548f82c42b6750d65f569

Linux 内存管理新特性 - Memory folios 解读 | 龙蜥技术

五花肉:内存管理特性分析(七):Linux内核复合页(Compound Page)原理分析

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

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

相关文章

虚良SEO多口子权重蜘蛛池怎么正确使用

一、蜘蛛池的工作原理 蜘蛛池的核心在于通过大量的页面和内容&#xff0c;提高网站的搜索引擎排名&#xff0c;从而获得更多的流量和曝光机会。这种策略通常被用于网站推广和SEO优化。通过将网站链接发布到蜘蛛池中&#xff0c;可以增加网站的曝光率&#xff0c;吸引更多的搜索…

数据结构与算法--稀疏数组

1.引入 比如在编写五子棋时要实现存盘退出和继续上盘的功能。 如果使用二维数组来记录&#xff0c;每行每列&#xff0c;白子对应2&#xff0c;黑子对应1&#xff0c;默认值对应0.然后这里黑子对应二维数组a[1][2]。白子对应二维数组a[2][3]。 如果棋子很少&#xff0c;那么这…

AtCoder Regular Contest 176 C. Max Permutation(计数 分类讨论)

题目 思路来源 乱搞ac 题解 1. 如果有边的权值是1&#xff0c;意味着有两个点的权值都是1&#xff0c;无解 2. 如果一个点i被多个max条件控制&#xff0c;它的值不能超过这些max里最小的那个&#xff0c;记做up[i] 3. 如果同一个权值w对应的边不少于2条&#xff0c;这些边…

Spring Task学习记录

介绍 cron表达式 cron表达式在线生成器 链接: link 入门案例 Component Slf4j public class MyTask {/*** 定时任务 每隔5秒触发1次*/Scheduled(cron "0/5 * * * * ?")public void executeTask(){log.info("定时任务开始执行&#xff1a;{}", new Date…

AtCoder Beginner Contest 173 F - Intervals on Tree(计数 树的性质 贡献)

题目 思路来源 洛谷题解AT_abc173_f Intervals on Tree 题解 - 洛谷专栏 题解 一棵树&#xff0c;考虑加边的过程&#xff0c;加一条边减少一个连通块 那么&#xff0c;逆向这个过程&#xff0c;没删一条边&#xff0c;就多一个连通块 树&#xff1a;点的个数边的个数1 森…

后端端口也可以直接在浏览器访问

比如在浏览器输入http://localhost:8078/hello/helloword访问的是后端的 RestController RequestMapping("/hello") public class HelloWord {RequestMapping("/helloword")public String helloWord(){return "hello word";} }浏览器将会返回

JavaEE——介绍 HTTPServlet 三部分使用与 cookie 和 session 的阐述

文章目录 一、HTTPServlet介绍其中的关键 三个方法 二、HTTPServletRequest(处理请求)1.分块介绍方法作用get 为前缀的方法字段中 含有 getParameter 字段 的方法(前后端交互)&#xff1a;字段中 含有 getHeader 字段 的方法&#xff1a; 2.解释前后端的交互过程3.使用 json 格…

科技感十足特效源码

源码介绍 科技感十足特效源码&#xff0c;源码由HTMLCSSJS组成&#xff0c;记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面 源码截图 源码下载 科技感十足特效源码

Python_AI库 Matplotlib的应用简例:绘制与保存折线图

本文默认读者已具备以下技能&#xff1a; 熟悉Python基础语法&#xff0c;以自行阅读python代码块熟悉Vscode或其它编辑工具的应用 在数据可视化领域&#xff0c;Matplotlib无疑是一个强大的工具。它允许我们创建各种静态、动态、交互式的可视化图形&#xff0c;帮助我们更好…

pyaibote--安卓自动化环境配置与基础的使用方法

前言 欢迎来到我的博客 个人主页:北岭敲键盘的荒漠猫-CSDN博客 pyaibote介绍 pyaibote是一个全新&#xff0c;强大的办公自动化库。 支持找图&#xff0c;识别像素等操作。 比appium快十倍。 文章介绍 有大佬给我提到这个库后&#xff0c;我来查看。然后发现这个库太新了&am…

Coursera: An Introduction to American Law 学习笔记 Week 04: Constitutional Law

An Introduction to American Law 本文是 https://www.coursera.org/programs/career-training-for-nevadans-k7yhc/learn/american-law 这门课的学习笔记。 文章目录 An Introduction to American LawInstructors Week 04: Constitutional LawKey Constitutional Law TermsSup…

redission原理笔记

加锁成功的线程&#xff0c;将UUID和线程id和key绑定&#xff0c; 加锁成功后&#xff0c;内部有一个看门狗机制&#xff0c;每隔十秒看下当前线程是否还持有锁&#xff0c;延长生存时间。 没有获取锁的就一直自旋等待&#xff0c;直到超时。 如果redis是主从同步的&#xff0…

Android Studio gradle 默认sourceSets配置

一. AS默认的sourceSets配置 sourceSets在Android插件中如何使用的&#xff1a;android {sourceSets {main {manifest.srcFile AndroidManifest.xmljava.srcDirs [src]resources.srcDirs [src]aidl.srcDirs [src]renderscript.srcDirs [src]res.srcDirs [res]assets.srcD…

Anti Rookit -- 检测隐藏进程

Anti Rookit 一&#xff1a;检测隐藏进程 引言 检测隐藏进程除了众所周知的枚举进程ID之外&#xff0c;还有枚举句柄表的方式。不过今天给大家带来的是第三种方法。 探究 应用层通过接口 C r e a t e P r o c e s s \textcolor{cornflowerblue}{CreateProcess} CreateProcess…

现代信号处理7_最小二乘(CSDN_20240428)

最小二乘法最早由高斯在18世纪提出&#xff0c;几百年以来&#xff0c;这种方法一直被广泛应用。 最小二乘简介 这里是研究最小二乘的起点。随机变量只能存在与理论计算中&#xff0c;我们在工程实践中对随机变量的认识与理论计算中得到的关于随机变量的各种性质相比&#xff…

Penpad 再获 Animoca Brands 投资,全新生态历程

Penpad是Scroll生态的LaunchPad & Yield Aggregator平台&#xff0c;该平台近日在融资上取得了系列进展。据悉&#xff0c;Penpad在前不久率先获得了来自于Gate Labs以及Scroll联合创始人Sandy Peng的融资&#xff0c;并且在近日&#xff0c;其又获得了来自于知名加密投资机…

Coursera: An Introduction to American Law 学习笔记 Week 01: Tort Law

An Introduction to American Law 本文是 https://www.coursera.org/programs/career-training-for-nevadans-k7yhc/learn/american-law 这门课的学习笔记。 文章目录 An Introduction to American LawInstructors SyllabusWeek 01: Tort LawKey Tort Law TermsTort Law: Part …

2024.阳光能源追光计划暨大陆考察团交流分享会

近日大陆考察团抵达香港&#xff0c;受到了本司热情接待和安排。公司于4月27日下午举办了阳光能源追光计划主题交流会。 会上公司营销部总监张超&#xff0c;分享了阳光能源近几年的能源发展之路及公司新推出的追光计划&#xff0c;得到了大陆考察交流团团长杨国均先生的高度赞…

CSS3(响应式布局)

#过渡# 属性连写&#xff1a; transition: width 2s linear 1s; //前一个时间用于表示过渡效果持续时间&#xff0c;后一个时间用于表示过渡效果的延迟。 #转换# #2D转换# 和 #3D转换# 注意&#xff1a;其中angle对应单位为&#xff1a;deg #圆角# #边框# …

java中的泛型(三)——通配符

在前面的文章中我们简要介绍了泛型的概念以及泛型类和泛型方法的使用。在介绍泛型时我们说过在在java中一般用E、T、K、V、N、?这几个字母和符号来表示泛型&#xff0c;对于前面的几个字符它们的使用没有区别&#xff0c;只要注意它们所代表的类型就好。而对于最后一个&#x…
最新文章