Linux內核設計與實現---進程地址空間

進程地址空間

  • 1 內存描述符
    • 分配內存描述符
    • 銷毀內存描述符
    • mm_struct與內核線程
  • 2 內存區域
    • VMA標志
    • VMA操作
    • 內存區域的樹形結構和內存區域的鏈表結構
  • 3 操作內存區域
    • find_vma()
    • find_vma_prev()
    • find_vma_intersection()
  • 4 mmap()和do_mmap():創建地址空間
    • mmap() 系統調用
  • 5 munmap()和do_munmap():刪除地址空間
    • munmap()系統調用
  • 6 頁表

內核除了管理本身的內存外,還必須管理進程的地址空間,也就是系統中每個用戶空間地址所對應的內存。Linux操作系統采用虛擬內存技術,因此,系統中的所有進程之間以虛擬方法共享內存,對每個進程來說,它們好像都可以訪問整個系統的所有物理內存。

進程地址空間由每個進程中的線性地址區組成,每個進程都有一個32位或64位的平坦地址空間,空間的具體大小取決于體系結構,平坦地址空間是指地址空間范圍是一個獨立的連續空間(比如,地址從0擴展到429496729位地址空間)。一些操作系統提供了段地址空間,這種地址空間并非是一個獨立的線性區域,而是被分段的,但現代采用虛擬內存的操作系統通常都使用平坦地址空間而不是分段式的內存模式。通常情況下,每個進程都有唯一的這種平坦地址空間,而進程地址空間之間彼此互不相干,兩個不同的進程可以在它們各自地址空間的相同地址內存放不同的數據。進程之間也可以選擇共享地址空間,我們稱這樣的進程為線程。

1 內存描述符

內核使用內存描述符結構體表示進程的地址空間,該結構包含了和進程地址空間有關的全部信息。內存描述符由mm_struct結構體表示,定義在文件linux/sched.h中。

struct mm_struct {struct vm_area_struct * mmap;		/* list of VMAs */struct rb_root mm_rb;struct vm_area_struct * mmap_cache;	/* last find_vma result */unsigned long (*get_unmapped_area) (struct file *filp,unsigned long addr, unsigned long len,unsigned long pgoff, unsigned long flags);void (*unmap_area) (struct vm_area_struct *area);unsigned long mmap_base;		/* base of mmap area */unsigned long free_area_cache;		/* first hole */pgd_t * pgd;atomic_t mm_users;			/* How many users with user space? */atomic_t mm_count;			/* How many references to "struct mm_struct" (users count as 1) */int map_count;				/* number of VMAs */struct rw_semaphore mmap_sem;spinlock_t page_table_lock;		/* Protects page tables, mm->rss, mm->anon_rss */struct list_head mmlist;		/* List of maybe swapped mm's.  These are globally strung* together off init_mm.mmlist, and are protected* by mmlist_lock*/unsigned long start_code, end_code, start_data, end_data;unsigned long start_brk, brk, start_stack;unsigned long arg_start, arg_end, env_start, env_end;unsigned long rss, anon_rss, total_vm, locked_vm, shared_vm;unsigned long exec_vm, stack_vm, reserved_vm, def_flags, nr_ptes;unsigned long saved_auxv[42]; /* for /proc/PID/auxv */unsigned dumpable:1;cpumask_t cpu_vm_mask;/* Architecture-specific MM context */mm_context_t context;/* Token based thrashing protection. */unsigned long swap_token_time;char recent_pagein;/* coredumping support */int core_waiters;struct completion *core_startup_done, core_done;/* aio bits */rwlock_t		ioctx_list_lock;struct kioctx		*ioctx_list;struct kioctx		default_kioctx;
};

