moonspark 发表于 2006-12-16 19:11:30

事关毕业,请知道的大虾帮个忙,关于内存管理

最近论文里面要涉及到Linux的内存管理,有一个根本的概念弄不清楚:
就是Linux里面“系统空间”和“用户空间”到底是怎样划分的,基于什么样的标准来划分的?可以动态改变吗?从代码的角度,在哪里说明了哪一段是“系统空间”?哪一段是“用户空间”?

“系统空间和用户空间尽管是开始的时候通过TASK_SIZE的定义把最高的1GB给了系统,但是在系统完成这一部分的初始化之后,在接下来的初始化过程中可以通过别的方式(非修改TASK_SIZE)调整系统空间和用户空间的大小,甚至可使两者交错出现”,这个观点,对吗?

“只有系统态能访问到的空间才能称为系统空间,只要是用户进程可以直接访问的空间都是用户空间,用户进程在不经过系统调用的前提下,无法访问系统空间”,这个观点,对吗?

manioster 发表于 2006-12-17 13:11:16

<<understanding linux kernel>>结合图表对这部分讲得很清楚,建议去系统地看一看,毕竟事关毕业嘛.

zyzii 发表于 2006-12-17 13:51:12

我的理解:
<1>
   一般而言,划分的情况是0-3G是“用户空间”,3-4G是“系统空间”。“系统空间”是CPU的RING 0级,“用户空间“是RING 3级。内核在编译时,将内核代码的偏移都认为加上了3G,见/arch/i386/kernel/vmlinux.lds:
      SECTIONS
{
. = (0xC0000000 + 0x100000);
....
}
    将当前地址设为3G+1M;
   用户的进程在创建时,其空间不会大于3G。过程较复杂。

<2>
“系统空间和用户空间尽管是开始的时候通过TASK_SIZE的定义把最高的1GB给了系统,但是在系统完成这一部分的初始化之后,在接下来的初始化过程中可以通过别的方式(非修改TASK_SIZE)调整系统空间和用户空间的大小,甚至可使两者交错出现”
不是很清楚这句话的意思。”调整系统空间和用户空间的大小“,都编译了怎么调整?至于“两者交叉”,安全怎么保证?LINUX就是靠把二者分开才赖以活着的基础,中断怎么处理?


<3>“只有系统态能访问到的空间才能称为系统空间,只要是用户进程可以直接访问的空间都是用户空间,用户进程在不经过系统调用的前提下,无法访问系统空间”
    系统态是可以访问所有空间的,包括系统空间和用户空间。get_user()函数就是在系统态RING0 下从用户空间拷贝到系统空间的函数。
   用户进程只能访问3G 以下,它访问的空间就是用户空间了。
“用户进程在不经过系统调用的前提下,无法访问系统空间”,对的这句。

moonspark 发表于 2006-12-17 14:30:33

很感激各位的回答,我具体说一下我现在面临的情况,拜托各位帮忙看看,谢谢了:
我的毕业设计是在ARM7TDMI(带MMU)上移植的一个ARMLinux(2.40.8版内核),然后在上面做的一些应用。我的试验板上有16M的SDRAM,但是在开始的machine_desc中,只上报给了系统15M,这样系统所管理的内存被我隐藏了1M。在接下来的系统初始化过程中,我在map_desc中把这1M以下面的形式加了上去:{0xC0F00000,0xC0F00000,0,DOMAIN_KERNEL,1,1},而在系统初始化的create_mapping()函数中自然会调用这个生成此时的系统空间映射表项。
当系统初始化完成之后,我调用用户进程,用户进程可以直接访问该段内存(隐藏下来的那1M空间)进行读写。而同时,我在IRQ进程中,其也可以直接访问该段内存进行读写。我现在的问题就是,这1M空间到底是属于系统空间还是用户空间?

