主頁 > 作業系統 > Linux內核虛擬記憶體管理之匿名映射缺頁例外分析

Linux內核虛擬記憶體管理之匿名映射缺頁例外分析

2020-09-10 18:06:58 作業系統

今天我們就來討論下這種缺頁例外,讓大家徹底理解它,注:本文使用linux-5.0內核源代碼,文章分為以下幾節內容:

  1. 匿名映射缺頁例外的觸發情況

  2. 0頁是什么?為什么使用0頁?

  3. 源代碼分析

    3.1 觸發條件

    3.2 第一次讀匿名頁

    3.3 第一次寫匿名頁

    3.4 讀之后寫匿名頁

  4. 應用層實驗

  5. 總結

在講解匿名映射缺頁例外之前我們先要了解以下什么是匿名頁?與匿名頁相對應的是檔案頁,檔案頁我們應該很好理解,就是映射檔案的頁,如:通過mmap映射檔案到虛擬記憶體然后讀檔案資料,行程的代碼資料段等,這些頁有后備快取也就是塊設備上的檔案,而匿名頁就是沒有關聯到檔案的頁,如:行程的堆、堆疊等,還有一點需要注意:下面討論的都是私有的匿名頁的情況,共享匿名頁在內核演變為檔案映射缺頁例外(偽檔案系統),后面有機會我們會講解,感興趣的小伙伴可以看一看mmap的代碼實作對共享匿名頁的處理,

一,匿名映射缺頁例外的觸發情況

前面我們講解了什么是匿名頁,那么思考一下什么情況下會觸發匿名映射缺頁例外呢?這種例外對于我們來說非常常見:

1.當我們應用程式使用malloc來申請一塊記憶體(堆分配),在沒有使用這塊記憶體之前,僅僅是分配了虛擬記憶體,并沒有分配物理記憶體,第一次去訪問的時候才會通過觸發缺頁例外來分配物理頁建立和虛擬頁的映射關系,

2.當我們應用程式使用mmap來創建匿名的記憶體映射的時候,頁同樣只是分配了虛擬記憶體,并沒有分配物理記憶體,第一次去訪問的時候才會通過觸發缺頁例外來分配物理頁建立和虛擬頁的映射關系,

3.當函式的區域變數比較大,或者是函式呼叫的層次比較深,導致了當前的堆疊不夠用了,這個時候需要擴大堆疊,當然了上面的這幾種場景對應應用程式來說是透明的,內核為用戶程式做了大量的處理作業,下面幾節會看到如何處理,

二,0頁是什么?為什么使用0頁?

這里為什么會說到0頁呢?什么是0頁呢?是地址為0的頁嗎?答案是:系統初始化程序中分配了一頁的記憶體,這段記憶體全部被填充0,下面我們來看下0頁如何分配的:在arch/arm64/mm/mmu.c中:


    61 /*
    62  * Empty_zero_page is a special page that is used for zero-initialized data
    63  * and COW.
    64  */
    65 unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)] __page_aligned_bss;
    66 EXPORT_SYMBOL(empty_zero_page);

可以看到定義了一個全域變數,大小為一頁,頁對齊到bss段,所有這段資料內核初始化的時候會被清零,所有稱之為0頁,

那么為什么使用0頁呢?一個是它的資料都是被0填充,讀的時候資料都是0,二是節約記憶體,匿名頁面第一次讀的時候資料都是0都會映射到這頁中從而節約記憶體(共享0頁),那么如果有行程要去寫這個這個頁會怎樣呢?答案是發生COW重新分配頁來寫,

三,源代碼分析

3.1 觸發條件