mm_users域記錄正在使用該地址的進程數目。比如,有兩個進程共享該地址空間,那么mm_users的值便等于2;mm_count是mm_struct的主引用計數,只要mm_users不為0,那么mm_count值就等于1。當mm_users的值減為0時,mm_count域的值才為0,如果mm_count的值等于0,說明已經沒有任何指向該mm_struct結構體的引用了,這個時候該結構體會被銷毀。
mmap和mm_rb這兩個數據結構描述的對象是相同的:該地址空間中的全部內存區域。但是mmap是以鏈表形式存放而后者以紅-黑樹形式存放。mmap結構體作為鏈表,利于簡單、高效地遍歷所有元素,而mm_rb結構體更適合搜索指定元素。
所有的mm_struct結構體都通過自身的mmlist域連接在一個雙向鏈表中,該鏈表的首元素是init_mm內存描述符,它代表init進程的地址空間,內存描述符的總數存放在mmlist_nr全局變量中,該變量定義在kernel/fork.c中。

分配內存描述符

在進程的struct task_struct進程描述符中,mm域存放著該進程使用的內存描述符,所以current->mm便指向當前進程的內存描述符。fork函數利用copy_mm()函數復制父進程的內存描述符,也就是current->mm域給其子進程,而子進程中的mm_struct結構體實際是通過文件kernel/fork.c中的allocate_mm()宏從mm_cachep slab緩存中分配得到的。

如果父進程希望和其子進程共享地址空間,可以在調用clone()時,設置CLONE_VM標志。我們把這樣的進程稱作線程。當CLONE_VM被指定后,內核就不再需要調用allocate_mm()函數了,而僅僅需要在調用copy_mm()函數中將mm域指向其父進程的內存描述符就可以了。
在這里插入圖片描述

銷毀內存描述符

當進程退出時,內核會調用exit_mm函數,該函數執行一些常規的銷毀工作,同時更新一些統計量。其中,該函數會調用mmput()函數減少內存描述符的mm_users用戶計數,如果mm_users降到0,繼續調用mmdrop()函數,減少mm_count,如果mm_count也等于0了,說明該內存描述符不再有任何使用者了,那么調用free_mm宏通過kmem_cache_free()將mm_struct結構體歸還到mm_cachep_slab緩存中。

mm_struct與內核線程

內核線程沒有進程地址空間,也沒有相關的內存描述符,所以內核線程對應的進程描述符中mm域為空。

當一個進程被調度時,該進程的mm域指向的地址空間被裝載到內存,進程描述符中的active_mm域會被更新,指向新的地址空間。內核線程沒有地址空間,所以mm域為NULL,于是當一個內核線程被調用時,就會保留前一個進程的地址空間,隨后內核更新內核線程對應的進程描述符中的active_mm域,使其指向前一個進程的內存描述符。

2 內存區域

內存區域由vm_area_struct結構體描述,定義在文件linux/mm.h中,內存區域在內核中也經常被稱作虛擬內存區域或VMA。
vm_area_struct結構體描述了指定地址空間內連續區間上的一個獨立內存范圍。內核將每個內存區域作為一個單獨的內存對象管理,每個內存區域都擁有一致的屬性,

struct vm_area_struct {struct mm_struct * vm_mm;	/* The address space we belong to. */unsigned long vm_start;		/* Our start address within vm_mm. */unsigned long vm_end;		/* The first byte after our end addresswithin vm_mm. *//* linked list of VM areas per task, sorted by address */struct vm_area_struct *vm_next;pgprot_t vm_page_prot;		/* Access permissions of this VMA. */unsigned long vm_flags;		/* Flags, listed below. */struct rb_node vm_rb;/** For areas with an address space and backing store,* linkage into the address_space->i_mmap prio tree, or* linkage to the list of like vmas hanging off its node, or* linkage of vma in the address_space->i_mmap_nonlinear list.*/union {struct {struct list_head list;void *parent;	/* aligns with prio_tree_node parent */struct vm_area_struct *head;} vm_set;struct prio_tree_node prio_tree_node;} shared;/** A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma* list, after a COW of one of the file pages.  A MAP_SHARED vma* can only be in the i_mmap tree.  An anonymous MAP_PRIVATE, stack* or brk vma (with NULL file) can only be in an anon_vma list.*/struct list_head anon_vma_node;	/* Serialized by anon_vma->lock */struct anon_vma *anon_vma;	/* Serialized by page_table_lock *//* Function pointers to deal with this struct. */struct vm_operations_struct * vm_ops;/* Information about our backing store: */unsigned long vm_pgoff;		/* Offset (within vm_file) in PAGE_SIZEunits, *not* PAGE_CACHE_SIZE */struct file * vm_file;		/* File we map to (can be NULL). */void * vm_private_data;		/* was vm_pte (shared mem) */#ifdef CONFIG_NUMAstruct mempolicy *vm_policy;	/* NUMA policy for the VMA */
#endif
};