我个人的看法是它应该属于“系统空间”,理由有下面几点,不一定对,希望您能帮我指点一下,现在自己是有点迷惑了:
1、因为一开始已经在TASK_SIZE里面定义了0xC0000000UL,而后面并没有作任何改动。所以用户空间的上限就应该只到0xC0000000,并且进程的PCB里面有一个专门的字段addresslimit也说明了只要不修改的话,PCB的用户空间是固定的(0~addresslimit),而我现在这里直接访问的地址是0xC0F00000UL,已经处于了非用户空间的区域;
2、在Linux里面,我认为限制用户进程访问一段系统空间的关键是在于一个访问权限的设置。但是假如我修改了一段系统空间的访问权限,也就是说我让一段系统空间的访问权限低于用户态,这样即使用户进程也可以直接访问系统空间。例如ARM里面,其用来控制访问权限的就是域值和AP值两个手段,假如我让其变成都可以访问的,那这样一来,用户进程也一样可以直接访问系统空间了;
3、Linux中,假如是x86结构,其通过GDT、LDT、IDT和TSS来进行内存管理,其中GDT就只有一张,其映射的空间应该是系统空间,为所有的进程共享。当进程切换的时候,LDT是要随进程更换的,但是GDT不会。而在ARMLinux中,内核进程关于系统空间的映射是放在init_mm里面的一张映射表,所有的进程都会不加修改地继承这张映射表,而在不同的进程切换的时候,其用户空间的映射表是会因为Copy On Write 机制而改变的。并且,我在试验中,我只是修改了内核进程的页面映射表(因为我是在系统初始化的时候进行的这个页面映射表项的生成,这个时候根本还没有用户进程的),假如这个页面映射表不属于系统空间的页面映射表,那如何能够被所有的用户进程所继承并顺利访问该段空间?既然现在能够被用户进程继承并顺利访问,所以我认为这个页面映射表项是在系统空间的页面映射表里面的,这样一来的话,是否能说这段空间就是“系统空间”呢?
4、我想您肯定看到了,我在修改map_desc结构的时候,还专门把它放到了kernel域,不过,试验结果却表明只要我修改了AP值,无论放在kernel或者user、I/O域,都不会影响用户进程的访问。如此看来,是否能够支持我的第二个观点的成立?但是,这样一来,在ARM上,系统空间和用户空间的界定标准到底是什么呢?在毛德操、胡希明老师的《嵌入式系统--采用公开源码和StrongARM/Xscale处理器》的书中第286页开始,说道:“当(ARM)的AP值为0b01时,CPU在用户态是不能访问该页面的,而系统态则不受影响,所以这实际上说明了相应页面是属于用户空间还是系统空间”,所以但我把这1M的内存的AP值改为0b01的时候,确实可以发生用户进程越权访问的错误,这是否说明其确实还是处于系统空间的位置呢?但是当改为0b11的时候,用户进程就可以直接访问了。这是否还能说明该空间还是属于“系统空间”呢?
5、从用户进程不经系统调用而直接访问系统空间,好像是有可能的。曾经在网上看到过,有人利用/proc的特点直接访问系统空间,或者,像我这样的一种方式。不过自己确实是不能肯定,这样的几种方式,访问到的“系统空间”,能称之为“系统空间”吗?我觉得是,毕竟还是经过MMU,同时该地址确确实实是超过了PCB里面的addresslimit,不过不确定,请您指点一下。:)谢谢了。

zyzii 发表于 2006-12-17 16:59:03

首先先要分清什么是系统空间,什么是物理内存。
   系统空间是要经过MMU映射的物理内存。
   系统空间的数据,还有用户空间的数据都是放在物理内存的,二者区别就是经过MMU转换后映射到不同的页面。
   在用户的进程中是可以用读/dev/kmem来访问整个物理内存的,(注意这种情况是不经过MMU的),当然系统空间的数据也可以被访问到。一个例子是ALAN COX编的dmidecode访问1M下的物理内存。但这个可以是漏洞,ALAN COX也这样怀疑。可以读dmidecode的代码注释。
   至于/proc下东西,之所以可以访问一些内核数据,是因为它本是是/proc的文件系统。用户进程可以通过该文件系统访问一些内核的数据,但这不能说是“用户进程不经系统调用而直接访问系统空间”,因为这种方式是没有用中断,但也不是“直接”,而是利用proc文件的特性来访问的。
   ARM 我没有弄过,所有没有什么发言权。

