Linux内核笔记005 - 越界访问内存,Linux内核处理过程

2023-10-27

本文转自网络文章,内容均为非盈利,版权归原作者所有。
转载此文章仅为个人收藏,分享知识,如有侵权,马上删除。
原文作者:jmpcall
专栏地址:https://zhuanlan.kanxue.com/user-815036.htm

1. 几个重要的数据结构和函数

  • 内存管理本质

        在Linux内核笔记004中,已经引出了"分配"的概念,它本质上就是在保护模式下,做两件事:

        ① 隔离同一进程已使用和未使用虚拟内存空间,以及整个系统的已使用和未使用物理内存空间

            具体实现:记录已每个进程已使用的虚拟地址,和整个系统已使用的物理地址,分配时使用未使用的。

        ② 隔离不同进程的用户空间

            具体实现:为每个进程0-3G范围的虚拟地址,建立独立的映射,映射不同的物理地址(内存共享除外)。

        现实生活中,所有的管理都依赖一道"关卡",同样的,软件层"内存管理"的实现,依赖CPU硬件层的"地址映射"特性。比如,在实模式状态下,每个程序,都可以用指令中的地址,直接访问到物理地址(比如Linux内核笔记004中记录的"ljmp 0x100000"跳转指令),就没有这道"关卡"。

 

  • 物理地址管理

        之前的笔记,已经详细描述了映射过程,包括CPU内部执行"段式/页式地址映射"的过程,以及Linux内核为"0xC0000000 - 0xC0000000+8MB"这块虚拟空间建立映射的过程,紧接着的学习,就是关于内核对"已使用/未使用"地址的管理,以下为物理地址管理相关的结构:

        ① struct page

typedef struct page {
    struct list_head list;
    struct address_space *mapping;
    unsigned long index;
    struct page *next_hash;
    atomic_t count;
    unsigned long flags;  /* atomic flags, some possibly updated asynchronously */
    struct list_head lru;
    unsigned long age;
    wait_queue_head_t wait;
    struct page **pprev_hash;
    struct buffer_head * buffers;
    void *virtual; /* non-NULL if kmapped */
    struct zone_struct *zone;
} mem_map_t;

        这个结构,跟记录"已使用/未使用"有什么关系呢?

        Linux内核,利用的是80386的页式管理,所以分配释放的最小单位必须是"页",每个4K倍数的地址,都是一页内存的开始,如果只是记录"已使用/未使用",使用一个位图即可,每个位的0/1值代表相应页的"已使用/未使用"状态,然而,内存管理还需要记录其它很多信息,从而定义了以上结构,里面的成员,随着后续的学习,都会接触到,暂时不用关心。

        此外,内核在启动时,根据实际内存的大小,创建了一个mem_map[]数组,数组的每个成员都是一个struct page"对象",0下标对应"0地址页"信息,1下标对应"4K地址页"信息..,所以struct page中没有表示页地址的成员,另外,"已使用/未使用"状态,是通过list成员挂在"已使用区/未使用区"区分。

start_kernel()
 |- setup_arch()
     |- paging_init()
         |- free_area_init()
             |- free_area_init_core()
                 |  // 根据实际的物理内存大小,创建mem_map[]数组(*gmap == mem_map)
                 |- *gmap = alloc_bootmem_node()

        一定要注意,struct page"对象",跟页面本身不是同一个东西,它只是用于记录一块4K大小的物理内存的使用情况而已,它们在位置上也没有任何联系。

 

        ② struct zone_struct

            Linux内核将所有物理页面,划分成三个管理区:ZONE_DMA、ZONE_NORMAL、ZONE_HIGHMEM。

            为什么要划分一个ZONE_DMA管理区?

            DMA设计的用意是:磁盘数据往内存的读写,不用CPU参与。那么,DMA本身就有访问内存的能力,同实模式/保护模式类似,DMA访问的地址,可以直接是物理地址,也可以是需要经过映射的虚拟地址,取决于内存管理单元(MMU)是单独实现,还是集成在CPU内部实现。80386就没有设计单独的MMU,所在DMA直接访问物理内存。

            首先,由于有些外设,不能访问过高的地址,所以要在低地址区,划分一块ZONE_DMA管理区,专门用于DMA,避免被其它程序占用。

            其次,如果某个外设希望通过DMA访问连续8K的内存,那就需要两个连续的物理页面,而不能像经过MMU那样,只要保证虚拟地址连续即可,前4K映射到一个物理页面,后4K映射到另一个相隔很远的物理页面都没关系,所以也要单独划分一块ZONE_DMA管理区,方便保证这一点。

            最后,整个内存也有被用完的时候,单独划分一块ZONE_DMA管理区,也能保证DMA始终有内存可用。

            除了ZONE_DMA管理区,在实际内存大于1G时,还会划分一个ZONE_HIGHMEM管理区(暂不关心,后期学习),其余部分则为ZONE_NORMAL管理区。

            每个管理区信息对应的结构如下:

typedef struct zone_struct {
    /*
     * Commonly accessed fields:
     */
    spinlock_t      lock;
    unsigned long      offset;
    unsigned long      free_pages;
    unsigned long      inactive_clean_pages;
    unsigned long      inactive_dirty_pages;
    unsigned long      pages_min, pages_low, pages_high;
 
    /*
     * free areas of different sizes
     */
    struct list_head   inactive_clean_list;
    free_area_t     free_area[MAX_ORDER];
 
    /*
     * rarely used fields:
     */
    char            *name;
    unsigned long      size;
    /*
     * Discontig memory support fields.
     */
    struct pglist_data *zone_pgdat;
    unsigned long      zone_start_paddr;
    unsigned long      zone_start_mapnr;
    struct page        *zone_mem_map;
} zone_t;

        ③ struct pglist_data

            如下图所示,CPU访问不同内存条,代价是不一样的,有的需要跨总线,有的不需要,这种情况叫做"非均匀存储结构(NUMA)"。

            

            比如下面是一台机器的CPU信息,"Socket(s)"表示实际插在主板上的CPU个数为2,"Core(s) per socket"表示每个CPU上的物理核数为8,"Thread(s) per core"表示每个物理核开了2个超线程,所以总共有32个核,这些核就分别靠近两个不同的NUMA节点。

            

            Linux内核,会对不同NUMA节点中的内存,进行独立管理。假设有2块属于不同NUMA节点的内存条,大小都是2G,将整个4G内存看作一个整体也是没问题的,但是Linux内核是将这2个2G内存,分别划分成3个管理区,从而在代码中定义了struct pglist_data结构,用于记录一个NUMA节点的信息。

typedef struct pglist_data {
    zone_t node_zones[MAX_NR_ZONES];
    zonelist_t node_zonelists[NR_GFPINDEX];
    struct page *node_mem_map;
    unsigned long *valid_addr_bitmap;
    struct bootmem_data *bdata;
    unsigned long node_start_paddr;
    unsigned long node_start_mapnr;
    unsigned long node_size;
    int node_id;
    struct pglist_data *node_next;
} pg_data_t;

            到此为止,可以得出一个结论:利用struct pglist_data结构,可以表达系统中有多少个NUMA节点,利用struct zone_struct结构,可以表达每个NUMA节点中有哪些管理区,利用struct page结构,可以表达每个管理区包含的物理页面,最终相当于描述了一个物理页面"仓库",物理页面的管理,也正是对这个"仓库"的管理。

 

        ④ 伙伴算法

            struct zone_struct有一个数组成员:

free_area_t     free_area[MAX_ORDER];    // MAX_ORDER为10

            数组的每个成员,又是一个struct free_area_struct"对象":