每個內存描述符都對應于進程地址空間的唯一區間,vm_start域指向區間的首地址,vm_end域指向區間的尾地址之后的第一個字節,vm_end~vm_start的大小便是內存區間的長度,內存區域的位置就在[vm_start,vm_end]之中,注意,在同一個地址空間內的不同內存區間不能重疊。

vm_mm域指向和VMA相關的mm_struct結構體,注意每個VMA對其相關的mm_struct來說都是唯一的,所以即使兩個獨立的進程將同一個文件映射到各自的地址空間,它們分別都會有一個vm_area_struct結構體標志自己的內存區域,但是如果兩個線程共享一個地址空間,那么它們也同時共享其中所有的vm_area_struct結構體。

VMA標志

VMA標志是一種位標志,其定義在linux/mm.h中,它包含在vm_flags域內,標志了內存區域所包含的頁面的行為和信息,和物理頁的訪問權限不同,VMA標志反映了內核處理頁面所需要遵守的行為準則,而不是硬件要求。
在這里插入圖片描述

VMA操作

vm_area_struct結構體中的vm_ops指向與指定內存區域相關的操作函數表,內核使用表中的方法操作VMA。vm_area_struct作為通用對象代表了任何類型的內存區域,而操作表描述針對特定的對象實例的特定方法。
操作函數表由vm_operations_struct結構體表示,定義在文件linux/mm.h中

/** These are the virtual MM functions - opening of an area, closing and* unmapping it (needed to keep files on disk up-to-date etc), pointer* to the functions called when a no-page or a wp-page exception occurs. */
struct vm_operations_struct {void (*open)(struct vm_area_struct * area);void (*close)(struct vm_area_struct * area);struct page * (*nopage)(struct vm_area_struct * area, unsigned long address, int *type);int (*populate)(struct vm_area_struct * area, unsigned long address, unsigned long len, pgprot_t prot, unsigned long pgoff, int nonblock);
#ifdef CONFIG_NUMAint (*set_policy)(struct vm_area_struct *vma, struct mempolicy *new);struct mempolicy *(*get_policy)(struct vm_area_struct *vma,unsigned long addr);
#endif
};

內存區域的樹形結構和內存區域的鏈表結構

上面說過,可以通過內存描述符中的mmap和mm_rb域之一訪問內存區域,這兩個域各自獨立地指向與內存描述符相關的全部內存區域對象vm_area_struct。

mmap使用單獨鏈表連接所有的內存區域對象vm_area_struct,每一個vm_area_struct結構體通過自身的vm_next域被連入鏈表,所有的區域按地址增長的方向排序,mmap域指向鏈表中第一個內存區域,鏈中最后一個VMA結構體指針指向空。

mm_rb域使用紅-黑樹連接所有內存區域對象,mm_rb域指向紅-黑樹的根結點,地址空間中每一個vm_area_struct結構體通過自身的vm_rb域連接到樹中。

鏈表用于需要遍歷全部結點的時候,而紅-黑樹適用于在地址空間中定位特定內存區域的時候。內核為了內存區域上的各種不同操作都能獲得高性能,所以同時使用了這兩種數據結構。

3 操作內存區域

