当前时区为 UTC + 8 小时



发表新帖 回复这个主题  [ 10 篇帖子 ] 
作者 内容
1 楼 
 文章标题 : 编译内核过程中部分内核配置的新手指南。
帖子发表于 : 2007-02-21 0:15 

注册: 2006-10-28 21:14
帖子: 53
地址: 北京
送出感谢: 0 次
接收感谢: 0 次
最近发现论坛中有很多人对自己动手编译内核很感兴趣,正好赶上春节放假,闲着没事,于是想写一篇介绍如何编译内核的文章。但其实无论是网上还是论坛里这类文章都挺多的,不过我发现这些文章大部分只讲编译内核的步骤,对如何配置内核确很少提及,而我恰恰觉得配置内核是编译内核的过程中最重要的一步,因此在这里我决定将自己在这方面的经验写出来与大家分享。

在正式介绍之前先作几点声明:
1)由于linux内核实在是太庞大了,涉及的内容包罗万象,我自己的水平也很有限,所以我不可能将内核配置的方方面面都写出来,本文中我只是将我认为最重要的一些部分作一个介绍。当然,仁者见仁,智者见智,也许我认为重要的其他人不觉得重要,反之亦然,所以欢迎大家对本文进行补充,也欢迎大家对错误之处进行指正。
2)本文介绍的内核配置是针对i386体系结构,对其它体系结构不完全适用(当然大部分也适用)。
3)本文在介绍内核配置的过程中假设你的ubuntu是以桌面应用为主要目的,也就是说我对内核配置的推荐做法都是在保证系统吞吐量(throughput)的情况下尽最大可能性来提高系统的响应能力(responsiveness)。
4)本文仅仅针对2.6.x内核,参考的内核是2.6.20。
5)本文只介绍对内核的配置,并不会介绍如何从头到尾编译出一个内核,因此请对编译内核的步骤还不太清楚的弟兄们先参考一些相关的文章。

本文所指的内核配置其实最核心的东西就是一个文件,这个文件名为.config,位于内核源码的根目录下。这个文件在你刚刚将内核源码解压缩后并不存在,需要你进行过一次配置后才会产生出来。这个文件是一个文本文件,里面有许多行信息,这些信息决定了在你编译内核的时候要支持哪些特性以及哪些模块。下面举两个例子,比如在我的.config文件中有一行为:CONFIG_SMP=y,这就表示我编译的内核要支持对称多处理器(Symmetric multi-processing),这意味着在编译内核的c文件时会定义一个叫CONFIG_SMP的宏;另外还有一行:CONFIG_NTFS_FS=m,这一行表示编译内核的过程中要编译出一个支持NTFS文件系统的可加载内核模块(LKM,loadable kernel module)。
上面的例子中提到了可加载内核模块(LKM,loadable kernel module)的概念,我在这里有必要对这个概念进行一些介绍。首先说linux是单内核(monolithic kernel)结构的,linux编译后可以视为一个大的程序,这个程序为所有的调用者提供服务(当然,内核中还有许多的内核线程在运转着)。那么凡是需要支持的功能(比如对某种硬件的支持)都应该编译进内核的镜像中去,但世界上有成千上万种硬件设备,如果把支持它们的程序都编译进内核镜像中去会导致内核过于庞大,而且这些设备并不是在每台机器上都有,实际上常用的也就那么几十种而已,把它们都编译进内核镜像也会造成大量的浪费。所以内核的开发者们设计出一种能够动态地增加内核功能的机制---可加载内核模块(LKM,loadable kernel module,本文以后简称LKM),LKM可以在需要的时候动态地加载到内核中去,一旦加载上就成为了内核的一部分,从而扩展了原有内核的功能(读者可以用/sbin/lsmod来观看系统中有哪些LKM被加载了)。上面提到的CONFIG_NTFS_FS=m会使编译内核的工具将支持NTFS文件系统的程序编译为一个LKM。如果将这一句话改为CONFIG_NTFS_FS=y那么就会使得实现NTFS文件系统的程序被直接编译到内核的镜像中去。
总体来讲,内核配置的过程就是编辑.config文件的过程。当然你可以用任何一个文本编辑器来手工编辑.config文件,不过如果真的决定这样做,我只能说愿佛祖保佑你:-)。在.config文件中可以编辑的条目达数千之多,为了简化编辑过程,内核的开发者们开发出了一些工具专门用于编辑.config文件,这些工具都附带在内核的源码压缩包中,但是这些工具在第一次使用的时候要进行编译才能使用(因为linux内核源码包不能直接将binary的文件打进去,毕竟它要在各种硬件平台下使用)。因此使用这些工具之前,你的ubuntu必须安装相应的开发环境,比如GTK或者QT的开发环境,关于如何安装这些环境许多文章都讲过,我就不赘述了。这几个工具都可以在内核源码的根目录下通过输入make xxx命令的方法来调用,下面分别介绍。
1)menuconfig,调用命令:sudo make menuconfig。这是一个字符界面的配置工具,虽然是字符界面但也提供了简单的菜单界面,它的好处是可以在没有X Window的环境下对内核进行配置。该工具需要libncurses的开发环境。
2)qconf,调用命令:sudo make xconfig。这是一个GUI的配置工具,基于QT。这个工具非常直观易用,大家一看就明白怎么用。要求具备QT的开发环境。
3)gconf,调用命令:sudo make gconfig。与上面的相似,只不过基于GTK,所以要求GTK开发环境。

