久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

linux內(nèi)核分析筆記----進(jìn)程地址空間

 rookie 2012-04-12
      前邊我已經(jīng)說過了內(nèi)核是如何管理物理內(nèi)存。但事實(shí)是內(nèi)核是操作系統(tǒng)的核心,,不光管理本身的內(nèi)存,,還要管理進(jìn)程的地址空間。linux操作系統(tǒng)采用虛擬內(nèi)存 技術(shù),,所有進(jìn)程之間以虛擬方式共享內(nèi)存,。進(jìn)程地址空間由每個(gè)進(jìn)程中的線性地址區(qū)組成,而且更為重要的特點(diǎn)是內(nèi)核允許進(jìn)程使用該空間中的地址,。通常情況況 下,,每個(gè)進(jìn)程都有唯一的地址空間,而且進(jìn)程地址空間之間彼此互不相干。但是進(jìn)程之間也可以選擇共享地址空間,,這樣的進(jìn)程就叫做線程,。
      內(nèi)核使用內(nèi)存描述符結(jié)構(gòu)表示進(jìn)程的地址空間,由結(jié)構(gòu)體mm_struct結(jié)構(gòu)體表示,,定義在linux/sched.h中,,如下:
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
struct mm_struct {
        struct vm_area_struct  *mmap;               /* list of memory areas */
        struct rb_root         mm_rb;               /* red-black tree of VMAs */
        struct vm_area_struct  *mmap_cache;         /* last used memory area */
        unsigned long          free_area_cache;     /* 1st address space hole */
        pgd_t                  *pgd;                /* page global directory */
        atomic_t               mm_users;            /* address space users */
        atomic_t               mm_count;            /* primary usage counter */
        int                    map_count;           /* number of memory areas */
        struct rw_semaphore    mmap_sem;            /* memory area semaphore */
        spinlock_t             page_table_lock;     /* page table lock */
        struct list_head       mmlist;              /* list of all mm_structs */
        unsigned long          start_code;          /* start address of code */
        unsigned long          end_code;            /* final address of code */
        unsigned long          start_data;          /* start address of data */
        unsigned long          end_data;            /* final address of data */
        unsigned long          start_brk;           /* start address of heap */
        unsigned long          brk;                 /* final address of heap */
        unsigned long          start_stack;         /* start address of stack */
        unsigned long          arg_start;           /* start of arguments */
        unsigned long          arg_end;             /* end of arguments */
        unsigned long          env_start;           /* start of environment */
        unsigned long          env_end;             /* end of environment */
        unsigned long          rss;                 /* pages allocated */
        unsigned long          total_vm;            /* total number of pages */
        unsigned long          locked_vm;           /* number of locked pages */
        unsigned long          def_flags;           /* default access flags */
        unsigned long          cpu_vm_mask;         /* lazy TLB switch mask */
        unsigned long          swap_address;        /* last scanned address */
        unsigned               dumpable:1;          /* can this mm core dump? */
        int                    used_hugetlb;        /* used hugetlb pages? */
        mm_context_t           context;             /* arch-specific data */
        int                    core_waiters;        /* thread core dump waiters */
        struct completion      *core_startup_done;  /* core start completion */
        struct completion      core_done;           /* core end completion */
        rwlock_t               ioctx_list_lock;     /* AIO I/O list lock */
        struct kioctx          *ioctx_list;         /* AIO I/O list */
        struct kioctx          default_kioctx;      /* AIO default I/O context */
};
      mm_users記錄了正在使用該地址的進(jìn)程數(shù)目(比如有兩個(gè)進(jìn)程在使用,那就為2),。mm_count是該結(jié)構(gòu)的主引用計(jì)數(shù),,只要mm_users不為 0,它就為1,。但其為0時(shí),,后者就為0。這時(shí)也就說明再也沒有指向該mm_struct結(jié)構(gòu)體的引用了,,這時(shí)該結(jié)構(gòu)體會(huì)被銷毀,。內(nèi)核之所以同時(shí)使用這兩個(gè) 計(jì)數(shù)器是為了區(qū)別主使用計(jì)數(shù)器和使用該地址空間的進(jìn)程的數(shù)目。mmap和mm_rb描述的都是同一個(gè)對(duì)象:該地址空間中的全部?jī)?nèi)存區(qū)域,。不同只是前者以鏈 表,,后者以紅黑樹的形式組織。所有的mm_struct結(jié)構(gòu)體都通過自身的mmlist域連接在一個(gè)雙向鏈表中,,該鏈表的首元素是init_mm內(nèi)存描述 符,,它代表init進(jìn)程的地址空間。另外需要注意,,操作該鏈表的時(shí)候需要使用mmlist_lock鎖來防止并發(fā)訪問,,該鎖定義在文件 kernel/fork.c中。內(nèi)存描述符的總數(shù)在mmlist_nr全局變量中,,該變量也定義在文件fork.c中,。

      我前邊說過的進(jìn)程描述符中有一個(gè)mm域,這里邊存放的就是該進(jìn)程使用的內(nèi)存描述符,,通過current->mm便可以指向當(dāng)前進(jìn)程的內(nèi)存描述符,。 fork函數(shù)利用copy_mm()函數(shù)就實(shí)現(xiàn)了復(fù)制父進(jìn)程的內(nèi)存描述符,而子進(jìn)程中的mm_struct結(jié)構(gòu)體實(shí)際是通過文件 kernel/fork.c中的allocate_mm()宏從mm_cachep slab緩存中分配得到的,。通常,,每個(gè)進(jìn)程都有唯一的mm_struct結(jié)構(gòu)體。

      前邊也說過,,在linux中,,進(jìn)程和線程其實(shí)是一樣的,唯一的不同點(diǎn)就是是否共享這里的地址空間,。這個(gè)可以通過CLONE_VM標(biāo)志來實(shí)現(xiàn),。linux內(nèi) 核并不區(qū)別對(duì)待它們,,線程對(duì)內(nèi)核來說僅僅是一個(gè)共向特定資源的進(jìn)程而已。好了,,如果你設(shè)置這個(gè)標(biāo)志了,,似乎很多問題都解決了。不再要 allocate_mm函數(shù)了,,前邊剛說作用,。而且在copy_mm()函數(shù)中將mm域指向其父進(jìn)程的內(nèi)存描述符就可以了,如下:

1
2
3
4
5
6
7
8
if (clone_flags & CLONE_VM) {
        /*
         * current is the parent process and
         * tsk is the child process during a fork()
         */
         atomic_inc(&current->mm->mm_users);
         tsk->mm = current->mm;
}

      最后,,當(dāng)進(jìn)程退出的時(shí)候,,內(nèi)核調(diào)用exit_mm()函數(shù),這個(gè)函數(shù)調(diào)用mmput()來減少內(nèi)存描述符中的mm_users用戶計(jì)數(shù),。如果計(jì)數(shù)降為0,, 繼續(xù)調(diào)用mmdrop函數(shù),減少mm_count使用計(jì)數(shù),。如果使用計(jì)數(shù)也為0,,則調(diào)用free_mm()宏通過kmem_cache_free()函數(shù) 將mm_struct結(jié)構(gòu)體歸還到mm_cachep slab緩存中,。

      但對(duì)于內(nèi)核而言,,內(nèi)核線程沒有進(jìn)程地址空間,也沒有相關(guān)的內(nèi)存描述符,,內(nèi)核線程對(duì)應(yīng)的進(jìn)程描述符中mm域也為空,。但內(nèi)核線程還是需要使用一些數(shù)據(jù)的,比如 頁表,,為了避免內(nèi)核線程為內(nèi)存描述符和頁表浪費(fèi)內(nèi)存,,也為了當(dāng)新內(nèi)核線程運(yùn)行時(shí),避免浪費(fèi)處理器周期向新地址空間進(jìn)行切換,,內(nèi)核線程將直接使用前一個(gè)進(jìn)程 的內(nèi)存描述符,。回憶一下我剛說的進(jìn)程調(diào)度問題,,當(dāng)一個(gè)進(jìn)程被調(diào)度時(shí),,進(jìn)程結(jié)構(gòu)體中mm域指向的地址空間會(huì)被裝載到內(nèi)存,進(jìn)程描述符中的active_mm 域會(huì)被更新,,指向新的地址空間,。但我們這里的內(nèi)核是沒有mm域(為空),所以,,當(dāng)一個(gè)內(nèi)核線程被調(diào)度時(shí),,內(nèi)核發(fā)現(xiàn)它的mm域?yàn)镹ULL,就會(huì)保留前一個(gè)進(jìn) 程的地址空間,,隨后內(nèi)核更新內(nèi)核線程對(duì)應(yīng)的進(jìn)程描述符中的active域,,使其指向前一個(gè)進(jìn)程的內(nèi)存描述符,。所以在需要的時(shí)候,內(nèi)核線程便可以使用前一個(gè) 進(jìn)程的頁表,。因?yàn)閮?nèi)核線程不妨問用戶空間的內(nèi)存,,所以它們僅僅使用地址空間中和內(nèi)核內(nèi)存相關(guān)的信息,這些信息的含義和普通進(jìn)程完全相同,。
     