zyzii 发表于 2006-12-17 17:09:22

”当系统初始化完成之后,我调用用户进程,用户进程可以直接访问该段内存(隐藏下来的那1M空间)进行读写。而同时,我在IRQ进程中,其也可以直接访问该段内存进行读写。我现在的问题就是,这1M空间到底是属于系统空间还是用户空间? “

   请将用户进程访问这1M内存的过程说清楚;“IRQ进程”是什么意思?

moonspark 发表于 2006-12-17 17:35:03

:)谢谢您的回复。事实上,我在下午的回帖里面提到了,因为我将该1M空间重新按照自己的要求定义了map_desc:{0xC0F00000,0xC0F00000,0,DOMAIN_KERNEL,1,1}
这样,该1M的虚拟地址和物理地址都是一样的,同时也处于kernel域,AP值为1,1;
这样我试验中直接就在应用程序里面访问的0xC0F00000,事实上,也确实可以在IRQ先往这段内存写入数据之后,用户程序通过直接访问的方式获取到IRQ写入的数据。所以,我在这里的疑问就是,这1M的空间到底应该算系统空间还是用户空间?

假如这1M的空间算用户空间,那下面这两个问题应该如何解释?
1、明明我们是在系统初始化时定义的该1M空间的页面映射表项,为什么它却跑到了用户空间的页面映射表里?初始化的时候还没有用户进程,哪里有用户空间页面映射表呢?
2、每一个用户进程的用户空间页面映射表都是不一样的,但是为什么在我之前的试验里不同的用户进程(division、original)却能够用同样的虚拟地址访问同一段内存呢?应用程序里面虚地址写的都是0xC0F00000,也同样都对同一段内存进行了清零(我当时试验里面曾经通过调试手段看到过的),所以假如说两个用户进程访问不是同一段内存也好像说不过去。可是这个问题应该如何用"用户空间"的定义来解释呢?

moonspark 发表于 2006-12-17 17:36:13

呵呵,不好意思,“IRQ进程”是我的一个笔误。应该是IRQ中断处理程序。:)

zyzii 发表于 2006-12-17 18:01:00

因为你已经在启动时,加上了映射,所有在IRQ中断处理函数中访问这段内存应该是没有什么问题的,此时的这1M页面是在系统空间的,也就是说通过内核的MMU映射可以找到这段物理内存。
    但在用户进程中通过访问0xC0F00000来访问这段地址在标准的LINXU内核中是不可能的。但在ARM 版的LINUX代码中也许是可以的,这跟具体的代码实现有关。你可以看一下ARM LINUX的用户进程的地址解析过程 。嵌入式的代码也许对安全性没有什么要求,所有才造成这种情况。
   那么这1M算用户空间吗?我的理解是用户空间的严格定义只有对标准的LINUX才有意义,对ARM LINUX版本就不是那么严格了。
   你可以试一下,在IRQ中在这段内存写一段数据,然后在用户进程中读出,看和你写的是不是一样的。若真的是一样的,就说明ARM LINUX的用户映射是不同于标准的LINUX的。

moonspark 发表于 2006-12-17 18:15:34

嗯,您说的那个现在IRQ中写,再从用户空间读的试验,我做过了,完全没有问题,是一样的。
事实上,我自己对于这个地方的理解:
我想Linux里面用来控制用户访问的实际上是两个方面,一方面赋予进程内核级和用户级的区分,一方面对访问的页面做一个访问权限的设定,这样综合两方面,保证用户进程只能访问系统空间。但是,毕竟Linux说白了,也是人为写的一段代码,能写自然能改,假如我们把访问权限改到最低,令即使是用户进程也能访问到,但是其仍然还是在系统空间页面映射表里(因为如果是系统初始化阶段完成的这个修改,该表项也只能在系统空间页面映射表里,因为这个时候连用户进程都没有,哪里来的用户空间映射表呢)。所以我们能够实现用户进程访问系统空间页面映射表里映射的空间。
当然,这是我在ARM上作的试验,x86体系上面因为是由GDTR、LDTR、IDTR、TR四个寄存器严格控制了内存管理,能不能做到,我也不太清楚。
以上是我个人的理解,请您指正。:)