这三个工具我推荐使用qconf,因为menuconfig不够直观,gconf我觉得有bug,而且在最新的2.6.20内核中的gconf我没能成功地使用起来,它总是会弹出一个空的窗体然后就没响应了,不知道是为什么。
在使用上面提到的工具进行配置前你一定要准备一个.config文件的模板,因为.config文件中可以编辑的条目太多了,大部分都是与驱动程序相关的,如果你一项一项地进行配置需要花费大量的精力(坦白地讲,我也没有过这种经历,因为驱动程序太多了,我也不知道哪些有用哪些没用)。因此在配置之前,你应该找到一份模板,然后集中精力编辑一些比较重要的配置条目。ubuntu的/boot目录下就有一些很好的模板,比如/boot/config-2.6.17-11-generic,你只要把这样的文件copy到你的内核源码根目录下并更名为.config即可。另外,注意在qconf中,大多数条目都可以有三种状态:打上对勾,打上圆点,无勾选,这三种状态分别对应.config文件中的=y,=m,=n,也即直接编译进内核镜像,编译为LKM,不定义宏或不编译。

下面我就将我认为重要的配置条目逐一地向大家介绍(大部分都与桌面性能相关)。下面每个条目的名称以它在gconf菜单中的全路径表示。

1)General setup-->Cpuset support:
该条目为y或者n,为y时定义CONFIG_CPUSETS宏。建议不勾选这项。因为这个特性是主要是用于NUMA架构和拥有大量逻辑CPU的SMP架构,开启这一特性会浪费一些内存,对一般人的桌面环境没有任何帮助。

2)Processor type and features-->Symmetric multi-processing support:
该条目为y或者n,为y时定义CONFIG_SMP宏。勾选这一项使得内核支持对称多处理器。建议勾选,因为最新的CPU大多支持双核或者超线程,就算你现在是单CPU,难保你哪天升级:)。

3)Processor type and features-->Processor family
这是一个单选条目,目的是为了指定CPU的具体型号以便进行优化,应该慎重选择,选错有可能造成内核无法正常工作。举个例子来说明这个选项的优化作用,比如你选中了Athlon/Duron/K7,那么内核在进行memcpy()的时候就会使用经过3DNow!指令集优化过的版本。下面我大致把市面上常见的CPU应该选哪项做一个粗糙地分类:

针对AMD的CPU:
a)Sempron(闪龙)/Duron(毒龙)/Athlon(速龙):选择Athlon/Duron/K7。
b)Sempron64/Athlon64/Athlon64 X 2/Opteron(皓龙):选择Opteron/Athlon64/Hammer/K8。
c)比上述CPU都古老的:选择K6/K6-II/K6-III。(当然如果你的CPU是K5的就得选586/K5/5x86/6x86/6x86MX了。)

针对INTEL的CPU:
a)奔腾2:选择Pentium-II/Celeron(pre-Coppermine)
b)奔腾3:选择Pentium-III/Celeron(Coppermine)/Pentium-III Xeon
c)奔腾4/奔腾D/赛扬D:选择Pentium-4/Celeron(P4-based)/Pentium-4 M/older Xeon
d)酷睿2:选择Core 2/newer Xeon
注意:如果你的CPU是赛扬(赛扬D除外),你必须先确认它到底是基于P2,P3还是P4的,一般来讲主频高于1G的是基于P4的,高于500M但小于1G的是基于P3的,其它的是基于P2的。另外,志强处理器你也要区分是基于P3,P4或者是Core 2。