內(nèi) 存區(qū)域由vm_area_struct結(jié)構(gòu)體描述,,定義在linux/mm.h中,內(nèi)存區(qū)域在內(nèi)核中也經(jīng)常被稱作虛擬內(nèi)存區(qū)域或VMA.它描述了指定地址 空間內(nèi)連續(xù)區(qū)間上的一個(gè)獨(dú)立內(nèi)存范圍,。內(nèi)核將每個(gè)內(nèi)存區(qū)域作為一個(gè)單獨(dú)的內(nèi)存對(duì)象管理,,每個(gè)內(nèi)存區(qū)域都擁有一致的屬性。結(jié)構(gòu)體如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct vm_area_struct {
        struct mm_struct             *vm_mm;        /* associated mm_struct */
        unsigned long                vm_start;      /* VMA start, inclusive */
        unsigned long                vm_end;        /* VMA end , exclusive */
        struct vm_area_struct        *vm_next;      /* list of VMA's */
        pgprot_t                     vm_page_prot;  /* access permissions */
        unsigned long                vm_flags;      /* flags */
        struct rb_node               vm_rb;         /* VMA's node in the tree */
        union {         /* links to address_space->i_mmap or i_mmap_nonlinear */
                struct {
                        struct list_head        list;
                        void                    *parent;
                        struct vm_area_struct   *head;
                } vm_set;
                struct prio_tree_node prio_tree_node;
        } shared;
        struct list_head             anon_vma_node;     /* anon_vma entry */
        struct anon_vma              *anon_vma;         /* anonymous VMA object */
        struct vm_operations_struct  *vm_ops;           /* associated ops */
        unsigned long                vm_pgoff;          /* offset within file */
        struct file                  *vm_file;          /* mapped file, if any */
        void                         *vm_private_data;  /* private data */
};

      每個(gè)內(nèi)存描述符都對(duì)應(yīng)于地址進(jìn)程空間中的唯一區(qū)間,。vm_mm域指向和VMA相關(guān)的mm_struct結(jié)構(gòu)體,。兩個(gè)獨(dú)立的進(jìn)程將同一個(gè)文件映射到各自的地 址空間,它們分別都會(huì)有一個(gè)vm_area_struct結(jié)構(gòu)體來標(biāo)志自己的內(nèi)存區(qū)域,;但是如果兩個(gè)線程共享一個(gè)地址空間,,那么它們也同時(shí)共享其中的所有 vm_area_struct結(jié)構(gòu)體。

      在上面的vm_flags域中存放的是VMA標(biāo)志,,標(biāo)志了內(nèi)存區(qū)域所包含的頁面的行為和信息,,反映了內(nèi)核處理頁面所需要遵循的行為準(zhǔn)則,如下表下述:

     image

      上表已經(jīng)相當(dāng)詳細(xì)了,,而且給出了說明,,我就不說了。在vm_area_struct結(jié)構(gòu)體中的vm_ops域指向域指定內(nèi)存區(qū)域相關(guān)的操作函數(shù)表,,內(nèi)核使 用表中的方法操作VMA,。vm_area_struct作為通用對(duì)象代表了任何類型的內(nèi)存區(qū)域,而操作表描述針對(duì)特定的對(duì)象實(shí)例的特定方法,。操作函數(shù)表由 vm_operations_struct結(jié)構(gòu)體表示,,定義在linux/mm.h中,如下:

1
2
3
4
5
6
struct vm_operations_struct {
        void (*open) (struct vm_area_struct *);
        void (*close) (struct vm_area_struct *);
        struct page * (*nopage) (struct vm_area_struct *, unsigned long, int);
        int (*populate) (struct vm_area_struct *, unsigned long, unsigned long,pgprot_t, unsigned long, int);
};

open:當(dāng)指定的內(nèi)存區(qū)域被加入到一個(gè)地址空間時(shí),,該函數(shù)被調(diào)用,。