內核定義了許多內存區域操作函數,它們都聲明在文件linux/mm.h中

find_vma()

find_vma()函數定義在mm/mmap.c中。

/* Look up the first VMA which satisfies  addr < vm_end,  NULL if none. */
extern struct vm_area_struct * find_vma(struct mm_struct * mm, unsigned long addr);

該函數在指定的地址空間中搜索第一個vm_end大于addr的內存區域。換句話說,該函數尋找第一個包含addr或首地址大于addr的內存區域,如果沒有發現這樣的區域,該函數返回NULL。否則返回指向匹配的內存區域的vm_area_struct結構體指針,返回的結構會被緩存在內存描述符的mmap_cache域中,所以find_vma會先在緩存中查找,如果指定的地址不在緩存中,那么必須搜搜和內存描述符相關的所有內存區域,這種搜索通過紅-黑樹進行。

find_vma_prev()

find_vma_prev()函數和find_vma()工作方式相同,但是它返回第一個小于addr的VMA。該函數定義和聲明分別在文件mm/mmap.c中和文件linux/mm.h中

extern struct vm_area_struct * find_vma_prev(struct mm_struct * mm, unsigned long addr,struct vm_area_struct **pprev)

pprev參數存放指向先于addr的VMA指針。

find_vma_intersection()

find_vma_intersection()返回第一個和指定地址區間相交的VMA。因為該函數和內聯函數,所以定義在文件linux/mm.h中:

static inline struct vm_area_struct * find_vma_intersection(struct mm_struct * mm, unsigned long start_addr, unsigned long end_addr)
{struct vm_area_struct * vma = find_vma(mm,start_addr);if (vma && end_addr <= vma->vm_start)vma = NULL;return vma;
}

第一個參數是要搜索的地址空間,start_addr是區間的開始首位置,end_addr是區間的尾位置,

4 mmap()和do_mmap():創建地址空間

內核使用do_mmap()函數創建一個新的線性地址區間。如果創建的地址區間和一個已經存在的地址區間相鄰,并且它們具有相同的訪問權限的話,那么兩個區間將合并為一個。do_mmap()函數會將一個地址區間加入到進程的地址空間中。

do_mmap()函數定義在linux/mm.h中

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

該函數映射由file指定的文件,具體映射的是文件中從偏移量offset處開始,長度為len字節的范圍內的數據。如果file參數是NULL并且offset參數也是0,那么就代碼這次映射沒有和文件相關,該情況被稱作匿名映射,如果指定了文件名和偏移量,那么該映射被稱為文件映射。

addr是可選參數,它指定搜索空閑區域的起始位置。
prot參數指定內存區域中頁面的訪問權限。訪問權限標志定義在文件asm/mman.h中。
flag參數指定了VMA標志,這些標志也定義在文件asm/mman.h中

mmap() 系統調用

在用戶空間可以通過mmap()系統調用獲取內核函數do_mmap()的功能。

5 munmap()和do_munmap():刪除地址空間

do_munmap()函數從特定的進程地址空間中刪除指定地址區間,該函數定義在文件linux/mm.h中:

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

第一個參數指定要刪除區域所在的地址空間,刪除從地址start開始,長度為len字節的地址區間,如果成功,返回0.

munmap()系統調用

系統調用munmap()給用戶空間程序提供了一種從自身地址空間刪除指定區間的方法。

int munmap(void *start ,size_t length)

該系統調用定義在mm/mmap.c中,它是對do_munmap的一個簡單封裝

6 頁表

雖然應用程序操作的對象是映射到物理內存之上的虛擬內存,但是處理器直接操作的卻是物理內存,所以當應用程序訪問一個虛擬地址時,首先必須將虛擬地址轉化為物理地址,然后處理器才能解析地址訪問請求。地址的轉換工作是通過查詢頁表完成的。
頁表對應的結構體依賴具體的體系結構,所以定義在文件asm/page.h中

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/379616.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/379616.shtml
英文地址,請注明出處:http://en.pswp.cn/news/379616.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

