91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

為什么Linux內核常常用Unsigned Long來代替指針

發布時間:2021-11-01 14:35:49 來源:億速云 閱讀:213 作者:柒染 欄目:系統運維

這篇文章給大家介紹為什么Linux內核常常用Unsigned Long來代替指針,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

昨天我犯了一個錯誤把指針和整數“混淆”的錯誤,幸得隊友王童鞋指正,今早起床,我把這個心得花一點時間記錄下來。

大抵掌握一個技術或者知識都是這三個階段:

  • 不知道自己不知道;

  • 知道自己不知道;

  • 知道自己知道。

比較難突破的是“不知道自己不知道”的階段,因為“不知道自己不知道”,所以才往往特別自信,覺得“老子天下第一”。基本上,本文要記錄的一個小點,也是一個我從“不知道自己不知道”到“知道自己知道”的過程。

我們都知道(???),指針和整數在C語言里面是兩種不同含義的:

  • 指針:主要是為了方便引用(Dereferencing)一個內存地址, Dereferencing is used to access or  manipulate data contained in memory location pointed to by a  pointer。所以指針的目的其實就是為了這樣的讀寫操作:

*p = a; b = *p;
  • 整數:整數是一個數值,它的主要目的是為了加減等計算、比對、做數組下標、做索引之類的。它的目的不是為了引用一個內存。指針和整數(這里主要是unsigned  long,因為unsigned long的位數一般等于CPU可尋址的內存地址位數)本身是八竿子打不著的,但是它們之間的一個有趣聯系是:

如果我們只是關心這個地址的值,而不是關心通過這個地址去訪問內存,這個時候,內核經常喜歡用unsigned long代替指針。

我們下面來看2個不同的場景:

指針是指針?

copy_from_user(void *to,       const void __user *from,       unsigned long n);  copy_to_user(void __user *to,       const void *from,      unsigned long n);

在這2個函數里面,void __user *from,void __user  *to都清楚地表明用戶空間的虛擬地址是一個指針。這2個函數這樣做的原因是非常清晰的,我就是要去Dereference用戶空間的地址,進行內存拷貝的,所以它的目的是為了通過指針來訪問內存。

類似的例子比如file_operations里面read、write什么的:

為什么Linux內核常常用Unsigned Long來代替指針

指針是整數?

/**               * get_user_pages() - pin user pages in memory  * @start: starting user address  * ...  */ long get_user_pages(    unsigned long start,    unsigned long nr_pages,    unsigned int gup_flags, struct page **pages,    struct vm_area_struct **vmas)

注釋清楚地寫明,start是用戶態的起始地址:@start: starting user address

所以,本質上,和copy_from_user()里面的void __user *from一樣的,但是這里它用的是unsigned long start  !!!不是void __user * start !!!

原因非常清楚,get_user_pages()只關心start這個數值本身,它用于去運算、查找、比對。它不是要:

*start = 100;

類似的例子還有:

long pin_user_pages(    unsigned long start, unsigned long nr_pages,    unsigned int gup_flags, struct page **pages,    struct vm_area_struct **vmas);

更加不要提著名的find_vma():

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

它根據addr用戶態地址,在進程的mm里面去找到addr位于的VMA。顯然,這個時候,它的目的是為了完成addr與進程每個VMA起始和結束地址的比多。

這個時候,我們來看看VMA結構體的長相,就更加有意思了。我們都知道,VMA是為了記錄進程每一段虛擬地址空間的(比如代碼段、數據段、堆、棧、mmap等):

為什么Linux內核常常用Unsigned Long來代替指針

然后我們看看VMA的定義:

為什么Linux內核常常用Unsigned Long來代替指針

看到沒有,vm_start和vm_end都是妥妥的unsigned long啊!!!

我于是試圖弄清楚這么做的科學依據是什么,發現LDD3里面赫然寫著這么一段話(LDD3第11章289頁):

為什么Linux內核常常用Unsigned Long來代替指針