4)Processor type and features-->Generic x86 support
该条目为y或者n,为y时定义CONFIG_X86_GENERIC宏,目的是针对x86体系结构进行一般优化。比如,当CONFIG_X86_GENERIC宏定义的情况下(当然CONFIG_X86_TSC宏也要定义,这个取决于前面Processor family的选择。),内核中的get_cycles()函数会使用TSC(time stamp counter)获取cycles值。这个选项一定要勾选上。

5)Processor type and features-->Maximum number of CPUs
这个条目是一个数字的宏(NR_CPUS,范围为2-255),该数字决定内核最多支持的逻辑CPU数量。恰当地设定该数字可以使你的内核节省一些内存,这是因为内核中有许多数据结构是根据NR_CPUS的大小来固定分配空间的,即使你现有的逻辑CPU少于NR_CPUS,这些空间也会被分配出来从而造成浪费。如何确定你有多少个逻辑CPU呢?举例来说吧:比如你的系统是单CPU,但是支持超线程(Hyper-threading),那么逻辑CPU数量为2;再比如说你的系统是双CPU,每个CPU又支持双核,那么逻辑CPU数量为4。一般来说,大家应该把这个数值设定为大于等于你系统的逻辑CPU数。考虑到大多数人应该还是使用单CPU系统,我推荐大家将这个值设定为4,因为目前个别INTEL的CPU同时支持双核与超线程,另外INTEL还推出了四核心的CPU,AMD应该也会很快推出四核心CPU,所以将这个值设定为4是为了给不久的未来上个保险同时对于少于4个逻辑CPU的朋友又不会浪费你太多的内存。

6)Processor type and features-->SMT (Hyperthreading) scheduler support
该条目为y或者n,为y时定义CONFIG_SCHED_SMT宏。定义它的目的是为了对支持超线程的CPU提供能好的调度功能。我举例说明一下:比如一个系统有两个CPU,每个CPU又支持超线程,那么系统中有四个逻辑CPU,我们将这个四个CPU记为C00, C01, C10, C11,其中C00与C01是一个物理CPU上的两个硬件线程,而C10与C11则是另一个物理CPU上的两个硬件线程。假设某一个时刻系统中有两个线程在执行,在没有定义CONFIG_SCHED_SMT宏的情况下内核很可能会将这两个线程分别调度到C00与C01上去,但这是不优化的,因为C00与C01是一个物理CPU上的两个硬件线程,它们共享了许多硬件资源,导致两个线程运行时并不能充分发挥这个系统的资源优势;而定义了CONFIG_SCHED_SMT宏的情况下,内核的调度器就会将这两个线程调度到位于不同物理CPU的逻辑CPU上,比如C00与C10上。
乍一看似乎只要你的CPU支持超线程,那么你就应该勾选这一项,但是我认为并不一定这样。比如你的系统是单CPU并且支持超线程,那么这个时候CONFIG_SCHED_SMT宏对于提高调度效果并没有什么意义(当然前提是我对内核调度器的理解无误的话:)),而且定义CONFIG_SCHED_SMT宏还会对调度增加额外的开销。
我的建议是:在你的CPU支持超线程的前提下,只有当你的系统有多个CPU或者支持多核的时候才有必要勾选这一项。

7)Processor type and features-->Multi-core scheduler support
该条目为y或者n,为y时定义CONFIG_SCHED_MC宏。它的作用很类似于前面提到的CONFIG_SCHED_SMT,只不过它针对多核(multi-core)。我的建议是只有你的系统拥有多个物理CPU时才有必要勾选。比如我家里的机器CPU为单AMD Athlon 64 X2 4200+,虽然是双核,但只有一个物理CPU,我就没有必要定义这个宏。

