分类: LINUX
2010-04-30 15:53:25
转:
Linux 操作系统的文件构成内核虽说是Linux操作系统的精华所在,是其它程序赖以运行的基础,但是如果一个实用的操作系统仅仅只有内核,而在其上没有丰富、强大的系统程序和应用程序供用户使用,就好比大厦建好了,也通了电,但却没有电梯、电话和办公设备,用户仍然无法入住使用。因此Linux操作系统的发行版除了带有内核以外,还带有大量的系统程序和应用程序,比如最新红帽子系统的发行版本句需要2-3张光盘,其中绝大部分是应用程序。
要想深入学习linux内核,首先需要能熟练使用Linux操作系统,了解整个系统文件构成——正所谓,刨丁解牛,始见无非全牛者——由外至内的学习Linux,再从内向外推敲;从感性深入理性,再由理性返回感性,才会获得最深刻的认识。内核的众多特点最终还是要反映到用户应用上的,所以先熟悉应用无疑会对内核学习有很大裨益。而且学习内核结构的一个重要目的就是推动我们更有效的使用Linux操作系统,无论是从系统管理角度来说或是从程序开发角度上说,掌握内核级别的系统调用、资源分配、中断控制或进程调度等技术都是不凡的价值,可以帮助你有效开发和驾驭系统。
这节我们先在这里简要介绍一下Linux系统的文件构成,将Linux操作系统的外在全貌展现给大家。我们在系统启动后,进入系统所能观察到的就是一系列目录(使用ls或dir)
,认识这些目录构成是学习使用Linux系统的第一步,下面我们就罗列出主要目录并简要描述各自内容。
Linux系统根目录/下包含包含:
bin:该目录存放最常用的基本命令,比如拷贝命令cp、编辑命令vi、删除命令rm等。
boot:该目录包含了系统启动需要的配置文件、内核(vmliuxz)和系统镜像(initrd….img)等。
dev:该目录下存放的是Linux中使用或未使用的外部设备文件(fd代表软盘,hd代表硬盘等),使用这些设备文件可以用操作文件的方式操作设备。
etc:该目录下包含了所有系统服务和系统管理使用的配置文件;比如系统日志服务的配置文件syslog.conf,系统用户密码文件passwd等
home:该目录下包含了除系统管理员外的所有用户的主目录,用户主目录一般以用户登陆帐号命名。
Lib:该目录下包含了系统使用的动态连接库(*.so)和内核模块(在modules下)。
host+found:该目录包含了磁盘扫描检测到的文件碎片,如果你非法关机,那么下次启动时系统会进行磁盘扫描,将损坏的碎片存到该目录下。
mnt:该目录下包含用户动态挂载的文件系统。如果要使用光盘,U盘都一般应该将它们安装到该目录下的特定位置。
proc:该目录属于内存影射的一个虚拟目录,其中包含了许多系统现场数据,比如进程序数,中断情况,cpu信息等等,它其中的信息都是动态生成的,不在磁盘中存储。
root:该目录是系统管理员(root用户)的主目录。
sbin:该目录下包含系统管理员使用的系统管理命令,比如防火墙设置命令iptable,系统停机命令halt等
tmp:该目录下包含一些临时文件。
usr:该目录下一般来说包含系统发布时自带的程序(但具体放什么东西,并没有明确的要求),其中最值得说明的有三个子目录
/usr/src :Linux内核源代码就存在这个目录
/usr/man :Linux中命令的帮助文件
/usr/local : 新安装的应用软件一般默认在该目录下
var:该目录中存放着在不断扩充着的信息,比如日志文件。
以上就是Linux文件系统的原始构成,熟悉它们是应用Linux操作系统的前提,希望大家亲自打开各目录看看。
搭建Linux试验系统进入后续章节讨论的内核前,我们先与读者一同从头构架一个试验操作系统。这样既有助大家熟悉Linux操作系统的组成结构,也会在构建过程中学习介绍一些Linux命令和
使用技巧,加深理解Linux操作系统的运作方式。
实验系统将在保证实用价值的基础上,尽量小巧。希望大家通过亲手构建系统的过程中,能消除对Linux的恐惧感,更希望读者自己能使用裁减的系统,给自己带来成就感和学习热情。
必备的基础知识 对于第一次接触Linux的朋友,仅仅看下面的内容显然不能指望学会Linux的操作方法和系统行为,建议你去找本系统一点的Linux系统教程慢慢咀嚼吧。对于像系统管理员这种大牛,跳过下面内容吧,再高的就去看看新浪体育新闻什么的,别在这瞎转了J。
搭建系统过程中将离不开敲击各种各样的命令,离不开执行大大小小的shell脚本。而最整个过程中重要的是理解系统的运行思路,一切活动的指导思想都要围绕系统运行的步伐,要“顺从“系统运行自己和系统运行服务这一指导思想。所以基础知识也从这几个角度展开。 不过我们蜻蜓点水,不做深究。
基本命令我们首先介绍一组搭建Linux系统需要使用的基本的命令。当登陆到Linux系统上后,出现在我们面前的是一个shell 提示符(# 或 $等),该提示符号告诉我们系统已经准备接受命令了,你可以用键盘输入命令行来操作系统了,你输入的命令将在屏幕上显示出来,并议回车键表述命令输入结束、发送命令给系统的标志
Shell和SHELL编程 Shell是什么?在你登陆到系统后,系统首先运行的是一个特别的应用程序,它显示一个提示符号表明系统已经准备好开始接受你的命令了,当你键入你要执行的命令后,该应用程序将命令提交给Linux系统去处理,然后等处理完毕再把结果返回给你,这之后她又将回到提示状态,去等待你下次输入命令。这个特殊的“接待”程序就被称为shell,其作用相当是一个内核与用户交流的界面,她周而复始地向内核解释用户命令,因此Shell又被称称为命令解释器。
SHELL作为一种应用程序并非只有唯一一种,目前流行的shell有sh / bash /ksh /tcsh/csh等等,他们其实也就始一个应用程序,你可以使用命令whereis ksh/sh/bash来查看其存在于系统中的具体位置。
有兴趣得话,你可以通过命令 echo $SHELL来观察系统默认的SHELL属于那一种。你也可以在登陆后(使用Ctrl+D可以重新登陆)使用chsh来改变选择使用的shell程序,或干脆直接在默认shell上执行新的shell程序——只要键入新shell名字并回车即可,如果想推出新shell,就再执行exit程序。
各种shell程序各有特点,功能也有强又弱,但是相同点都需要能够执行程序或命令;能够处理程序或命令的输入输出;能够执行shell脚本。(shell 能执行三种不同概念的文件:1命令指shell程序自己内置的基本命令——如 cd 命令,管道 | 命令 >重定向命令——和以二进制文件形式存在的系统命令——如ls cp等。2
程序指用户安装和编译身成的二进制文件;3脚本指包含逻辑关系的程序和命令序列)
shell执行文件需要必要的环境,这些环境包含文件搜索路径,当前目录,用户主目录,默认编辑器等等(你可以从man shell种获得这些信息)。这些信息属于环境变量,可以通过env观察当前系统默认的环境变量,改变这些变量可以通过:变量=设置(如 PATH= /opt)命令方式和修改存在于用户目录下的相关配置文件(如对bash来说配置文件爱你为~/.bashrc,~/.bash_profile)
shell编程 shell编程简单地讲就始讲命令序列化后执行,而不用被编译成二进制可执行文件。这类似于dos下地BAT批处理文件。使用shell程序的意义在于,有些任务无法通过现有的命令完成,必须使用一组命令协作才能完成,而且各种命令之间不是简单的罗列而是按照设定的逻辑关系有机结合。由此可见shell程序需要能够控制各种命令的执行流,能够读写临时数据,因此,shell程序存在自己控制语句和变量,而且对其使用也由相关语法。
Shell程序,也可以成为shell脚本,以普通的Linux文本文件形式存在。可以是用vi等文本编辑器生成,再将其属性改为可执行即可运行。
比如 touch test 生成文件test
chmod u+x test 修改属性
./test
保险其间可以再脚本头先使用#!符号来强制当前shell运行其后的制定shell文件来执行该脚本。
当然shell编程觉非上面说的那样简单,想要真正学习shell编程并能使用它可不那么容易。有兴趣的朋友可以参看有关资料了解shell编程。
系统服务安装过Linux的朋友一定熟悉安装过程种系统会提示你选择何种服务,或安装完毕使用setup命令也可看到一个配置界面其中包含系统服务配置。系统服务包含一系列形形色色的服务,很多服务选项我们闻所未闻,或者仅仅听说过罢了。着很正常,因为服务太多太杂了,很少有人能全部搞清楚这些服务是干什么的。我们这里也不追究所有服务的详细作用,仅仅从系统运行角度介绍一下这些服务的使用方法。(想知道系统到底有那些服务,试试setup命令吧。)
系统服务程序和普通应用程序或系统命令本质是相同的,都是一些二进制文件。但其运行方式却有一些自身特点。系统服务多数情况下都处于后台运行,因此运行结果一般不再屏幕显示(往往被重新定向到/dev/null中),但是为了安全目的或分析目的,大多记录都要求保存到相关日志中;另外系统服务程序运行时多需要进行一定配置,比如ftp服务器有用户访问权限配置,工作目录配置,因此需要从配置文件取数据初始化服务程序。最后就时服务程序很多时随系统启动就开始运行,而不需要用户自己启动。
由于这些特点系统服务程序的启动或停止一般都存在相应的shell脚本文件管理,利用这些脚本可以控制服务程序的配置,启动,日志记录以及关闭服务和清理临时文件操作。这样相比用户手动操作要方便安全得多。
Linux系统中的服务程序运行脚本(启动或关闭)都存放在目录/etc/rc.d/init.d下——Linux系统的文件组织层次遵循FHS规范,包括脚本位置——比如我们启动/停止网络所用的network 等脚本。这些脚本都具有相同的使用方法运行:服务脚本 {start|stop|restart|reload|status}。如果你需要手动启动或停止某项服务,键入/etc/rc.d/init.d/服务脚本名 start|stop 即可,除此方法外也可以利用命令 service服务脚本名 start|stop,它们执行作用相同。
系统服务程序多数情况下随系统启动开始运行,系统关闭停止运行,这也正是你开机或关机时为什么能在屏幕上看到一系列的服务启动[ok] 或服务停止[stop]的原因。那么系统如何启动和关闭这些服务呢?
谈到这里很有必要说一下Linux系统运行级别这个问题。所谓运行级别更通俗的讲就是指定系统的行为,每种运行级别都对应一组该级别应用程序。
运行级 |
描述 |
0 |
系统停止 |
1 |
单用户系统,不需要登陆 |
2 |
多用户系统但不支持NFS,命令行模式登陆 |
3 |
完整多用户模式,命令行模式登陆 |
4 |
未用 |
5 |
X11图形模式,图形模式登陆 |
6 |
重新启动系统 |
我们可以使用命令init(后问会说明它) 级别来切换系统的运行级别。一般服务器系统使用级别3,如果需要图形界面使用5,对于单用户或嵌入系统使用运行级1即可。
其中级别0和6可以使用来安全停止系统,它们会将除根目录以外的文件系统卸载,并且以只读方式重新安装根文件系统,这样一来防止了破坏文件系统。
言归正传,回到系统服务程序。我们应该能猜到不同的运行级别也对应了不同的系统服务集合。比如运行级别5至少就需要比级别3多启动x服务器和xfs(字体服务器)等。你可以利用命令chkconfig –list来观察每个运行级别下的各种系统服务是否允许。显然级别5开启的服务最多,下来是级别3 。总之,功能越强要求服务越多。
下面的启动部分回告诉大家,系统根文件安装后,首先寻找init程序并运行它,该程序的任务就是从配置文件确定系统的运行级,并且根据级别启动相应的服务程序。具体的过程如下
init程序从inittab中获得系统运行级别X ,后会依次运行/etc/rc.d/rcX.d/中以大写S开头的shell脚本来启动对应的服务。
Linux系统启动的标准流程 对于系统装载过程我们暂时不做介绍,我们假设内核已经被载入内存并且已经完成了异常表、中断表、调度程序、时钟、控制台、内存等初始化,最后进行进程管理器的初始化,从此内核可以开始使用真正的进程了。
初始化完成后,内核创建第一个进程(初始进程),该进程作为系统的第0号进程,在进程描述符表中由task[0]或INIT_TASK表示。该进程进而再创建了一个进程去执行init()函数进行第二阶段的初始化操作,而初始进程(INIT_TASK)本身则去执行idle循环,可见初始进程在内核初始化后唯一的作用就是去使用空闲的CPU时间。
第二阶段的初始化工作要比前一阶段轻松一点,因为现在是由一个真正进程完成它们的,而前一阶段都是由“硬件进程”手工去做的。该阶段,这个由INI_TASK创建的新进程需要初始化总线、网络并启动系统中的各种系统内核后台线程,然后再初始化外设、设置文件格式,在这之后,它要为进入系统做最后的准备——初始化文件系统,安装根文件,打开/dev/console设备,重定向stdin、stdout和stderr到控制台,然后搜索文件系统中的init程序,并使用 execve()系统调用加载执行init程序。系统自此进入了用户态。
init程序接着将依照initab配置文件中的选项依次执行:
1 确定运行级别(1-6)
2 运行rc.sysinit脚本中的的系统服务,如激活交换分区,检查磁盘,加载硬件模块等
3 运行规定级别下的服务:/etc/rc.d/rc*.d/下的S打头的服务,如网络服务S*NETWORK。
4 在指定串口上运行getty程序,getty打开终端线,并设置模式,然后运行login程序。如果用户帐号和密码正确(需要通过/etc/passwd验证),则进入用户的工作目录,并按照其工作目录中的设置执行相应的shell。
到这里用户才可以真正实用操作系统了。
Idle进程是个奇怪的进程,它是在没有别的任务使用CPU时是才使用CPU的,它的存在价值据说可以延长CPU寿命。
内核后台线程是种执行在内核态的进程,它们和用户进程一样受调度程序调度,系统利用它们周期性(不一定固定周期)地执行一些自身管理方面的“家务事”。主要的几种内核线程为:bdflush——清理被写过的内存缓冲区;kupdate——按时将内存缓冲区中的信息更新到磁盘中; Kswapd——将内存页交换到磁盘;keventd——关系系统事件;Ksoftirq——执行软件中断。
搭建实验系统很抱歉搭建一个Linux操作系统到目前为止还没有一个很标准的流程或规范,不过大体流程都大通小异,无非是首先编译内核——将内核源代码编译成一个可执行的镜像文件,当然编译内核时可能会带有一些模块也需要同期进行编译和安装(是否有模块取决于你的具体选择)。
有了编译后的内核,接着就需要创建一个根文件系统,在其中又需要创建必要的
目录。至于其中使用的软件和库函数你可以选择下载源代码包,然后交叉编译,再进行安装。或者我们偷个懒,从一个发布的完整系统里直接拷贝需要的软件和库,同时将必要的设备文件、配置文件和服务脚本也拷贝过来,你这时所要做得仅仅是去修改一些相关的配置文件就可以拥有一个自己的文件系统了。
内核与文件系统都有了,就可以说一切具备只欠东风,你所需要做得只剩下将内核和文件系统绑定到一起,让系统被引导载入内核,内核载入后可以找到根文件系统,并执行其中的初始化程序。你可别以为这个收尾动作能轻松搞定,往往初学者都在这里要栽跟头。
怎么能在最小的代价学习搭建系统呢?想想看可不是每个网友都能找个空硬盘或者磁盘(看看你的机器,也许连软驱都没)来做新系统的,为了保护原有系统,即便开一个新分区都不能鼓励。所以最好的方法就是用内存模拟一个磁盘,将创建的根文件系统放在其中,系统引导后,就登陆到内存模拟的磁盘上运行。这时你彻底跳出了你的物理硬盘。这种方法有时在嵌入系统中会被使用,或希望断电后数据被抹掉的安全系统中使用。
下面我们就一同做个这样的试验系统,你付出的唯一代价是消耗些时间和无数次击健。
编译内核
第一步要做的工作就是挑选一个合适版本的内核源代码包,然后编译它。不要以为编译内核很神秘,其实它和编译普通程序差不多,内核源代码其实就是“一大堆”程序,编译它就等于分别编译个个程序然后在将它们链接成一个单一的可执行镜像文件。这个镜像就是你在/boot目录下看到的vmlinuz-*(如果你细心的话,一定能发现在该目录下还有一个叫vmlinux的文件。其实这两个文件是一回事,但前面那个是经过压缩的)
正如第一部分所说,Linux内核具有很强的伸缩性,在内核里面许多功能是可选择的,如果需要就可以被编译到内核,不过内核会因此变的肥胖。一种可替代的方式是将某些功能编译成模块放在文件系统内,等你真正需要它时,再由被载入到内核,这样就内核就可以轻装上阵了,启动起来也快许多。
虽然是个试验系统,但还是尽力让它功能做强点吧。所以在编译前,配置内核选项时,除了支持最基本的ext2文件系统,PCI接口,自动装在模块等功能外,再将ext3,JFS,即插即用,网络,SCSI,USB等比较常用的功能加入。再一个就是为了能实现我们的虚拟内存中建立根文件系统,内核还需要支持Ramdisk 和initrd。
内核网络设备选项里包含大量网卡驱动程序,你必须知道自己网卡内型才能正确选择,一般情况都将网卡驱动编译未模块,在系统启动后载入。我们试验系统运行在vmware下,而vmware虚拟网卡驱动为pcnet32,因此这个模块被包含进来了。
编译步骤 先去下载一个内核源代买不用我在多说了吧。如果你实在是个衣来伸手的家伙,好吧告诉你,到网站上荡一个想要版本的内核源代码。如果是gz结尾的压缩源文件,就使用tar xvzf linux-2.4.18.tar.gz解开,如果是gz2结尾的,就使用tar xvjf linux-2.4.18.tar.bz2解开。
内核版本编号可是有点讲究的,简单的说,偶数为稳定版本,奇数为开发版本,所以我们用2.4.18版,一是因为它属于稳定版,再一个就使我机器里以前下载过它,不想再换了J。
然后进入存放解开后的内核原代码的目录(标准系统默认情况下在目录/usr/src/linux下存放该系统的内核源代码),执行命令make menuconfig进行内核功能配置,选择需要的功能以模块形式编译或直接编译到内核。配置信息默认情况下记录在隐含文件.config中,你也可以选择将其记录到自定义文件中,比如可以把信息记录在MiniSys.config中。在以后配置内核时可以方便地导入指定的配置文件。
make menucofig提供给你一个文本图形界面的配置菜单,其中列出了内核所能提供的全部功能,如果你在选项前选则*号,那么该选项被编译到内核中,如果选M则被编译为模块,对于你不清楚的选项可以使用?查看其解释。除了用make meunconfig外你有复古情节的话,可以试试使用make config,它完成同样的功能,不过你得有足够得耐心去忍受刷平一样得命令行选择
保存内核配置后,就执行
make dep
/*确保所有的相依关系,例如 include files 都没问题.除非你的电脑真的很慢,否则它不会花太久时间的*/
make clean /*清除核心编译的所有目的档以及其它东西.在重建一个核心之前不要忘记这个步骤*/
make bzImage 或zImage/*编译内核——bz和z格式内核之间最大的差别是对于内核体积大小的限制。zImage内核需要放在实模式1MB的内存之内,其体积受到了限制。而bzImage的内核没有1MB内存限制*/
记住内核编译完了,还必须再编译模块, 即使您在配置内核时没有使用任何模块,也不要跳过此步骤,在编译完 bzImage 后立刻编译并安装模块是个好习惯。而且,如果您真的没有模块需要编译,这个步骤也非常快就结束了。
make modules; /*编译内核模块,凡是配置内核时标记为M功能都将被编译未模块*/
make modules_install。/*这将导致模块被编译而且被安装到 /usr/lib/<内核版本号> 目录下。不过如果你想改变内核镜像或模块的所在目录,都可以通过修改内核源码中的Makefile文件来达到,比如修改INSTALL_MOD_PATH来改变模块安装目录*/
等黄蜂一样的字符风暴再屏幕上停止后,你现在拥有了新内核了。它藏在内核源码目录下arch/i386/boot下叫bzImage或zImage。新内核随带的模块被安装到了lib下的modules目录中。
组建文件系统组建根文件系统说白了更简单,一来格式化文件系统的宿主设备,二来就是拷贝需要的文件。简单明了吧!我们先来一同拷贝文件吧,等考完了再谈宿主设备的问题,别忘了我们可都是完空手套百狼呀!(除了拷贝文件外,更标准的方法是下栽各种工具包,在本地交叉编译,在进行安装,不过为了省事,我们采用拷贝标准系统文件的方法来构造文件系统,不但方便而且异曲同工。但前提是新系统体系结构——处理器——和我们原料系统一致,如果你想在0x86系统上编译运行在ARM机上的文件系统,那么最好是去下载源代码包重新交叉编译吧)
文件系统基本要求 Linux文件系统的结构上文已经给出,我们这里着手搭建一个精简的文件系统,它包含最基本的目录以及文件,配置文件也尽量修改简洁明了。下面列出文件系统必须包含的内容。
文件系统最小需要包含/dev 、/proc、/bin 、/sbin 、etc 、/lib 、/usr 、/tmp 等目录
需要一组基本命令
支持上述命令的运行库函数,其中也包括编译内核产成的模块
必须的设备文件
一些必要的配置文件
我们要做地就是按部就班地生成和拷贝以上内容,唯一地要就就是你要够心细。
创建根文件系统内容我们先来建立一个将包含根文件系统内容的新目录“rootfs”(mkdir /rootfs),然后开始在其中生成(拷贝)根文件系统需要地所有目录和文件。
第一步当然是在rootfs目录下建立根目录下地必要地子目录啦,用一行命令就可完成mkdir dev,proc,bin,etc,lib,usr,tmp,sbin 。
第二步拷贝你需要的命令。比如你需要 ls 命令,你先确定它在系统中的位置whereis ls (发现在/bin/ls目录下),然后将该命令拷贝到你工作目录下相同的目录结构下 cp /bin/ls /workdir/bin/ls,但是仅仅拷贝命令文件还不够,还必须考被该命令所用到的动态共享库文件。如何发现命令用到了那些动态共享库呢?很简单,利用ldd /bin/ls 可以察看命令使用的共享库,显示在输出右列的就是被用到的共享库文件(名字中有so)。
[i]比如在我的系统上,该操作输出为:[/i][i][/i]
[i]libtermcap.so.2 => /lib/libtermcap.so.2 (0x4001f000)[/i][i][/i]
[i]libacl.so.1 => /lib/libacl.so.1 (0x40023000)[/i][i][/i]
[i]libc.so.6 => /lib/libc.so.6 (0x40029000)[/i][i][/i]
[i]libattr.so.1 => /lib/libattr.so.1 (0x40149000)[/i][i][/i]
[i]/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)[/i][i][/i]
我们要做的是将命令要用到的库文件按照它的目录结构拷贝到我们工作目录下的/lib或/lib/i386。(用户所用到的命令多集中在/bin和/sbin下,另外一些脚本会用到一些出现在/usr/bin和/usr/sbin下的文件,如果你要使用这些脚本,不用说这些命令和它们要用的库都不能少)。但是这些文件并非我们实际想要的,它门只是实际库文件的一个符号链接,系统只所以要使用符号链接,是为了便于库文件升级换代时不影响使用它的应用程序。因此我们单单拷贝符号链接是没有意义,同时也必须将符号链接指向的实际库文件一同拷贝到workdir/lib录下面去。
/lib目录下还有一个重要目录就是modules目录,它里面包含了内核编译产生模块,对于不同版本的模块存放在以版本号命名的文件中。要们可别忘了拷贝这个目录到我们的新系统中。
在原始时期/lib下的这些库就足够用了,但现在的Linux系统对安全多了许多要求,尤其是系统从安全性考虑,增加了许多验证手段,因此往往你还必须具有和安全验证相关的库。这些库不会在命令中直接使用,但却间接地要被系统的安全框架利用到,多数都是由配置文件中说明如何关联,安全框架通过查看配置文件,选择调用具体地验证库(这些配置文件后问会提及)。安全框架方面话题,我们不多说,有兴趣的可以查查 pam 和 nss等的用法。在这里我们不管它三七二十一将在/lib/security/下和pam相关地库和/lib下nss相关地所有libnss*库都考到我们地/lib下的/security下和/lib下。虽然很笨,但确省事。
Linux系统将那些会被多数应用程序频繁使用的库函数,多数都不会以静态的方式编译连接到应用程序中,而是采取动态库的方式,集中存储管理。这样如果多各程序都用到某个共享库,那么该库文件只被调入内存一次,驻留在内存一个拷贝,因此利用共享库大大节约了空间,缩减了执行文件提及。当然天下没有免费的午餐,虽然共享库相比静态库灵活,但却学要而外的路径搜索,而且调入时间也更耗时