當第一節中的觸發情況發生的時候,處理器就會發生缺頁例外,從處理器架構相關部分過渡到處理器無關部分,最終到達handle_pte_fault函式:

  3742 static vm_fault_t handle_pte_fault(struct vm_fault *vmf)
  3743 {
  3744         pte_t entry;
  ...
  3782         if (!vmf->pte) {
  3783                 if (vma_is_anonymous(vmf->vma))
  3784                         return do_anonymous_page(vmf);
  3785                 else
  3786                         return do_fault(vmf);
  3787         }

3782和3783行是匿名映射缺頁例外的觸發條件:

1.發生缺頁的地址所在頁表項不存在,

2.是匿名頁發生的,即是vma->vm_ops為空,

當滿足這兩個條件的時候就會呼叫do_anonymous_page函式來處理匿名映射缺頁例外,


  2871 /*
  2872  * We enter with non-exclusive mmap_sem (to exclude vma changes,
  2873  * but allow concurrent faults), and pte mapped but not yet locked.
  2874  * We return with mmap_sem still held, but pte unmapped and unlocked.
  2875  */
  2876 static vm_fault_t do_anonymous_page(struct vm_fault *vmf)
  2877 {
  2878         struct vm_area_struct *vma = vmf->vma;
  2879         struct mem_cgroup *memcg;
  2880         struct page *page;
  2881         vm_fault_t ret = 0;
  2882         pte_t entry;
  2883 
  2884         /* File mapping without ->vm_ops ? */
  2885         if (vma->vm_flags & VM_SHARED)
  2886                 return VM_FAULT_SIGBUS;
  2887 
  2888         /*
  2889         |* Use pte_alloc() instead of pte_alloc_map().  We can't run
  2890         |* pte_offset_map() on pmds where a huge pmd might be created
  2891         |* from a different thread.
  2892         |*
  2893         |* pte_alloc_map() is safe to use under down_write(mmap_sem) or when
  2894         |* parallel threads are excluded by other means.
  2895         |*
  2896         |* Here we only have down_read(mmap_sem).
  2897         |*/
  2898         if (pte_alloc(vma->vm_mm, vmf->pmd))
  2899                 return VM_FAULT_OOM;
  2904 
  ...

2885行判斷:發生缺頁的vma是否為私有映射,這個函式處理的是私有的匿名映射,

2898行 如何頁表不存在則分配頁表(有可能缺頁地址的頁表項所在的直接頁表不存在),

3.2 第一次讀匿名頁情況


  ...
  2905         /* Use the zero-page for reads */
  2906         if (!(vmf->flags & FAULT_FLAG_WRITE) &&
  2907                         !mm_forbids_zeropage(vma->vm_mm)) {
  2908                 entry = pte_mkspecial(pfn_pte(my_zero_pfn(vmf->address),
  2909                                                 vma->vm_page_prot));
  2910                 vmf->pte = pte_offset_map_lock(vma->vm_mm, vmf->pmd,
  2911                                 vmf->address, &vmf->ptl);
  2912                 if (!pte_none(*vmf->pte))
  2913                         goto unlock;
  2914                 ret = check_stable_address_space(vma->vm_mm);
  2915                 if (ret)
  2916                         goto unlock;
  2917                 /* Deliver the page fault to userland, check inside PT lock */
  2918                 if (userfaultfd_missing(vma)) {
  2919                         pte_unmap_unlock(vmf->pte, vmf->ptl);
  2920                         return handle_userfault(vmf, VM_UFFD_MISSING);
  2921                 }
  2922                 goto setpte;
  2923         }
  ...
  2968 setpte:
  2969         set_pte_at(vma->vm_mm, vmf->address, vmf->pte, entry);
  

2906到2923行是處理的是私有匿名頁讀的情況:這里就會用到我們上面將的0頁了,

2906和 2907行判斷是否是由于讀操作導致的缺頁而且沒有禁止0頁,

2908-2909行是核心部分:設定頁表項的值映射到0頁,

我們主要研究這個陳述句:pfn_pte用來將頁幀號和頁表屬性拼接為頁表項值:

arch/arm64/include/asm/pgtable.h:
77 #define pfn_pte(pfn,prot)       \
78         __pte(__phys_to_pte_val((phys_addr_t)(pfn) << PAGE_SHIFT) | pgprot_val(prot))

是將pfn左移PAGE_SHIFT位(一般為12bit),或上pgprot_val(prot)

先看my_zero_pfn:


include/asm-generic/pgtable.h:
   875 static inline unsigned long my_zero_pfn(unsigned long addr)
   876 {
   877         extern unsigned long zero_pfn;
   878         return zero_pfn;
   879 }

-->

mm/memory.c:
   126 unsigned long zero_pfn __read_mostly;
   127 EXPORT_SYMBOL(zero_pfn);
   128 
   129 unsigned long highest_memmap_pfn __read_mostly;
   130 
   131 /*
   132  * CONFIG_MMU architectures set up ZERO_PAGE in their paging_init()
   133  */
   134 static int __init init_zero_pfn(void)
   135 {
   136         zero_pfn = page_to_pfn(ZERO_PAGE(0));
   137         return 0;
   138 }
   139 core_initcall(init_zero_pfn);

-->

arch/arm64/include/asm/pgtable.h:
   54 /*
   55  * ZERO_PAGE is a global shared page that is always zero: used
   56  * for zero-mapped memory areas etc..
   57  */
   58 extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)];
   59 #define ZERO_PAGE(vaddr)        phys_to_page(__pa_symbol(empty_zero_page))

