成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

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

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

 

本文目錄

  • 不知道自己不知道!
  • 指針何時指針?
  • 指針何時是整數(shù)?
  • 物理地址是指針?
  • 模糊地帶
  • 絕世好代碼?

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

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

  • 不知道自己不知道;
  • 知道自己不知道;
  • 知道自己知道。

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

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

  • 指針:主要是為了方便引用(Dereferencing)一個內(nèi)存地址, Dereferencing is used to access or manipulate data contained in memory location pointed to by a pointer。所以指針的目的其實就是為了這樣的讀寫操作:
  1. *p = a; 
  2. b = *p; 
  • 整數(shù):整數(shù)是一個數(shù)值,它的主要目的是為了加減等計算、比對、做數(shù)組下標(biāo)、做索引之類的。它的目的不是為了引用一個內(nèi)存。指針和整數(shù)(這里主要是unsigned long,因為unsigned long的位數(shù)一般等于CPU可尋址的內(nèi)存地址位數(shù))本身是八竿子打不著的,但是它們之間的一個有趣聯(lián)系是:

如果我們只是關(guān)心這個地址的值,而不是關(guān)心通過這個地址去訪問內(nèi)存,這個時候,內(nèi)核經(jīng)常喜歡用unsigned long代替指針。

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

指針是指針?

  1. copy_from_user(void *to,  
  2.      const void __user *from,  
  3.      unsigned long n); 
  4.  
  5. copy_to_user(void __user *to,  
  6.      const void *from,  
  7.     unsigned long n); 

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

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

指針是整數(shù)?

  1. /**              
  2.  * get_user_pages() - pin user pages in memory 
  3.  * @start: starting user address 
  4.  * ... 
  5.  */ 
  6. long get_user_pages( 
  7.    unsigned long start, 
  8.    unsigned long nr_pages, 
  9.    unsigned int gup_flags, struct page **pages, 
  10.    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()只關(guān)心start這個數(shù)值本身,它用于去運算、查找、比對。它不是要:

  1. *start = 100; 

類似的例子還有:

  1. long pin_user_pages( 
  2.    unsigned long start, unsigned long nr_pages, 
  3.    unsigned int gup_flags, struct page **pages, 
  4.    struct vm_area_struct **vmas); 

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

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

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

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

然后我們看看VMA的定義:

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

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

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

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

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

物理地址是指針?

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

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

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

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

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

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

模糊地帶

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

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

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

這看起來是不是特別地“精分”?折騰來折騰去,折騰什么鬼呢?這一篇文章有解釋: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()吧:

  1. *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就是假方向。

“絕世好代碼”是不存在的,“水至清則無魚”,往死里爭反而陷入了鉆牛角尖。等你寫出絕對完美代碼的時候,黃花菜早就歇了。別忘了,還有最重要的一招,“天下武功,無堅不破,唯快不破。”

本文轉(zhuǎn)載自微信公眾號「Linux閱碼場」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系Linux閱碼場公眾號。 

 

責(zé)任編輯:武曉燕 來源: Linux閱碼場
相關(guān)推薦

2009-12-17 15:18:47

2015-05-13 14:12:52

Linux內(nèi)核內(nèi)核獲得成功

2021-09-06 07:45:08

LinuxLinux內(nèi)核

2015-07-27 11:34:03

Linux內(nèi)核指針

2022-02-18 00:15:58

Linux指令CPU

2024-03-15 08:54:59

Linux內(nèi)核NUMA

2023-02-28 09:47:42

2014-07-02 16:51:08

WOT2014高效技術(shù)團隊

2010-03-01 13:32:42

Linux常用文件

2021-09-27 09:52:41

FacebookBOLTLinux

2016-09-26 13:50:52

Linux系統(tǒng)性能

2020-03-18 14:08:48

Windows操作系統(tǒng)功能

2021-07-04 15:16:14

索引B+數(shù)據(jù)庫

2023-01-03 15:47:09

Linux內(nèi)核C語言

2012-04-06 10:35:30

SpringJavaHibernate

2014-03-26 10:09:14

指針指針使用

2024-07-01 12:53:19

2021-02-20 11:34:43

Linux內(nèi)核指針

2022-03-25 12:31:49

Linux根文件內(nèi)核

2009-12-21 17:11:38

Linux認證
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 超碰人人插 | 亚洲人在线观看视频 | 欧美一级特黄aaa大片在线观看 | 性网站免费 | 久久精品国产99国产精品 | 91精品国产自产在线老师啪 | 国产一区二区视频在线 | 乱一性一乱一交一视频a∨ 色爱av | 一区二区在线 | 日韩欧美在线播放 | 91久久久久久 | 久久久久国产 | 99re热这里只有精品视频 | 国产精品国产精品国产专区不卡 | 玖玖视频免费 | 午夜视频在线免费观看 | 国产区精品在线观看 | 久草在线 | 欧美日本久久 | 老司机精品福利视频 | 爱爱免费视频 | 成人福利网 | 亚洲欧美一区二区三区国产精品 | 国产专区在线 | 欧美一级欧美一级在线播放 | 精品在线播放 | 国产精品高清在线 | 91成人精品 | 521av网站| 亚洲免费av一区 | 欧美日韩国产一区二区 | a欧美| 日韩高清一区二区 | 中文字幕一区二区三区四区五区 | 99色在线 | 最新中文字幕久久 | 日韩成人影院 | 欧美 日本 国产 | 国产精品久久久久久久久久久免费看 | 国产精品小视频在线观看 | 国产精品久久久久aaaa樱花 |