typedef struct free_area_struct {
    struct list_head   free_list;
    unsigned int       *map;
} free_area_t;

            每个struct free_area_struct"对象"包含一个链表头,用于挂接连续的"空闲页面块"(通过页面块中首个页面对应的struct page"对象"的list成员),并且free_area[0]、free_area[1]..、free_area[9]的free_list,分别用于挂接大小为2、4..、1024(2^10)的连续页面块(所以可以分配的最大连续页面块为1024*4K=4M)。

            虽然保护模式下,程序使用的是虚拟地址,即使分配超大块的内存时,只要保证虚拟地址是连续的即可,映射到的物理页面是不是连续,对于程序是"透明"的,但是"分配"发生频率超高,并且经常需要映射到多个物理页面,那么付出少量的代价,维持尽量多的连续物理页面,能在"分配"这一面获取非常大的效率收益。

            伙伴算法就是用于快速将小页面块,合并为大页面块:

        

            上图是我绞尽脑汁想到一种理解的方法:假设实际内存最开始16个页面的状态如上图,蓝色表示已分配、白色表示未分配,然后先别管抽象,无脑的跟着以下步骤去做:

            将第一行(2n, 2n+1)下标处的内容(即(0,1)、(2,3)..伙伴算法不会将(1,2)、(3,4)..当成伙伴),两两圈在一起,它们各自属于一对伙伴。伙伴都忙(蓝色数字),表示其中一个释放,不可以合并成块,在下一行中用蓝色的0表示;伙伴都闲(黑色数字),表示已经成块,不需要再次合并,在下一行中用黑色的0表示;否则等正在忙的那个伙伴被释放时,就可以合并成块,在下一行中用蓝色的1表示(总之下一行的值,是由上一行中两个伙伴的颜色决定,而不是值)。

            然后依次对第二行、第三行..做同样的操作,直到只剩一个数字,无法组成伙伴的那一行。此时再去验证每一个0、1,比如:

            order(0)中的第3个1:page4已分配,page5空闲,当page5释放后就可以和page4合并;

            order(1)中的第2个0:page4-7有3个已分配页面,只释放其中一个,不能进行合并;

            order(2)中的第2个1:page8-15,只有page11已分配,当它释放后,就可以合并成一个8页面的空闲块;

            ...

            然后,是不是就理解伙伴算法了?

            伙伴算法用于释放时,将小块合并成大块,并将其移动到更高下标的free_area[]中,直到最大支持的1024个。而分配时,可能会将大块切分成小块:比如分配连续2个物理页面,程序会优先到free_area[0]中找,如果为空,就到free_area[1]中找,假设还为空,就到free_area[2]中找,假设不再为空了,此时就找到一个8页面空闲块,程序会将它切分成两个4页面块,其中一个挂接到free_area[1],另一个继续切分成2个2页面块,其中一个挂接到free_area[0],剩余一个就可以作为分配到的2个连续物理页面了。

 

        ⑤ 页表项(PTE)低12位

            页表项用于指向最终映射到的物理页面,而物理页面的地址,都是按4K对齐(因为第一个页面的地址为0,每个页面的大小为4K),所以CPU只把PTE的高20位当作地址(左移12位即可),低12位用于PTE所指页面的属性,在后续的换入换出管理中,就可以看到对这些属性的利用。

            另外,每个目录项指向的也是页面,并且,它指向的页面都被内核当作页表使用。

 

  • 虚拟地址管理

        虚拟地址也是资源,每个进程可以独立使用的有只有3G个地址,即使在64位系统中,数量就目前来讲已经不成问题了,那也得避免逻辑上的歧义,不能用同一个逻辑地址指向两个不同"对象",所以至少要对正在使用的虚拟地址,做记录管理,以下为虚拟地址管理相关的结构和函数:

        ① struct vm_area_struct

            虚拟地址的分配,依赖程序本身,每个程序编译后,就有一部分要使用的虚拟地址是确定的了,比如代码段、数据段占用的虚拟地址,对于动态分配,要分配的连续大小也是由程序指定的,根据这样理解,虚拟地址管理的逻辑,本身就已经很固定了,所以整个逻辑,比对物理页面的管理要简单的多,struct vm_area_struct结构,就是程序指定要分配多大的内存时,内核分配这样一个"对象",将该虚拟地址区域记录下来,各个成员的含义在书中有详细的说明,笔记中不再重复。

struct vm_area_struct {
    struct mm_struct * vm_mm;    /* VM area parameters */
    unsigned long vm_start;
    unsigned long vm_end;
 
    /* linked list of VM areas per task, sorted by address */
    struct vm_area_struct *vm_next;
 
    pgprot_t vm_page_prot;
    unsigned long vm_flags;
 
    /* AVL tree of VM areas per task, sorted by address */
    short vm_avl_height;
    struct vm_area_struct * vm_avl_left;
    struct vm_area_struct * vm_avl_right;
 
    /* For areas with an address space and backing store,
     * one of the address_space->i_mmap{,shared} lists,
     * for shm areas, the list of attaches, otherwise unused.
     */
    struct vm_area_struct *vm_next_share;
    struct vm_area_struct **vm_pprev_share;
 
    struct vm_operations_struct * vm_ops;
    unsigned long vm_pgoff;       /* offset in PAGE_SIZE units, *not* PAGE_CACHE_SIZE */
    struct file * vm_file;
    unsigned long vm_raend;
    void * vm_private_data;       /* was vm_pte (shared mem) */
};

        ② find_vma()函数

            查找某个虚拟地址所在的虚拟区间。

 