最終我們看到使用的就是內核初始化設定的empty_zero_page這個0頁得到頁幀號,再看看pfn_pte的第二個引數vma->vm_pageprot,這是vma的訪問權限,在做記憶體映射mmap的時候會被設定,

那么我們想知道的時候是什么時候0頁被設定為了只讀屬性的(也就是頁表項何時被設定為只讀)?

我們帶著這個問題去在內核代碼中尋找答案,其實代碼看到這里一般看不到頭緒,但是我們要知道何時vma的vm_page_prot成員被設定的,如何被設定的,有可能就能找到答案,

我們到mm/mmap.c中去尋找答案:我們以do_brk_flags函式為例,這是設定堆的函式我們關注到3040行設定了vm_page_prot:

3040         vma->vm_page_prot = vm_get_page_prot(flags);  

-->

  110 pgprot_t vm_get_page_prot(unsigned long vm_flags)
   111 {
   112         pgprot_t ret = __pgprot(pgprot_val(protection_map[vm_flags &
   113                                 (VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)]) |
   114                         pgprot_val(arch_vm_get_page_prot(vm_flags)));
   115 
   116         return arch_filter_pgprot(ret);
   117 }
   118 EXPORT_SYMBOL(vm_get_page_prot);

vm_get_page_prot函式會根據傳遞來的vmflags是否為VMREAD|VMWRITE|VMEXEC|VMSHARED來轉換為保護位組合,繼續往下看

    78 /* description of effects of mapping type and prot in current implementation.
    79  * this is due to the limited x86 page protection hardware.  The expected
    80  * behavior is in parens:
    81  *
    82  * map_type     prot
    83  *              PROT_NONE       PROT_READ       PROT_WRITE      PROT_EXEC
    84  * MAP_SHARED   r: (no) no      r: (yes) yes    r: (no) yes     r: (no) yes
    85  *              w: (no) no      w: (no) no      w: (yes) yes    w: (no) no
    86  *              x: (no) no      x: (no) yes     x: (no) yes     x: (yes) yes
    87  *
    88  * MAP_PRIVATE  r: (no) no      r: (yes) yes    r: (no) yes     r: (no) yes
    89  *              w: (no) no      w: (no) no      w: (copy) copy  w: (no) no
    90  *              x: (no) no      x: (no) yes     x: (no) yes     x: (yes) yes
    91  *
    92  * On arm64, PROT_EXEC has the following behaviour for both MAP_SHARED and
    93  * MAP_PRIVATE:
    94  *                                                              r: (no) no
    95  *                                                              w: (no) no
    96  *                                                              x: (yes) yes
    97  */
    98 pgprot_t protection_map[16] __ro_after_init = {
    99         __P000, __P001, __P010, __P011, __P100, __P101, __P110, __P111,
   100         __S000, __S001, __S010, __S011, __S100, __S101, __S110, __S111
   101 };

protection_map陣列定義了從P000到S111一共16種組合,P表示私有(Private),S表示共享(Share),后面三個數字依次為可讀、可寫、可執行,如:_S010表示共享、不可讀、可寫、不可執行,


