当前位置: 首页 > 科技观察

为什么Linux内核经常使用UnsignedLong而不是指针

时间:2023-03-14 20:51:39 科技观察

不知道这篇文章的内容是不是!什么时候是指针?什么时候指针是整数?物理地址是指针吗?感谢队友王同谢指正指针和整数“混淆”的错误,今天早上起床,抽空记录下这段经历。一般来说,掌握一门技术或知识有三个阶段:不知道你不知道;不知道你不知道;知道你不知道;知道你知道。突破“不知道就是不知道”的阶段比较难,因为“不知道就是不知道”,所以往往很自信,觉得“我是世上最好的”。基本上,本文要记录的一个小点,也是从“我不知道我不知道”到“我知道我知道”的一个过程。我们都知道(???),指针和整数在C语言中有两种不同的含义:一个指针。所以指针的用途其实就是为了这样的读写操作:*p=a;b=*p;索引之类的。它的目的不是引用内存。指针和整数(这里主要是unsignedlong,因为unsignedlong的位数一般等于CPU可寻址内存地址的位数)本身是密不可分的,但是它们之间一个有趣的联系是:如果我们只关心的该地址的值与通过该地址访问内存无关。这时候内核往往喜欢用unsignedlong来代替指针。我们来看两种不同的场景:指针是指针?copy_from_user(void*to,constvoid__user*from,unsignedlongn);copy_to_user(void__user*to,constvoid*from,unsignedlongn);在这两个函数中,void__user*from、void__user*to都明确表示用户空间的虚拟地址是一个指针。这两个函数这样做的原因很明确。我只是想Dereference到用户空间的地址,复制内存,所以它的目的是通过指针来访问内存。类似的例子比如file_operations中的读写:指针是整数?/***get_user_pages()-pinuserpagesinmemory*@start:startinguseraddress*...*/longget_user_pages(unsignedlongstart,unsignedlongnr_pages,unsignedintgup_flags,structpage**pages,structvm_area_struct**vmas)注释明确指出start是用户态的起始地址:@start:起始用户地址所以,本质上和copy_from_user()中的void__user*from是一样的,只不过这里用的是unsignedlongstart!!!不是void__user*开始!!!道理很明确,get_user_pages()只关心start本身的值,用于计算、查找和比较。它不是:*start=100;类似的例子有:longpin_user_pages(unsignedlongstart,unsignedlongnr_pages,unsignedintgup_flags,structpage**pages,structvm_area_struct**vmas);更不用说著名的find_vma()了:它根据addr的用户态地址在进程的mm中找到addr所在的VMA。显然,此时它的目的是完成addr与进程的各个VMA的起止地址的比值。这时候再来看看VMA结构的外观,就更有意思了。我们都知道VMA是记录进程的每一个虚拟地址空间(如代码段、数据段、堆、栈、mmap等):那么我们来看一下VMA的定义:看看有没有看到,vm_start和vm_end是正确的unsignedlong!!!我想弄清楚这样做的科学依据是什么,发现在LDD3(LDD3Chapter11,page289)中写了这么一段话:它的科学依据是,既然你不是为了dereferencing,那我就make吧你不可能取消引用,以免你再次取消引用,这会导致错误。有人说,如果我强制unsignedlong是一个指针,我可以访问它吗?还不需要投吗?在施放之前,您会考虑一下。为什么这里的指针是整数呢?你想了解,也许不会去参观。这样一来,实际上达到了震撼心灵的效果。到目前为止,我们还在谈论虚拟地址,那么让我们谈谈物理地址。物理地址是指针吗?在带有MMU的系统中,物理地址永远不是指针。物理地址是从下往上的整数!!!记得以前经常有人给内核打补丁,用指针*p来描述物理地址。淋浴。因为你不可能使用物理地址来取消引用某些东西。内核中对物理地址的描述是:要么是32位整数,要么是64位整数。那么,物理地址什么时候是指针呢?小时候在大学里玩《仙剑奇侠传》和《轩辕剑:天之痕》的时候就是在玩单片机。单片机中没有MMU,所以没有虚拟地址的概念。所有通过物理地址“指针”正确访问内存。所以,如果一个人一辈子都在玩单片机,他肯定会认为我的文章是胡说八道,因为他还处于“不知所云”的阶段??。还有一些比较模糊的地方,比如__get_free_page()、__get_free_pages()等API返回的是unsignedlong而不是指针:想死就改成unsignedlong!!!事实上,内核有时需要访问__get_free_page()返回的内存。在此之前,它需要进行一次强制类型转换:这样看起来是不是特别“好”?折腾折腾,折腾什么鬼?这篇文章有解释:An(unsigned)longstoryaboutpageallocationhttps://lwn.net/Articles/669015/统计表明,在90%以上的情况下,__get_free_page()返回的unsignedlong会被强行转换成指针!!!但是这个returnunsignedlong在历史的第一天,linux的0.01是这样的。AlVirohttps://lwn.net/Articles/668852/但是改动太多了,改了近600个文件。Linus对此的态度是:“我们根本不可能突然改变从第一天开始就存在的接口的语义。”所以Linus的建议是,当你真的需要一个指针时,你应该调用kmalloc():*kmalloc(size_tsize,gfp_tflags);绝世好代码很多工程师都喜欢真实,那就是必须在0和1之间做出选择。这个选择有时候真的很难,所以Linus的意思是0和1都不需要。这个0和1的问题我就不和你争论了,给你第三条路。在这里,我看到了Linus的大智大愚!我个人不喜欢项目中无意义的0和1争论,感觉是在浪费时间。我对事物的看法是,争取0.7或0.3是可以的。0.7是真实方向,0.3是错误方向。“绝世良典”不存在,“水清则无鱼”。如果你死战,你将陷入死胡同。当你写出绝对完美的代码时,黄花菜就会停止。别忘了,还有最重要的一招,“天下武道,没有攻不破的劲道,唯有攻不破的速度”。本文转载自微信公众号“Linux代码阅读领域”,可通过以下二维码关注。转载本文,请联系Linux代码阅读领域。