8)Processor type and features-->Preemption Model
这是一个单选条目。它是一条对桌面响应能力有很重要意义的选项,我强烈建议大家选择Preemptible Kernel (Low-Latency Desktop) ,选中这项会定义CONFIG_PREEMPT宏,表示该内核将支持抢占式内核(Preemptible Kernel )特性。为了突出它的重要性,我下面通过一个例子简单介绍一下抢占式内核的概念:假设某系统中(假定只有一个逻辑CPU)有两个线程在运行,一个叫线程H,一个叫线程L,线程H的动态优先级要比线程L高,在某一时刻,线程H由于等待键盘响应进入了阻塞状态,这时内核将会调度线程L将其投入运行,而再假设在线程L运行的过程中由于某种原因进入了内核空间(这里主要指由于系统调用或者异常),而恰巧在L进入了内核之后用户按下了键盘,键盘中断导致线程H从阻塞状态进入了就绪状态,那么按照正常的逻辑此时应该立刻将CPU交给线程H(因为线程H的动态优先级高),从而让线程H立刻进行用户按键后的操作处理。这个时候,如果系统支持抢占式内核特性的话,那么linux确实会像刚才的逻辑那样做,但是如果不支持抢占式内核特性的话,那么线程H将无法立刻得到执行,它必须要等到线程L完成了在内核空间中的所有工作并返回用户空间的前一刻才能得到调度,这就会对线程H造成一个调度上的延迟,这种延迟最大可能达到几百毫秒之多,而几百毫秒的延时可能让人产生响应迟钝的感觉。因此对于以响应能力为重要指标的桌面系统来说,抢占式内核特性是很重要的(据我所知windows与mac os x的内核都属于抢占式内核),因此极力建议大家选中这项。
抢占式内核特性是从2.6.x内核开始有的,我从fedora core 2一直到fedora core 6以及ubuntu 6.10自己编译的内核都开启这个特性,从来没有遇到过任何稳定性方面的问题,所以请大家放心。另外,再多说一句,不要把抢占式内核与抢占式多任务(preemptive multitasking)混为一谈,linux从最开始就是支持抢占式多任务的。

9)Processor type and features-->Preempt The Big Kernel Lock
该条目为y或者n,为y时定义CONFIG_PREEMPT_BKL宏。定义该宏是为了在某线程占有大内核锁(Big Kernel Lock)的时候仍然可以被其它高优先级线程抢占调度。这个特性同样是为了提高桌面系统的响应能力,建议勾选。

10)Processor type and features-->High Memory Support
这是一个单选条目。目的是用于配置linux内核对high memory的支持。这个条目解释起来比较麻烦,我就不细说了。我的建议是:如果你的物理内存小于等于896MB,并且在短期内没有升级内存的可能性的话,你就选择off;如果内存大于896MB或者说有可能升级到大于896MB内存的话,并且小于4GB,那么选择4GB;如果你的内存大于4GB的话,原则上你应该选择64GB,但是为了充分利用你的系统资源,这种情况我还是建议你安装x86_64版本的ubuntu。

11)Processor type and features-->Allocate 3rd-level pagetables from highmem
该条目为y或者n,为y时定义CONFIG_HIGHPTE宏。这个宏的功能解释起来也比较麻烦,我也就不细说了。但注意这个宏只有在前一条目选择了4GB或者64GB才有意义。我的建议是:如果你的物理内存数量超出896MB不是很多的时候就不要选中这项,比如说只有1GB内存的时候就我觉得就没有必要选;但如果超出很多就有必要选中,比如说2GB内存的时候。

12)Processor type and features-->Timer frequency
这又是一个单选条目,选择完成后最终会定义一个叫HZ的宏,这是一个数字宏,目前在2.6.20内核中这个数字可以为100,250,300与1000四种。这个数字决定了每秒钟时钟中断发生的次数,每次时钟中断要处理许多事情,但就桌面性能来说,它的频率决定了内核定时器与用户空间定时器的精度。这个数字越大,定时器的精度就越高,而用于处理定时器中断所造成的开销也就越大。不过为了提高桌面系统的多媒体性能,我建议大家将这一条目选择为1000 HZ。当然,如果你觉得自己的CPU性能不够高,也可以考虑选择HZ_250或者HZ_300(当然,性能够不够高没有一个绝对的准绳,我觉的凡是主频高于1G的CPU选则1000 HZ是毫无问题的。)。