arch/arm64/include/asm/pgtable-prot.h:
   93 #define PAGE_NONE               __pgprot(((_PAGE_DEFAULT) & ~PTE_VALID) | PTE_PROT_NONE | PTE_RDONLY | PTE_NG | PTE_PXN | PTE_UXN)
   94 #define PAGE_SHARED             __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN | PTE_WRITE)
   95 #define PAGE_SHARED_EXEC        __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_WRITE)
   96 #define PAGE_READONLY           __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_RDONLY | PTE_NG | PTE_PXN | PTE_UXN)
   97 #define PAGE_READONLY_EXEC      __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_RDONLY | PTE_NG | PTE_PXN)
   98 #define PAGE_EXECONLY           __pgprot(_PAGE_DEFAULT | PTE_RDONLY | PTE_NG | PTE_PXN)
   99 
  100 #define __P000  PAGE_NONE
  101 #define __P001  PAGE_READONLY
  102 #define __P010  PAGE_READONLY
  103 #define __P011  PAGE_READONLY
  104 #define __P100  PAGE_EXECONLY
  105 #define __P101  PAGE_READONLY_EXEC
  106 #define __P110  PAGE_READONLY_EXEC
  107 #define __P111  PAGE_READONLY_EXEC
  108 
  109 #define __S000  PAGE_NONE
  110 #define __S001  PAGE_READONLY
  111 #define __S010  PAGE_SHARED
  112 #define __S011  PAGE_SHARED
  113 #define __S100  PAGE_EXECONLY
  114 #define __S101  PAGE_READONLY_EXEC
  115 #define __S110  PAGE_SHARED_EXEC
  116 #define __S111  PAGE_SHARED_EXEC

可以發現對于私有的映射只有只讀(PTE_RDONLY)沒有可寫屬性(PTE_WRITE)105-107行 ,雖然之前設定的時候是設定了可寫(VM_WRITE)!而對應共享映射則會有可寫屬性,

而這個被設定的保護位組合最侄訓在缺頁例外中被設定到頁表中:上面說到的do_anonymous_page函式:

2908                 entry = pte_mkspecial(pfn_pte(my_zero_pfn(vmf->address),
2909                                                 vma->vm_page_prot));

對于私有匿名映射的頁,假設設定的vmflags為VMREAD|VMWRITE則對應的保護位組合為:P110即為PAGE_READONLY_EXEC=pgprot(_PAGE_DEFAULT | PTE_USER | PTE_RDONLY | PT_ENG | PTE_PXN)不會設定為可寫,

所以就將其頁表設定為了只讀!!!

2922行 跳轉到setpte去將設定好的頁表項值填寫到頁表項中,

當匿名頁讀之后再次去寫時候會由于頁表屬性為只讀導致COW缺頁例外,詳將COW相關文章,再此不在贅述,下面用圖說話:

image

3.3 第一次寫匿名頁的情況

接著do_anonymous_page函式繼續往下分析:


 2876 static vm_fault_t do_anonymous_page(struct vm_fault *vmf)
  2877 {
  ...
  2924 
  2925         /* Allocate our own private page. */
  2926         if (unlikely(anon_vma_prepare(vma)))
  2927                 goto oom;
  2928         page = alloc_zeroed_user_highpage_movable(vma, vmf->address);
  2929         if (!page)
  2930                 goto oom;
  2931 
  2932         if (mem_cgroup_try_charge_delay(page, vma->vm_mm, GFP_KERNEL, &memcg,
  2933                                         false))
  2934                 goto oom_free_page;
  2935 
  2936         /*
  2937         |* The memory barrier inside __SetPageUptodate makes sure that
  2938         |* preceeding stores to the page contents become visible before
  2939         |* the set_pte_at() write.
  2940         |*/
  2941         __SetPageUptodate(page);
  2942 
  2943         entry = mk_pte(page, vma->vm_page_prot);
  2944         if (vma->vm_flags & VM_WRITE)
  2945                 entry = pte_mkwrite(pte_mkdirty(entry));
 2946 
  2947         vmf->pte = pte_offset_map_lock(vma->vm_mm, vmf->pmd, vmf->address,
  2948                         &vmf->ptl);
  2949         if (!pte_none(*vmf->pte))
  2950                 goto release;
  2951 
  2952         ret = check_stable_address_space(vma->vm_mm);
  2953         if (ret)
  2954                 goto release;
  2955 
  2956         /* Deliver the page fault to userland, check inside PT lock */
  2957         if (userfaultfd_missing(vma)) {
  2958                 pte_unmap_unlock(vmf->pte, vmf->ptl);
  2959                 mem_cgroup_cancel_charge(page, memcg, false);
  2960                 put_page(page);
  2961                 return handle_userfault(vmf, VM_UFFD_MISSING);
  2962         }
  2963 
  2964         inc_mm_counter_fast(vma->vm_mm, MM_ANONPAGES);
  2965         page_add_new_anon_rmap(page, vma, vmf->address, false);
  2966         mem_cgroup_commit_charge(page, memcg, false, false);
  2967         lru_cache_add_active_or_unevictable(page, vma);
  2968 setpte:
  2969         set_pte_at(vma->vm_mm, vmf->address, vmf->pte, entry);
  2970 
  2971         /* No need to invalidate - it was non-present before */
  2972         update_mmu_cache(vma, vmf->address, vmf->pte);
  2973 unlock:
  2974         pte_unmap_unlock(vmf->pte, vmf->ptl);
  2975         return ret;
  2976 release:
  2977         mem_cgroup_cancel_charge(page, memcg, false);
  2978         put_page(page);
  2979         goto unlock;
  2980 oom_free_page:
  2981         put_page(page);
  2982 oom:
  2983         return VM_FAULT_OOM;
  2984 }