close:當(dāng)指定的內(nèi)存區(qū)域從地址空間刪除時(shí),該函數(shù)被調(diào)用,。

nopages:當(dāng)要訪問的頁不在物理內(nèi)存中時(shí),,該函數(shù)被頁錯(cuò)誤處理程序調(diào)用。

populate:該函數(shù)被系統(tǒng)調(diào)用remap_pages調(diào)用來為將要發(fā)生的缺頁中斷預(yù)映射一個(gè)新映射,。

      記性好的你一定記得內(nèi)存描述符中的mmap和mm_rb域都獨(dú)立地指向與內(nèi)存描述符相關(guān)的全體內(nèi)存區(qū)域?qū)ο?。它們包含完全相同? vm_area_struct結(jié)構(gòu)體的指針,僅僅組織方式不同而已,。前者以鏈表的方式進(jìn)行組織,,所有的區(qū)域按地址增長(zhǎng)的方向排序,,mmap域指向鏈表中第 一個(gè)內(nèi)存區(qū)域,鏈中最后一個(gè)VMA結(jié)構(gòu)體指針指向空,。而mm_rb域采用紅--黑樹連接所有的內(nèi)存區(qū)域?qū)ο?。它指向紅--黑輸?shù)母?jié)點(diǎn)。地址空間中每一個(gè) vm_area_struct結(jié)構(gòu)體通過自身的vm_rb域連接到樹中,。關(guān)于紅黑二叉樹結(jié)構(gòu)我就不細(xì)講了,,以后可能會(huì)詳細(xì)說這個(gè)問題。內(nèi)核之所以采用這兩 種結(jié)構(gòu)來表示同一內(nèi)存區(qū)域,,主要是鏈表結(jié)構(gòu)便于遍歷所有節(jié)點(diǎn),,而紅黑樹結(jié)構(gòu)體便于在地址空間中定位特定內(nèi)存區(qū)域的節(jié)點(diǎn)。我么可以使用/proc文件系統(tǒng)和 pmap工具查看給定進(jìn)程的內(nèi)存空間和其中所包含的內(nèi)存區(qū)域,。這里就不細(xì)說了,。

      內(nèi)核也為我們提供了對(duì)內(nèi)存區(qū)域操作的API,定義在linux/mm.h中:

(1)find_vma<定義在mm/mmap.c>中,,該函數(shù)在指定的地址空間中搜索一個(gè)vm_end大于addr的內(nèi)存區(qū)域,。換句話說,該函數(shù)尋找第一個(gè)包含
    addr或者首地址大于addr的內(nèi)存區(qū)域,,如果沒有發(fā)現(xiàn)這樣的區(qū)域,,該函數(shù)返回NULL;否則返回指向匹配的內(nèi)存區(qū)域的vm_area_struct結(jié)構(gòu)
    體指針,。
(2)find_vma_prev().函數(shù)定義和聲明分別在文件mm/mmap.c中和文件linux/mm.h中,,它和find_vma()工作方式相同,但返回的是第一個(gè)小于 
    addr的VMA.
(3)
find_vma_intersection().定義在文件linux/mm.h中,,返回第一個(gè)和指定地址區(qū)間相交的VMA,該函數(shù)是一個(gè)內(nèi)斂函數(shù)。

      接下來要說的兩個(gè)函數(shù)就非常重要了,,它們負(fù)責(zé)創(chuàng)建和刪除地址空間,。
      內(nèi)核使用do_mmap()函數(shù)創(chuàng)建一個(gè)新的線性地址空間。但如果創(chuàng)建的地址區(qū)間和一個(gè)已經(jīng)存在的地址區(qū)間相鄰,,并且它們具有相同的訪問權(quán)限的話,,那么兩 個(gè)區(qū)間將合并為一個(gè)。如果不能合并,,那么就確實(shí)需要?jiǎng)?chuàng)建一個(gè)新的vma了,,但無論哪種情況,do_mmap()函數(shù)都會(huì)將一個(gè)地址區(qū)間加入到進(jìn)程的地址空 間中,。這個(gè)函數(shù)定義在linux/mm.h中,,如下:

1
unsigned long do_mmap(struct file *file, unsigned long addr, unsigned long len, unsigned long prot,unsigned long flag, unsigned long offset)

      這個(gè)函數(shù)中由file指定文件,具體映射的是文件中從偏移offset處開始,,長(zhǎng)度為len字節(jié)的范圍內(nèi)的數(shù)據(jù),,如果file參數(shù)是NULL并且 offset參數(shù)也是0,,那么就代表這次映射沒有和文件相關(guān),該情況被稱作匿名映射,。如果指定了文件和偏移量,,那么該映射被稱為文件映射(file- backed mapping),其中參數(shù)prot指定內(nèi)存區(qū)域中頁面的訪問權(quán)限,,這些訪問權(quán)限定義在asm/mman.h中,,如下:

     image

      flag參數(shù)指定了VMA標(biāo)志,這些標(biāo)志定義在asm/mman.h中,,如下:

     image

      如果系統(tǒng)調(diào)用do_mmap的參數(shù)中有無效參數(shù),,那么它返回一個(gè)負(fù)值;否則,,它會(huì)在虛擬內(nèi)存中分配一個(gè)合適的新內(nèi)存區(qū)域,,如果有可能的話,將新區(qū)域和臨近區(qū)域進(jìn)行合并,,否則內(nèi)核從vm_area_cach
ep 長(zhǎng)字節(jié)緩存中分配一個(gè)vm_area_struct結(jié)構(gòu)體,,并且使用vma_link()函數(shù)將新分配的內(nèi)存區(qū)域添加到地址空間的內(nèi)存區(qū)域鏈表和紅黑樹 中,隨后還要更新內(nèi)存描述符中的total_vm域,,然后才返回新分配的地址區(qū)間的初始地址,。在用戶空間,我們可以通過mmap()系統(tǒng)調(diào)用獲取內(nèi)核函數(shù) do_mmap()的功能,,這個(gè)在unix環(huán)境高級(jí)編程中講的很詳細(xì),,我就不好意思繼續(xù)說了。我們繼續(xù)往下走,。
我們說既然有了創(chuàng)建,,當(dāng)然要有刪除了,是不,?do_mummp()函數(shù)就是干這事的,。它從特定的進(jìn)程地址空間中刪除指定地址空間,該函數(shù)定義在文件linux/mm.h中,,如下:

1
int do_munmap(struct mm_struct *mm, unsigned long start, size_t len)

      第一個(gè)參數(shù)指定要?jiǎng)h除區(qū)域所在的地址空間,,刪除從地址start開始,長(zhǎng)度為len字節(jié)的地址空間,,如果成功,,返回0,否則返回負(fù)的錯(cuò)誤碼,。與之相對(duì)應(yīng)的用戶空間系統(tǒng)調(diào)用是munmap,。

      下面開始最后一點(diǎn)內(nèi)容:頁表

      我們知道應(yīng)用程序操作的對(duì)象是映射到物理內(nèi)存之上的虛擬內(nèi)存,但是處理器直接操作的確實(shí)物理內(nèi)存,。所以當(dāng)應(yīng)用程序訪問一個(gè)虛擬地址時(shí),,首先必須將虛擬地址 轉(zhuǎn)化為物理地址,,然后處理器才能解析地址訪問請(qǐng)求。這個(gè)轉(zhuǎn)換工作需要通過查詢頁面才能完成,,概括地講,,地址轉(zhuǎn)換需要將虛擬地址分段,使每段虛地址都作為一 個(gè)索引指向頁表,,而頁表項(xiàng)則指向下一級(jí)別的頁表或者指向最終的物理頁面,。linux中使用三級(jí)頁表完成地址轉(zhuǎn)換。多數(shù)體系結(jié)構(gòu)中,,搜索頁表的工作由硬件完 成,,下表描述了虛擬地址通過頁表找到物理地址的過程:

     image

      在上面這個(gè)圖中,頂級(jí)頁表是頁全局目錄(PGD),,二級(jí)頁表是中間頁目錄(PMD).最后一級(jí)是頁表(PTE),該頁表結(jié)構(gòu)指向物理頁,。上圖中的頁表對(duì)應(yīng) 的結(jié)構(gòu)體定義在文件asm/page.h中。為了加快查找速度,,在linux中實(shí)現(xiàn)了快表(TLB),其本質(zhì)是一個(gè)緩沖器,,作為一個(gè)將虛擬地址映射到物理 地址的硬件緩存,當(dāng)請(qǐng)求訪問一個(gè)虛擬地址時(shí),,處理器將首先檢查TLB中是否緩存了該虛擬地址到物理地址的映射,,如果找到了,物理地址就立刻返回,,否則,,就 需要再通過頁表搜索需要的物理地址。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多