13) File systems-->Ext3 journalling file system support
该条目为y,m或者是n。这个条目决定了内核对Ext3文件系统的支持,前面提到过y/m/n三者的含义。我的建议是:如果大家大量使用Ext3文件系统的话,那么选则y(也就是在qconf中打上对勾,即把Ext3的支持代码直接编译进内核镜像);否则的话,选则m(也就是在qconf中打上圆点,即把Ext3的支持代码编译成一个LKM)。
那么为什么在大量使用Ext3的时候就选择y呢?这是因为LKM在需要的时候会被加载到内核空间的非线性映射区域,这块区域的页表是动态的,需要在不同的进程之间进行同步。而且一旦进程发生了切换之后,描述这一区域的页表项的缓存(TLB)将会被flush掉,从而导致进程切换后访问相同LKM的性能有所降低;而如果直接将代码编译进内核镜像的话,这些代码将会位于内核空间的线性映射区域,而这块区域的页表项是全局的,其缓存不会在进程切换后被flush掉,从而提高了性能。
同理,如果你大量使用其它文件系统(比如Reiserfs)的话,你也可以将相应的条目选择为y。

14)File systems-->DOS/FAT/NT Filesystems-->NTFS file system support
该条目为y,m或者是n。这个条目与桌面性能倒是没有什么关系,只不过我估计大多数人都是linux/windows双系统,为了能够访问NTFS文件系统,记得要将这一项选择为m或者y。另外,值得一提的是:这个条目下面还有一个子条目NTFS file system support,这个条目只能为y或者n,为y时定义NTFS_RW宏,定义该宏使得该模块支持对NTFS文件系统的写入操作。请注意,对于早期的2.6.x内核来说,这种写入操作是不安全的;而在2.6.20里面说现在写操作已经安全了,但是只能写已经存在了的文件,而且文件长度还不能改变,这样一来用处也不大了。所以我建议不要选中这个条目。

15)Kernel hacking-->Kernel debugging-->Use 4Kb for kernel stacks instead of 8Kb
该条目为y或者n。为y时定义CONFIG_4KSTACKS宏。这个特性是将每个线程的核心堆栈从8Kb减少到4Kb,而发生外部中断时则给每个逻辑CPU准备一个IRQ stack以补偿核心堆栈减小带来的损失。这个特性的好处是在系统中有大量线程存在时降低了内存的使用量,另外更重要的是由于i386体系结构一个页面的大小就是4Kb,每次分配4Kb会减少内核中page allocator分配页面时造成的内存碎片效果;它的坏处是发生中断时会有切换堆栈的额外操作发生。我只建议内存比较少又需要同时运行大量线程的朋友开启这个选项。

写到这里,我能想到的比较重要的条目也就都罗列出来了,我深深感到自己对linux内核的了解还远远不够,尤其是对于具体设备的配置方面我没能写出一个有用的建议,深感惭愧!另外,请注意,其实还有很多也必须要选择的条目我并没有罗列出来(比如说General setup-->Configure standard kernel features (for small systems)-->Enable futex support),但是这些条目或者与桌面性能关系不大,或者在前面提到的.config文件模板中几乎肯定已经选中了,所以我也就不提了。
其实写本文的主要目的除了可以给新手们提供一个参考以外,还希望它能够起到抛砖引玉的作用,使得论坛里的高手能够将自己对内核配置的心得分享出来,让我们大家一起学习,一起进步。


页首
 用户资料  
 
2 楼 
 文章标题 :
帖子发表于 : 2007-02-21 5:15 
头像

注册: 2006-05-13 4:02
帖子: 10606
送出感谢: 0 次
接收感谢: 7
好~
++


_________________
HP Pavilion DV6-2064CA: AMD Turion II Ultra Dual-Core Mobile M640, HD4650, 2GBx2 DDR2-800, Seagate 500GB 7200RPM SATA, BD-ROM
DELL UltraSharp 2209WA
Arch64, Testing repo


页首
 用户资料  
 
3 楼 
 文章标题 :
帖子发表于 : 2007-02-22 9:19 

注册: 2006-11-17 6:33
帖子: 85
送出感谢: 0 次
接收感谢: 0 次
我如果打开主动抢占内核,和调到1000Hz时
用realplay放rstp时会出现马赛克,关掉时则正常,用的是2.6.20.1内核,我试了两三次都这样
大家的使用情况怎么样?


页首
 用户资料  
 
4 楼 
 文章标题 :
帖子发表于 : 2007-02-22 12:30 
头像