2. 越界访问堆区

    接下来,书中设定了一个情景:某个应用程序存在bug,它执行时,先通过mmap()函数获取一块内存,但会在munmap()之后,继续访问这块内存中的地址,从而发生"段错误"。

    刚开始学习内核的时候,我始终想不明白内核为什么比应用程序"有权"(Linus写的代码可以执行CPU特权指令,我写的却不可以),以及它是如何将"权力"控制在自己手里。其实之前的笔记已经举了一些例子,用于感性的体会,这里再相对完整的理一遍:

    内核对"权力"的把握,其实就是对CPU状态和内核空间控制权的把握:

    ① 开机时,CPU执行的入口是内核代码,所以说"权力"一开始就给了内核;

    ② 内核在为应用程序的执行,创造好准备条件之后,是先将CPU切换到低权限状态,才跳转,所以用户态的代码就不能执行特权指令;

    ③ 应用程序的代码在低权限状态执行,如果希望CPU回到高权限状态,必须穿过一道内核设置的"门"(详细内容在书中第三章——中断、异常和系统调用),权限提高的同时,指令也必须回到内核代码执行;

    ④ 应用程序的代码在低权限状态执行,就无法利用特权指令,建立/修改虚拟地址到物理地址的映射状态,再加上虚拟地址到物理地址映射的过程,CPU会进行权限检查,从而内核又掌握了对内核空间的"控制权",虽然每个进程都可以访问内核空间,但同样必须是穿过一道"门"进入内核态,调用内核的接口访问;

    ⑤ 内核除了将内核空间设置为"高权限才能访问"外,同时也保证将每一个内核空间中的虚拟地址,映射到的相同的物理地址,让内核空间成为所有进程的"公共空间"。

    综上五点,最终的效果就是,任何进程都必须穿过"门",回到内核态,调用内核接口,进入到公共的内核空间,才能修改/获取整个系统中某个全局的管理信息,应用程序如果有bug,也只能影响到自己用户空间映射的那块独立的物理内存。

    

 

    关于"门",仍然可以先感性的体会一下:进程如何才可以进入"门"?

  • 主动进入
#include <stdio.h>
 
int main()
{
        printf("hello\n");
        return 0;
}

        这个程序中调用的printf()函数,叫libc函数,libc库封装了很多"系统调用"函数(内核提供给应用程序的接口),比如执行"gcc test.c -g -Wall"编译上面的程序,然后执行"strace ./a.out",就可以列出来printf()内部调用了哪些"系统调用"函数,其中就有一个是write()函数,它在执行的过程中,就会切换到内核态,write()函数也可以不经过libc的封装直接调用,不管哪种方式,都属于应用程序主动进入内核态。

 

  • 被动进入

        如果只依赖应用程序主动进入内核态,那么,当应用程序执行到while(1){}循环里面的时候,CPU就只能永远为这个进程执行指令了,因为其它进程的信息都在内核空间,无法回到内核态,当然也就没有机会切换到其它进程。

        所以硬件设计了一个"时钟中断"的功能,每过一段时间,不管CPU目前在干什么,都把当前的状态保存下来,进入"内核态"执行一个"定时函数",这个函数是由内核设置。

        除了"时钟中断",如果应用程序访问一个还未映射到物理地址的虚拟地址,或者执行"除以0"这些异常操作,CPU也会自动切换到内核态,并且执行内核事先设置的"异常/陷阱"函数,这些虽然触发的源头是应用程序,但往往是"不经意"的,比如"/0"一切是程序有bug,然后由于"段错误"而被迫停止运行。

 

    到这里,就可以回到书中设定的情景了,由于地址的映射关系,已经被munmap()函数撤消了,再次访问时,CPU在执行映射过程中,必定会遇到值为0的目录项或页表项,这种情况下,CPU就会按照上面描述的,保存当前状态,切换到内核态,并且跳转到内核的do_page_fault()函数执行。

    跳转到do_page_fault(),完整和正规的描述,书中第三章有详细的介绍,目前直接分析do_page_fault()的执行过程,在分析之前,可以先看一下Linux内核对进程虚拟空间的布局(按照书中对代码逻辑的解释,在Linux-2.4.0版本的时候,应该是如下左图):

    

 

    do_page_fault()主要流程:

// 出现上述情景,CPU隐式跳转到do_page_fault()函数执行,并且自动把当时处于用户态/内核态、读/写访问信息,压栈作为error_code参数
do_page_fault()
 |  // CPU也会自动把访问出错的虚拟地址,存入CR2寄存器,这里取出,方便在C函数中使用
 |- __asm__("movl %%cr2,%0":"=r" (address))
 |- 获取内核为当前进程设置的进程管理结构,然后从该结构中获取虚拟地址管理结构
 |  // 暂时将上述隐式跳转到的函数,理解为中断函数,那么do_page_fault()本身就是中断函数,in_interrupt()表示跳转过来前,也是在中断函数
 |  // 所有应用程序,都有自己独立的虚拟空间,所以mm一定不为空,上述情景不满足以下判断条件
 |- if (in_interrupt() || !mm)
 |  // 当前进程一定能在已使用的用户空间中,找出结束地址大于出错地址的第一个区间,因为至少也有个栈区在最上方
 |- find_vma()
 |  // 当前情景找到的区间,会是从堆中动态分配的,并且起始地址也大于出错地址,因为出错地址所在的区间已经被munmap()函数撤消了
 |  // 从堆中分配的区间,增长方向向上,所以符合以下判断条件,goto到bad_area处执行
 |- if (!(vma->vm_flags & VM_GROWSDOWN))
 |  // 以下判断出错时是否在用户态,上述情景符合判断
 |- if (error_code & 4)
     |  // 设置软中断,让程序coredump(详细内容在书中第三章——中断、异常和系统调用)
     |- info.si_signo = SIGSEGV

 

    根据do_page_fault()的逻辑可以看出,访问没有映射的地址,必然会coredump,但按照平时的实际经验,又会发现,应用程序中越界访问堆区,有时候并不会coredump。那是因为应用程序使用的是libc中的malloc()/free(),调用free(),libc层并不一定立即调用内核的brk()函数,而是等到一定程度,才会真正撤消映射。

 

3. 越界访问栈区

    上述说明了内核对堆区越界访问(分配范围以外)的处理过程,书中接着又设定了一个对栈区越界访问的情景:

    Linux内核创建新进程时,会为新进程分配一块初始大小的栈区,当程序执行到某个状态,需要向栈中压入很多局部变量和函数参数时,初始分配的栈可能就不够用了,比如如同下图的状态,再向栈区存入一个变量,esp寄存器就要指向未分配区 了:

  

    需要注意的是,这种情景跟安全领域中的"栈溢出"不是一个概念。这个情景针对的是分配,影响的是esp的指向,并且程序也不一定存在bug,而"栈溢出"针对的是对变量的写操作,影响的是已分配栈空间的内容,并且程序一定是存在bug,比如:

#include <stdio.h>
#include <string.h>
 
int main()
{
    char buf[10] = { 0 };
 
    // 会在buf[10]处写一个'\0'字符,超出了数组的范围
    strcpy(buf, "0123456789");
    printf("%s\n", buf);
 
    return 0;
}

   回到书中设定的情景,do_page_fault()主要执行流程如下: 

