发信人: mutecat ((I love Linux)), 信区: Linux
标 题: 接触tmpfs文件系统
发信站: 武汉白云黄鹤站 (2005年11月27日22:14:02 星期天), 站内信件
前几天发现服务器的内存(ram)和swap使用率非常低,于是就想这么多的资源
不用岂不浪费了?google了一下,认识了tmpfs,总的来说tmpfs是一种虚拟内存文件系统
正如这个定义它最大的特点就是它的存储空间在VM里面,这里提一下VM(virtual memory)
,VM是由linux内核里面的vm子系统管理的东东,现在大多数操作系统都采用了虚拟内存
管理机制?更详细的说明请参考<<UnderStanding The Linux Virtual Memory Manager>)
linux下面VM的大小由RM(Real Memory)和swap组成,RM的大小就是物理内存的大小,而Swap
的大小是由你自己决定的。Swap是通过硬盘虚拟出来的内存空间,因此它的读写速度相对
RM(Real Memory)要慢许多,我们为什么需要Swap呢?当一个进程申请一定数量的内存时
,如内核的vm子系统发现没有足够的RM时,就会把RM里面的一些不常用的数据交换到Swap
里面,如果需要重新使用这些数据再把它们从Swap交换到RM里面。 如果你有足够大的物理
内存,根本不需要划分Swap分区。
通过上面的说明,你该知道tmpfs使用的存储空间VM是什么了吧? 前面说过VM由
RM+Swap两部分组成,因此tmpfs最大的存储空间可达(The size of RM + The size of
Swap)。 但是对于tmpfs本身而言,它并不知道自己使用的空间是RM还是Swap,这一切
都是由内核的vm子系统管理的。
怎样使用tmpfs呢?
#mount -t tmpfs -o size=20m tmpfs /mnt/tmp
上面这条命令分配了上限为20m的VM到/mnt/tmp目录下,用df命令查看一下,确实
/mnt/tmp挂载点显示的大小是20m,但是tmpfs一个优点就是它的大小是随着实际存储的
容量而变化的,换句话说,假如/mnt/tmp目录下什么也没有,tmpfs并不占用VM。上面的
参数20m只是告诉内核这个挂载点最大可用的VM为20m,如果不加上这个参数,tmpfs默认
的大小是RM的一半,假如你的物理内存是128M,那么tmpfs默认的大小就是64M,
tmpfs有没有缺点呢?
当然有,由于它的数据是在VM里面,因此断电或者你卸载它之后,数据就会立即丢
失,这也许就是它叫tmpfs的原故。不过这其实不能说是缺点。那tmpfs到底有什么用呢?
tmpfs的用途
由于tmpfs使用的是VM,因此它比硬盘的速度肯定要快,因此我们可以利用这个优点
使用它来提升机器的性能。
#mount -t tmpfs -o size=2m tmpfs /tmp
上面这条命令分配了最大2m的VM给/tmp。
由于/tmp目录是放临时文件的地方,因此我们可以使用tmpfs来加快速度,由于
没有挂载之前/tmp目录下的文件也许正在被使用,因此挂载之后系统也许有的程序不能
正常工作。没有关系,只要在/etc/fstab里面加上下面的语句
tmpfs /tmp tmpfs size=2m 0 0
重启电脑之后就一切OK了。
强列建议你看看下面这篇文章。
http://www-128.ibm.com/developerworks/cn/linux/filesystem/l-fs3/
另外还可以参考
2.4内核里面的Documentation/filesystems/tmpfs.txt
发信人: snowall (snowall), 信区: DianLinux
标 题: (原创)proftpd + mysql实现FTP用户权限管理和配额.
发信站: 喻信星空 (2005年04月23日23:04:29 星期六), 站内信件
环境:SUSE Linux + mysql4.1.11 + proftpd1.2.10
首先到Mysql网站上下载:mysql-standard-4.1.11-pc-linux-gnu-i686.tar.gz
解压后拷贝到/usr/local/mysql使用root用户登录
groupadd mysql
useradd -g mysql mysql
cd /usr/local
chown -R root.root mysql
cd mysql
scripts/mysql_install_db –user=mysql #初始化数据库
chown -R mysql.mysql data
cd support-files
./mysql.server start
#到此mysql就启动好了
用root登录mysql
create database proftpd;
use proftpd
#下面几个表的定义在proftpd的文档中都可以找到
#create users table in mysql database
CREATE TABLE users (
userid VARCHAR(30) NOT NULL UNIQUE,
passwd VARCHAR(80) NOT NULL,
uid INTEGER UNIQUE,
gid INTEGER,
homedir VARCHAR(255),
shell VARCHAR(255)
);
#create groups table in mysql database
#如果要使用组控制权限则必须要此表, 参考SQLAuthenticate in proftpd.conf
CREATE TABLE groups (
groupname VARCHAR(30) NOT NULL,
gid INTEGER NOT NULL,
members VARCHAR(255)
)
#quota表
CREATE TABLE quotalimits (
name VARCHAR(30),
quota_type ENUM("user", "group", "class", "all") NOT NULL,
per_session ENUM("false", "true") NOT NULL,
limit_type ENUM("soft", "hard") NOT NULL,
bytes_in_avail FLOAT NOT NULL,
bytes_out_avail FLOAT NOT NULL,
bytes_xfer_avail FLOAT NOT NULL,
files_in_avail INT UNSIGNED NOT NULL,
files_out_avail INT UNSIGNED NOT NULL,
files_xfer_avail INT UNSIGNED NOT NULL
);
#该表由quota模块管理,不需要管,可以查看
CREATE TABLE quotatallies (
name VARCHAR(30) NOT NULL,
quota_type ENUM("user", "group", "class", "all") NOT NULL,
bytes_in_used FLOAT NOT NULL,
bytes_out_used FLOAT NOT NULL,
bytes_xfer_used FLOAT NOT NULL,
files_in_used INT UNSIGNED NOT NULL,
files_out_used INT UNSIGNED NOT NULL,
files_xfer_used INT UNSIGNED NOT NULL
);
向数据库中加入数据如下:
insert into users values(‘dian’,password(‘123′),40,49,’/home/ftp’,”);
………….
mysql> select * from users;
+——–+——————————————-+——+——+———
——–+——-+
| userid | passwd | uid | gid |
homedir | shell |
+——–+——————————————-+——+——+———
——–+——-+
| dian | *23AE809DDACAF96AF0FD78ED04B6A265E05AA257 | 40 | 49 |
/home/ftp | |
| diana | *23AE809DDACAF96AF0FD78ED04B6A265E05AA257 | 40 | 49 |
/home/ftp/diana | |
| dianb | *23AE809DDACAF96AF0FD78ED04B6A265E05AA257 | 40 | 49 |
/home/ftp/dianb | |
+——–+——————————————-+——+——+———
——–+——-+
3 rows in set (0.00 sec)
#这样加用户可以保证dian用户有所有权限,而diana和dianb只在自已的目录下有权限
mysql> \! id ftp
uid=40(ftp) gid=49(ftp) groups=49(ftp)
mysql> select * from quotalimits;
+——-+————+————-+————+—————-+———–
——+——————+—————-+—————–+—————-
–+
| name | quota_type | per_session | limit_type | bytes_in_avail |
bytes_out_avail | bytes_xfer_avail | files_in_avail | files_out_avail |
files_xfer_avail |
+——-+————+————-+————+—————-+———–
——+——————+—————-+—————–+—————-
–+
| diana | user | false | soft | 1.024e+06
| 0 | 0 | 0 | 0
| 0 |
| dianb | user | false | soft | 1.024e+06
| 0 | 0 | 0 | 0
| 0 |
+——-+————+————-+————+—————-+———–
——+——————+—————-+—————–+—————-
–+
2 rows in set (0.00 sec)
磁盘限额,这里采用软限额方式,两者的区别为:
软限制:当用户上传某个文件后超过磁盘限额,用户在当前文件传送完成后,FTP不会删
除上传的文件,但不能再上传新文件
硬限制:当用户上传某个文件后超过磁盘限额,用户在当前文件传送完成后,FTP会删除
最后一个上传的文件,但由于这时磁盘配额会没有满,所以还能再上传文件,但如果上
传后超过配额还是会被删除
proftpd的quota模块只会计算用户上传的文件大小和删除的文件大小来进行配额,不会统
计原来存在的文件,也不会统计其它用户传到自已目录的文件(这也算是一个缺陷吧!).
最后给用户适当的权限使表能够工作:
grant insert,delete,update,select on proftpd.* to proftpd@localhost;
mysql的配置完成。
下面开始安装proftpd
解压proftpd1.2.10(proftpd1.2.10自带quota和mysql模块,不用单独去找)
./configure –prefix=/usr/local/proftpd \
–with-modules=mod_sql:mod_sql_mysql:mod_quotatab:mod_quotatab_sql \
–with-includes=/usr/local/mysql/include \
–with-libraries=/usr/local/mysql/lib
make & make install
(这里注意一下mysql的include和lib的路径,网上很多地方说要改源代码,其实不用改)
配置proftpd.conf
#proftpd.conf
ServerName "ProFTPD Default Installation"
ServerType standalone
DefaultServer on
Port 21
Umask 022
DefaultRoot /home/ftp #限制用户范围
<Global>
SQLConnectInfo proftpd@localhost proftpd # db@host:port username
password
SQLAuthTypes Backend # 密码加密保存
SQLUserInfo users userid passwd uid gid homedir NULL
SQLDefaultHomedir "/home/ftp"
SQLDefaultGID 49 #ftp user’s gid on my computer
SQLDefaultUID 40 #ftp user’s uid on my computer
RequireValidShell off
SQLAuthenticate users # 这里我没有使用组控制,若需要则加上groups,而groups表
则必须在数据库中存在
#Quota below
QuotaDirectoryTally on
#Unit "b"|"Kb"|"Mb"|"Gb"
QuotaDisplayUnits "Mb"
QuotaEngine on
QuotaLog "/usr/local/proftpd/var/quota.log"
QuotaShowQuotas on
SQLNamedQuery get-quota-limit SELECT "name, quota_type, per_session,
limit_type, bytes_in_avail, bytes_out_avail, bytes_xfer_avail,
files_in_avail, files_out_avail, files_xfer_avail FROM quotalimits WHERE
name = ‘%{0}’ AND quota_type = ‘%{1}’"
SQLNamedQuery get-quota-tally SELECT "name, quota_type, bytes_in_used,
bytes_out_used, bytes_xfer_used, files_in_used, files_out_used,
files_xfer_used FROM quotatallies WHERE name = ‘%{0}’ AND quota_type =
‘%{1}’"
SQLNamedQuery update-quota-tally UPDATE "bytes_in_used = bytes_in_used +
%{0}, bytes_out_used = bytes_out_used + %{1}, bytes_xfer_used =
bytes_xfer_used + %{2}, files_in_used = files_in_used + %{3},
files_out_used = files_out_used + %{4}, files_xfer_used = files_xfer_used +
%{5} WHERE name = ‘%{6}’ AND quota_type = ‘%{7}’" quotatallies
SQLNamedQuery insert-quota-tally INSERT "%{0}, %{1}, %{2}, %{3}, %{4},
%{5}, %{6}, %{7}" quotatallies
QuotaLimitTable sql:/get-quota-limit
QuotaTallyTable
sql:/get-quota-tally/update-quota-tally/insert-quota-tally
# other options
AllowForeignAddress on
AllowRetrieveRestart on
AllowStoreRestart on
CreateHome on
IdentLookups off
</Global>
MaxInstances 30
User ftp
Group ftp
UseReverseDNS off
<Directory />
AllowOverwrite on
<Limit MKD XMKD RNFR RNTO DELE RMD XRMD STOR WRITE>
DenyUser !dian # 限制除dian用户外的用户在根目录下的权限
</Limit>
</Directory>
<Directory ~>
<Limit MKD XMKD RNFR RNTO DELE RMD XRMD STOR WRITE>
AllowAll # 给用户在自已主目录下所有的权限
</Limit>
</Directory>
最后启动proftpd
使用diana登录,在FlashFXP的命令行中执行site quota显示如下:
site quota
Apr 23 13:35:16 mod_quotatab/1.2.13[22372]: SITE QUOTA requested by user
diana
200-The current quota for this session are [current/limit]:
Name: diana
Quota Type: User
Per Session: False
Limit Type: Soft
Uploaded Mb: 0.00/0.98
Downloaded Mb: unlimited
Transferred Mb: unlimited
Uploaded files: unlimited
Downloaded files: unlimited
Transferred files: unlimited
200 Please contact root@linux.site if these entries are inaccurate
上传一个3.85Mb的文件:
site quota
Apr 23 14:28:56 mod_quotatab/1.2.13[28818]: SITE QUOTA requested by user
diana
200-The current quota for this session are [current/limit]:
Name: diana
Quota Type: User
Per Session: False
Limit Type: Soft
Uploaded Mb: 3.85/0.98 # 由于是软限制方式,FTP不会删除超出大小的文
件
Downloaded Mb: unlimited
Transferred Mb: unlimited
Uploaded files: unlimited
Downloaded files: unlimited
Transferred files: unlimited
200 Please contact root@linux.site if these entries are inaccurate
这时再传会出现:
Apr 23 14:33:37 mod_quotatab/1.2.13[28818]: STOR denied: quota exceeded:
used 3.85 of 0.98 upload Mb
552 STOR denied: quota exceeded: used 3.85 of 0.98 upload Mb
由于信息是使用Mysql数据库来存储,可以很方便的实现FTP用户与配额的Web化管理,用
户密码Web自助修改,用户在多个FTP站点通用等功能。
其实proftpd的特长并不在磁盘限额(实现的不是很好),主要的特长在于实现灵活的权限
控制与上下传速率的控制。大家有空可以慢慢研究。
发信人: mutecat ((I love Linux)), 信区: Linux
标 题: linux启动过程详解
发信站: 武汉白云黄鹤站 (2005年07月19日23:01:07 星期二), 站内信件
这几天看了很多文档,算是对linux的启动过程有了比较细致的了解.
网上有很多文章谈到这方面的内容,但总觉得没有一篇完全的解析linux启动的
细节,下面是我小弟在学习的过程中总结出来的一些东东.这个是完整的linux启动过程,
不涉及内核,但是我觉得比较详细哦.
(由于本人比较懒,这一段是从网上抄的)
机器加电启动后,BIOS开始检测系统参数,如内存的大小,日期和时间,磁盘
设备以及这些磁盘设备用来引导的顺序,通常情况下,BIOS都是被配置成首先检查
软驱或者光驱(或两者都检查),然后再尝试从硬盘引导。如果在这些可移动的设
备中,没有找到可引导的介质,那么BIOS通常是转向第一块硬盘最初的几个扇区,
寻找用于装载操作系统的指令。装载操作系统的这个程序就是boot loader.
linux里面的boot loader通常是lilo或者grub,从Red Hat Linux 7.2起,GRUB(
GRand Unified Bootloader)取代LILO成为了默认的启动装载程序。那么启动的时候
grub是如何被载入的呢?
grub有几个重要的文件,stage1,stage2,有的时候需要stage1.5.这些文件一般都
在/boot/grub文件夹下面.grub被载入通常包括以下几个步骤:
1. 装载基本的引导装载程序(stage1),stage1很小,网上说是512字节,但是在我的系统上
用 du -b /boot/grub/stage1 显示的是1024个字节,不知道是不是grub版本不同的
缘故还是我理解有误.stage1通常位于主引导扇区里面,对于硬盘就是MBR了,stage1的
主要功能就是装载第二引导程序(stage2).这主要是归结于在主引导扇区中没有足够的
空间用于其他东西了,我用的是grub 0.93,stage2文件的大小是 107520 bit.
2. 装载第二引导装载程序(stage2),这第二引导装载程序实际上是引出更高级的功能,
以允许用户装载入一个特定的操作系统。在GRUB中,这步是让用户显示一个菜单或
是输入命令。由于stage2很大,所以它一般位于文件系统之中(通常是boot所在的根
分区).
上面还提到了stage1.5这个文件,它的作用是什么呢? 你到/boot/grub目录下看看,
fat_stage_1.5 e2fs_stage_1.5 xfs_stage_1.5等等,很容易猜想stage1.5和文件系统
有关系.有时候基本引导装载程序(stage1)不能识别stage2所在的文件系统分区,那么这
时候就需要stage1.5来连接stage1和stage2了.因此对于不同的文件系统就会有不同的
stage1.5.但是对于grub 0.93好像stage1.5并不是很重要,因为我试过了,在没有stage1.5
的情况下, 我把stage1安装在软盘的引导扇区内,然后把stage2放在格式化成ext2或者
fat格式的软盘内,启动的时候照常引导,并不需要e2fs_stage_1.5或者fat_stage_1.5.
下面是我的试验:
#mkfs.ext2 /dev/fd0
#mount -t ext2 /dev/fd0 /mnt/floppy
#cd /mnt/floppy
#mkdir boot
#cd boot
#mkdir grub (以上三步可用mkdir -p boot/grub命令完成)
#cd grub
#cp /boot/grub/{stage1,stage2,grub.conf} ./
#cd; umount /mnt/floppy
以上几步把软盘格式化成ext2格式,然后把stage1,stage2,grub.conf这几个启动的
时候必须的文件拷贝到软盘的指定目录下.下面安装grub到软盘上.
#grub (进入grub环境)
grub> install (fd0)/boot/grub/stage1 (fd0) (fd0)/boot/grub/stage2
p (fd0)/boot/grub/grub.conf
以上这条命令也可以用下面的两句代替
grub>root (fd0) #grub的根目录所在的分区
grub>setup (fd0) #这一步就相当于上面的install命令
我在这里解释一下
install (fd0)/boot/grub/stage1 (fd0) (fd0)/boot/grub/stage2 p
(fd0)/boot/grub/grub.conf 这条命令.
install
告诉GRUB将(fd0)/boot/grub/grub/stage1
安装到软驱的引导扇区(fd0).
(fd0)/boot/grub/stage2
告诉grub stage2这个文件所在的位置.
p 参数后面跟着(fd0)/boot/grub/grub.conf 告诉grub的配置文件所在的位置.
好了,让BIOS从软驱启动,试一下,没有e2fs_stage_1.5文件照样能够进入系统.
其实这就是一个小小的启动盘啊.(了解了grub的运行原理,就简单多了^_^)
3. 现在我们已经到grub的开机选单这一步了,接下来grub所需要做的就是装载在一个特
定分区上的操作系统,如linux内核。一旦GRUB从它的命令行或者配置文件中,接到开始
操作系统的正确指令,它就寻找必要的引导文件,然后把机器的控制权移交给操作系统.
由于篇幅有限,避免冗长,grub的命令我就不多说了,网上很有多的资料,一个典型
完整的引导linux的命令如下:
title 51base
root(hd0,0)
kernel /bzImage ro root=/dev/ram0
initrd /initrd.img
这里有必要注意一下几个问题:
(1)grub的磁盘以及分区的命名方式和linux有所区别,第一个磁盘是从0开始,第一
个分区也是从0开始.譬如第一个硬盘的第5分区在linux下面是/dev/hda5 ,而在grub里面
是(hd0,4).再如/dev/fd0在grub里面是(fd0,0).(最后一句如有错误望提醒)
(2)不管是IDE硬盘hda,hdb还是SCSI硬盘sda,sdb在grub里面都是以hd方式命名.
譬如虚拟机里面的/dev/sda2在grub里面是(hd0,1),再如/dev/hdb7在grub里面以(hd1,6)
命名.
(3)要搞清楚上面两个root的关系,root (hd0,0)中的root是grub命令,它用来指定
boot所在的分区作为grub的根目录.而root=/dev/ram0是kernel的参数,它告诉操作系统
内核加载完毕之后,真实的文件系统所在的设备.要注意grub的根目录和文件系统的根
目录的区别.
再回到上面的几行命令.
kernel命令用来指定内核所在的位置,"/"代表(hd0,0),也就是grub的根目录
initrd命令用来指定初始化ram的img文件所在位置.
grub载入内核bzImage并展开到指定位置(应该是0×100000这个地方),同时载入
initrd.img到内存(不知道是什么地方).
ps:
grub的任务至此就结束了,下面grub将机器的控制权转交给操作系统(linux).
操作系统接到控制权之后,开始start_kernel,接着内核将initrd.img展开到/dev/ram0
为临时根文件系统,执行里面的linuxrc文件.
P.这里有必要说一下initrd的作用特别是它里面的核心文件linuxrc的作用.
initrd是inital ram disk的宿写.
当存在initrd的时候,机器启动的过程大概是以下几个步骤(当initrd这一行用
noinitrd 命令代替后,就不存在initrd了)
1)boot loader(grub)加载内核和initrd.img
2)内核将压缩的initrd.img解压成正常的ram disk并且释放initrd所占的内存空间
3)initrd作为根目录以读写方式被挂载
4)initrd里面的文件linuxrc被执行
5)linuxrc挂载新的文件系统
6)linuxrc使用pivot_root系统调用指定新的根目录并将现有的根目录place到指定
位置.
7)在新的文件系统下正式init
8)initrd被卸载.
为了便于理解,我将red hat linnux9 里面的initrd-2.4.20-8.img拿出来分析一下.
这其实是一个压缩了的文件,是以gz结尾的.
[root@localhost root]#cp /boot/initrd-2.4.20-8.img /mnt/initrd-2.4.20-8.gz
[root@localhost root]#gunzip /mnt/initrd-2.4.20-8.gz
[root@localhost root]#mount -o loop /mnt/initrd-2.4.20-8 /mnt/ram
[root@localhost root]#cd /mnt/ram
[root@localhost ram]#ls
bin dev etc lib linuxrc loopfs proc sbin sysroot
[root@localhost ram]#ls bin
insmod modprobe nash
[root@localhost ram]#ls lib
Buslogic.o ext3.o jbd.o scsi_mod.o sd_mod.o
[root@localhost ram]ls dev
console null ram systty tty1 tty2 tty3 tty4
sbin目录是指向bin目录的一个连接,其他目录是空的.
[root@localhost ram]cat linuxrc
#!/bin/nash
1.echo "Loading scsi_mod.o module"
2.insmod /lib/scsi_mod.o
3.echo "Loading sd_mod.o module"
4.insmod /lib/sd_mod.o
5.echo "Loading BusLogic.o module"
6.insmod /lib/BusLogic.o
7.echo "Loading jbd.o module"
8.insmod /lib/jbd.o
9.echo "Loading ext3.o module"
10.insmod /lib/ext3.o
11.echo Mounting /proc filesystem
12.mount -t proc /proc /proc
13.echo Creating block devices
14.mkdevices /dev
15.echo Creating root device
16.mkrootdev /dev/root
17.echo 0×0100 > /proc/sys/kernel/real-root-dev
18.echo Mounting root filesystem
19.mount -o defaults –ro -t ext3 /dev/root /sysroot
20.pivot_root /sysroot /sysroot/initrd
21.umount /initrd/proc
上面的编号是我为了下面好说明加上去的.
首先我们必须注意的是这里使用的shell是nash而不是bash,nash是专门为linuxrc可执行
脚本设计的,因此你有必要看一看nash的man文档.
1-10行是加载一些必要的模快.11-12行加载proc内核文件系统,13-14行利用nash内建的
命令mkdevices创建块设备,mkdevices是根据/proc/partitions文件创建里面列出的所有
块设备.15-16行利用nash内建的命令mkrootdev,mkrootdev使它后面的参数/dev/root成
为一个块节点从而使得根分区设备被挂载,其中根分区设备由grub.conf里面的kernel命
令后面所带的参数root=决定,如果root=参数没有被指定,/proc/sys/kernel/real-root-
dev文件将提供根分区设备号.17行将数字256写入到后面的文件里面去.18-19行挂载根文
件系统到/sysroot目录下,/dev/root里面的内容就是root=参数所指定的设备里面的内容
20行调用pivot_root改变根目录所在地并place旧的根目录到指定的位置.21行卸载旧的
根目录里面的proc内核文件系统.
从这里面我们总结一下linuxrc的作用: (参考/usr/src/linux-2.4/Documenta
tion/initrd.txt文档)
2)/linuxrc文件决定在挂载真正的文件系统之前所需完成的事情(譬如加载必要的网
络驱动或者加载ext3文件系统).
3)/linuxrc加载必要的模块.
4)/linuxrc挂载根文件系统
5)/linuxrc调用pivot_root来改变根目录
关于initrd的用途可以查考上面提到的文档,想知道linux系统是如何安装的吗?那里
面由答案.
既然linuxrc的主要目的是加载模快用的,那如果我们的内核没有动态的模块而所需
的功能都是静态编译进内核的,那么是不是可以不用linuxrc文件呢?
答案是可以不用,在普通的linux操作系统里面可以加入noinitrd选项以告知boot
loader 不使用initrd.如果我们做网关,因为ram是我们的文件系统的载体,所以initrd
一行当然不能去掉,但是我们可以不用linuxrc文件,sysroot文件夹和initrd文件夹.
不信的话,试试看吧.
好了,initrd(linuxrc)已经介绍完了.
linuxrc执行完毕之后,系统就会以真正的根目录正式init.
系统在/bin/或者/sbin目录下找到init程式,然后根据它的配置文件/etc/fstab进行
初始化,最后调用mingetty程式启动login完成引导.
ps:init这一部分网上有很多的详细资料所以我在这里并没有展开来说.
终于写完了,希望对你有所帮助.如有错误,还望指正.
1.引言
本文着重讲述如何制作基于linux的usb启动盘,此usb启动盘能够实现以下功能。
a. usb启动盘的Linux内核usblinux.kernel应支持尽可能多的硬件(包括硬盘驱动,网卡驱动,usb驱动)
b. u盘根文件系统放在ramdisk中,启动之后解压到ram中。
c. usb启动盘启动之后能够在目标机上执行分区(fdisk),格式化(mkfs.*)以及在各种块设备上安装grub
d. usb启动盘能够临时充当局域网内的网关。
e. usb启动盘能够在一台机器上快速的搭建网关服务器。
2.定制操作系统所要考虑的事情
不管是在u盘上面还是在其他存储设备上(硬盘,软盘)安装Linux,我们所需要考虑的事情基本一样。
1)定制自己的内核(包括选定确当的内核版本,内核是否支持模块等等)
2) 定制自己的根文件系统(包括根文件系统的架构,根文件系统的内容,根文件系统所采用的文件系统类型等等)
3) 配备引导加载程序(bootloader)
3. UsbLinux 的系统架构
结合1和2我们现在来具体定制我们的UsbLinux。
1) 内核选用
2) 内核支持模块加载功能,上面说了我们的内核应该尽可能支持多的硬件,如果我们把这些硬件的驱动全部静态编译进内核的话,内核将会变的非常庞大,这是我们不希望看到的,这不仅仅会减慢Usblinux启动的速度,而且我们不能动态的去除不必要的内核模块。
3) 根文件系统选择ext2文件系统,前面说了我们的根文件系统是放在ramdisk中的,ramdisk正如其名,存在于ram中并且功能犹如块设备。因此也就决定了根文件系统里面的东西修改之后断电不能保存,ext2文件系统的特性主要包括可读写,可压缩,不具备断电可靠性,ext3,reiserfs等日志文件系统是具备断电可靠性的,它们用在硬磁盘上比较合适。我们这里的根文件系统存放在ram中,因此选用ext2文件系统比较合适。
4) bootloader 选择grub,我们不是在搞嵌入式所以grub是一个非常好的选择。
说明:下面的所有操作都是在red hat linux 9里面完成的。
4. 编译安装内核
4.1. 选择配置内核方式
配置内核有多种方法,切换到源码目录下,
a. make oldconfig,make config(注,除非你很喜欢这种配置方式,否则建议你还是不要使用它们,它们不但不直观,而且配置容易出错)
b.make menuconfig,这是最常用的一种配置方法,方便直观。如果没有其它特殊原因,建议你使用这种配置方法。
c.make xconfig, 这个需要X窗口的支持,如果你喜欢这个配置方法也很方便
4.2. 内核配置
这当然是很重要的一个步骤了,关于配置内核的具体细节网上有很多这样的文章,这里只重点说以下几个重要的地方。
Loadable module support: 这个我们支持,因为我们的驱动大多是编译成模块方式的。
Process type: 由于我们的UsbLinux是做成通用的而不是针对某个目标机的,所以处理器的选择至关重要,为什么这样说呢? 如果你的内核选择的处理器类型是的Athlon/Duron ,那么在一台386或者586的机器上到解压内核的时候可能会出问题,我试了在奔腾或者赛杨的处理器上也是到解压内核的时候就停在那边了。我们现在的处理器大多是X86系列的,因此处理器选择386,可以避免上述的麻烦,当然针对具体的机器这个必然会对机器的性能产生影响。
Block devices: 一定要选择ram disk support 和initrd support
NetWorking options: 这里面的是关于内核支持的网络功能,我们的UsbLinux需要临时充当网关,而且能够利用它搭建网关服务器,因此这里面的大多数选项都要选,这里面的选项比较多,最好对网络方面的知识有个大概的了解。
ATA/IDE support: 硬盘的类型,这个里面也有比较多的硬盘型号,我们现在大多是IDE硬盘,但是如果你想让你的UsbLinux的通用性好一些的话,最好上网查查硬盘厂商的相关资料。
SCSI support:
尽管现在很少见到SCSI的硬盘,不过我们还不得不让内核支持SCSI,为什么呢? linux把usb设备是当成SCSI看待的,因要支持USB就要支持SCSI才行。具体的把下面这两个选项编译进内核就可以了,其它的基本上用不着。
<*>SCSI Support
<*>SCSI Disk Support
另外为了在vmware上面测试,我把BusLogic这个驱动编译进了内核。
NetWork Devices Support:
网络设备支持,我们基本上只需要选择Ethernet(10 or 100Mbit)这里面的选项就可以了,其它的千M以太网卡,FDDI以及PPP之类的一般用的不多,当然还是要视具体情况而定。在
Charater Devices: 字符设备支持,把Virtual terminal,support console on virtual terminal, Standard/generitic serial support和Support for console on serial port这两项编译进内核就差不多了。
File Systems: 文件系统支持,由于我们使用的ext2文件系统,所以把Second extended fs support 这一项编译进内核。另外/proc文件系统也编译进内核,因为proc文件系统很有用。其余的ext3,fat,Vfat,ntfs 给M上吧,另外吧网络文件系统nfs也M上吧,Partion types这里面的选择PC BIOS就可以了,Native Language Support把codepage 437,936以及nls iso8859-1编译进内核,其它的就不用选了。
USB Support: Usb设备支持,把UHCI和OHCI support给M上,另外USB Mass Storage Suppor 一定要选上,把它编译进内核或者M上都可以。最后吧USB Device filesystems 给编译进内核,这会在/proc文件系统里面生成相应的选项,便于调试用。其它的声卡,红外线之类的设备都可以不选。
这里没有涉及的选项都是不常用的。
内核配置完毕,保存退出。
4.3. 建立依赖关系
这一步一个命令make dep就可以搞定。内核源码树中大多数文件都会与一些头文件有依赖关系,make dep期间会在内核源码树中每个子目录下面产生一个隐藏的.depend文件。此文件内包含子目录里面各文件所依赖的头文件清单。
4.4. 建立内核
make bzImage or make zImage
注意bzImage和在zImage都是经gzip算法压缩过的内核映像,所不同的是在zImage的大小无法超过500KB,而bzImage则没有这个限制,如果映象建立过程中有错误,一个就是你的内核配置不正确,还有一个可能就是需要make mrproper来消除上一次的编译记录,make mrproper之后内核源码相当于刚安装时候的状态。
4.5. 建立和安装模块
make modules & make modules_install
注意模块的默认安装位置是/lib/modules目录下,目录名即是你的内核版号,如果/lib/modules目录下有一个相同的内核版本号目录,如果很重要的话你最好把它备份,因为会被新的覆盖。至此内核以及相应的模块已经建立好了。
5.建立根文件系统
5.1.根文件系统的基本结构
首先建立一个roofs文件夹用来存放根文件系统的内容。
#mkdir /mnt/rootfs
根文件系统的顶层目录: (摘自<<构建嵌入式LINUX系统>> page 179)
|
目录 |
内容 |
|
bin |
必要的用户命令(二进制文件) |
|
boot |
引导加载程序使用的静态文件 |
|
dev |
设备文件和其它特殊文件 |
|
etc |
系统配置文件,包括启动文件 |
|
home |
用户主目录 |
|
lib |
必要的连接库,例如C连接库,内核模块 |
|
mnt |
安装点,用于暂时安装文件系统 |
|
opt |
附加的软件套件 |
|
proc |
用来提供内核与进程信息的虚拟文件系统 |
|
root |
root用户的主目录 |
|
sbin |
必要的系统管理员命令(二进制文件 ) |
|
tmp |
暂时性的文件 |
|
usr |
在第二层包括对大多数用户都有用的大量应用程序和文件(包括库文件) |
|
var |
监控程序和工具程序所存放的可变数据 |
5.2. 建立UsbLinux根文件系统的基本结构
对于u盘上的linux或者做网关的linux来说,用户的权限问题并不复杂,我们只需要设置一个超级用户以及其它系统服务必要的帐户即可(比如ftp,sshd等等),由于相当于是单用户,因此home目录也可以不要,其它的目录在我看来都是必要的,下面我们开始建立根文件系统的基本结构。
#cd /mnt/rootfs
#mkdir bin sbin dev etc lib root tmp usr var proc opt mnt
#mkdir usr/{bin,sbin ,lib,share}
#mkdir var/{lock,log,run,empty}
一般二进制命令可以存放的目录包括bin,sbin,usr/bin,usr/sbin,它们有什么区别呢?
二进制文件放在哪个目录,这与它在系统中所扮演的角色密切相关,如果这是普通用户和系统管理员必备的二进制文件(比如ls和mkdir等),就会放在bin目录下,如果只是系统管理员所具有的一些特权指令(普通用户使用这些指令有限制,如ifconfig),那么它应该放在sbin目录下,usr/bin目录下存放的是普通用户和管理员"不常用"的命令,而usr/sbin目录下是管理员"不常用"的命令。
上面已经说过我们不需要区分普通用户和系统管理员(root),因此我们只需创建一个系统管理员帐号就可以了,但是这里为什么我们还要创建这四个目录呢? 是因为我们用到busybox套件,下面说明安装定制应用程序的时候会讲到。
下面我们把刚才建立的内核模块拷贝到lib目录下(注:以后我们所有的操作都是以/mnt/rootfs为根操作的)
#mkdir lib/modules
#cp –dpR /lib/modules/
5.3. 选择链接库
链接库是文件系统中一个非常重要的部分, 它也是整个根文件系统中最耗空间的一个部分。链接库是应用程序执行期间必不可少的一部分,当然如果你编译应用程序的时候库都是静态编译进去的,那么就不需要额外麻烦的自己建立链接库了,比如嵌入式系统中的uClinux,不过这个做法的缺点就是会额外的消耗存储空间。我们这里采用动态链接的方法。目前比较流行的链接库有glibc和uClibc,glibc是gnu的C链接库,一般的linux发型版都是使用的glibc库,而uClibc主要是针对嵌入式系统而定制的库,它的特点就是小。尽管uClibc也可以用在X86机器上,但它并不具有glibc的稳定性,一般在pc机上都是使用的glibc库。我们的目标机是pc机,所以我们选择glibc库,我们可以直接把宿主机上面的库文件拿来用。
5.4 建立设备文件
依照unix的传统,在Linux系统中任何对象(包括设备 )都可以视为文件,在Linux系统中所有的设备文件都放在dev目录下面,建立设备文件通常有两种方法,一个是直接把宿主机上面的拷贝过来(拷贝的时候需要加上dpR参数),另外一种方法就是自己手工用mknod创建(注意这两种方法都需要root权限)。
一般情况下我们都是使用第一种方法,但是这里面为了便于大家更深入的理解设备文件,我使用第二种方法来创建。首先来看如何使用mknod命令来创建特定的设备文件。
#cd dev
#mknod -m 666 null c 1 3
上面这条命令创建了null设备,-m 参数指定所创建设备的基本权限,null是设备名称,c代表是字符设备,相应的块设备用b表示,1是主编号,3是次编号。
关于设备的主次编号的权威信息的来源可以查看内核源码树中的
Documentation/devices.txt
我们这里需要建立的dev条目如表格所示。
|
文件名 |
说明 |
类型 |
主编号 |
次编号 |
权限位 |
|
mem |
物理内存存取 |
字符设备 |
1 |
1 |
600 |
|
console |
系统控制台 |
字符设备 |
5 |
1 |
600 |
|
urandom |
真随机数产生器 |
字符设备 |
1 |
8 |
644 |
|
null |
null(黑洞)设备 |
字符设备 |
1 |
3 |
666 |
|
zero |
以null byte(零值字节)为数据来源 |
字符设备 |
1 |
5 |
666 |
|
tty |
现行的tty设备 |
字符设备 |
5 |
0 |
666 |
|
tty0 |
现行的虚拟控制台 |
字符设备 |
4 |
0 |
600 |
|
tty1 |
第一个虚拟控制台 |
字符设备 |
4 |
1 |
600 |
|
ptyp0 |
first pseudo-tty master |
字符设备 |
2 |
0 |
666 |
|
ttyp0 |
first BSD pseudo-tty slave |
字符设备 |
3 |
0 |
666 |
|
ram0 |
第一块 ram盘 |
块设备 |
1 |
0 |
640 |
|
hda |
主IDE硬盘(或者光盘) |
块设备 |
3 |
0 |
660 |
|
hda1 |
上述主盘的第一个分区 |
块设备 |
3 |
1 |
660 |
|
hdb |
从IDE硬盘(或者光盘) |
块设备 |
3 |
64 |
660 |
|
hdb1 |
上述从盘的第一个分区 |
块设备 |
3 |
65 |
660 |
|
sda |
第一块scsi盘的整个盘 |
块设备 |
8 |
0 |
660 |
|
sda1 |
第一块scsi盘的第一个分区 |
块设备 |
8 |
1 |
660 |
|
sdb |
第二块scsi盘的整个盘 |
块设备 |
8 |
16 |
660 |
|
sdb1 |
第二块scsi盘的第一个分区 |
块设备 |
8 |
16 |
660 |
|
fd0 |
第一个软盘设备 |
块设备 |
2 |
0 |
660 |
下面我们写一个sh脚本mkdev.sh来创建这些设备,脚本内容如下:
#!/bin/sh
rm -rf mem console urandom null zero tty* ptyp* ram* hda* hdb* \
sda* sdb* fd* hdc cdrom
mknod -m 600 mem c 1 1
mknod -m 600 console c 5 1
mknod -m 644 urandom c 1 8
mknod -m 666 null c 1 3
mknod -m 666 zero c 1 5
mknod -m 666 tty c 5 0
for i in 0 1 2 3 4
do
mknod -m 600 tty$i c 4 $i
mknod -m 660 ttyp$i c 3 $i
done
for i in 0 1 2 3 4
do
mknod -m 660 ptyp$i c 2 $i
done
mknod -m 640 ram0 b 1 0
mknod -m 640 ram1 b 1 1
mknod -m 660 hda b 3 0
mknod -m 660 hdb b 3 64
mknod -m 660 hdc b 22 0
ln -s hdc cdrom
for i in 1 2 3 4 5 6 7 8 9 10
do
mknod -m 660 hda$i b 3 $i
mknod -m 660 hdbd$i b 3 `expr 64 + $i`
done
mknod -m 660 sda b 8 0
mknod -m 660 sdb b 8 16
for i in 1 2 3 4 5
do
mknod -m 660 sda$i b 8 $i
mknod -m 660 sdb$i b 8 `expr 16 + $i`
done
mknod -m 640 fd0 b 2 0
#end
#./mkdev.sh
5.5. 定制应用程序。
这个是个大头,但是由于一般的发行版自带的应用程序都很庞大,而且应用程序依赖的动态链接库也大的出奇,所以就有人想出很多办法,其中busybox在这方面做的非常的好,目前busybox在嵌入式方面应用非常广泛,BusyBox真如其名,它把所有的应用程序集成在一个命令里面,目前BusyBox支持unix/linux命令集里面的大部分命令,包括基本的命令cp,cd,chmod,chown,date,cat,ar等,编辑工具vi,awk等,网络工具ifconfig,netstat,route,telnet,ftpget,ftpput,telnetd等,模块工具lsmod,insmod,rmmod,modprobe,压缩解压缩工具gzip,gunzip,tar,bzip,bunzip,zip,unzip等,查找工具find,grep,xargs,init工具init,poweroff,halt,reboot,帐号密码管理工具addgroup,delgroup,adduser,deluser,getty,login,shadow,进程相关工具free,kill,killall,top,ps等,其它的last,fdisk,dmesg,swapon,swapoff等等都有,另外,BusyBox还提供几个shell,我们一般用ash,它和bash非常的相似。
下面说说如何使用busybox定制自己的应用程序。
首先到网上下载一个稳定的BusyBox版本,我用的版本是busybox-
#make menuconfig (不知道的选项看看help就可以了)
#make
#make install
注意busybox默认是安装到根目录下的_install文件文件夹。
#cd _install
#ls
你会发现bin目录下只有一个busybox二进制文件,其它的命令,bin,sbin以及
usr/bin,usr/sbin目录下的都是指向busybox的链接文件。牛逼吧,这么多命令一个应用程序就搞定了,我们看看busybox有多大
#du -k bin/busybox
420
这是我定制出来的busybox的大小,够小的吧。
在busybox的根目录下编译好二进制程序之后。
#make install PREFIX=/mnt/roofs
这样,应用程序就会安装到指定位置,切换到/mnt/rootfs下看看,bin目录,sbin目录,usr/bin目录,usr/sbin目录下是不是出现了很多的应用程序。
busybox安装好之后,看看它依赖的动态链接库
#ldd busybox
libm.so.6 => /lib/tls/libm.so.6 (0×40026000)
libcrypt.so.1 => /lib/libcrypt.so.1 (0×40048000)
libc.so.6 => /lib/tls/libc.so.6 (0×42000000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0×40000000)
#cd /mnt/rootfs
#cd lib
#cp -dpR /lib/libc-
#cp -dpR /lib/libc.so.6 ./
#cp -dpR /lib/libm* ./
#cp -dpR /lib/libcrypt-
#cp -dpR /lib/libcrypt.so.1 ./
#mkdir tls
#ln -s libc-
#ln -s libm-
除了基本的命令之外,BusyBox还支持init功能,如同其它的init一样,busybox的init也是完成系统的初始化工作,关机前的工作等等,我们知道在Linux的内核被载入之后,机器就把控制权转交给内核,linux的内核启动之后,做了一些工作,然后找到根文件系统里面的init程序,并执行它,BusyBox的init进程会依次进行以下工作:(参考<<构建嵌入式LINUX系统>> p201)
1. 为init设置信号处理过程
2. 初始化控制台
3. 剖析/etc/inittab文件
4. 执行系统初始化命令行,缺省情况下会使用/etc/init.d/rcS
5. 执行所有导致init暂停的inittab命令(动作类型:wait)
6. 执行所有仅执行一次的inittab(动作类型:once)
一旦完成以上工作,init进程便会循环执行以下进程:
1. 执行所有终止时必须重新启动的inittab命令(动作类型:once)
2. 执行所有终止时必须重新启动但启动前必须询问用户的inittab命令(动作类型:askfirst)
初始化控制台之后,BusyBox会检查/etc/inittab文件是否存在,如果此文件不存在,BusyBox会使用缺省的inittab配置,它主要为系统重引导,系统挂起以及init重启动设置缺省的动作,此外它还会为四个虚拟控制台(tty1到tty4)设置启动shell的动作。如果未建立这些设备文件,BusyBox会报错。
inittab文件中每一行的格式如下所示:(busybox的根目录下的example文件夹下有详尽的inittab文件范例)
id:runlevel:action:process
尽管此格式与传统的Sytem V init类似,但是,id在BusyBox的init中具有不同的意义。对BusyBox而言,id用来指定启动进程的控制tty。如果所启动的进程并不是可以交互的shell,例如BusyBox的sh(ash),应该会有个控制tty,如果控制tty不存在,Busybox的sh会报错。BusyBox将会完全忽略runlevel字段,所以空着它就行了,你也许会问既然没用保留着它干吗,我想大概是为了和传统的Sytem V init保持一致的格式吧。process字段用来指定所执行程式的路径,包括命令行选项。action字段用来指定下面表中8个可应用到process的动作之一。
|
动作 |
结果 |
|
sysinit |
为init提供初始化命令行的路径 |
|
respawn |
每当相应的进程终止执行便会重新启动 |
|
askfirst |
类似respawn,不过它的主要用途是减少系统上执行的终端应用程序的数量。它将会促使init在控制台上显示“Please press Enter to active this console”的信息,并在重新启动之前等待用户按下enter键 |
|
wait |
告诉init必须等到相应的进程完成之后才能继续执行 |
|
once |
仅执行相应的进程一次,而且不会等待它完成 |
|
ctratldel |
当按下Ctrl+Alt+Delete组合键时,执行相应的进程 |
|
shutdown |
当系统关机时,执行相应的进程 |
|
restart |
当init重新启动时,执行相应的进程,通常此处所执行的进程就是init本身 |
以下是我的usblinux的inittab文件
::sysinit:/etc/init.d/rcS
::respawn:/sbin/getty 38400 tty1
tty2::askfirst:-/bin/sh
tty3::askfirst:-/bin/sh
::restart:/sbin/init
::ctrlaltdel:/bin/umount -a -r
这个inittab执行下列动作
1. 将/etc/init.d/rcS设置成系统的初始化文件
2. 在38400 bps的虚拟终端tty1上启动一个登陆会话 (注意getty的用法)
3. 在虚拟终端tty2和tty3上启动askfirst动作的shell
4. 如果init重新启动,将/sbin/init设置成它会执行的程序
5. 告诉init,在系统关机的时候执行umount命令卸载所有文件系统,并且在卸载失败时用只读模式冲新安装以保护文件系统。
5.6 系统的配置文件
配置文件一般放在/etc/目录下,
#!/bin/sh
/bin/mount -n -t proc /proc /proc
/bin/mount -n -o remount,rw /
/bin/mount -av
/bin/hostname usblinux
/etc/init.d/rc.nic
/etc/init.d/rc.network
/etc/init.d/rc.usb
/etc/init.d/rc.local
1. 安装proc文件系统,但是不写入/etc/mtab文件(mtab文件是df命令读取的文件)
2. 以读写模式重新挂载根文件系统(需要/etc/fstab文件)
3. mount -av 挂载所有在/etc/fstab里面列出的文件系统
4. 主机名命名为usblinux
5. 依次执行rc.nic(自动检测网卡),rc.network(配置网络),rc.usb(加载usb模块),rc.local(执行本地的一些服务)
上面使用mount命令的时候已经涉及到了fstab文件,下面是我的fstab文件
/dev/ram0 / ext2 defaults 0 0
proc /proc proc defaults 0 0
关于fstab配置文件的语法可以man fstab
其实rcS里面的mount -av没有必要加上去。
这个文件里面的内容是动态变化的,当mount一个文件系统的时候,如果mount 没有加上-n参数,那么安装信息就会写入mtab文件,df命令读取的就是这个文件。
这个文件是sh用的,当用户获得一个shell后,sh就会根据这个文件配置用户的登陆环境,下面是我的profile文件。
PATH=/bin:/sbin:/usr/bin:/usr/sbin
PS1=’[\u@\h \W]\$ ‘
HOSTNAME=’/bin/hostname’
export PATH HOSTNAME PS1
alias l.=’ls -d .[a-zA-Z]* –color=tty’
alias rm=’rm -i’
alias cp=’cp -i’
alias mv=’mv -i’
export PATH=/usr/local/bin:$PATH
export PATH=/usr/local/sbin:$PATH
其中PATH环境变量指定当用户键入一个命令时,sh寻找这个命令的路径。
PS1指定sh提示符的格式
其它的export命令,alias命令不用说了吧,busybox里面的ash和bash非常相似,因此只要熟悉bash就会不成问题。
这两个文件是与lib库有关的,它们用来指定应用程序寻找lib库的路径。
我的ld.so.conf文件内容如下。
/lib
/usr/lib
/usr/local/lib
ld.so.cache里面的内容是由是ldconfig命令生成的,ldconfig根据ld.so.conf配置文件生成ld.so.cache里面的内容,应用程序读取的是ld.so.cache文件,因此如果变动了ld.so.conf文件,需要运行一次ldconfig才能是新的配置生效,另外如果新加了lib库文件,也需要ldconfig。
group里面存放的是用户组的信息。
我的group文件内容如下:
root:x:0:root
ftp:x:50:
passwd里面存放的是用户的信息
我的passwd文件内容如下:
root:x:0:0:root:/root:/bin/sh
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
sshd:x:74:74:piviledge-separated SSH:/var/empty/sshd:/sbin/nologin
注意,group和passwd里面的每一行都不是多余的。
shadow文件是pam认证用到的密码存放文件
网络应用程序用到的标准服务端口映射表
我的services文件如下:
tcpmux 1/tcp
tcpmux 1/udp
ftp-data 20/tcp
ftp 21/tcp
ssh 22/tcp
ssh 22/udp
telnet 23/tcp
nameserver 42/tcp name
syslog 514/udp
auth 113/tcp authentication tap ident
因为我需要开启sshd,syslogd服务,同时会使用ftp,ssh,telnet应用程序。所以就会加上上面的东西,但是注意并不是所有的网络应用程序都会依赖与services文件,比如telnet,但是也许telnetd需要。
这个文件的作用很大,没有它很多程序都不能正常运行,关于它的具体作用还是用man nsswitch.conf看看吧。
我的nsswitch.conf文件内容如下:
passwd: files
shadow: files
group: files
hosts: files dns
ethers: files
netmasks: files
networks: files
protocols: files
services: files
netgroup: files
publickey: files
automount: files
aliases: files
注意hosts这一行一定要加上dns,不然即使有resolv.conf文件,域名也不能解析,注意nsswitch.conf是对应一组lib库的,这里我们用了files,dns,因此需要拷贝相应的lib库到/lib目录下.
#cd /mnt/rootfs
#cp -dpR /lib/libnss_files* ./lib
#cp -dpR /lib/libnss_dns* ./lib
我的hosts文件内容如下,不用说也应该清楚什么意思了,不清楚的man hosts
127.0.0.1 localhost:localdomain localhost
host.conf和resolv.conf是域名解析的时候用到的配置文件,其中resolv.conf里面是域名服务器的ip地址,host.conf里面放的是主机的查找规则。不了解的还是man host.conf和man resolv.conf
我的host.conf与resolv.conf文件内容分别如下:
#cat /mnt/rootfs/etc/host.conf
order hosts,bind
#cat /mnt/rootfs/etc/resolv.conf
nameserver 202.112.20.131
syslogd服务程序读取的配置文件。
我的文件内容如下:
# Log all kernel messages to the console.
# Logging much else clutters up the screen.
#kern.* /dev/console
# Log anything (except mail) of level info or higher.
# Don’t log private authentication messages!
*.info;mail.none;authpriv.none;cron.none /var/log/messages
# The authpriv file has restricted access.
authpriv.* /var/log/secure
# Save boot messages also to boot.log
local7.* /var/log/boot.log
5.7 系统初始化的脚本
上面说到rcS文件时,涉及到了/etc/init.d/rc.nic,/etc/init.d/rc.network,/etc/init.d/rc.usb,/etc/init.d/rc.local四个初始化脚本文件。下面来说说它们的作用。
此文件用来自动加载网卡对应的模块(驱动),并且删除没有用到的网卡驱动模块。
#!/bin/sh
#
# detect network cards shell
#
dmesg -n 1 #prevent the messages from appearing on the console
ERR_DEV=/dev/null # redirect verbose messages
NetcardPath=/lib/modules/
loaded=4 #the kernel already detected card,you may want to change it
#according to file `/proc/net/dev`
# invoked by the init script or by the user?
[ "`basename $0`" = "rc.nic" ] && MODE=init || MODE=user
# number of NICs already detected
NICs=$((`cat /proc/net/dev | wc -l` -$loaded))
# when init, skip auto probing if we already found some NIC(s)
if [ $NICs -ge 1 -a "$MODE" = "init" ]; then
echo "Found $NICs network card(s). Skip auto probing …"
exit 0
fi
# official NIC modules
NET_MODs=`find /lib/modules/\`uname -r\`/kernel/drivers/net/ \
-type f -printf "%f\n" 2>$ERR_DEV \
| cut -d "." -f1`
# save the original dmesg messages first
[ -e /var/log/dmesg ] || dmesg > /var/log/dmesg
echo "Probing network cards … "
CUR_NICs=$NICs
for i in $NET_MODs ; do
ORG_MODs=`cat /proc/modules | wc -l`
ORG_NICs=$CUR_NICs
echo -en "\r\t$i \t"
#if modprobe fail we think that this module is not needed
#so we delete it to save the space
if ! modprobe $i 1>$ERR_DEV 2>$ERR_DEV
then
temp=`find $NetcardPath -name $i.o`
if [ -f $temp ]
then
rm -rf $temp
fi
fi
CUR_MODs=`cat /proc/modules | wc -l`
CUR_NICs=$((`cat /proc/net/dev | wc -l` -$loaded))
if [ $CUR_NICs -eq $ORG_NICs ]; then
if [ $CUR_MODs -gt $ORG_MODs ]; then #it is not a netcard module
#so we delete it
RMMODs=`head -$(($CUR_MODs-$ORG_MODs)) /proc/modules | cut -d" " -f1`
for m in $RMMODs ; do
rmmod $m 1>$ERR_DEV 2>$ERR_DEV
done
fi
else
echo " ($(($CUR_NICs-$ORG_NICs)))"
fi
done
echo -e "\r \rFound $(($CUR_NICs-$NICs)) card(s), done."
rmmod -as
exit $(($CUR_NICs-$NICs))
看懂这个脚本就需要你熟悉shell编程了。由于比较大,我这里不作解释。
这个脚本的内容大部分是从cdlinux上面copy过来的,但是对其进行了修改,其中最大的修改就是添加了删除没有用到的模块的功能,这主要是为了节省空间所用。
这个脚本主要是初始化网络配置,
#!/bin/sh
#/sbin/ifconfig eth1 218.199.20.98 up
/sbin/ifconfig eth0 192.168.0.3 up
/sbin/ifconfig lo 127.0.0.1 up
route add default gw 192.168.0.1 dev eth0
这个脚本需要根据不同的环境进行修改。接触过linux的人相信这个不难看懂。
加载usb驱动模块,当然如果你的内核是静态编译进usb模块的,那就没有必要在这里多此一举了,不过如果我们要做个网关服务器,还是把它做成动态的模块比较好,因为网关服务器基本上不需要用到u盘,我们大可删除掉usb驱动模块,以节省空间。我们之所以需要usb的驱动模块,不要忘了我们的usbinux是放在u盘上面运行的。当然并不是说要在u盘上面运行linux,一定需要内核支持usb才行,不管是硬盘,u盘,还是软盘都只是个载体,第一章已经说了,我们的文件系统是在ramdisk中的,因此只要内核和文件系统被载入内存,我们就不再需要载体(存储设备)。但是我们很多东西可以放在u盘上面,以节省ramdisk的存储空间,所以还是需要内核识别u盘,等系统启动之后再把需要的东西从u盘拷贝到ram里面。
rc.usb内容如下:
#!/bin/sh
## This script is to initilize usb controller and
## The driver module usb-storage
## To use usb under linux the module usbcore,scsi_mod
## and sd_mod are needed,in this
## system they are complied in kernel
#/sbin/usb is the small script to start or stop usb support
/sbin/usb start
#script rc.usb end
这个脚本里面用到了/sbin/usb这个程序,其实并不是一个真正的程序,它是我写的一个加载和卸载usb模块的shell脚本。/sbin/usb脚本内容如下:
#!/bin/sh
#
#A simple startup script to start usb for linux
#
case "$1" in
start)
/sbin/modprobe usbcore
/sbin/modprobe usb-uhci
/sbin/modprobe usb-storage
;;
stop)
/sbin/rmmod usbcore
/sbin/rmmod usb-uhci
/sbin/rmmod usb-storage
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
esac
#script /sbin/usb end
我之所以写了这个usb脚本,是因为我发现,启动usblinux后,当机子用过一个u盘时,即使这个u盘umount掉了,再使用另外一个u盘的时候就会出现问题。也许是我的哪边配置有误,不过不管怎么说,这个脚本还是有用的。
注意:虽然上面只列出了,usbcore,usb-uhci,usb-storage三个模块,要能够成功挂载u盘,还需要scsi的支持,也就是需要scsi_mod.o sd_mode.o两个模块,我的内核是把这两个模块静态编译进内核的,因此也就不需要再手动加载了。我之所以把对scsi支持的功能静态编译进内核,是为了测试方便的原故,我测试工具使用的是vmware虚拟机,因为vmware基本上使用scsi硬盘,为了避免添加linuxrc文件,我把vmware的硬盘驱动BusLogic.o静态编译进了内核,所以上面的scsi的两个模块也要编译进内核。
这个shell脚本没有什么好说的,主要是初始化本地的一些服务,比如sshd,syslogd等等
我的rc.local脚本内容如下:
#!/bin/sh
#/sbin/sshd
/sbin/syslogd
在本地启动syslogd服务。
5.8 添加额外的应用程序
虽然busybox支持大多数应用程序,但是往往我们需要的程序它并不支持,这时候我们就只有copy我们的宿主机上的了,第一章说了,我们的usblinux需要能够fdisk存储设备,能够mkfs文件系统,能够使用grub安装grub到指定存储设备。busybox有fdisk,但是mkfs.*在我使用的busybox版本里面还不支持,另外ldconfig这个必须的应用程序busybox里面好像也没有。另外我们也许想在usblinux里面临时使用一下ftp服务器,用来传输文件用,调试的时候strace是必不可少的工具,加进硬盘检测工具smartmontools也是不错的主意,充当网关服务器sshd必不可少吧,虽然可以用其它的比如telnetd等代替,但是它们并没有sshd来的安全,因此我还是选择了sshd。
上面说了这么多的应用程序,如果不采取任何措施直接copy到rootfs里面的话,可想而知,根文件系统的大小会相当大,sshd一个就会净增文件系统
有些应用程序譬如sshd,mkfs*,grub,strace等等,我们平时很少用到,因此如果我们能想出一个办法需要这个应用程序的时候调度出来,不需要的时候把它删掉,那就解决了问题。方法也许有很多,我使用的方法是把这些应用程序及其依赖的库文件压缩,然后把它们的压缩文件安装到opt目录下,需要的时候解压就行了,不需要的时候把解压出来的文件删除掉。我opt目录文件内容如下:
#ls opt
ldconfig.tar.gz rz.tar.gz strace.tar.gz
grub.tar.gz mkfs.tar.gz smartmontools.tar.gz
iptables.tar.gz pureftpd.tar.gz sshd.tar.gz
我之所以把ldconfig压缩,是因为ldconfig有536k之大,rz是学校校园上网认证的一个程序,strace是个调试程序,grub,mkfs不用说了吧,smartmontools是硬盘坏道检测工具,iptables做网关服务器所必须的,pureftpd一个很方便的ftp服务器程序,sshd远程登陆工具,包括服务端和客户端的。
下面使用两个脚本来调入和调出/opt下的指定应用程序。
1. 调入脚本expand
#!/bin/sh
TOOL=$1
if [ ! -f /opt/$TOOL.tar.gz ]
then
echo "No such tool!"
exit
fi
mkdir /tmp/tool
cp /opt/$TOOL.tar.gz /tmp/tool
cd /tmp/tool
tar zxf $TOOL.tar.gz
rm -rf $TOOL.tar.gz
cp -dpR /tmp/tool/$TOOL/etc/* /usr/local/etc 2>/dev/null
cp -dpR /tmp/tool/$TOOL/sbin/* /usr/local/sbin 2>/dev/null
cp -dpR /tmp/tool/$TOOL/bin/* /usr/local/bin 2>/dev/null
cp -dpR /tmp/tool/$TOOL/lib/* /usr/local/lib 2>/dev/null
rm -rf /tmp/tool
echo "done"
2. 调出脚本/sbin/depand
#!/bin/sh
path=/usr/local
case $
"ldconfig")
rm -rf $path/sbin/ldconfig
echo "done";;
"grub")
rm -rf $path/sbin/grub
echo "done";;
"smartmontools")
rm -rf $path/sbin/smartctl
echo "done";;
"strace")
rm -rf $path/bin/strace
echo "done";;
"rz")
rm -rf $path/bin/rz
rm -rf $path/etc/rz.conf
rm -rf $path/lib/libpcap*
echo "done";;
"mkfs")
rm -rf $path/sbin/mkfs*
rm -rf $path/lib/libext2*
rm -rf $path/lib/libe2p*
rm -rf $path/lib/libcom_err*
rm -rf $path/lib/libuuid*
echo "done";;
"pureftpd")
rm -rf $path/sbin/*pure*
echo "done";;
*)
echo "this tool is not installed!"
esac
譬如要调入grub这个工具,只需要在命令行敲入expand grub即可,使用完grub之后,只要depand grub就可释放grub占有的ram空间。
(注意:expand和depand并不适用与iptables与sshd这两个应用程序,对它们我们需要额外编写脚本,因此expand应该加上对这两个程序的拒绝执行动作,但是我这里没有加,为了安全起见,建议还是应该加上)
opt目录下的压缩文件并不是随便做的,我们必须遵循一定的规范,下面我们以制作mkfs工具来说明制作过程。
#cd /mnt/rootfs/opt
#mkdir mkfs
#cd mkfs
#mkdir bin sbin etc lib
#cp -dpR /lib/libext2fs.so.2* ./lib
#cp -dpR /lib/libe2p.so.2* ./lib
#cp -dpR /lib/libuuid.so.1* ./lib
#cp -dpR /lib/libcom_err.so.2* ./lib
#cp -dpR /sbin/mkfs* ./sbin
#cd ..
#tar zcf mkfs.tar.gz mkfs
#rm -rf mkfs
至此一个mkfs的压缩文件mkfs.tar.gz就做好了。
通过这个过程再配备上面的expand脚本你应该知道制作的规则了吧。
下面用目录树了来表示这个架构:
bin sbin lib etc
sshd以及ssh这两个程序依赖的库文件比较多,而且涉及的其它文件也比较多。然而它却是网关服务器所必不可少的。
要正确运行sshd要做以下几件事情
1. 安装sshd所依赖的库文件
2. 安装/lib/security目录
3. 安装/etc/pam.d文件夹
4. 安装/etc/ssh文件夹
5. 创建sshd帐号
6. 创建/var/empty/sshd目录
下面是整个流程
#cd opt
#mkdir sshd
#cd sshd
#mkdir bin etc lib
#cp /usr/bin/ssh bin
#cp /usr/sbin/sshd bin
#ldd /usr/sbin/sshd
libwrap.so.0 => /usr/lib/libwrap.so.0 (0×40026000)
libpam.so.0 => /lib/libpam.so.0 (0x
libdl.so.2 => /lib/libdl.so.2 (0×40037000)
libresolv.so.2 => /lib/libresolv.so.2 (0x
libutil.so.1 => /lib/libutil.so.1 (0x
libz.so.1 => /usr/lib/libz.so.1 (0x
libnsl.so.1 => /lib/libnsl.so.1 (0×4005e000)
libcrypto.so.4 => /lib/libcrypto.so.4 (0×40073000)
libkrb5.so.3 => /usr/kerberos/lib/libkrb5.so.3 (0×40164000)
libk5crypto.so.3 => /usr/kerberos/lib/libk5crypto.so.3 (0x
libcom_err.so.3 => /usr/kerberos/lib/libcom_err.so.3 (0×401d2000)
libc.so.6 => /lib/tls/libc.so.6 (0×42000000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0×40000000)
libgssapi_krb5.so.2 => /usr/kerberos/lib/libgssapi_krb5.so.2 (0×401d5000)
(把这里面列出的库文件拷贝到lib目录下,注意拷贝的时候加上dpR参数,把原文件和链接文件全部拷贝过来)
#cp -dpR /lib/security ./ (拷贝pam认证时需要的库文件)
#cp -R /etc/ssh etc/ (拷贝ssh以及sshd的配置文件)
#mkdir etc/pam.d
#cp /etc/pam.d/system-auth etc/pam.d
#cp /etc/pam.d/sshd etc/pam.d
(把宿主机上的这两个文件拷贝过来,然后加以适当的修改,修改之后两个文件内容如下)
#cat etc/pam.d/system-auth
#%PAM-1.0
# This file is auto-generated.
# User changes will be destroyed the next time authconfig is run.
auth required /lib/security/pam_env.so
auth sufficient /lib/security/pam_unix.so likeauth nullok
auth required /lib/security/pam_deny.so
account required /lib/security/pam_unix.so
password required /lib/security/pam_cracklib.so retry=3 type=
password sufficient /lib/security/pam_unix.so nullok use_authtok md5 shadow
password required /lib/security/pam_deny.so
session required /lib/security/pam_limits.so
session required /lib/security/pam_unix.so
#cat etc/pam.d/sshd
#%PAM-1.0
#auth required /lib/security/pam_securetty.so
auth required /lib/security/pam_stack.so service=system-auth
auth required /lib/security/pam_nologin.so
account required /lib/security/pam_stack.so service=system-auth
password required /lib/security/pam_stack.so service=system-auth
session required /lib/security/pam_stack.so service=system-auth
session optional /lib/security/pam_console.so
(根据这两个文件所用到的/lib/security目录下的库文件,对上面拷贝过来的库文件做适当的裁减,下面是我裁减了的security下的库文件列表)
#ls -l security
-rwxr-xr-x 1 root root 9684 12月 19 15:21 pam_access.so
-rwxr-xr-x 1 root root 47584 12月 19 15:21 pam_console.so
-rwxr-xr-x 1 root root 12936 12月 19 15:21 pam_cracklib.so
-rwxr-xr-x 1 root root 3404 12月 19 15:21 pam_deny.so
-rwxr-xr-x 1 root root 11592 12月 19 15:21 pam_env.so
-rwxr-xr-x 1 root root 8468 12月 19 15:21 pam_lastlog.so
-rwxr-xr-x 1 root root 12324 12月 19 15:21 pam_limits.so
-rwxr-xr-x 1 root root 4856 12月 19 15:21 pam_nologin.so
-rwxr-xr-x 1 root root 3708 12月 19 15:21 pam_permit.so
-rwxr-xr-x 1 root root 6544 12月 19 15:21 pam_securetty.so
-rwxr-xr-x 1 root root 11132 12月 19 15:21 pam_stack.so
lrwxrwxrwx 1 root root 11 12月 25 20:10 pam_unix_acct.so -> pam_unix.so
lrwxrwxrwx 1 root root 11 12月 25 20:10 pam_unix_auth.so -> pam_unix.so
lrwxrwxrwx 1 root root 11 12月 25 20:10 pam_unix_passwd.so -> pam_unix.so
lrwxrwxrwx 1 root root 11 12月 25 20:10 pam_unix_session.so -> pam_unix.so
-rwxr-xr-x 1 root root 48520 12月 19 15:21 pam_unix.so
(注意就是动态链接库,它也有一定的依赖关系,因此最好对lib/security目录下的库文件依次检查其依赖关系,然后检查/mnt/rootfs/opt/sshd/lib目录下是否有其缺少的库文件,如果缺少的需要拷贝相应的动态链接库到/mnt/rootfs/opt/sshd/lib目录下.)
到这里,前4步已经做完了,第5步已经在前面的配置文件一节做完了。最后一步
#mkdir -p /mnt/rootfs/var/empty/sshd
下面我们把opt目录下的sshd文件夹打包压缩。
#cd /mnt/rootfs/opt/
#tar zcf sshd.tar.gz sshd
#rm -rf sshd
到这里sshd工具就做完了,下面我们写个shell脚本,来控制sshd的调入与调出
脚本放在/sbin目录下,脚本文件名为ssh_sv,内容如下:
#!/bin/sh
case "$1" in
start)
ssh_sv stop
cd /tmp
tar zxf /opt/sshd.tar.gz
cd sshd
mv etc/* /etc
mv bin/ssh /usr/bin
mv bin/sshd /sbin/sshd
mv lib/* /usr/local/lib
mv security /lib
rm -rf /tmp/*
expand ldconfig
ldconfig
depand ldconfig
echo done;;
stop)
rm -rf /lib/security
rm -rf /etc/ssh
rm -rf /etc/pam.d
rm -rf /usr/bin/ssh
rm -rf /sbin/sshd
for i in libcom_err.so libcom_err.so.3 libcom_err.so.3.0 \
libcrack.so libcrack.so.2 libcrack.so.2.7 \
libcrypto.so.
libglib-1.2.so.
libgssapi_krb5.so libgssapi_krb5.so.2 libgssapi_krb5.so.2.2 \
libk5crypto.so libk5crypto.so.3 libk5crypto.so.3.0 \
libkrb5.so libkrb5.so.3 libkrb5.so.3.1 libnsl-
libnsl.so.1 libpam.so.0 libpam.so.0.75 libutil-
libutil.so libutil.so.1 libwrap.so libwrap.so.0 \
libwrap.so.
do
rm -rf /usr/local/lib/$i
done
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
esac
我们只要使用ssh_sv start 便可调入sshd与ssh,使用ssh_sv stop便可以调出sshd与ssh。
iptables.tar.gz 是我们网关服务器最重要的一个压缩包,我们的网关服务器的防火墙的配置,以及端口的映射等等都是通过iptables配置的。
#cd opt
#mkdir iptables
#cd iptables
#mkdir lib sbin etc
#cp -dpR /lib/iptables ./lib
#cp /sbin/iptables ./sbin
#touch etc/init.d/rc.firewall
rc.firewall为网关服务器的防火墙以及端口映射配置脚本。
我贴一个例子到这里,关于iptables的使用方法可参考相关文档。
echo 1 > /proc/sys/net/ipv4/ip_forward
/sbin/modprobe ip_conntrack
/sbin/modprobe ip_conntrack_ftp
/sbin/modprobe ip_nat_ftp
/sbin/iptables -F INPUT
/sbin/iptables -F OUTPUT
/sbin/iptables -F FORWARD
/sbin/iptables -t nat -F POSTROUTING
/sbin/iptables -t nat -F PREROUTING
/sbin/iptables -t nat -A POSTROUTING -o eth1 -s 192.168.0.0/24 -j MASQUERADE
/sbin/iptables -t nat -A PREROUTING -i eth1 -p tcp –dport 80 -j DNAT –to-destination 192.168.0.1:80
我只解释前四句,其它的可以man iptables
第一句打开内核的ip转发功能
后三句加载必要的模块,当然如果这些模块是静态编译进内核的就不需要手动加载了。
#cd ..
#tar zcf iptables.tar.gz iptables
#rm -rf iptables
调入和调出iptables的脚本我就不说了,由前面的说明你应该能够轻松的写一个脚本出来。
5.9 根文件系统里面的最后一些东东
#cd var
#ls
empty lock log run
#touch run/utmp (who命令读取的文件)
#touch log/wtmp (last命令用到的配置文件)
#touch log/lastlog (sshd开启登陆会话时读取的文件)
#cd ..
#mkdir -p usr/share/terminfo/l
#cp /usr/share/terminfo/l/linux usr/share/terminfo/l
(linux为grub成功启动所需要的终端)
6.创建ramdisk
第5章已经把根文件系统里面的内容做完了,现在我们建立ramdisk来存储根文件系统。
#du -ks rootfs
8356
我们的rootfs有
#cd /mnt
#dd if=/dev/zero of=usblinux bs=
(前面提到让ramdisk是虚拟的ram盘,它是在内存中虚拟出一块硬盘,因此我们需要对其进行格式化)
#mkfs.ext2 -m 0 usblinux
#mkdir usb
#mount -o loop usblinux usb
#cp -dpR /mnt/rootfs/* ./usb
#umount usb
#gzip -v9 usblinux
7 . 安装kernel ,ramdisk,grub到u盘
要想从U盘启动linux,可能需要把U盘格式化成启动盘,这里我们不关心这个,我们已经假定你的U盘可以以USB-HDD或者USB-ZIP方式启动。
插上准备好的U盘
#mount /dev/sda1 /mnt/usb (我这里的u盘设备是/dev/sda1,这个可以用fdisk -l命令查看)
#cd /mnt/usb
#mkdir boot/grub
#cp /usr/src/linux-2.4/arch/i386/boot/bzImage boot/usblinux.kernel
(把做好的内核拷贝到U盘上)
#cp /mnt/usblinux.gz boot/usblinux.img
(把做好的文件系统安装到U盘上)
#cp /boot/grub/{stage1,stage2} boot/grub/
(在u盘上安装grub需要用到的文件)
#vi boot/grub/grub.conf
default=0
timeout=10
title UsbLinux
root(hd0,0)
kernel /boot/usblinux.kernel ramdisk_size=13000 ro root=/dev/ram0
initrd /boot/usblinux.img
#cd ; umount /mnt/usb
最后一步就是安装grub到u盘上了。
#grub
GNU GRUB version 0.95 (640K lower / 3072K upper memory)
[ Minimal BASH-like line editing is supported. For the first word, TAB
lists possible command completions. Anywhere else TAB lists the
Possible completions of a device/filename.]
grub>root (hd1,0) (注,u盘所在的设备号,不同的系统可能不一样,用fdisk -l先看看,不要搞错了,另外,注意设备的命名规则)
grub>setup (hd1,0)
grub>quit
#
(注:关于grub的安装及其使用可以参考grub手册
http://www.gnu.org/software/grub/manual/grub.html#initrd)
到此为止,应该说我们的目的基本完成了,不过启动的时候可能会出问题,请查看faq。
8.Faq
1. 为什么我的usblinux启动后,到uncompressing usblinux.kernel…………booting the kernel 之后就没有反应了呢?
答:这种情况我碰到的有两个可能的原因
1) 内核的cpu型号选择错误,譬如如果选择athon的cpu,那么在奔4的或者赛杨的机器上跑就会出现这种情况。我们的内核cpu型号选择i386就可以避免这种情况
2) 内核的Charater devices这一项里面没有把Virtual terminal 以及support console on virtual terminal两个选项编译进内核。
2. 内核为什么么启动到Free unused kernel memory *k,然后就没有反应了?
答:如果cpu选择386的话,而lib库还是使用的宿主机上的686的lib库,那么就会出现这种情况,在red hat 9上glibc库有386的版本和686的版本,我们需要从光盘上取出386的版本,然后用windows 下面的7zip工具把里面的相关库文件去出来,把我们的usblinux文件系统里面对应的库文件换掉即可。
3. 为什么busybox的init启动之后,在启动getty的终端输入root密码总是提示出错?
答:busybox的login/passwd认证有两种方法,一个是使用它内部的认证方法,这是无需nsswitch.conf的支持,另外一种就是使用linux系统的认证,这时需要nsswitch.conf文件。我们的密码一般放在/etc/shadow文件里面,但是我两种方法试了都不行,也让busybox支持了shadow,后来用askfirst动作得到一个shell进去之后,用passwd修改密码才发现它的密码是存在/etc/passwd里面的。
具体的修改密码后passwd变成下面这样的格式
root::$1$ccZny60u$o4MBUmzFjENUGWlVPKbTp.:0:0:root:/root:/bin/sh
而/etc/shadow文件里面的内容根本没有任何变化。
下面你知道怎么解决这个问题了吧。
4. 为什么我开启sshd服务后,远程登陆输入密码后没有反映了呢?
ssh远程登陆到主机,需要主机的/dev/目录下有ptyp*设备以及ttyp*设备,当然如果内核支持pts文件系统,那么也可以使用pts/* 设备。
如果内核没有支持pts,或者pts文件系统没有正确安装,那么登陆会话就会使用ttyp*终端。但是如果没有ptyp*设备,就会出现上面的情况。
发信站: 武汉白云黄鹤站 (2003年01月14日15:36:07 星期二), 站内信件
小弟近日对Linux进行了小小的裁减,偶有心得,愿大家共享
说到裁减Linux,无非是为了减小磁盘占用或者是为了某些特定场合的应用(如嵌入式系统)。以RedHat 7.3为例,其最小安装仍然达到了300M,这不得不让人对一直号称小而全的Linux系统感到疑惑。
作为自己手中课题的一个铺垫,不久前我尝试了对Linux进行裁减,虽然没有达到预期的一张软盘大小,但结果也相当有吸引力。下面我对此一一做说明。
参考文档:
Linux bootdisk-HOWTO:
http://www.linux.org.tw/CLDP/gb/Bootdisk-HOWTO.html
initrd introduce on Linux system:
/usr/src/linux-2.4/Documentation/initrd.txt
裁减Linux一般有两种办法,其一是重新生成kernel和文件系统,其二是在原有的系统上删除不必要的文件缩小“体积”
对从一个完整的RedHat 7.3版本而言,其最小安装也有300M,因此,第二个方法是不太现实的。于是重构文件系统和kernel成为了必然。
裁减目标:构成一最小Linux系统担负实验室网关工作,系统载体为硬盘,运行使用RAMDISK,从而减小意外断电造成的文件系统修整消耗提高系统可靠性。
目标平台:P2-400,8G/64M,8139LAN adapt x 2
首先裁减kernel,既然是最小系统,则kernel里所有必须的部件都将直接编译进入内核。但是内核对module的支持需要保留。
如何编译内核,不再累述,具体说说哪些选项先:
Code maturity level options —> 不选
Loadable module support —> 当中的Set version….的可以不要,其他两个留着
Processor type and features —> 按照目标系统选择对应的Process Family ,其他的嘛,留下Machine Check Exception、Low Latency….、HIGHMEM Support,其余都可以不要
General setup —>当中,PCI的选上,其他的不要(注意对照你的系统),SYSTEM V IPC、BSD Process accounting、sysctl support留下,Kernel support ELF binary留着,其他的可以不要
Binary emulation of other systems —>
Memory Technology Devices (MTD) —>
Parallel port support —>
以上三项都是可以不要的
Plug and Play configuration —> 选上,不过如果没有ISA设备,可以不选对ISA P&P的支持(比如我的目标系统)
Block devices —>各取所需了,一般来说,如果你要用软盘,就选上Normal floppy disk support,大多数嵌入式系统是不要的。中间几个也是没有的;Loopback device是一定要的,Network block device我也没把握,可能可以不要不过我选了,呵呵,RAM Disk一定要,Initrd RAM Disk support当然要选。至于Default Ramdisk size就无所谓了,反正可以在启动的时候修改,呵呵。
Multi-device support (RAID and LVM) —> 这个一般也用不上,不选了。
Networking options —>既然是打算做网关,呵呵,里面大部分东西都要选上而且是[*],编译入内核(前面已经说过了,没有编译为模块的)。从上到下一直选到IP: Virtual Server Configuration —>(从这个开始(含),可以不要了)。
需要说明的是,其中的IP: Netfilter Configuration —>子项即便选择全部编译到内核,似乎并没有什么变化,用iptables的时候一样要iptables的.so支持
不过对irc和ftp的跟踪倒是不需要insmod了
Telephony Support —>
SCSI support —>
Fusion MPT device support —>
I2O device support —>
Amateur Radio support —>
IrDA (infrared) support —>
ISDN subsystem —>
Old CD-ROM drivers (not SCSI, not IDE) —>
上面几个都不用,为什么RedHat那么大,他们有不小的功劳哟~~~
Network device support —>里面找出你目标系统的网卡(我这里是8139)选上,其他的统统去掉吧。
Input core support —>如果你不是用的USB接口鼠标键盘,可以不用选他们。
Character devices —>这里面我只选了Virtual Terminal以及Support for console on virtual terminal,其他好多东西都没有选。
Multimedia devices —>
Crypto Hardware support —>
这两个对一般的最小系统来说都是不用的
File systems —>这个是内核大小的大头,ext2(Second extended…)是必要的,ext3也用上吧,/proc有必要,DOS FAT/VFAT(win-95)估计你可能也需要,分区表只要支持PC BIOS就可以了,Native Language我把iso8859-1给内置了
console drivers->我只选了VGA text console
Sound —>
USB support —>
Additional device driver support —>
Kernel hacking —>
这几个都没有选,make dep;make clean;make bzImage
看看吧,内核大概是700~800k左右
以前我总以为裁减kernel就是裁减Linux了,后来才发现是大错特错。
以前总以为最难的是裁减kernel,后来才发现自己多么无知。学习裁减内核,大概只用了一两天,编译一次内核也就20分钟不到,可是后来居然重建文件系统花了一两个星期,呜呜~~~
关于文件系统和kernel的关系,从参考文档里面可以知道,大家自己去看。如果连这个都不懂,建议暂时不要做裁减的事情,以为照着我的文章依葫芦画瓢多半是不能成功的。
首先按照ramdisk的生成方法或者loopback device的生成方法生成一个8M的磁盘挂接到/mnt(或者其他目录)上,就可以以/mnt为根目录构造文件系统。注意将其按照ext2方式格式化
lrwxrwxrwx 1 root root 4 Dec 28 09:31 bin -> sbin
drwxr-xr-x 5 root root 1024 Dec 27 13:42 dev
drwxr-xr-x 7 root root 1024 Jan 6 15:14 etc
drwxr-xr-x 2 root root 1024 Dec 12 08:33 initrd
drwxr-xr-x 4 root root 1024 Dec 30 06:52 lib
drwxr-xr-x 2 root root 1024 Dec 11 07:52 mnt
dr-xr-xr-x 24 root root 0 Jan 6 15:14 proc
drwxr-xr-x 2 root root 1024 Dec 26 03:03 root
drwxr-xr-x 2 root root 1024 Dec 30 07:28 sbin
drwxr-xr-x 2 root root 1024 Dec 26 03:04 sysroot
drwxr-xr-x 2 root root 1024 Apr 19 2002 tmp
drwxr-xr-x 3 root root 1024 Dec 12 07:45 usr
drwxr-xr-x 5 root root 1024 Dec 12 02:43 var
这几个目录是必须的
先看看bin下面有什么
lrwxrwxrwx 1 root root 6 Dec 30 07:28 ash -> ./bash
-rwxr-xr-x 1 root root 541096 Dec 30 07:27 bash
-rwxr-xr-x 1 root root 16020 Dec 13 08:56 cat
-rwxr-xr-x 1 root root 16680 Dec 27 15:40 chmod
-rwxr-xr-x 1 root root 36360 Dec 28 09:10 cp
-rwxr-xr-x 1 root root 62756 Dec 28 09:25 ftp
-rwxr-xr-x 1 root root 100624 Dec 28 09:14 grep
-rwxr-xr-x 1 root root 8672 Dec 26 03:27 halt
-rwxr-xr-x 1 root root 9624 Dec 28 09:14 hostname
-rwxr-xr-x 1 root root 54316 Dec 28 09:14 ifconfig
-rwxr-xr-x 1 root root 26920 Dec 12 02:42 init
-rwxr-xr-x 1 root root 105768 Dec 27 13:44 ip
-rwxr-xr-x 1 root root 60764 Dec 28 09:15 iptables
-rwxr-xr-x 1 root root 7764 Dec 26 17:26 kill
-rwxr-xr-x 1 root root 19080 Dec 12 02:25 login
-rwxr-xr-x 1 root root 9172 Dec 11 07:54 losetup
-rwxr-xr-x 1 root root 46888 Dec 13 08:55 ls
-rwxr-xr-x 1 root root 10316 Dec 13 08:37 mingetty
-rwxr-xr-x 1 root root 17992 Dec 27 14:15 mkdir
-rwsr-xr-x 1 root root 60104 Dec 11 07:54 mount
-rwxr-xr-x 1 root root 43496 Dec 28 10:02 mv
-rwxr-xr-x 1 root root 22196 Dec 26 02:09 nash
-rwxr-xr-x 1 root root 29464 Dec 28 09:49 ping
-r-xr-xr-x 1 root root 63304 Dec 26 16:57 ps
lrwxrwxrwx 1 root root 4 Dec 26 03:33 reboot -> halt
-rwxr-xr-x 1 root root 26216 Dec 26 17:35 rm
lrwxrwxrwx 1 root root 6 Dec 30 07:28 sh -> ./bash
-rwxr-xr-x 1 root root 14952 Dec 11 09:44 shutdown
-rwxr-xr-x 1 root root 219932 Dec 28 10:06 ssh
-rwxr-xr-x 1 root root 260616 Dec 27 14:04 sshd
lrwxrwxrwx 1 root root 6 Dec 26 16:48 swapoff -> swapon
-rwxr-xr-x 1 root root 7108 Apr 1 2002 swapon
-rwxr-xr-x 1 root root 27208 Dec 27 14:13 syslogd
-rwxr-xr-x 1 root root 78808 Dec 28 09:30 telnet
-rwsr-xr-x 1 root root 30664 Dec 27 14:23 umount
-rwxr-xr-x 1 root root 7832 Dec 12 01:54 update
-rwxr-xr-x 1 root root 386120 Dec 28 09:13 vi
-rwxr-xr-x 1 root root 13896 Dec 30 06:53 who
这里面包含了ftp、telnet、ssh客户端以及sshd服务器常用的命令和网络设置命令,iptables防火墙,vi编辑器,shell用的是bash,虽然ash很小但是总是不习惯没有auto complete功能,tcsh不大不小功能又全,可是对一些shell脚本的支持不太好。nash用来解析linuxrc,后面会讲到(如果你看了最前面提到的initrd.txt)也会明白。
接着,用ldd命令看bin目录下面的各个可执行文件分别都和哪些动态库连接把他们cp到/mnt/lib目录下,如用ldd看mv命令,结果如下
libc.so.6 => /lib/i686/libc.so.6 (0×42000000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0×40000000)
把/lib/i686/libc.so.6和/lib/ld-linux.so.2复制到/mnt/lib下面即可。
libc.so.6是基本的libc库,好像不同的处理器还不一样,我就在一台P-MMX下面用P2的libc,结果死掉了。查了半天
iptables命令除了其显示的以外,还要把/lib/iptables目录复制到/mnt/lib下面
/lib/security下面是PAM需要的库,最小系统只需要复制/lib/security下面的pam_unix.so、pam_stack.so到/mnt/lib/security下面即可
/etc下面的东西最是麻烦,要改的不是一点点
/etc/inittab init的配置文件,我改的是这样的:
id:3:initdefault:
si::sysinit:/etc/rc
1:2345:respawn:/sbin/mingetty tty1
2:23:respawn:/sbin/mingetty tty2
l0:0:wait:/etc/rc0
l6:6:wait:/etc/rc6
呵呵,简单吧
/etc/fstab记录系统启动应该mount的文件系统,因为系统在ramdisk上面跑,所以是这样的:
/dev/ram0 / ext2 defaults 1 0
none /proc proc defaults 0 0
从inittab里面知道,启动后执行脚本/etc/rc我是这样写的:
#!/bin/sh
/bin/mount -n -t proc /proc /proc
/bin/mount -n -o remount,rw /
/bin/mount -av
/bin/hostname MiniLinux
/etc/rc.network
/etc/rc.firewall
/bin/sshd
第一行mount /proc,第二行把根remount为rw模式(漏了这个害得我查了两三天),第三行检查fstab里面是否还有其他的需要mount的分区,第四行设置主机名,后面分别根据脚本设置网络和防火墙,最后开启sshd服务。
设置网络在最小系统里面再也不是像/etc/init.d/network start那么简单,
呵呵,其实也不麻烦。通过命令ip、ifconfig可以很方便的设置。例如rc.network为:
#!/bin/sh
/bin/ifconfig eth0 192.168.0.254
/bin/ifconfig eth1 211.69.200.1
/bin/ip route add default via 211.69.200.2 dev eth1
/bin/ip route replace 192.168.0.0/24 dev eth0 scope link
/bin/ip route replace 211.69.200.0/24 dev eth1 scope link
前两行设置ip地址,第三行设置缺省网关,后面两行更改本网路由。
rc.firewall的写法大家可以自己参照iptables的HOWTO去完成
/etc/passwd,/etc/shadow,/etc/group记录有系统帐号信息,在最小系统上,我只留了root组(用户)的信息。
裁减Linux过程中,PAM是一个很关键的部分,由于资料不多,很多人束手无策。
如果仅仅要使用最小系统,从console登陆需要修改/etc/pam.d/login,从ssh 上来修改/etc/pam.d/sshd,不妨复制系统原来的配置文件略作修改/etc/pam.d/login为
#%PAM-1.0
auth required /lib/security/pam_stack.so service=system-auth
account required /lib/security/pam_stack.so service=system-auth
password required /lib/security/pam_stack.so service=system-auth
session required /lib/security/pam_stack.so service=system-auth
/etc/pam.d/sshd和login的内容一样。从其可知它们调用了system-auth这个服务,则还需要/etc/pam.d/system-auth,内容为:
#%PAM-1.0
# This file is auto-generated.
# User changes will be destroyed the next time authconfig is run.
auth sufficient /lib/security/pam_unix.so likeauth nullok
account required /lib/security/pam_unix.so
password sufficient /lib/security/pam_unix.so nullok md5 shadow
session required /lib/security/pam_unix.so
关机和重启
关机和重启在完整的Linux下面是有很长的脚本支持的,就像启动脚本/etc/rc.sysinit 等等。但是在最小系统上面,这些都需要自己来写,复制原有系统的肯定不行。
不过从前面/etc/inittab里面可以知道,最小系统上面reboot执行的是/etc/rc6,关机是/etc/rc0,如果不需要“善后”,则很简单,rc6如下:
[root@MiniLinux etc]# cat rc6
/sbin/reboot -i -d
rc0则为:
[root@MiniLinux etc]# cat rc0
/sbin/halt -i -d -p
整个etc目录下的东西列表大致为:
[root@MiniLinux etc]# ll
total 891
-rw-r–r– 1 root root 6639 Apr 19 2002 fonts.cgz
-rw-r–r– 1 root root 109 Dec 30 06:19 fstab
-rw-r–r– 1 root root 14 Dec 14 00:10 group
-rw-r–r– 1 root root 146 Dec 27 15:41 inittab
-rw——- 1 root root 60 Jan 6 15:14 ioctl.save
-rw-r–r– 1 root root 57 Dec 12 01:26 issue
-rw-r–r– 1 root root 28436 Apr 19 2002 keymaps.gz
-rw-r–r– 1 root root 3758 Apr 19 2002 kon.cfg
-rw-r–r– 1 root root 1281 Apr 19 2002 lang-table
-rw-r–r– 1 root root 1320 Dec 30 06:55 ld.so.cache
-rw-r–r– 1 root root 18 Dec 12 07:53 ld.so.conf
-rw-r–r– 1 root root 54692 Apr 19 2002 loader.tr
-rw-r–r– 1 root root 1180 Dec 23 09:07 login.defs
-rw-r–r– 1 root root 30303 Apr 19 2002 minikon.fnt
-rw-r–r– 1 root root 0 Dec 13 23:39 mtab
-rw-r–r– 1 root root 270 Dec 23 04:03 nsswitch.conf
drwxr-xr-x 2 root root 1024 Dec 30 04:48 pam.d
-rw-r–r– 1 root root 28 Dec 30 06:29 passwd
-rwxr-xr-x 1 root root 401 Dec 30 07:43 profile
-rw-r–r– 1 root root 12359 Apr 19 2002 ramfs.img
lrwxrwxrwx 1 root root 7 Dec 26 03:03 rc -> rc.d/rc
drwxr-xr-x 2 root root 1024 Dec 27 15:33 rc.d
-rwxr-xr-x 1 root root 2631 Jan 6 02:18 rc.firewall
-rwxr-xr-x 1 root root 246 Jan 6 02:17 rc.network
-rwxr-xr-x 1 root root 20 Dec 27 15:38 rc0
-rwxr-xr-x 1 root root 19 Dec 27 15:39 rc6
-r——– 1 root root 59 Dec 30 06:20 shadow
drwxr-xr-x 2 root root 1024 Dec 26 06:37 ssh
-rw-r–r– 1 root root 737535 Dec 23 10:18 termcap
其中目录ssh为sshd的配置文件,复制原来机器上的即可。其他的大部分文件都是按照HOWTO上面提到的一些必备文件复制的。
nsswitch.conf是系统寻找一些配置文件的配置文件,呵呵,很拗口,man nsswitch.conf看看吧,稍作修改为:
[root@MiniLinux /]# cat etc/nsswitch.conf
passwd: files
shadow: files
group: files
hosts: files
services: files
networks: files
protocols: files
rpc: files
ethers: files
netmasks: files
bootparams: files
automount: files
aliases: files
netgroup: files
publickey: files
profile文件是bash shell的登陆脚本,主要为了限制历史命令记录大小
[root@MiniLinux etc]# cat profile
# /etc/profile
# System wide environment and startup programs, for login setup
# Functions and aliases go in /etc/bashrc
HISTSIZE=1000
HISTFILESIZE=20
PATH=/bin
PS1=’[\u@\h \W]\$ ‘
HOSTNAME=’/bin/hostname’
export PATH HISTSIZE HISTFILESIZE HOSTNAME PS1
alias l.=’ls -d .[a-zA-Z]* –color=tty’
alias ll=’ls -l –color=tty’
alias ls=’ls –color=tty’
alias rm=’rm -i’
alias cp=’cp -i’
alias mv=’mv -i’
关于/dev目录
完整linux的/dev目录下有很多设备文件,不过仔细辨别一下就会发现很多其实
用不上。我列出在我的最小Linux下面用到的设备文件:
[root@MiniLinux dev]# ls
agpgart hda hda6 input loop3 psaux ptyp3 ram3 tty3 ttyp4
console hda1 hda7 kbd loop4 ptmx ptyp4 ram4 tty4 urandom
fb hda2 hda8 kmem loop5 pts ram shm ttyp0 zero
fb0 hda3 hda9 loop0 mem ptyp0 ram0 tty0 ttyp1
fd0 hda4 initctl loop1 null ptyp1 ram1 tty1 ttyp2
fd1 hda5 initrd loop2 openprom ptyp2 ram2 tty2 ttyp3
其中input、shm、pts是目录,似乎是系统自己生成的,fb连接到fb0,ram连接到ram0 ,关于硬盘的保留了hda?,loop[0-5]用于支持回环设备(loopback devices),tty[0-4]用于支持主机直接操作,ttyp[0-4] & ptyp[0-4]用于共同支持ssh登陆,ram?用于支持虚拟盘,urandom是sshd服务必须的设备。
所有的设备文件均可以用cp -dpR从原系统的/dev目录下复制过来。
关于linuxrc
linuxrc是一个在initrd.img里面展开后直接自动执行的一个脚本。关于这个脚本的用途,建议大家读一下/usr/src/linux-2.4/Documentation/initrd.txt,我也是直接把系统提供的initrd-2.4.18-3.img里面带的linuxrc拿来用而已:
[root@MiniLinux /]# cat linuxrc
#!/bin/nash
echo Mounting /proc filesystem
mount -t proc /proc /proc
echo Creating root device
mkrootdev /dev/root
echo 0×0100 > /proc/sys/kernel/real-root-dev
echo Mounting root filesystem
mount –ro -t ext2 /dev/root /sysroot
umount /proc
pivot_root /sysroot /sysroot/initrd
注意,它用的shell是/bin/nash,而不是通常用的/bin/sh,大家man nash可以看到很多有意思的东西。
最后的一些工作
当你把一个文件虚拟为一个磁盘并挂接在系统上,复制了需要的可执行文件、库文件、配置文件并做了必要的修改之后,一个文件系统基本上已经成形了。
前面列出的最小系统的目录,如果没有特别提到都是留空的。这里要说的,最后的工作就是如何把kernel和文件系统结合起来。
我看到过很多讲一张或者两张软盘启动的linux,里面都提到用rdev定位文件系统,还要如何如何算。我是看明白了,不过觉得特别繁琐,就投机取巧了一番。
首先假定刚才我们挂载的根文件系统是挂载到现在的/mnt目录下,文件名是/root/newfs则首先umount
#umount /root/newfs
接着将newfs压缩
#gzip -v9 /root/newfs
此时会生成newfs.gz,接着rename:
#mv newfs.gz newfs.img
把它和前面编译的内核bzImage放到/boot目录下去。
我用的Linux引导器是grub。为什么不用LILO?我基本上没有用过LILO,是出道很晚很晚的Linux使用者,从grub的介绍上我发现它比LILO功能强很多,使用新内核不需要像grub那样重新安装,而且内置支持一些常见的文件系统。
看看/boot/grub/grub.conf吧,在其中增加一段:
title Test Combine
root (hd0,4)
kernel /bzImage ro ramdisk_size=8192 root=/dev/ram0
initrd /newfs.img
当然,你不可照抄我的配置,需要按照你的系统更改root (hd?,?),如果你建立的文件系统(未压缩前)容量是其他数值的,请用合适的值替代8192,单位是KB(还记得我前面说过的编译内核的时候不需要刻意更改缺省ramdisk容量吗?就在这里指定即可)
好了,可以试试裁减以后的系统了。大概有多大呢?我裁减出来的系统内核大约是<800k,文件系统8M,压缩成.img的是约3M,很小吧。
感想 及 说明
先说说这个最小Linux的启动过程。grub将内核载入以后,让内核将newfs.img载入内存并展开(自动展开到/dev/ram0)为临时根文件系统,此时执行/linuxrc,在linuxrc中又指定了新的文件系统。接着内核按照grub带入的参数root=/dev/ram0作为根文件系统正式init,此时/dev/ram0中的内容正是newfs.img的内容(如果你仔细观察的话,会发现完整linux启动过程中有Unmounting initrd….的字样,说明initrd.img是在进入init脚本的过程中才被卸载的),按照/etc/inittab脚本执行,缺省为runlevel 3,执行/etc/rc,最后由mingetty启动login完成引导。
我个人感觉,/linuxrc里面的脚本似乎对内核正式init时的根分区指定没有多少影响,并不像/usr/src/linux-2.4/Documentation/initrd.txt里面linuxrc例子那样要求严格。好像决定正式init根分区的是由grub带入内核的参数root=… 来决定的更多。有经验的大侠请多指点。
经过这次实验,感受颇多。首先对linux引导过程以及/etc下面的很多配置文件有了深入了解。Linux可改变的弹性很大,不过也需要大家沉得住气,慢慢去研究,man、HOWTO等等是少不了看的,第一手的资料还是man和英文HOWTO最权威,其次,缩减以后的大小让人非常振奋,裁减以后,系统加载重启登陆的速度都变得非常快,很多东西简直就是一眨眼就过去了。
文末,感谢各位耐心看完,不对之处请斧正。本人也是linux的新手,万望海涵。