注册: 2005-10-14 16:55
帖子: 1637
地址: 江苏
系统: OS X
送出感谢: 0 次
接收感谢: 0 次
2.6.17的时候打开过,不过忘记是不是调到1000Hz了,用mplayer看的时候没有马赛克,倒是看HDTV会丢帧……


_________________
Hardy Heron + Mac OS X

在某些人眼里(炫耀+摆阔+不懂音乐+在乎外表-内涵-鉴赏能力-文化)*跟风=买iPod


页首
 用户资料  
 
5 楼 
 文章标题 :
帖子发表于 : 2007-03-02 19:27 

注册: 2006-11-09 16:03
帖子: 43
送出感谢: 0 次
接收感谢: 0 次
楼主的文章写的好啊,看了很有收获,增长了不少知识,就喜欢这样的好文啊!

有几个问题,顺便问一下:
我在桌面终端里面运行 make qconf有错:

代码:
make: *** 没有规则可以创建目标“qconf”。 停止。


运行make menuconfig 则正常。

这是怎么回事,运行gcof的错误于qconf一样!

另外,我的File systems-->DOS/FAT/NT Filesystems-->NTFS file system support 这个项目也选择不了啊!?

不知道是怎么回事!


页首
 用户资料  
 
6 楼 
 文章标题 :
帖子发表于 : 2007-03-02 20:35 

注册: 2006-10-28 21:14
帖子: 53
地址: 北京
送出感谢: 0 次
接收感谢: 0 次
要使用qconf要在终端输入sudo make xconfig,如果用gconf则输入sudo make gconfig

SpringGao 写道:
另外,我的File systems-->DOS/FAT/NT Filesystems-->NTFS file system support 这个项目也选择不了啊!?

怎么个选不上法?你用sudo make xconfig再试试。


页首
 用户资料  
 
7 楼 
 文章标题 :
帖子发表于 : 2007-03-02 23:28 

注册: 2006-11-09 16:03
帖子: 43
送出感谢: 0 次
接收感谢: 0 次
jianliang79 写道:
要使用qconf要在终端输入sudo make xconfig,如果用gconf则输入sudo make gconfig

SpringGao 写道:
另外,我的File systems-->DOS/FAT/NT Filesystems-->NTFS file system support 这个项目也选择不了啊!?

怎么个选不上法?你用sudo make xconfig再试试。


刚刚发现了qconf的启动方法,我又学习了一下,问题都已经解决了。谢谢你了。

我不知道运行gconf要安装什么东西gtk的具体名字,半天没有看到,有知道的告诉我。
对了,我运行qconf后,速度怎么这么慢阿,根本就是受不了,拖动一个划块半天都没反应,上网浏览就正常。这是什么缘故?


页首
 用户资料  
 
8 楼 
 文章标题 :
帖子发表于 : 2007-03-03 14:49 

注册: 2007-01-13 22:39
帖子: 202
地址: 杭州
送出感谢: 0 次
接收感谢: 0 次
好东西,顶下
到学校后学习下
西西~


页首
 用户资料  
 
9 楼 
 文章标题 :
帖子发表于 : 2007-03-10 21:39 

注册: 2005-05-30 10:08
帖子: 36
送出感谢: 0 次
接收感谢: 0 次
不错,正需要,学习中。。。


页首
 用户资料  
 
10 楼 
 文章标题 :
帖子发表于 : 2007-08-22 13:15 

注册: 2007-08-14 20:06
帖子: 28
送出感谢: 0 次
接收感谢: 0 次
最近才接触内核,正需要,谢谢


页首
 用户资料  
 
显示帖子 :  排序  
发表新帖 回复这个主题  [ 10 篇帖子 ] 

当前时区为 UTC + 8 小时


在线用户

正在浏览此版面的用户:没有注册用户 和 5 位游客


不能 在这个版面发表主题
不能 在这个版面回复主题
不能 在这个版面编辑帖子
不能 在这个版面删除帖子
不能 在这个版面提交附件

前往 :  
本站点为公益性站点,用于推广开源自由软件,由 DiaHosting VPSBudgetVM VPS 提供服务。
我们认为:软件应可免费取得,软件工具在各种语言环境下皆可使用,且不会有任何功能上的差异;
人们应有定制和修改软件的自由,且方式不受限制,只要他们自认为合适。

Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
简体中文语系由 王笑宇 翻译