do_page_fault()
 |  // 到达这个判断条件之前,和munmap()的情景一样,但这时找到的虚拟区间,就会是栈区了
 |- if (!(vma->vm_flags & VM_GROWSDOWN))
 |  // 这个情景也是在用户态访问地址的
 |- if (error_code & 4)
 |   |  // 压栈数据最多的是pusha指令,可以一次性压入32字节
 |   |  // 内核无法完全判断应用程序的逻辑错误,比如*(&局部变量-xx),也有可能访问到这个范围,那样程序一定会出现别的异常现象
 |   |- if (address + 32 < regs->esp)
 |  // 栈是向下增长,正常的压栈操作,不符合以上判断,执行到这里对栈空间的大小进行扩展
 |- expand_stack()
 |   |  // Linux对每个进程的栈空间,限制了大小,执行"ulimit -s"可以查看,超过就不能继续扩展了
 |   |- if (RLIMIT_STACK检查)
 |   |- 扩展虚拟区间大小
 |- 进入读写操作检查,产生异常时是写操作,而栈区肯定是可写的,所以通过检查
 |  // 为虚拟区间中刚才扩展的部分,分配物理地址并建立映射
 |- handle_mm_fault()
     |  // .org 0x1000
     |  // ENTRY(swapper_pg_dir)
     |  // 整个系统只需要一个目录项,在系统初始化阶段就确定位置了(见"Linux内核笔记004")
     |- pgd_offset()
     |  // 32位CPU上,不使用中间目录,所以还是返回目录页的地址,不影响映射过程
     |- pmd_alloc()
     |  // 一个页表可以容纳1024个页表项,第一个需要这个页表的分配操作,分配这个页表,其它的直接使用
     |- pte_alloc()
     |  // 以上获取的pte,可能是正在使用中的,它们它的低12位可以知道物理页面是否换出到交换分区了
     |  // 本次设定情景,由于访问的是之前没有分配过的内存,所以pte也一定是0
     |- handle_pte_fault()
         |- if (!pte_present(entry))
             |- if (pte_none(entry))
             |   |  // 本次情景会执行到这里
             |   |- do_no_page()
             |       |  // mmap()、交换分区,都需要虚拟空间和文件有联系,vma->vm_ops就包含一些文件操作的函数指针
             |       |- if (!vma->vm_ops || !vma->vm_ops->nopage)
             |       |   |  // 栈空间跟文件没有联系,所以本次情景会执行这里
             |       |   |- do_anonymous_page()
             |       |       |- if (write_access)
             |       |       |   |  // 分配物理页面,如果是读访问,先映射到ZERO_PAGE,写的时候再分配(COW)
             |       |       |   |- alloc_page()
             |       |       |   |- pte_mkwrite()
             |       |       |  // 让pte指令新分配的物理页面
             |       |       |- set_pte()
             |       |- vma->vm_ops->nopage()
             |- do_swap_page()

 

    内核执行完do_page_fault(),会再回到应用程序中压栈的那条指令执行,这样,就是压栈的那条指令,在应用程序"感受"不到的情况下,导致进程进入内核走了一圈,扩展了栈的大小后,又回到了这条指令,这个过程叫做"缺页异常"。

 

    最后顺便提一下,在以上的内容中,提到"中断",异常和中断,在从内核态返回用户态时,是有区别的:

    

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux内核笔记005 - 越界访问内存,Linux内核处理过程 的相关文章

  • 提高mysql导入速度[关闭]

    Closed 这个问题是与编程或软件开发无关 help closed questions 目前不接受答案 我有一个很大的数据库22GB 我曾经用过进行备份mysqldumpgzip 格式的命令 当我提取 gz 文件时 它会生成 sql文件的
  • 如何在文件夹中的 xml 文件中 grep 一个单词

    我知道我可以使用 grep 在这样的文件夹中的所有文件中查找单词 grep rn core 但我当前的目录有很多子目录 我只想搜索当前目录及其所有子目录中存在的所有 xml 文件 我怎样才能做到这一点 我试过这个 grep rn core
  • 如何将 elf 解释器(ld-linux.so.2/ld-2.17.so)构建为静态库?

    如果我的问题不准确 我深表歉意 因为我没有太多 Linux 相关经验 我目前正在构建一个 Linux 从头开始 主要遵循 linuxfromscratch org 版本的指南 7 3 我遇到了以下问题 当我构建可执行文件时 获取一个称为 E
  • 套接字发送调用被阻塞很长时间

    我每 10 秒在套接字上发送 2 个字节的应用程序数据 阻塞 但发送调用在下面的最后一个实例中被阻塞超过 40 秒 2012 06 13 12 02 46 653417 信息 发送前 2012 06 13 12 02 46 653457 信
  • 由于 abi::cxx11 符号导致的链接问题?

    我们最近收到一份报告 因为GCC 5 1 libstdc 和双 ABI http gcc gnu org onlinedocs libstdc manual using dual abi html 它似乎Clang 不知道 GCC 内联名称
  • 如何使用 VSCode 调试 Linux 核心转储?

    我故意从我使用 VSCode 编写的 C 应用程序生成核心转储 我不知道如何调试核心转储 有没有人愿意分享这方面的经验 更新 我相信我现在已经可以使用了 我为核心文件创建了第二个调试配置 我需要添加指向生成的转储文件的 coreDumpPa
  • 打印本周星期一的日期(在 bash 中)

    我想获取本周星期一的 YYYYMMdd 格式的日期 例如 今天是 20110627 从明天到周日 我仍然想打印周一 今天 的日期 然后下周重复这个过程 monday date dmonday Y m d last monday date d
  • 为什么无论 -rdynamic 如何,backtrace 都不包含 Objective-C 符号?

    Update 我正在 Linux 上使用 GNU 运行时 问题是not发生在带有 Apple 运行时的 MacOS 上 更新2 我在 MacOS 上编译了 GNU 运行时并用它构建了示例 该错误确实not发生在带有 GNU 运行时的 Mac
  • 当在 python linux 中执行命令 os.system() 时,在 python 中给出响应 yes/no

    考虑一个像这样的命令 yum install boto 当我在终端中执行时 要继续 会询问我是 否 我可以像这样用 python 回应它吗 os system yum install boto Next Yes 将通过相同的 python
  • 无法连接到 Azure Ubuntu VM - 公钥被拒绝

    我们在 Azure 上使用 Ubuntu VM 一段时间了 很少遇到任何问题 然而 其中一台虚拟机最近出现了问题 出乎意料的是 Ubuntu VM 开始拒绝公钥 ssh i azure key email protected cdn cgi
  • 如何使用 nohup 获取正在运行的程序列表

    我正在通过 SSH 连接访问运行 CentOS linux 发行版 的服务器 由于我无法始终保持登录状态 因此我使用 nohup command 来运行我的程序 我找不到如何获取我开始使用 nohup 的所有程序的列表 工作 只有在我注销之
  • 如何在 Linux 中向热敏打印机发送 ESC/POS 命令

    我正在尝试在热敏打印机上发送 ESC POS 命令 但每当我发送它们时 热敏打印机都会将它们打印为文本 而不是作为命令执行它们 我在 prn 文件中编写这些命令 每当我执行 lp 命令来打印文件时 这些 prn 文件也会被打印 但作为文本
  • C++ Linux GCC 应用程序中的 GUID

    我有很多服务器运行这个 Linux 应用程序 我希望他们能够生成一个碰撞概率较低的 GUID 我确信我可以从 dev urandom 中提取 128 个字节 这可能没问题 但是有没有一种简单易用的方法来生成与 Win32 更等效的 GUID
  • 点击界面没有出现

    我决定添加一个点击界面并在我的代码中使用它 但我能够得到它的状态 sudo ip f link tuntap add tap10 mode tap sudo ip link set tap10 up 之后当我执行 ip link 时 tap
  • EULA 接受 Bash 脚本

    我有一个尝试安装垃圾箱的脚本 除了 bin 在 more 中打开 EULA 之外 一切正常 在脚本再次开始并自行完成安装之前 您必须手动 ctrl c 退出此 more 实例 因为这更多的是逃离 shell 所以脚本在打开后不知道要运行什么
  • 重新链接匿名(未链接但打开)文件

    在 Unix 中 可以创建匿名文件的句柄 例如 使用 creat 创建并打开它 然后使用 unlink 删除目录链接 留下一个带有 inode 和存储的文件 但没有可能的方法重新打开它 此类文件通常用作临时文件 通常这就是 tmpfile
  • 进程如何知道它已收到信号

    如果我错了 请纠正我 以下是我对信号的理解 据我所知 信号生成 和信号传递有2个不同 事物 为了产生信号 操作系统只是在位数组中设置一个位 在过程控制中维护 工艺块 PCB 每一位 对应于特定信号 当设置一个位时 这意味着 该位对应的信号为
  • sudo pip install python-Levenshtein 失败,错误代码 1

    我正在尝试在 Linux 上安装 python Levenshtein 库 但每当我尝试通过以下方式安装它时 sudo pip install python Levenshtein 我收到此错误 命令 usr bin python c 导入
  • PHP 日志文件颜色

    我正在编写一个 PHP 日志文件类 但我想为写入文件的行添加颜色 我遇到的问题是颜色也会改变终端的颜色 我想要实现的是仅更改写入日志文件的行的颜色 class logClass extends Singleton private funct
  • 在Linux中使用C/C++获取机器序列号和CPU ID

    在Linux系统中如何获取机器序列号和CPU ID 示例代码受到高度赞赏 Here http lxr linux no linux v2 6 39 arch x86 include asm processor h L173Linux 内核似