當判斷不是讀操作導致的缺頁的時候,則是寫操作造成,處理寫私有的匿名頁情況,請記住這依然是第一次訪問這個匿名頁只不過是寫訪問而已,

2928 行會分配一個高端 可遷移的 被0填充的物理頁,2941 設定頁中資料有效

2943 使用頁幀號和vma的訪問權限設定頁表項值(注意:這個時候頁表項屬性依然為只讀),

2944-2945行 如果vma可寫,則設定頁表項值為臟且可寫(這個時候才設定為可寫),

2964行 匿名頁計數統計

2965行 添加到匿名頁的反向映射中

2967行 添加到lru鏈表

2969 將設定好的頁表項值填充到頁表項中,

下面用圖說話:

image

3.4 讀之后寫匿名頁

讀之后寫匿名頁,其實已經很簡單了,那就是發生COW寫時復制缺頁,下面依然看圖說話:

image

四,應用層實驗

實驗1:主要體驗下內核的按需分配頁策略!實驗代碼:mmap映射10 * 4096 * 4096/1M=160M記憶體空間,映射和寫頁前后獲得記憶體使用情況:

    1 #include <stdio.h>
    2 #include <stdlib.h>
    3 #include <sys/mman.h>
    4 #include <unistd.h>
    5 
    6 
    7 #define MAP_LEN (10 * 4096 * 4096)
    8 
    9 int main(int argc, char **argv)
   10 {
   11         char *p;
   12         int i;
   13 
   14 
   15         puts("before mmap ->please exec: free -m\n");
   16         sleep(10);
   17         p = (char *)mmap(0, MAP_LEN, PROT_READ |PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
   18 
   19         puts("after mmap ->please exec: free -m\n");
   20         puts("before write....\n");
   21         sleep(10);
   22 
   23         for(i=0;i <4096 *10; i++)
   24                 p[4096 * i] = 0x55;
   25 
   26 
   27         puts("after write ->please exec: free -m\n");
   28 
   29         pause();
   30 
   31         return 0;
   32 }            

執行結果:

出現“before mmap ->please exec: free -m”列印后執行:


$ free -m
              總計         已用        空閑      共享    緩沖/快取    可用
記憶體:15921        6561         462         796        8897        8214
交換:16290         702       15588

出現“after mmap ->please exec: free -m”列印后執行:

$ free -m
              總計         已用        空閑      共享    緩沖/快取    可用
記憶體:15921        6565         483         771        8872        8236
交換:16290         702       15588

出現“after write ->please exec: free -m”后執行:

$:~/study/user_test/page-fault$ free -m
              總計         已用        空閑      共享    緩沖/快取    可用
記憶體:15921        6727         322         770        8871        8076
交換:16290         702       15588

我們只關注已用記憶體,可以發現映射前后基本上已用記憶體沒有變化(考慮到其他記憶體申請情況存在,也會有記憶體變化)是6561M和6565M,說明mmap的時候并沒有分配物理記憶體,寫之后發現記憶體使用為6727M, 6727-6565=162M與我們mmap的大小基本一致,說明了匿名頁實際寫的時候才會分配等量的物理記憶體,

實驗2:主要體驗下匿名頁讀之后寫記憶體頁申請情況

實驗代碼:mmap映射10 * 4096 * 4096/1M=160M記憶體空間,映射、讀然后寫頁前后獲得記憶體使用情況:


    1 #include <stdio.h>
    2 #include <stdlib.h>
    3 #include <sys/mman.h>
    4 #include <unistd.h>
    5 
    6 
    7 #define MAP_LEN (10 * 4096 * 4096)
    8 
    9 int main(int argc, char **argv)
   10 {
   11         char *p;
   12         int i;
   13 
   14 
   15         puts("before mmap...pls show free:.\n");
   16         sleep(10);
?  17         p = (char *)mmap(0, MAP_LEN, PROT_READ |PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
   18 
   19         puts("after mmap....\n");
   20 
   21         puts("before read...pls show free:.\n");
   22         sleep(10);
   23 
   24         puts("start read....\n");
   25 
   26 
   27         for(i=0;i <4096 *10; i++)
   28                 printf("%d ", p[4096 * i]);
   29         printf("\n");
   30 
   31         puts("after read....pls show free:\n");
   32 
   33         sleep(10);
   34 
   35         puts("start write....\n");
   36 
   37         for(i=0;i <4096 *10; i++)
   38                 p[4096 * i] = 0x55;
   39 
   40 
   41         puts("after write...pls show free:.\n");
   42 
   43         pause();
   44 
   45         return 0;
   46 }

執行結果:出現"before mmap ->please exec: free -m" 后執行:


$ free -m
              總計         已用        空閑      共享    緩沖/快取    可用
記憶體:15921        6590         631         780        8700        8164
交換:16290         702       15588

出現"before read ->please exec: free -m"后執行:

$ free -m
              總計         已用        空閑      共享    緩沖/快取    可用
記憶體:15921        6586         644         770        8690        8178
交換:16290         702       15588

出現"after read ->please exec: free -m"后執行:

$ free -m
              總計         已用        空閑      共享    緩沖/快取    可用
記憶體:15921        6587         624         789        8709        8158
交換:16290         702       15588

出現"after write ->please exec: free -m"后執行:

$ free -m
              總計         已用        空閑      共享    緩沖/快取    可用
記憶體:15921        6749         462         789        8709        7996
交換:16290         702       15588

可以發現:讀之后和之前基本上記憶體使用沒有變化(實際上映射到了0頁,這是內核初始化時候分配好的),知道寫之后6749-6587=162M符合預期,而且列印可以發現資料全為0,

分析:實際上,mmap的時候只是申請了一塊vma,讀的時候發生一次缺頁例外,映射到0頁,所有記憶體沒有分配,當再次寫這個頁面的時候,發生了COW分配新頁(cow中分配新頁的時候會判斷原來的頁是否為0頁,如果為0頁就直接分配頁然后用0填充),

五,總結

匿名映射缺頁例外是我們遇到的一種很常用的一種例外,對于匿名映射,映射完成之后,只是獲得了一塊虛擬記憶體,并沒有分配物理記憶體,當第一次訪問的時候:如果是讀訪問,會將虛擬頁映射到0頁,以減少不必要的記憶體分配;如果是寫訪問,則會分配新的物理頁,并用0填充,然后映射到虛擬頁上去,而如果是先讀訪問一頁然后寫訪問這一頁,則會發生兩次缺頁例外:第一次是匿名頁缺頁例外的讀的處理,第二次是寫時復制缺頁例外處理,

轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/2996.html

標籤:嵌入式

上一篇:Android 功耗(6)---整機功耗測驗

下一篇:ZooKeeper常用服務命令

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • CA和證書

    1、在 CentOS7 中使用 gpg 創建 RSA 非對稱密鑰對 gpg --gen-key #Centos上生成公鑰/密鑰對(存放在家目錄.gnupg/) 2、將 CentOS7 匯出的公鑰,拷貝到 CentOS8 中,在 CentOS8 中使用 CentOS7 的公鑰加密一個檔案 gpg -a ......

    uj5u.com 2020-09-10 00:09:53 more
  • Kubernetes K8S之資源控制器Job和CronJob詳解

    Kubernetes的資源控制器Job和CronJob詳解與示例 ......

    uj5u.com 2020-09-10 00:10:45 more
  • VMware下安裝CentOS

    VMware下安裝CentOS 一、軟硬體準備 1 Centos鏡像準備 1.1 CentOS鏡像下載地址 下載地址 1.2 CentOS鏡像下載程序 點擊下載地址進入如下圖的網站,選擇需要下載的版本,這里選擇的是Centos8,點擊如圖所示。 決定選擇Centos8后,選擇想要的鏡像源進行下載,此 ......

    uj5u.com 2020-09-10 00:12:10 more
  • 如何使用Grep命令查找多個字串

    如何使用Grep 命令查找多個字串 大家好,我是良許! 今天向大家介紹一個非常有用的技巧,那就是使用 grep 命令查找多個字串。 簡單介紹一下,grep 命令可以理解為是一個功能強大的命令列工具,可以用它在一個或多個輸入檔案中搜索與正則運算式相匹配的文本,然后再將每個匹配的文本用標準輸出的格式 ......

    uj5u.com 2020-09-10 00:12:28 more
  • git配置http代理

    git配置http代理 經常遇到克隆 github 慢的問題,這里記錄一下幾種配置 git 代理的方法,解決 clone github 過慢。 目錄 git配置代理 git單獨配置github代理 git配置全域代理 配置終端環境變數 git配置代理 主要使用 git config 命令 git單獨 ......

    uj5u.com 2020-09-10 00:12:33 more
  • Linux npm install 裝包時提示Error EACCES permission denied解

    npm install 裝包時提示Error EACCES permission denied解決辦法 ......

    uj5u.com 2020-09-10 00:12:53 more
  • Centos 7下安裝nginx,使用yum install nginx,提示沒有可用的軟體包

    Centos 7下安裝nginx,使用yum install nginx,提示沒有可用的軟體包。 18 (flaskApi) [root@67 flaskDemo]# yum -y install nginx 19 已加載插件:fastestmirror, langpacks 20 Loading ......

    uj5u.com 2020-09-10 00:13:13 more
  • Linux查看服務器暴力破解ssh IP

    在公網的服務器上經常遇到別人爆破你服務器的22埠,用來挖礦或者干其他嘿嘿嘿的事情~ 這種情況下正確的做法是: 修改默認ssh的22埠 使用設定密鑰登錄或者白名單ip登錄 建議服務器密碼為復雜密碼 創建普通用戶登錄服務器(root權限過大) 建立堡壘機,實作統一管理服務器 統計爆破IP [root ......

    uj5u.com 2020-09-10 00:13:17 more
  • CentOS 7系統常見快捷鍵操作方式

    Linux系統中一些常見的快捷方式,可有效提高操作效率,在某些時刻也能避免操作失誤帶來的問題。 ......

    uj5u.com 2020-09-10 00:13:31 more
  • CentOS 7作業系統目錄結構介紹

    作業系統存在著大量的資料檔案資訊,相應檔案資訊會存在于系統相應目錄中,為了更好的管理資料資訊,會將系統進行一些目錄規劃,不同目錄存放不同的資源。 ......

    uj5u.com 2020-09-10 00:13:35 more
最新发布
  • vim的常用命令

    Vim的6種基本模式 1. 普通模式在普通模式中,用的編輯器命令,比如移動游標,洗掉文本等等。這也是Vim啟動后的默認模式。這正好和許多新用戶期待的操作方式相反(大多數編輯器默認模式為插入模式)。 2. 插入模式在這個模式中,大多數按鍵都會向文本緩沖中插入文本。大多數新用戶希望文本編輯器編輯程序中一 ......

    uj5u.com 2023-04-20 08:43:21 more
  • vim的常用命令

    Vim的6種基本模式 1. 普通模式在普通模式中,用的編輯器命令,比如移動游標,洗掉文本等等。這也是Vim啟動后的默認模式。這正好和許多新用戶期待的操作方式相反(大多數編輯器默認模式為插入模式)。 2. 插入模式在這個模式中,大多數按鍵都會向文本緩沖中插入文本。大多數新用戶希望文本編輯器編輯程序中一 ......

    uj5u.com 2023-04-20 08:42:36 more
  • docker學習

    ###Docker概述 真實專案部署環境可能非常復雜,傳統發布專案一個只需要一個jar包,運行環境需要單獨部署。而通過Docker可將jar包和相關環境(如jdk,redis,Hadoop...)等打包到docker鏡像里,將鏡像發布到Docker倉庫,部署時下載發布的鏡像,直接運行發布的鏡像即可。 ......

    uj5u.com 2023-04-19 09:26:53 more
  • 設定Windows主機的瀏覽器為wls2的默認瀏覽器

    這里以Chrome為例。 1. 準備作業 wsl是可以使用Windows主機上安裝的exe程式,出于安全考慮,默認情況下改功能是無法使用。要使用的話,終端需要以管理員權限啟動。 我這里以Windows Terminal為例,介紹如何默認使用管理員權限打開終端,具體操作如下圖所示: 2. 操作 wsl ......

    uj5u.com 2023-04-19 09:25:49 more
  • docker學習

    ###Docker概述 真實專案部署環境可能非常復雜,傳統發布專案一個只需要一個jar包,運行環境需要單獨部署。而通過Docker可將jar包和相關環境(如jdk,redis,Hadoop...)等打包到docker鏡像里,將鏡像發布到Docker倉庫,部署時下載發布的鏡像,直接運行發布的鏡像即可。 ......

    uj5u.com 2023-04-19 09:19:04 more
  • Linux學習筆記

    IP地址和主機名 IP地址 ifconfig可以用來查詢本機的IP地址,如果不能使用,可以通過install net-tools安裝。 Centos系統下ens33表示主網卡;inet后表示IP地址;lo表示本地回環網卡; 127.0.0.1表示代指本機;0.0.0.0可以用于代指本機,同時在放行設 ......

    uj5u.com 2023-04-18 06:52:01 more
  • 解決linux系統的kdump服務無法啟動的問題

    問題:專案麒麟系統服務器的kdump服務無法啟動,沒有相關日志無法定位問題。 1、查看服務狀態是關閉的,重啟系統也無法啟動 systemctl status kdump 2、修改grub引數,修改“crashkernel”為“512M(有的機器數值太大太小都會導致報錯,建議從128M開始試,或者加個 ......

    uj5u.com 2023-04-12 09:59:50 more
  • 解決linux系統的kdump服務無法啟動的問題

    問題:專案麒麟系統服務器的kdump服務無法啟動,沒有相關日志無法定位問題。 1、查看服務狀態是關閉的,重啟系統也無法啟動 systemctl status kdump 2、修改grub引數,修改“crashkernel”為“512M(有的機器數值太大太小都會導致報錯,建議從128M開始試,或者加個 ......

    uj5u.com 2023-04-12 09:59:01 more
  • 你是不是暴露了?

    作者:袁首京 原創文章,轉載時請保留此宣告,并給出原文連接。 如果您是計算機相關從業人員,那么應該經歷不止一次網路安全專項檢查了,你肯定是收到過資訊系統技術檢測報告,要求你加強風險監測,確保你提供的系統服務堅實可靠了。 沒檢測到問題還好,檢測到問題的話,有些處理起來還是挺麻煩的,尤其是線上正在運行的 ......

    uj5u.com 2023-04-05 16:52:56 more
  • 細節拉滿,80 張圖帶你一步一步推演 slab 記憶體池的設計與實作

    1. 前文回顧 在之前的幾篇記憶體管理系列文章中,筆者帶大家從宏觀角度完整地梳理了一遍 Linux 記憶體分配的整個鏈路,本文的主題依然是記憶體分配,這一次我們會從微觀的角度來探秘一下 Linux 內核中用于零散小記憶體塊分配的記憶體池 —— slab 分配器。 在本小節中,筆者還是按照以往的風格先帶大家簡單 ......

    uj5u.com 2023-04-05 16:44:11 more