它的科學依據是,既然你不是為了dereferencing,我就讓你dereferencing不了,免得你又跑去dereferencing,從而導致bug。有的人說,我強制轉化unsigned  long為指針,不就可以訪問了嗎?

你不是還是需要強制轉換不是?你強制轉換之前,會想一下,這個地方指針為啥是個整數呢?你想明白了,說不定就不去訪問了。這樣它實際達到了震懾心靈的效果。

到這里,我們談的都還是虛擬地址,那么下面我們來談下物理地址。

物理地址是指針?

在一個有MMU的系統中,物理地址從來都不是指針。物理地址,從骨子里就是一個整數!!!我記得之前經常有人往內核發patch,把物理地址用個指針

*p來描述,這是錯到根子里面的事情,所以每次都被罵地狗血淋頭。

因為你根本不可能用物理地址去Dereferencing 什么東西。物理地址在內核的描述是:

為什么Linux內核常常用Unsigned Long來代替指針

它要么是一個32位的整數,要么是一個64位的整數。

那么,物理地址什么時候是一個指針呢?在我還是一個小屁孩在大學玩《仙劍奇俠傳》和《軒轅劍:天之痕》的時候,我那個時候玩單片機,單片機里面沒有MMU,所以也沒虛擬地址的概念,都是妥妥地通過物理地址“指針”來訪問內存的。

所以,如果一個人,一輩子都是玩單片機,它肯定會覺得我這篇文章在胡扯,因為他還是一個“不知道自己不知道”的階段。

模糊地帶

這里面仍然有一些模糊地帶,比如__get_free_page()、__get_free_pages()這樣的API,返回的也是unsigned  long而不是指針:

為什么Linux內核常常用Unsigned Long來代替指針

往死里作也要把它弄成unsigned long!!!

實際上,內核要有時候需要訪問__get_free_page()返回的內存,此前它需要進行強制類型轉換:

為什么Linux內核常常用Unsigned Long來代替指針

這看起來是不是特別地“精分”?折騰來折騰去,折騰什么鬼呢?這一篇文章有解釋:An (unsigned) long story about page  allocation

https://lwn.net/Articles/669015/

統計表明,90%以上的情況下,__get_free_page()返回的unsigned long都會被強制轉化為指針!!!但是這個返回unsigned  long是在歷史的第一天,Linux的0.01就這樣了。Al Viro

https://lwn.net/Articles/668852/

但是改動實在太多了,改了接近600個文件,對此Linus的態度是:

"No way in hell do we suddenly change the semantics of an interface that has  been around from basically day #1."

所以Linus的建議是,你真的需要一個指針的時候,你還是去調用kmalloc()吧:

*kmalloc(size_t size, gfp_t flags);

絕世好代碼

很多工程師喜歡較真,就是必須在0和1之間做一個選擇。這個選擇有時候真的很難,所以Linus的意思是,0和1踏馬地都不要,我不去跟你爭這個0和1的問題,我給你第三條路。這里,我看出了Linus的大智若愚啊!

我個人在工程里面對無意義的0和1的爭論也也沒什么好感,感覺在浪費我的時間。我對事情的看法是,爭一個0.7或者0.3就OK了。0.7就是真方向,0.3就是假方向。

關于為什么Linux內核常常用Unsigned Long來代替指針就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

浙江省| 九江县| 白玉县| 靖边县| 遂昌县| 会昌县| 河池市| 扶沟县| 郎溪县| 吉林市| 古浪县| 嘉兴市| 张北县| 福贡县| 繁昌县| 比如县| 嘉善县| 连平县| 从江县| 四子王旗| 防城港市| 云霄县| 汤阴县| 泾阳县| 秦皇岛市| 射阳县| 宣威市| 轮台县| 建宁县| 唐河县| 高唐县| 鹰潭市| 响水县| 彰武县| 长泰县| 文安县| 杭锦旗| 玉龙| 甘谷县| 平原县| 融水|