随机推荐

  • git 签出(恢复)指定文件

    在项目开发中 偶尔会因为误删文件或其他原因需要从git仓库中恢复某些文件 此篇文章将介绍如何通过git从历史提交记录 分支记录恢复指定文件 1 git checkout 说明 使用git checkout除了可以切换分支外 还可以签出指定文
  • Elasticsearch Java High Level REST Client(Exists API)

    Exists API 如果文档存在 则existsAPI返回true 否则返回false Exists请求 它就像Get API一样使用GetRequest 支持所有可选参数 由于exists 只返回true或false 我们建议关闭获取
  • 目标检测之Yolov3与Anchor-Free

    原文 目标检测之RCNN Yolo SSD RetinaNet与Anchor Free dagongji10的博客 CSDN博客 2 2 Yolo v3 2018 Yolo v3 论文比 Yolo v2 还要随意 具体优化内容主要有 bbo
  • 用python绘制曼彻斯特编码等八种常见数据编码方式的波形图

    用python绘制八种数据编码方式的波形图 2020春季北京航空航天大学计算机学院物联网引论课程作业 介绍八种常见数据编码方式并实践画出波形图 本文使用了python中的二维图像模块matplotlib 博主在信号与通信原理方面功底不深 如
  • deepin的踩坑问题与解决方案,以及使用分享(持续更新)

    笔者目前的电脑环境是Redmibook 14 AMD的锐龙版 R5 3700U 只有集成显卡 不同换环境下问题原因不一定相同 如驱动等兼容性问题 可以借鉴解决思路 但更多问题还是具有共性 Q1 u盘安装的时候 卡在蓝色背景图 无安装程序启动
  • e3 服务器虚拟机,e3 虚拟机

    e3 虚拟机 内容精选 换一换 Hypervisor能实现同一物理机上不同虚拟机之间的资源隔离 避免虚拟机之间的数据窃取或恶意攻击 保证虚拟机的资源使用不受周边虚拟机的影响 用户使用虚拟机时 仅能访问属于自己的虚拟机的资源 如硬件 软件和数
  • 【Ethernet】以太网卡LAN8720A分析和使用

    文章目录 1 LAN8720A简介 2 PHYAD 0 PHY地址配置 3 MODE 2 0 Mode配置 4 nINTSEL nINT REFCLKO配置 5 REGOFF 配置内部 1 2V电压源 6 SMI MDC MDIO 总线接口
  • android studio jdk se 8,java - Is JDK 1.8 fully supported by Android Studio? - Stack Overflow

    Actually you get all sorts of crazy Gradle errors when trying to build Android Studio projects with Java 8 like 2016 04
  • C++避坑——most vexing parse问题

    1 坑 的问题是什么 先看一段代码 class Functor public void operator std cout lt lt 我是线程的初始函数 lt lt std endl int main std thread t Funct
  • 第六章:认识Java的API-使用Java函数库

    该系列文章系个人读书笔记及总结性内容 任何组织和个人不得转载进行商业活动 第六章 认识Java的API 使用Java函数库 Java内置有数百个类 如果你知道如何从统称Java API的Java的函数库中查找所需功能 那就不用再造轮子了 核
  • 检测属性

    点上面关注免费学习前端知识 JavaScript对象可以看做属性的集合 我们经常会检测集合中成员的所属关系 判断某个属性是否存在于某个对象中 可以通过in运算符 hasOwnPreperty 和propertyIsEnumerable 方法
  • HTML常见标签总结

    目录 1 标题标签 2 段落标签 3 字体修饰标签 4 图片标签 5 超链接标签 6 表格标签 7 列表标签 8 表单标签 9 下拉菜单 10 多行文本框 1 标题标签 一级标题是 h1 h1 中间填上标题的内容 一共可以设置六级标题 数字
  • 【Git笔记】添加暂存区与提交本地库

    本文以提交 hello txt 为例 在开始之前普及一个快捷方式 在 Linux 中 文本复制为 esc gt yy 粘贴为 d 查看本地库状态 git status 当文本只停留在工作区时 本地状态如下 添加暂存区 git add hel
  • Naviact无法连接到MySQL数据库

    无法连接到MySQL数据库 省流版 服务器没有开MySQL的端口 之所以还要写一篇文章 是因为开端口只需要一分钟 而根据各种现象与线索推断出没有开端口则需要三个小时 问题背景 在服务器上安装了Linux虚拟机 然后在虚拟机上安装MySQL
  • 文本分类之模型初探

    这里写自定义目录标题 简说回归模型 逻辑回归模型介绍 逻辑回归模型应用 简说回归模型 回归模型是对统计关系进行定量描述的数学模型 研究的是因变量和自变量之间的关系 研究回归模型要用到回归方法 常见的回归方法有线性回归 逻辑回归 多项式回归等
  • C语言编写一个简单的选择题答题系统

    上个星期 我们老师给我们布置了一道非常变态的作业题 开始一看只有一道题 欣喜若狂 以为老师终于大发慈悲了一回 结果点开一看 长这样 顿时就傻了眼 对于一个刚接触C语言一两个月的小白来说 不能说不会做 只能说看不懂 哭笑 哭笑 但是经过几天的
  • html,css初学

    安装VSCODE 插件 live server html support html 然后为了更好地理解 请逐步输入 并及时查看效果 div class box h1 非顶级程序员 h1 h2 hhh h2 img src tes div
  • Git/Ctags/Vim/GDB基础笔记

    gt gt gt gt gt gt gt gt gt gt gt gt gt gt gt gt gt gt gt gt gt gt gt gt gt gt gt Git基础知识 gt gt gt gt gt gt gt gt gt gt g
  • linux rootfs.img的制作

    cramfs是只读压缩的文件系统 文件系统类型可以是ext2 ext3 什么的 cramfs和romfs只是一个文件系统类型 ramdisk相当于一块硬盘空间 可以理解为在内存中虚拟出一块硬盘来 所以它上面就可以有你linux支持的各种文件
  • Linux内核笔记005 - 越界访问内存,Linux内核处理过程

    本文转自网络文章 内容均为非盈利 版权归原作者所有 转载此文章仅为个人收藏 分享知识 如有侵权 马上删除 原文作者 jmpcall 专栏地址 https zhuanlan kanxue com user 815036 htm 1 几个重要的