中文字幕日韩精品一区二区免费_精品一区二区三区国产精品无卡在_国精品无码专区一区二区三区_国产αv三级中文在线

為什么Linux內(nèi)核常常用UnsignedLong來代替指針

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

成都創(chuàng)新互聯(lián)公司是一家專業(yè)提供普洱企業(yè)網(wǎng)站建設,專注與網(wǎng)站設計制作、網(wǎng)站設計、H5建站、小程序制作等業(yè)務。10年已為普洱眾多企業(yè)、政府機構等服務。創(chuàng)新互聯(lián)專業(yè)的建站公司優(yōu)惠進行中。

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

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

  • 不知道自己不知道;

  • 知道自己不知道;

  • 知道自己知道。

比較難突破的是“不知道自己不知道”的階段,因為“不知道自己不知道”,所以才往往特別自信,覺得“老子天下第一”?;旧希疚囊涗浀囊粋€小點,也是一個我從“不知道自己不知道”到“知道自己知道”的過程。

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

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

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

如果我們只是關心這個地址的值,而不是關心通過這個地址去訪問內(nèi)存,這個時候,內(nèi)核經(jīng)常喜歡用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個函數(shù)里面,void __user *from,void __user  *to都清楚地表明用戶空間的虛擬地址是一個指針。這2個函數(shù)這樣做的原因是非常清晰的,我就是要去Dereference用戶空間的地址,進行內(nèi)存拷貝的,所以它的目的是為了通過指針來訪問內(nèi)存。

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

為什么Linux內(nèi)核常常用Unsigned Long來代替指針

指針是整數(shù)?

/**               * 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是用戶態(tài)的起始地址:@start: starting user address

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

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

*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);

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

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

為什么Linux內(nèi)核常常用Unsigned Long來代替指針

然后我們看看VMA的定義:

為什么Linux內(nèi)核常常用Unsigned Long來代替指針

看到?jīng)]有,vm_start和vm_end都是妥妥的unsigned long啊!!!

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

為什么Linux內(nèi)核常常用Unsigned Long來代替指針

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

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

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

物理地址是指針?

在一個有MMU的系統(tǒng)中,物理地址從來都不是指針。物理地址,從骨子里就是一個整數(shù)!!!我記得之前經(jīng)常有人往內(nèi)核發(fā)patch,把物理地址用個指針

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

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

為什么Linux內(nèi)核常常用Unsigned Long來代替指針

它要么是一個32位的整數(shù),要么是一個64位的整數(shù)。

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

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

模糊地帶

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

為什么Linux內(nèi)核常常用Unsigned Long來代替指針

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

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

為什么Linux內(nèi)核常常用Unsigned Long來代替指針

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

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

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

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

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

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

所以Linus的建議是,你真的需要一個指針的時候,你還是去調(diào)用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內(nèi)核常常用Unsigned Long來代替指針就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

名稱欄目:為什么Linux內(nèi)核常常用UnsignedLong來代替指針
本文路徑:http://www.rwnh.cn/article14/jsdhge.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站建設、軟件開發(fā)、關鍵詞優(yōu)化商城網(wǎng)站、用戶體驗微信小程序

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站建設
霍城县| 长治县| 铜陵市| 富平县| 孙吴县| 马边| 祁连县| 房山区| 新田县| 普定县| 临西县| 仙桃市| 永寿县| 东丽区| 铅山县| 镶黄旗| 襄樊市| 和平县| 平塘县| 田林县| 镇安县| 隆回县| 剑阁县| 武隆县| 邻水| 常德市| 湘阴县| 呼伦贝尔市| 高雄市| 巴彦县| 常山县| 金寨县| 新巴尔虎左旗| 白河县| 麻阳| 常宁市| 马关县| 长岭县| 大石桥市| 策勒县| 宁阳县|