JavaScript中帶有示例的Math.log10()方法

JavaScript | Math.log10()方法 (JavaScript | Math.log10() Method) Math operations in JavaScript are handled using functions of math library in JavaScript. In this tutorial on Math.log10() method, we will learn about the log10() method and its working with e…

JSP技術

一、jsp腳本和注釋 jsp腳本&#xff1a; 1&#xff09;<%java代碼%> ----- 內部的java代碼翻譯到service方法的內部 2&#xff09;<%java變量或表達式> ----- 會被翻譯成service方法內部out.print() 3&#xff09;<%!java代碼%> ---- 會被翻譯成servlet的成…

jQuery.ajax不能實現return值調用問題

我們使用jQuery.ajax函數是不能實現success方法return值的&#xff0c;而有時候我們需要對成功返回的數據進行處理&#xff0c;一般來說&#xff0c;與服務器交互后會返回很多的數據&#xff0c;而有些數據需要進行特別處理&#xff0c;這時需要實現success方法return&#xff…

Linux內核設計與實現---頁高速緩存和頁回寫

頁高速緩存和頁回寫1 頁高速緩存2 基樹3 緩沖區高速緩存4 pdflush后臺例程膝上型電腦模式bdflush和kupdated避免擁塞的方法&#xff1a;使用多線程頁高速緩存&#xff08;cache&#xff09;是Linux內核實現的一種主要磁盤緩存&#xff0c;通過把磁盤中的數據緩存到物理內存中&a…

EL技術

1&#xff0e;EL 表達式概述 EL&#xff08;Express Lanuage&#xff09;表達式可以嵌入在jsp頁面內部&#xff0c;減少jsp腳本的編寫&#xff0c;EL 出現的目的是要替代jsp頁面中腳本的編寫。 2&#xff0e;EL從域中取出數據(EL最重要的作用) jsp腳本&#xff1a;<%requ…

math.trunc_JavaScript中帶有示例的Math.trunc()方法

math.truncJavaScript | Math.trunc()方法 (JavaScript | Math.trunc() Method) Math.trunc() is a function in math library of JavaScript that is used to extract the integer part of the given floating-point number. It removes the decimal and all the digits after…

.NET程序員的書單

zz from sjtu bbs: http://bbs.sjtu.edu.cn/bbscon?boardDotNET&fileM.1126188158.A 發信人: luckySeven(lucky為這位mm默哀), 信區: DotNET 標 題: .NET程序員的書單 發信站: 飲水思源 (2005年09月08日22:02:45 星期四), 轉信 發信人: AtomAndBit (原子與比特), 信區: D…

SVN+AnkhSVN端配置

對于ankhSVN我想很多人不陌生&#xff0c;因為經常使用&#xff0c;但是我還是發現很多人并不怎么會配置&#xff0c;或者完全不知道其需要配置&#xff0c;如果不配置的話&#xff0c;當兩個人同時需要修改某個文件的時候就容易中彈了。SVN默認是不支持“鎖定-編輯-解鎖”的&a…

Linux內核設計與實現---模塊

模塊1 構建模塊放在內核源代碼樹中放在內核代碼外2 安裝模塊3 產生模塊依賴性4 載入模塊5 管理配置選項6 模塊參數7 導出符號表Linux內核是模塊化組成的&#xff0c;它允許內核在運行時動態地向其中插入或從中刪除代碼。 與開發的內核核心子系統不同&#xff0c;模塊開發更接近…

JSTL技術

1&#xff0e;JSTL概述 JSTL&#xff08;JSP Standard Tag Library)&#xff0c;JSP標準標簽庫&#xff0c;可以嵌入在jsp頁面中使用標簽的形式完成業務邏輯等功能。jstl出現的目的同el一樣也是要代替jsp頁面中的腳本代碼。JSTL標準標準標簽庫有5個子庫&#xff0c;但隨著發展…