另外,在PCB的数据结构里面,有一个数据项叫addresslimit,这是不是就实际上定义了一个进程的用户空间上限?(因为我的理解,用户进程的用户空间起始地址都是0x0)

zyzii 发表于 2006-12-18 10:07:33

用离散数学的映射观点解释应该比较清楚,就是 :
   
                                       内核的映射关系 : f            
      [1]    系统空间地址------------》   物理页面


                                       用户进程的映射关系: g
      [2]    用户空间地址------------》   物理页面

   
       在标准的LINUX代码中,[1]和[2]的映射的物理页面不会发生重叠,所有用户的进程访问不到系统空间的页面,也及
                     f(系统空间地址) != g(用户空间地址)

      但ARM LINUX的代码的映射关系,会出现:
                     f(系统空间地址) == g(用户空间地址)

      这就是你在用户下也能访问和系统空间一样的页面的原因。

moonspark 发表于 2006-12-18 11:02:56

我想您说的在ARMLinux下面的这种映射关系:
f(系统空间地址) == g(用户空间地址)
应该是偶尔或者说可能的情况,也即是可能偶尔几个进程会发生这样的问题。但是我现在的这个系统,所有的用户进程都可以通过这个映射表项访问到同一段物理内存。
从理论上说,所有的用户进程用户空间页面映射表相同的可能性应该不是很大吧?
况且该部分内存的映射定义是放在内核初始化阶段做的,用户空间地址映射应该是生成用户进程的时候动态生成的吧?这样说来,似乎并不能保证每一次都存在您所说的那样的映射情况:f(系统空间地址) == g(用户空间地址)。但是事实上,是每次都可以的。
我想可能还是因为ARMLinux的页面映射表使用方式或者地址解析过程可能存在一些不同吧,我回去再找找看。:)

zyzii 发表于 2006-12-18 12:28:20

“用户空间地址映射应该是生成用户进程的时候动态生成的吧?”

   在标准的LINXU中,(我不知道ARM是怎么做的) :
    用户进程的映射目录结构是在内核的系统空间里的,是在fork进程时创建的,假设页目录的结构的地址是va,先将va变为物理地址 ,pa = (va - 3G),然后将pa 放入CR3寄存器。用户空间的地址映射时,是根据CR3来找页目录的,这样在用户空间就找到了系统空间里建立好的页目录了。
    进程切换的时候,会根据不同进程所拥有的页目录地址来填充 CR3,以达到访问各自进程的页目录的目的。

zyzii 发表于 2006-12-18 12:31:50

在ARMLinux下面的这种映射关系:
f(系统空间地址) == g(用户空间地址)

你可以看一下ARM下在处理地址大于3G的情况下的处理就应该知道了。肯定是和标准的LINUX不一样的,才造成可以访问你那个内存的。

moonspark 发表于 2006-12-18 20:57:35

嗯,我下来会针对这个问题仔细再看看ARMLinux的代码,还有做一些相关的试验。:)这几天为了这个问题麻烦您了。不过,这两天马上就要答辩了,可能会忙一些别的事情。
很谢谢您这几天的指教,很希望以后还有机会直接跟您做一个交流。
忙完这阵子,自己会好好再深入研究一下这一个部分,现在的阶段性结论已经可以帮助我完成毕业论文了。当然,这得要谢谢您的指教。:)

我的e-mail是[email protected]
页: [1] 2
查看完整版本: 事关毕业,请知道的大虾帮个忙,关于内存管理