asinh函數_JavaScript中帶有示例的Math.asinh()方法

asinh函數JavaScript | Math.asinh()方法 (JavaScript | Math.asinh() Method) Math.asinh() is a function in math library of JavaScript that is used to find the value of hyperbolic arc-sine of a number. Math.asinh()是JavaScript數學庫中的函數&#xff0c;用于查找…

使用PHP創建一個REST API(Create a REST API with PHP)

譯者前言&#xff1a; 首先這是一篇國外的英文文章&#xff0c;非常系統、詳盡的介紹了如何使用PHP創建REST API&#xff0c;國內這方面的資料非常非常的有限&#xff0c;而且基本沒有可操作性。這篇文章寫的非常好&#xff0c;只要對PHP稍有了解的程序員&#xff0c;看完本文基…

old-

大數問題:求用一段C或C程序寫求 f(x)100! 的完整程序大數問題&#xff0c; 我用數組作的&#xff0c;輸出格式應該是是222,222,222 #include "stdafx.h" #include<stdio.h> #include<stdlib.h> int a[1000]{0}; in…

javaEE的開發模式

1&#xff0e;什么是模式 模式在開發過程中總結出的“套路”&#xff0c;總結出的一套約定俗成的設計模式 2&#xff0e;javaEE經歷的模式 model1模式&#xff1a; 技術組成&#xff1a;jspjavaBean model1的弊端&#xff1a;隨著業務復雜性 導致jsp頁面比較混亂 model2模式…

Linux內核設計與實現---kobject sysfs

kobject sysfs1 kobject2 ktype3 kset4 subsystem5 別混淆了這些結構體6 管理和操作kobject7 引用計數kref8 sysfssysfs中添加和刪除kobject向sysfs添加文件9 內核事件層2.6內核增加了一個引人注目的新特性—同一設備模型。設備模型提供了獨立的機制專門表示設備&#xff0c;并…

開發Windows Mobile今日插件 -- 內存電量,桌面便箋,桌面記單詞

本篇文章講解的是開發 Windows Mobile 上的今日插件。關于是今日插件&#xff0c;在 PPC 或者 SP SDK 的幫助文檔中有相關的章節介紹&#xff0c;在網絡上也有一些帖子和資源講解。在這里簡要回顧一下。今日插件就是在windows mobile的桌面上顯示的條目&#xff0c;例如系統提供…

c語言中將函數指針作為形參_在C中將有效指針作為NULL指針

c語言中將函數指針作為形參Prerequisite: An Example of Null pointer in C 先決條件&#xff1a; C中的空指針示例 Any pointer that contains a valid memory address can be made as a NULL pointer by assigning 0. 通過分配0&#xff0c;可以將包含有效內存地址的任何指…

[轉]一個清華計算機博士生的退學申請

偶然間在網上看到這篇帖子&#xff0c;回想起自己的求學經歷&#xff0c;思索良久。。。 本想找到原帖及作者&#xff0c;但是幾經搜索&#xff0c;發現原帖出自科學網&#xff0c;已被刪除。對此&#xff0c;我還能說啥&#xff1f;&#xff01; http://www.sciencenet.cn/m/u…

算法---遞歸

遞歸結題三部曲 何為遞歸&#xff1f;程序反復調用自身即是遞歸。 我自己在剛開始解決遞歸問題的時候&#xff0c;總是會去糾結這一層函數做了什么&#xff0c;它調用自身后的下一層函數又做了什么…然后就會覺得實現一個遞歸解法十分復雜&#xff0c;根本就無從下手。 相信…

給定條件找最小值c語言程序_根據給定條件最小化n的最小步驟

給定條件找最小值c語言程序Problem statement: 問題陳述&#xff1a; Given a number n, count minimum steps to minimize it to 1 performing the following operations: 給定數字n &#xff0c;執行以下操作&#xff0c;計算最少的步驟以將其最小化為1&#xff1a; Operat…