FreeBSD手册――配置FreeBSD内核Unix系统

时间:2022-11-25 00:39:53 作者:我再也不发朋友 综合材料 收藏本文 下载本文

“我再也不发朋友”通过精心收集,向本站投稿了9篇FreeBSD手册――配置FreeBSD内核Unix系统,下面是小编给大家带来关于FreeBSD手册――配置FreeBSD内核Unix系统,一起来看看吧,希望对您有所帮助。

篇1:FreeBSD手册――配置FreeBSD内核Unix系统

转自中文FreeBSD用户组 journal.cnfug.org/issue8/000045.html#45 FreeBSD手册――配置FreeBSD内核[阅读次数: 1761次] 顾宏军(redarmy) redarmy@ linux aid.com.cn 9 配置FreeBSD 内核 9.1 大纲 9.2 为什么要构建一个定制的内核? 9.3 建立并安装一个

转自中文FreeBSD用户组 journal.cnfug.org/issue8/000045.html#45

FreeBSD手册――配置FreeBSD内核  [阅读次数: 1761次]

顾宏军(redarmy) linuxaid.com.cn>

9 配置FreeBSD 内核

9.1 大纲

9.2 为什么要构建一个定制的内核?

9.3 建立并安装一个定制的内核

9.4 配置文件

9.5 建立设备节点

9.6 出现问题如何解决

----------------------------------------------------------------------

Chapter 9 配置FreeBSD 内核

由Jake Hamby最初投稿,由Jim Mock更新

9.1 大纲

内核是FreeBSD系统的核心,它用来进行内存管理,安全控制,网络,磁盘访问等等。而有

时你需要重新配置和编译你的内核。

读完这一章,你将知道以下内容:

* 为什么需要建构一个定制的内核.

* 怎么样写一个内核配置文件,或修改一现有的配置文件.

* 怎么样使用内核配置文件创造并构建一新内核.

* 怎么样安装一个新内核.

* 在需要的情况下,怎么样在/dev 中创建设备文件.

* 出问题后,如何解决所出现的问题.

----------------------------------------------------------------------

9.2 为什么要构建一个定制的内核?

以前,freebsd的内核是一个宏内核。这意味着,内核是一个支持固定数量设备的大

型程序。如果你想改变内核的行为,就必须从新编译内核,并用新内核从新启动系统。.

如今,freebsd内核正快速向一模块化方向迁移;也就是说核由模块组成,内核功能

由模块实现,我们能根据需要随时加载或卸载功能模块。这使得内核能迅速识别新硬件

(如笔记本电脑上的PCMCIA卡),能容易的在内核中加入最初编译的内核所不具备的功

能,

这就是所说的模块化内核。通俗的讲,就是KLDs。.

尽管如此,仍然有必要做一些静态的内核编译。因为在某些情况下,功能性相互交叉,

无法实现动态加载;也有可能就是还没有人写出实现这种功能的内核可动态加载的模块.

构建一个定制的内核几乎是每一个UNIX用户都该有的重要能力之一。这样做虽然会

消耗一定的时间,但它将会对你的FreeBSD 系统带来很多好处。定制的内核将只包含对

你PC的硬件设备的支持,这点不象需支持很广泛的硬件设备的GENERIC 内核。它会带来

许多好处:

* 更短的启动时间。因为内核只检测你机器上有的硬件,那么系统启动所需的时间

将大大减少。

* 较少的内存使用量。一个定制的内核通常会比GENERIC 内核使用更少的内存。这

一点很重要,因为内核必须始终驻留实存,占用内存。所以,一个定制的内核对于内存较

少的机器来说尤为重要.

* 额外的硬件支持。一个定制的内核允许你为 声卡等不为GENERIC 内核所支持的

设备提供支持.

----------------------------------------------------------------------

9.3 建立并安装一个定制的内核

首先,我们粗略浏览一下构建内核的目录。所有提及的目录都是相对于/usr/src/sys

而言的,它们也可以通 过/sys 访问。这里的子目录代表内核的不同部分。但对我们而言,

最重要的是/arch/conf ,在这儿你可以定 制内核的配置,然后进行编译,它是所译内核

的存放地。这里的arch 也可能是i386,alpha,或pc98(pc硬件的 一种体系,在日本比较流

行) 。在一个特殊的体系结构目录内的所有代码是这个体系结构所独有的;其它部 分的

代码是所有平台共享的。注意一下目录的逻辑结构,所有支持的设备、文件系统和选项,

等都在它们各自 的子目录下。FreeBSD 5.x及以后的版本已经支持sparc64,还有一些其

它平台的支持正在开发中。

注意:如果你的系统没有/usr/src/sys 目录,这表明内核源代码就没有被安装。最

容易的安装方式是以root 的身份运行/stand/sysinstall ,然后选择

configure->Distributions->src->sys。要是你不喜欢用sysinstall,但有freebsd

的官方安装盘,那么你可 以用以下的命令安装源代码:

# mount /cdrom

# mkdir -p /usr/src/sys

# ln -s /usr/src/sys /sys

# cat /cdrom/src/ssys.[a-d]* | tar -xzvf -

原文转自:www.ltesting.net

篇2:FreeBSD内核配置Unix系统

我的机器内核配置文件, machinei386 cpuI686_CPU optionsCPU_ENABLE_SSE optionsPQ_MEDIUMCACHE optionsCPU_UPGRADE_HW_CACHE options PANIC_REBOOT_WAIT_TIME=16 ptionsCONSPEED=115200 optionsSC_NO_HISTORY optionsSC_DISABLE_REBOOT optionsSC_DISABLE_

我的机器内核配置文件。

machine i386

cpu I686_CPU

options CPU_ENABLE_SSE

options PQ_MEDIUMCACHE

options CPU_UPGRADE_HW_CACHE

options PANIC_REBOOT_WAIT_TIME=16

options CONSPEED=115200

options SC_NO_HISTORY

options SC_DISABLE_REBOOT

options SC_DISABLE_DDBKEY

options MAXMEM=“(256*1024)”

options MAXDSIZ=“(256*1024*1024)”

options MAXSSIZ=“(256*1024*1024)”

options DFLDSIZ=“(256*1024*1024)”

ident TRAVEL

maxusers 0

#makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols

#options MATH_EMULATE #Support for x87 emulation

options INET #InterNETworking

#options INET6 #IPv6 communications protocols

options FFS #Berkeley Fast Filesystem

options FFS_ROOT #FFS usable as root device [keep this!]

options SOFTUPDATES #Enable FFS soft updates support

options UFS_DIRHASH #Improve performance on big directories

#options MFS #Memory Filesystem

#options MD_ROOT #MD is a potential root device

#options NFS #Network Filesystem

#options NFS_ROOT #NFS usable as root device, NFS required

#options MSDOSFS #MSDOS Filesystem

options CD9660 #ISO 9660 Filesystem

#options CD9660_ROOT #CD-ROM usable as root, CD9660 required

options PROCFS #Process filesystem

options COMPAT_43 #Compatible with BSD 4.3 [KEEP THIS!]

#options SCSI_DELAY=15000 #Delay (in ms) before probing SCSI

options UCONSOLE #Allow users to grab the console

options USERCONFIG #boot -c editor

options VISUAL_USERCONFIG #visual boot -c editor

options KTRACE #ktrace(1) support

options SYSVSHM #SYSV-style. shared memory

options SYSVMSG #SYSV-style. message queues

options SYSVSEM #SYSV-style. semaphores

options P1003_1B #Posix P1003_1B real-time extensions

options _KPOSIX_PRIORITY_SCHEDULING

options ICMP_BANDLIM #Rate limit bad replies

options KBD_INSTALL_CDEV # install a CDEV entry in /dev

options AHC_REG_PRETTY_PRINT # Print register bitfields in debug

# output. Adds ~128k to driver.

options AHD_REG_PRETTY_PRINT # Print register bitfields in debug

# output. Adds ~215k to driver.

options SHMMAXPGS=65536

options SEMMNI=40

options SEMMNS=240

options SEMUME=40

options SEMMNU=120

options QUOTA

options USER_LDT

options DEVICE_POLLING

options HZ=1000

options NMBCLUSTERS=8192

options RANDOM_IP_ID

options IPSTEALTH

options IPFILTER

options IPFILTER_LOG

options IPFILTER_DEFAULT_BLOCK

options ACCEPT_FILTER_DATA

options ACCEPT_FILTER_HTTP

device pcm

# To make an SMP kernel, the next two are needed

#options SMP # Symmetric MultiProcessor Kernel

#options APIC_IO # Symmetric (APIC) I/O

# To support HyperThreading, HTT is needed in addition to SMP and APIC_IO

#options HTT # HyperThreading Technology

device isa

#device eisa

device pci

# Floppy drives

#device fdc0 at isa? port IO_FD1 irq 6 drq 2

#device fd0 at fdc0 drive 0

#device fd1 at fdc0 drive 1

#

# If you have a Toshiba Libretto with its Y-E Data PCMCIA floppy,

# don't use the above line for fdc0 but the following one:

#device fdc0

# ATA and ATAPI devices

#device ata0 at isa? port IO_WD1 irq 14

#device ata1 at isa? port IO_WD2 irq 15

device ata

device atadisk # ATA disk drives

device atapicd # ATAPI CDROM drives

#device atapifd # ATAPI floppy drives

#device atapist # ATAPI tape drives

#options ATA_STATIC_ID #Static device numbering

# SCSI Controllers

#device ahb # EISA AHA1742 family

#device ahc # AHA2940 and onboard AIC7xxx devices

#device ahd # AHA39320/29320 and onboard AIC79xx devices

#device amd # AMD 53C974 (Tekram DC-390(T))

#device isp # Qlogic family

#device mpt # LSI-Logic MPT/Fusion

#device ncr # NCR/SymbiosLogic

#device sym # NCR/Symbios Logic (newer chipsets)

#options SYM_SETUP_LP_PROBE_MAP=0x40

# Allow ncr to attach legacy NCR devices when

# both sym and ncr are configured

#device adv0 at isa?

#device adw

#device bt0 at isa?

#device aha0 at isa?

#device aic0 at isa?

#device ncv # NCR 53C500

#device nsp # Workbit Ninja SCSI-3

#device stg # TMC 18C30/18C50

# SCSI peripherals

#device scbus # SCSI bus (required)

#device da # Direct Aclearcase/“ target=”_blank“ >ccess (disks)

#device sa # Sequential Access (tape etc)

#device cd # CD

#device pass # Passthrough device (direct SCSI access)

# RAID controllers interfaced to the SCSI subsystem

#device asr # DPT SmartRAID V, VI and Adaptec SCSI RAID

#device dpt # DPT Smartcache - See LINT for options!

#device iir # Intel Integrated RAID

#device mly # Mylex AcceleRAID/eXtremeRAID

#device ciss # Compaq SmartRAID 5* series

# RAID controllers

#device aac # Adaptec FSA RAID, Dell PERC2/PERC3

##device aacp # SCSI passthrough for aac (requires CAM)

#device ida # Compaq Smart RAID

#device amr # AMI MegaRAID

#device mlx # Mylex DAC960 family

#device twe # 3ware Escalade

# atkbdc0 controls both the keyboard and the PS/2 mouse

device atkbdc0 at isa? port IO_KBD

device atkbd0 at atkbdc? irq 1 flags 0x1

#device psm0 at atkbdc? irq 12

device vga0 at isa?

# splash screen/screen saver

pseudo-device splash

# syscons is the default console driver, resembling an SCO console

device sc0 at isa? flags 0x100

# Enable this and PCVT_FREEBSD for pcvt vt220 compatible console driver

#device vt0 at isa?

#options XSERVER # support for X server on a vt console

#options FAT_CURSOR # start with block cursor

# If you have a ThinkPAD, uncomment this along with the rest of the PCVT lines

#options PCVT_SCANSET=2 # IBM keyboards are non-std

device agp # support several AGP chipsets

# Floating point support - do not disable.

device npx0 at nexus? port IO_NPX irq 13

# Power management support (see LINT for more options)

#device apm0 at nexus? disable flags 0x20 # Advanced Power Management

# PCCARD (PCMCIA) support

#device card

#device pcic0 at isa? irq 0 port 0x3e0 iomem 0xd0000

#device pcic1 at isa? irq 0 port 0x3e2 iomem 0xd4000 disable

# Serial (COM) ports

device sio0 at isa? port IO_COM1 flags 0x10 irq 4

device sio1 at isa? port IO_COM2 irq 3

#device sio2 at isa? disable port IO_COM3 irq 5

#device sio3 at isa? disable port IO_COM4 irq 9

# Parallel port

device ppc0 at isa? irq 7

device ppbus # Parallel port bus (required)

device lpt # Printer

#device plip # TCP/IP over parallel

#device ppi # Parallel port interface device

##device vpo # Requires scbus and da

# PCI Ethernet NICs.

#device de # DEC/Intel DC21x4x (``Tulip'')

#device em # Intel PRO/1000 adapter Gigabit Ethernet Card (``Wiseman'')

#device txp # 3Com 3cR990 (``Typhoon'')

#device vx # 3Com 3c590, 3c595 (``Vortex'')

# PCI Ethernet NICs that use the common MII bus controller code.

# NOTE: Be sure to keep the 'device miibus' line in order to use these NICs!

device miibus # MII bus support

#device dc # DEC/Intel 21143 and various workalikes

#device fxp # Intel EtherExpress PRO/100B (82557, 82558)

#device pcn # AMD Am79C97x PCI 10/100 NICs

device rl # RealTek 8129/8139

#device sf # Adaptec AIC-6915 (``Starfire'')

#device sis # Silicon Integrated Systems SiS 900/SiS 7016

#device ste # Sundance ST201 (D-Link DFE-550TX)

#device tl # Texas Instruments ThunderLAN

#device tx # SMC EtherPower II (83c170 ``EPIC'')

#device vr # VIA Rhine, Rhine II

#device wb # Winbond W89C840F

#device xl # 3Com 3c90x (``Boomerang'', ``Cyclone'')

#device bge # Broadcom BCM570x (``Tigon III'')

# ISA Ethernet NICs.

# 'device ed' requires 'device miibus'

device ed0 at isa? disable port 0x280 irq 10 iomem 0xd8000

#device ex

#device ep

#device fe0 at isa? disable port 0x300

# Xircom Ethernet

#device xe

# PRISM I IEEE 802.11b wireless NIC.

#device awi

# WaveLAN/IEEE 802.11 wireless NICs. Note: the WaveLAN/IEEE really

# exists only as a PCMCIA device, so there is no ISA attachment needed

# and resources will always be dynamically assigned by the pccard code.

#device wi

# Aironet 4500/4800 802.11 wireless NICs. Note: the declaration below will

# work for PCMCIA and PCI cards, as well as ISA cards set to ISA PnP

# mode (the factory default). If you set the switches on your ISA

# card for a manually chosen I/O address and IRQ, you must specify

# those parameters here.

#device an

# The probe order of these is presently determined by i386/isa/isa_compat.c.

#device ie0 at isa? disable port 0x300 irq 10 iomem 0xd0000

##device le0 at isa? disable port 0x300 irq 5 iomem 0xd0000

#device lnc0 at isa? disable port 0x280 irq 10 drq 0

#device cs0 at isa? disable port 0x300

#device sn0 at isa? disable port 0x300 irq 10

# Pseudo devices - the number indicates how many units to allocate.

pseudo-device loop # Network loopback

pseudo-device ether # Ethernet support

#pseudo-device sl 1 # Kernel SLIP

pseudo-device ppp 1 # Kernel PPP

pseudo-device tun # Packet tunnel.

pseudo-device pty # Pseudo-ttys (telnet etc)

pseudo-device md # Memory ”disks“

#pseudo-device gif # IPv6 and IPv4 tunneling

#pseudo-device faith 1 # IPv6-to-IPv4 relaying (translation)

# The `bpf' pseudo-device enables the Berkeley Packet Filter.

# Be aware of the administrative consequences of enabling this!

pseudo-device bpf #Berkeley packet filter

# USB support

device uhci # UHCI PCI->USB interface

device ohci # OHCI PCI->USB interface

device usb # USB Bus (required)

#device ugen # Generic

#device uhid # ”Human Interface Devices“

#device ukbd # Keyboard

#device ulpt # Printer

#device umass # Disks/Mass storage - Requires scbus and da

device ums # Mouse

#device uscanner # Scanners

#device urio # Diamond Rio MP3 Player

# USB Ethernet, requires mii

#device aue # ADMtek USB ethernet

#device cue # CATC USB ethernet

#device kue # Kawasaki LSI USB ethernet

# FireWire support

#device firewire # FireWire bus code

#device sbp # SCSI over FireWire (Requires scbus and da)

#device fwe # Ethernet over FireWire (non-standard!)

原文转自:www.ltesting.net

篇3:FreeBSD其他相关系统和组织

自由使用和免费是FreeBSD的特征,在英文里都可以使用 “free” 来表示,这表示使用者不仅仅可以免费使用这个系统,还可以对软件进行适合自己需要的改动,并能够影响这个软件的发展等等,这正是free这个词更深一层的含义。

支持自由软件的人通常认为,每个计算机的使用者都有自己特定的需要,不可能有一个包罗万象、适合所有需求的软件系统。因此软件开发者和系统管理员希望了解软件内部的情况,以便可以对软件进行定制和扩展。而商业软件只提供软件的二进制代码,将软件内部的操作完全封闭起来,使用者就丧失了对软件的主动权。另外,任何软件产品都存在各种BUG,商业软件的使用者在遇到软件产品的这些问题时,就只能被动的等待厂商的补丁程序,整个软件系统不得不停顿或冒一定的风险继续使用。第三,计算机的使用者并不会完全信赖商业软件,某些软件厂商会故意在软件中加入恶意代码,或加入某种未公开的后门,对使用者造成危害。即使这些软件中的后门或恶意代码是打着防止盗版、提供更方便的功能等旗号加入的,但也可能被偶然触发,危害使用者的利益。而在提供软件源代码的软件中,就不会存在这种恶意的行为。

这些问题对于一个普通用户或者不重要,但对于一个较大的系统或要求较高安全性的系统中,就能表现出重要性了。支持自由软件的人认为,如果一个软件提供了源代码,那么使用者就能根据情况自己修改或请他人修改软件,从而适合自己的最终需要,并修正系统中因为有意或无意导致存在的问题。当前,开放源代码已经得到越来越多的计算机使用者和厂商的认可,他们组成了开放源代码组织(Open Source),来进一步推动这个思想。

另一方面,由于某些种类的商业软件中不存在竞争,垄断的结果就造成软件的价格昂贵,使得这些软件不是任何有需要的人都能够用得起的。而软件最大的价值是被人使用,因此有很多人认为软件开发者要允许别人使用他的软件,应该从软件服务中获得收益,而不是从软件本身中谋取暴利。他们认为计算机应该属于大众,而不应该由某些公司垄断。因此这些崇尚自由软件的人员就想为所有用户开发一整套系统,使用户在现有商业软件之外还可以进行选择,而不至于必须为商业软件支付额外的费用。这些人包括FreeBSD的开发人员、Linux的开发人员、GNU计划的开发人员以及其他众多的自由软件开发者,他们开发的软件构成了计算机领域内多姿多彩的一面,使得即使不使用任何商业软件,使用者也同样能够在计算机上以更好的性能完成所有的工作。

GNU's Not Unix

GNU不是Unix,采用这种递归方式定义的GNU计划是由Richard Stallman提出的,他建立自由软件基金会(FSF)并提出GNU计划的目的是开发一个完全自由的,与Unix类似但功能更强大的操作系统,以便为所有的计算机使用者提供一个功能齐全,性能良好的基本系统。GNU HURD为GNU操作系统的内核,但是当前还没有正式发布,仍处于测试阶段。很多人使用Linux内核和GNU的应用软件组成了GNU/Linux系统。右图为GNU的标志。

FSF开发了大量的自由软件来达到这个目的,这些软件与Unix上原有的软件功能相同,但由于GNU开发软件的时候硬件的处理能力更强了,因此GNU软件充分利用计算机的硬件能力,比Unix中的同样的软件功能更强,因此非常流行。这些软件在GNU通用公共许可的保护下允许任何人免费使用和传播(但必须同时提供源程序),因此被大量的用在其他的Unix中,FreeBSD上也提供了很多GNU软件。

Stallman创造了一个词copyleft,由于通常意义上的版权copyright是用来限制别人在没有许可的条件下不能使用和传播软件,而copyleft则鼓励任何人可以使用和传播软件。copyleft限制妨碍使用和传播GNU软件的行为,例如不提供GNU软件的源代码。然而copyleft与copyright并不冲突,因为 copyleft是针对软件的传播者而非针对最终使用者的。GNU软件必须使用copyleft保护,限制将GNU软件本身用于牟利目的,而其他软件仍然可以使用copyright保护版权不被侵犯。Stallman进一步使用GNU 通用公共许可来详细阐述copyleft的观念,要求传播GNU软件必须同时传播其源代码。由于GNU通用公共许可十分流行,因此很多非自由软件基金会的自由软件也使用GNU通用公共许可来保护自己软件,不被商业公司用于获利。

GNU软件许可相当宽松,有很多公司利用GNU软件进行商业活动,但只要同时提供了GNU软件的源代码,就没有违反GNU软件许可。

FreeBSD本身是在BSD许可的保护下,BSD许可权与GNU通用公共许可略有不同,一些方面的限制甚至更为宽松。GNU通用公共许可要求传播(包括销售)任何符合GNU软件应该包括源代码,反对利用保守GNU软件的源代码的方式牟利,而BSD许可则允许以二进制形式发布软件,对于某些不打算公开源代码的商业运用并没有进行限制。

GNU/Linux

FreeBSD并不是唯一的免费 “Unix” 操作系统,也不是最流行的Unix系统,这个荣誉属于Lin ux。这主要由于Linux出现得早两年,那时BSD的源代码还受AT&T许可权的限制,不能自由获得,而I ntel的32位处理器386的普及已经使Internet上诸多Unix爱好者对PC平台上的免费Unix系统提出了需求,Linux应时而生。近来在Netscape等商业公司的倡导下,Linux得到众多商业公司的支持,无形中成为了反Microsfot联盟的一杆旗帜,声势更为浩大。

Linux是由芬兰人Linus Torvalds编写的Unix兼容系统。由Linus Torvalds 负责核心开发和维护,其他组织和商业公司负责将Linux的核心和应用程序组织在一起发布,由于大部分程序为GNU软件,因此更严格的说Linux应该被称为GNU/Linux。当前流行的Linux版本有slackware 3.6 ,Debian 2.0,RedHat 5.1等。

Linux和FreeBSD同为英特尔平台上的Unix操作系统,因此有很多相似之处。几乎所有的自由软件都同时支持Linux和FreeBSD,因此在应用方面他们也很相似。但Linux和FreeBSD也有相当大的不同之处,可以说各具特色。

从历史渊源上讲,FreeBSD继承了BSD Unix的代码,因此代码相当成熟和稳定。而Linux是一个全新的系统,是遵循POSIX规范开发的一个独立操作系统,但又提供了与BSD Unix或Unix System V的兼容性,

因此Linux完全没有受到1992年的AT&T诉讼的影响,结果成为了最流行的自由操作系统。虽然今天那次法律纠纷已经解决,4.4BSD Lite的代码可以自由获得,但FreeBSD的使用者仍要少于Lin ux的使用者,FreeBSD的使用者多数为网络和计算机专业人士,普及程度较低。

出于这个历史渊源的原因,FreeBSD是一个完整的系统,从系统核心到系统提供的程序均来自BSD Uni x,部分GNU软件是用于代替BSD Unix中的一些老版本软件。而Linux仅仅只是一个操作系统的内核,其应用程序大部分为GNU软件,然后由不同的组织或个人将这些GNU软件与Linux内核整合在一起的。因此就存在多个不同的Linux版本,而且不同的Linux版本使用不同的组合方式,因此各种Linux版本之间有很大的差异,并且可以预计这些版本会随着发展进一步增大彼此的差异。而FreeBSD的版本比较整齐清晰,只有一个FreeBSD版本。

通常FreeBSD更关心稳定性,而Linux具备的各种新特性更多。由于Linux得到了更多的商业公司支持,因而Linux下的商业软件也更多。但是这些商业软件均可以通过FreeBSD对Linux执行文件的支持,也能够运行在FreeBSD上。这个不同主要是由于Linux和FreeBSD的开发过程的差异造成。

虽然FreeBSD和Linux的开发过程都是非常开放的方式,每个开发者独立进行自己的工作,通过Inte rnet来互相交流。任何对系统开发有兴趣的人都能贡献新的想法,并组建一个开发队伍或加入现有开发队伍将自己的想法实现。但是FreeBSD与Linux开发方式的不同是,FreeBSD有一个开发者组成的核心小组(FreeBSD core team)对整个FreeBSD负责。任何人都可以利用FreeBSD提出自己的开发计划,但只有Free BSD核心小组认为这个计划对整个FreeBSD发展有益,这个计划才能列入FreeBSD计划本身。所有的Free BSD开发者共享同一个源代码树,使用并发版本维护工具CVS进行维护。有了FreeBSD核心小组从整体上规划Fr eeBSD,并邀请其他开发者加入核心小组,因此FreeBSD显的更简洁和干净,版本发展也比较稳定。而Linux 仅由Linus对内核进行维护,任何人都可以组织自己的计划,贡献自己的代码,没有一个统一的源代码树,只是在最后由 Linus进行整合和发布正式内核。由于没有严格的控制,并且Linux的开发者很多,因此Linux中虽然有更多的新特性,却显得略为杂乱,并且有些新特性的代码没有经过时间验证,可能存在有问题。

一个新功能没有进入FreeBSD正式版本的原因很多,有的是由于这个功能不够成熟,有的是由于核心小组认为这个特性意义不大,还有的是因为正式内核已经有类似功能,或者由于专利或版权问题(例如,该软件的作者不打算让该软件成为自由软件),使得十分成熟的功能也不能被列入正式发行的版本。

由于Linux和FreeBSD互为竞争对手,在Internet上关于Linux和FreeBSD的争论很多,然而这种争论一般不会得到什么明显的结果。事实上他们都能完成各种应用需求,但各有自己的侧重点,至于那种系统最适合自己,需要根据具体情况而定,使用者的个人喜好也是影响选择的一个重要因素。一般FreeBSD的使用者也曾经(或同时)是Linux的使用者,然而有Linux的使用经验对学习和使用FreeBSD也有不少帮助。只有竞争才能促进发展,Linux和FreeBSD不但是竞争的关系,也是相互学习的关系,只有如此各自才能不断发展。

在作者个人的使用经验中,FreeBSD仅有过一次崩溃,并且这种崩溃不是随机出现的,其原因是使用了所有优化选项来编译内核,编译器某项优化造成了内核的BUG,结果就在执行某项操作时就被触发。在降低了优化级别之后,问题就消失了。因此这个问题应归结为编译器的问题而非内核的问题。在Linux中,问题更为严重一些,过于求新求功能的多样性,使得系统崩溃更频繁,并且很多系统崩溃无法找到原因。

注意:系统崩溃和系统死锁不同,一些情况下一些应用软件死锁会造成键盘或显示没有响应,然而仍然可以通过网络或其他终端设备登录进系统杀死死锁进程。例如在FreeBSD下执行Linux的X Server时,进行切换虚拟控制台的操作就会导致键盘死锁。

当前Linux的一个发展热点是各个商业公司的参与,这的确丰富了Linux下的软件。然而另一方面,商业公司也试图把专有设计引入Linux,将原有开放的环境变为商品化的环境,以便从中牟利。当前不同版本的Linux之间的差异已经相当大了,很多人已经意识到了Linux面临的分裂危险,可以想象以后某个版本的Linux会与其他版本存在兼容性问题。而FreeBSD有统一的源代码树,不存在分裂的问题。

其他BSD系统

商业版本的BSD/OS也是一个非常优秀的操作系统,它是由原BSD Unix的部分开发人员组成的BSDI 公司的产品。由于存在商业支持,因此在某些方面具备免费的FreeBSD不具备的优势。例如某些硬件厂家不愿意公开自己的硬件资料(或者是有条件的公开),因而使得FreeBSD无法支持他们的硬件,而他们可以为BSDI提供资料以供开发驱动程序。一些不能理解自由软件、要使用FreeBSD不支持但BSDI支持的硬件,或对服务和技术支持提出较高要求的使用者,可以选择使用BSDI。

NetBSD和OpenBSD也是基于4.4BSD的自由Unix操作系统,可以算是FreeBSD的兄弟。甚至在最初,NetBSD和FreeBSD的大部分开发者都完全相同,同样的开发者同时为两个系统进行开发。而开发者和使用者都是将这两个系统作为BSD Unix在不同硬件平台的实现来看待的,FreeBSD用于Intel平台,而 NetBSD用于多硬件平台,主要用于RISC工作站硬件。随着两个系统的不断发展,两者逐渐相分离,而OpenBS D是从NetBSD分出的一个分支。但FreeBSD是为个人计算机平台设计的,后来才尝试移植到其他平台上(已经有 Alpha和Sparc芯片下的FreeBSD系统,但还不十分成熟),因此更适合个人计算机用户,而NetBSD和 OpenBSD一开始就设计为能够运行在多系统平台上,包括各种RISC工作站,因而NetBSD和OpenBSD的使用范围就更窄一些。

以上这些BSD Unix系统的使用数量要少于FreeBSD系统。FreeBSD由于专注于Intel平台,支持Intel平台上的硬件种类最多,并对易用性作了很大改善,因而使用范围更广泛,是最常见的BSD Unix系统。但是由于这些系统同属BSD来源,在开发过程中常常相互交流,因此使用方法和管理基本相同。在FreeBSD、N etBSD、OpenBSD之间进行选择的决定性因素常常是安装BSD系统的硬件平台。

篇4:FreeBSD VM内核内存管理

本文涉及到的源码是FreeBSD5.0Release,参考4.4BSD设计与实现相关章节,Matt Dillon的文章,

VM系统涉及的主要数据结构描述

1. vmspace

该结构用于描述一个进程的虚拟地址空间,其包含了平台无关性的vm_map结构和平台相关性的pmap结构,以及该进程内存使用的一些统计计量。

2. vm_map

该结构是描述与平台无关性的虚拟地址空间的最高层数据结构,其包含了一系列虚拟地址有效地址映射实体和这些映射的属性。

3. vm_map_entry

该结构描述了一段虚拟地址空间(start – end),以及该段地址空间代表的是一种VM对象、另一个地址映射还是一个地址子映射,及其相应的共享保护和继承等属性。

4. vm_object

该结构描述了一段虚拟地址空间的数据来源,它可以描述一个文件、一段为零的内存和一个设备等等。

5. vm_page

该结构描述了一页物理内存,是VM用于表述物理内存的低层数据结构。页尺寸是在系统启动时,由平台决定的。

6. pagerops (vm_pager)

该结构描述了VM对象的后台存储如何访问,在FreeBSD中,是通过pagerops结构描述函数指针,实现不同类型的对象的具体操作,在vm_object结构中,通过handle成员指定具体类型对象对应的数据结构,比如设备类型对应dev_t (cdev结构指针)。在一般OS描述中,采用vm_pager描述该目的的数据结构。

本文集中讨论FreeBSD内核虚拟地址空间的管理,涉及到内核地址空间分配和内核地址空间动态分配。FreeBSD的内核空间总是被映射到每一个进程的地址空间的最高部分。和任何其它进程一样,内核也是通过包含一系列的vm_map_entry结构实体的vm_map结构来管理一段地址空间的使用。子映射是内核映射特有的,用于隔离、限制一段地址空间以提供给内核子系统使用,比如mbuf操作。本文主要讨论与平台无关性的内容,涉及到平台相关性时,以i386为例简要说明。

1. SI_SUB_VM初始化

在系统启动时,mi_startup函数会调用SI_SUB_VM初始化与平台无关的VM系统,其定义是:“SYSINIT(vm_mem, SI_SUB_VM, SI_ORDER_FIRST, vm_mem_init, NULL)”。在vm_mem_init函数初始化之后,我们就只使用虚拟内存了,现在分析该函数的实现:

vm_set_page_size();

该函数设置页面尺寸,i386是PAGE_SIZE(4K),记录在系统统计vmmeter结构类型的全局变量cnt的v_page_size成员中。

virtual_avail = vm_page_startup(avail_start, avail_end, virtual_avail);

该语句初始化常驻内存模块。分析函数vm_page_startup的参数和返回值:avail_start的值是从系统启动时,汇编语言调用init386的入参first,指明有效内存的起始地址;avail_end是在getmemsize()函数结束时,通过avail_end = phys_avail[pa_indx];语句获得,该函数是一个与平台相关的函数,这里不作详细讨论,只须明白getmemsize()函数是获得具体物理内存的尺寸;virtual_avail是指向第一个可用页面的虚拟地址,在调用vm_page_startup函数前,是在pmap_bootstrap函数中获得初始值,并在vm_page_startup函数中调整获得真实的值。函数vm_page_startup将物理内存整理、分配为页面单元,并初始化页面管理模块所需信息,每一个页面单元被放置在自由链表中,该函数实现的详细讨论在页调度中讨论,作为内核管理涉及到的区域分配器初始化的一部分是在该函数中通过调用uma_startup函数实现的,该函数的实现在随后讨论。

vm_object_init();

初始化VM的对象模块,FreeBSD是通过统一的vm_object结构使用虚拟内存,该函数完成虚拟内存对象模块所需信息的初始化。

vm_map_startup();

初始化VM地址映射模块。

kmem_init(virtual_avail, virtual_end);

该函数创建内核虚拟地址映射关系,将内核文本、数据、BSS和所有系统启动时已经分配了的空间做一个映射,插入VM_MIN_KERNEL_ADDRESS和virtual_avail之间,余下的virtual_avail和virtual_end之间的地址空间是可用的自由空间。

pmap_init(avail_start, avail_end);

该函数初始化物理内存地址空间的映射关系。

vm_pager_init();

该函数实现系统所支持的所有页面接口类型的初始化,页面接口为数据在其支持的存储空间和物理内存之间的移动提供了一种机制,比如磁盘设备与内存之间,文件系统与内存之间。

至此,vm_mem_init函数执行完成,VM系统初始化完成。

2. 内核地址空间分配

VM系统内核使用的虚拟地址空间段提供了一套用于分配和释放的函数,这些空间段可以从内核地址映射和子映射中分配获得。

根据申请的页是否可以被pageout守护进程调度,内核内存分配有两种路径。在VM子系统初始化时,调用了kmem_init函数创建了内核映射。我们分析该函数的具体实现:

函数void kmem_init(vm_offset_t start, vm_offset_t end)

m = vm_map_create(kernel_pmap, VM_MIN_KERNEL_ADDRESS, end);

函数vm_map_create根据给定了kernel_map物理地址,创建一个新的地址映射m,而VM_MIN_KERNEL_ADDRESS和end给出了该映射范围的下水位(lower address bound)和上水位(upper address bound)。

kernel_map = m;

kernel_map->system_map = 1;

(void) vm_map_insert(m, NULL, (vm_offset_t) 0,

VM_MIN_KERNEL_ADDRESS, start, VM_PROT_ALL, VM_PROT_ALL, 0);

vm_map_unlock(m);

由于函数kmem_init仅用于系统初始化,创建内核地址映射,因此,将获得的地址映射赋给全局变量kernel_map保存,通过vm_map_insert函数创建一个vm_map_entry实体记录相关值,VM_PROT_ALL和VM_PROT_ALL标识这段虚拟地址的访问权限,参见/sys/vm/vm.h定义。

2.1 Wired (nonpageable,不可被pageout调度的页)分配函数

固定页(wired page)是从来不会产生页错误(page fault)。其分配是由kmem_alloc函数和kmem_malloc函数实现的。

函数vm_offset_t kmem_alloc(vm_map_t map, vm_size_t size)

该函数用于在内核地址映射或子映射中,分配内存。

size = round_page(size);

调整申请内存的尺寸,使之为PAGE_SIZE的整数倍。

vm_map_lock(map);

if (vm_map_findspace(map, vm_map_min(map), size, &addr)) {

vm_map_unlock(map);

return (0);

}

offset = addr - VM_MIN_KERNEL_ADDRESS;

vm_object_reference(kernel_object);

vm_map_insert(map, kernel_object, offset, addr, addr + size,

VM_PROT_ALL, VM_PROT_ALL, 0);

vm_map_unlock(map);

在vm_map结构的lock锁机制保护下。通过调用vm_map_findspace函数查询map地址映射是否有足够的空间满足申请的内存尺寸,如果成功,则可用空间的起始地址存于addr中;如果失败则返回1,则kmem_alloc函数调用失败。获得该内存分配空间起始地址与VM_MIN_KERNEL_ADDRESS的偏移。通过vm_object_reference函数对内核对象kernel_obj计数器ref_count加1,kernel_obj的初始化是在VM初始化时,调用vm_object_init实现的,其类型是OBJT_DEFAULT。调用vm_map_insert函数将刚找到的虚拟地址空间插入地址映射map的vm_map_entry链表中。

for (i = 0; i < size; i += PAGE_SIZE) {

vm_page_t mem;

mem = vm_page_grab(kernel_object, OFF_TO_IDX(offset + i),

VM_ALLOC_ZERO | VM_ALLOC_RETRY);

if ((mem->flags & PG_ZERO) == 0)

pmap_zero_page(mem);

mem->valid = VM_PAGE_BITS_ALL;

vm_page_flag_clear(mem, PG_ZERO);

vm_page_wakeup(mem);

}

接下来的这段代码非常有意思,对于申请的内存空间每一页,通过调用vm_page_grab函数,查看该页是否已经被kernel_object持有,如果是,则根据vm_page成员flags标识,如果是PG_BUSY,则等待该标识清PG_BUSY,将该页重新设置为PG_BUSY,返回该地址映射(mem),如果该页没有被kernel_object持有,则分配一个新页(mem)。通过判断PG_ZERO标识,保证该页已经清零。最后通过vm_page_wakeup函数,给正在等待该页分配的线程一个唤醒的机会。这段代码在查找kernel_object持有页时,采用了自顶向下的展开算法(Sleator and Tarjan's top-down splay algorithm)。

(void) vm_map_wire(map, addr, addr + size, FALSE);

设置该段内存是wired。

函数kmem_alloc是非常低层的,一般和平台相关性的函数在申请内存会调用该函数,比如sysarch()。通常kmem_alloc是使用kernel_map地址映射和kernel_object VM对象。

函数vm_offset_t kmem_malloc(vm_map_t map, vm_size_t size, int flags)

该函数同样是用于在内核地址映射或子映射中,分配内存。区别是:

a) kmem_alloc函数在不能获得内存时,可以阻塞等待,而在中断层,分配内存是不能阻塞的,因此需要kmem_malloc以M_NOWAIT标识调用。

b) kmem_malloc函数为malloc调用(malloc(9))提供一种实现机制,即:在内核需要动态分配内存(malloc)时,当申请尺寸大于其阀值,最终通过kmem_malloc实现空间分配。

c) kmem_alloc路径是使用地址映射kernel_map和对象kernel_object;而kmem_malloc路径是使用kernel_map的子映射kmem_map和对象kmem_object,后者的具体讨论在后一节说明。

size = round_page(size);

addr = vm_map_min(map);

首先,根据入参调整、设置尺寸和地址。

vm_map_lock(map);

if (vm_map_findspace(map, vm_map_min(map), size, &addr)) {

vm_map_unlock(map);

if (map != kmem_map) {

static int last_report; /* when we did it (in ticks) */

if (ticks < last_report || (ticks - last_report) >= hz) {

last_report = ticks;

}

goto bad;

}

if ((flags & M_NOWAIT) == 0)

panic(”kmem_malloc(%ld): kmem_map too small: %ld total allocated“,

(long)size, (long)map->size);

goto bad;

}

offset = addr - VM_MIN_KERNEL_ADDRESS;

vm_object_reference(kmem_object);

vm_map_insert(map, kmem_object, offset, addr, addr + size,

VM_PROT_ALL, VM_PROT_ALL, 0);

这段代码的思路和kmem_alloc函数相应的代码是一致的,找到足够的内存空间,返回该空间的起始地址addr,并获得该地址与内核地址映射的偏移(offset),不同之处在于区别c。

retry:

m = vm_page_alloc(kmem_object, OFF_TO_IDX(offset + i), pflags);

if (m == NULL) {

if ((flags & M_NOWAIT) == 0) {

vm_map_unlock(map);

VM_WAIT;

vm_map_lock(map);

goto retry;

}

while (i != 0) {

i -= PAGE_SIZE;

m = vm_page_lookup(kmem_object, OFF_TO_IDX(offset + i));

vm_page_lock_queues();

vm_page_free(m);

vm_page_unlock_queues();

}

vm_map_delete(map, addr, addr + size);

vm_map_unlock(map);

goto bad;

}

if (flags & M_ZERO && (m->flags & PG_ZERO) == 0)

pmap_zero_page(m);

vm_page_flag_clear(m, PG_ZERO);

m->valid = VM_PAGE_BITS_ALL;

针对申请的内存空间的每一页(for (i = 0; i < size; i += PAGE_SIZE)):首先通过vm_page_alloc分配一页,如果分配成功(m != NULL),则根据入参是否含有M_ZERO决定是否需要对分配的页清零,最后设置该页的标识。

如果分配失败(m == NULL):如果入参标识不含有M_NOWAIT,则可以阻塞等待(VM_WAIT),再次尝试;如果含有M_NOWAIT,则说明该次申请是不能阻塞等待,则释放所有已经分配的页面,申请失败(goto bad)。

if (!vm_map_lookup_entry(map, addr, &entry) ||

entry->start != addr || entry->end != addr + size || entry->wired_count != 0)

panic(”kmem_malloc: entry not found or misaligned“);

entry->wired_count = 1;

vm_map_simplify_entry(map, entry);

对于该次申请分配空间对应的vm_map_entry实体成员设值,通过调用函数vm_map_simplify_entry建立该entry实体与map其它实体建立关联。

for (i = 0; i < size; i += PAGE_SIZE) {

m = vm_page_lookup(kmem_object, OFF_TO_IDX(offset + i));

vm_page_lock_queues();

vm_page_wire(m);

vm_page_wakeup(m);

vm_page_unlock_queues();

pmap_enter(kernel_pmap, addr + i, m, VM_PROT_ALL, 1);

vm_page_flag_set(m, PG_WRITEABLE | PG_REFERENCED);

}

vm_map_unlock(map);

return (addr);

这段代码是对该次申请的每一页,通过pmap_enter函数将每一页的虚拟地址映射关系加入在指定的kernel_pmap中。最后返回该次申请的内存起始地址。

bad:

return (0);

如果该次申请出错,则返回空值。

2.2 Pageable (可以由pageout调度的页)分配函数

函数vm_offset_t kmem_alloc_pageable(vm_map_t map, vm_size_t size)

该函数用于分配可以被pageout调度的内存空间,其中map只能是kernel_map或其子映射。

size = round_page(size);

addr = vm_map_min(map);

result = vm_map_find(map, NULL, 0,

&addr, size, TRUE, VM_PROT_ALL, VM_PROT_ALL, 0);

if (result != KERN_SUCCESS) {

return (0);

}

return (addr);

这段代码十分清晰,通过vm_map_find函数,如果能找到足够的内存空间,则分配给addr,并在该map中插入一个vm_map_entry实体表示该段内存映射。如果不成功,则返回空值。

函数vm_offset_t kmem_alloc_nofault (vm_map_t map, vm_size_t size)

该函数的实现和kmem_alloc_pageable函数基本一致,只是在调用vm_map_find函数时,其最后一位参数是MAP_NOFAULT,具体用意,我不太确定。

函数 vm_offset_t kmem_alloc_wait(vm_map_t map, vm_size_t size)

该函数实现内核一个子映射的内存分配功能,在调用该函数时,可能会阻塞。

vm_map_lock(map);

if (vm_map_findspace(map, vm_map_min(map), size, &addr) == 0)

break;

if (vm_map_max(map) - vm_map_min(map) < size) {

vm_map_unlock(map);

return (0);

}

map->needs_wakeup = TRUE;

vm_map_unlock_and_wait(map, FALSE);

无限循环执行这段代码,除非break或是return。如果找到有足够空间分配这段内存vm_map_findspace返回为0,则跳出循环。如果没有,则判断是否该子映射是否有足够空间给申请者,如果没有则返回空值。如果该子映射有足够空间,只是暂时没有,则通过vm_map_unlock_and_wait睡眠,等待有空间可用。

vm_map_insert(map, NULL, 0, addr, addr + size, VM_PROT_ALL, VM_PROT_ALL, 0);

vm_map_unlock(map);

当分配了足够的空间后,将对应的vm_map_entry实体插入map中。

2.3 内核内存空间释放函数

函数void kmem_free(vm_map_t map, vm_offset_t addr, vm_size_t size)

该函数用于释放内核内存分配的内存空间。

(void) vm_map_remove(map, trunc_page(addr), round_page(addr + size));

函数kmem_free通过调整需要释放的空间的起始地址add和size,作为vm_map_remove的入参释放这段空间。

函数void kmem_free_wakeup (vm_map_t map, vm_offset_t addr, vm_size_t size)

vm_map_lock(map);

(void) vm_map_delete(map, trunc_page(addr), round_page(addr + size));

if (map->needs_wakeup) {

map->needs_wakeup = FALSE;

vm_map_wakeup(map);

}

vm_map_unlock(map);

该函数和kmem_free函数的功能一样,只不过在释放空间后,会检查needs_wakeup成员,如果有其它线程阻塞于该map,则通过vm_map_wakeup函数试图唤醒。

2.4 内核子映射分配

FreeBSD5.0通过kernel_map提供了内核地址映射,基于kernel_map,FreeBSD5.0提供了kmem_map、clean_map和exec_map子映射。其中clean_map由分出buffer_map和pager_map两个子映射。

Ø kmem_map:为malloc机制和mbuf提供内核内存空间映射。

Ø exec_map:为exec进程执行等系统调用提供内核内存空间映射。

Ø buffer_map:为文件系统提供内核内存映射。

Ø pager_map:为页机制提供内核内存映射。

本小节讨论内核内存地址子映射分配的实现。

函数vm_map_t kmem_suballoc(vm_map_t parent, vm_offset_t* min,

vm_offset_t* max, vm_size_t size)

该函数实现了内核内存地址的子映射分配。

GIANT_REQUIRED;

size = round_page(size);

*min = (vm_offset_t) vm_map_min(parent);

ret = vm_map_find(parent, NULL, (vm_offset_t) 0,

min, size, TRUE, VM_PROT_ALL, VM_PROT_ALL, 0);

if (ret != KERN_SUCCESS) {

printf(”kmem_suballoc: bad status return of %d.n“, ret);

panic(”kmem_suballoc“);

}

在父映射parent中,查找是否有足够的空间给申请分配的子映射,如果失败,则出现异常错误。

*max = *min + size;

result = vm_map_create(vm_map_pmap(parent), *min, *max);

if (result == NULL)

panic(”kmem_suballoc: cannot create submap“);

通过vm_map_create函数创建新的地址映射vm_map,赋给result,如果不能创建,也是出现异常错误。

if (vm_map_submap(parent, *min, *max, result) != KERN_SUCCESS)

panic(”kmem_suballoc: unable to change range to submap“);

return (result);

通过vm_map_submap函数,建立parent和result之间的联系,如果不能建立也是异常错误。最后返回新创建的子映射结构的指针。

3. 区域分配器机制(UMA子系统)

FreeBSD5.0通过区域分配器(zone allocator)提供了对动态大小的内存分配的有效管理。内核中经常使用的malloc函数也是区域分配器的一个封装。区域分配器第一次出现在FreeBSD3.0中,但是在FreeBSD5.0中已经完全重写了。

3.1 数据结构

区域分配器涉及到的主要数据结构是:uma_zone、uma_slab、uma_bucket、uma_cache和uma_hash,其中uma_hash是为了提高效率。

一个uma_zone可以有多个uma_slab,每个uma_slab是通过单向链表串联(根据slab的状态,可以在不同的链表中);每个slab是由多个item组成,每个item是真正供数据存储所用的空间。而uma_bucket和uma_cache是为每个CPU提供cache和并行处理机制。

3.2 区域分配器初始化

区域分配器的初始化分为三个部分,分别在内核执行SI_SUB_VM、SI_SUB_KMEM和SI_SUB_VM_CONF模块中调用。

函数void uma_startup(void *bootmem)

该函数实现了区域分配器初始化的一部分,其入参bootmem是由系统启动时,在vm_page_startup函数给出的,指向了系统分配给区域管理所需内存的首地址。该段内存的尺寸应该是UMA_SLAB_SIZE * UMA_BOOT_PAGES,即30页。

mtx_init(&uma_mtx, ”UMA lock“, NULL, MTX_DEF);

初始化全局互斥体uma_mtx,该uma_mtx用于保护对全局区域管理器链表uma_zones的操作。

args.name = ”UMA Zones“;

args.size = sizeof(struct uma_zone) + (sizeof(struct uma_cache) * (maxcpu - 1));

args.ctor = zone_ctor;

args.dtor = zone_dtor;

args.uminit = zero_init;

args.fini = NULL;

args.align = 32 - 1;

args.flags = UMA_ZONE_INTERNAL;

zone_ctor(zones, sizeof(struct uma_zone), &args);

手工创建zones,通过zone_ctor函数对zones初始化,并插入全局链表uma_zones中。关于zones的定义参考:

static struct uma_zone masterzone;

static uma_zone_t zones = &

所有的区域分配管理器都是从masterzone派生的产物。

for (i = 0; i < UMA_BOOT_PAGES; i++) {

slab = (uma_slab_t)((u_int8_t *)bootmem + (i * UMA_SLAB_SIZE));

slab->us_data = (u_int8_t *)slab;

slab->us_flags = UMA_SLAB_BOOT;

LIST_INSERT_HEAD(&uma_boot_pages, slab, us_link);

uma_boot_free++;

}

根据bootmem提供的内存地址,分配30个页面,串联在uma_boot_pages链表中。

slabsize = UMA_SLAB_SIZE - sizeof(struct uma_slab); /*4k – 24*/

slabsize /= UMA_MAX_WASTE; /*(4k-24)/(4k/10) = 9*/

slabsize++; /* 10*/

slabsize += sizeof(struct uma_slab); /*10 + 24 = 34*/

计算slabsize尺寸,在i386体系中,是34。

slabzone = uma_zcreate(”UMA Slabs“, slabsize, NULL, NULL, NULL, NULL,

UMA_ALIGN_PTR, UMA_ZONE_INTERNAL);

hashzone = uma_zcreate(”UMA Hash“, sizeof(struct slabhead *) * UMA_HASH_SIZE_INIT, NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_INTERNAL);

bucketzone = uma_zcreate(”UMA Buckets“, sizeof(struct uma_bucket),

NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_INTERNAL);

调用uma_zcreate函数创建三个新的区域,分别赋给slabzone、hashzone和bucketzone。系统中所有的uma_slab结构都是从slabzone区域分配;hashzone负责哈希表的初始化分配;bucketzone负责所有uma_bucket分配。

在区域分配器执行完uma_startup之后,如图所示其结构:

masterzone

zones->uz_part_slab

item =>slabzone

item =>hashzone

item =>bucketzone

函数void uma_startup2(void)

该函数十分简单,就是几个全局变量的设置。通过bucket_enable函数设置bucketdisable的值,该值决定了bucketzone区域是否可用。

booted = 1;

bucket_enable();

函数static void uma_startup3(void)

callout_init(&uma_callout, 0);

callout_reset(&uma_callout, UMA_WORKING_TIME * hz, uma_timeout, NULL);

该函数用于设置定时执行器,每隔UMA_WORKING_TIME * hz的时间,执行一个uma_timeout函数。主要是用于统计系统的区域使用信息,以及相应的每个CPU的cache使用情况。

3.3 区域接口函数

关于区域分配器的接口函数描述可以参考zone(9)。本小节基于zone(9)的论述,详细讨论区域分配器的接口函数的实现。

函数uma_zone_t uma_zcreate(char *name, size_t size, uma_ctor ctor, uma_dtor dtor,

uma_init uminit, uma_fini fini, int align, u_int16_t flags)

该函数用于创建新的区域管理器(uma_zone结构类型)。入参:name是用于调试所用的文本字符串;size表示需要创建的区域分配管理器的每一个item的尺寸;ctor和dtor都是函数指针,分别用于uma_zalloc和uma_zfree系列函数回调使用;uminit和fini同样是函数指针,用于区域分配对象的优化;align是位掩码;flags说明了需要创建的区域的特性。

flags定义如下:

#define UMA_ZFLAG_OFFPAGE 0x0001 /* Struct slab/freelist off page */

#define UMA_ZFLAG_PRIVALLOC 0x0002 /* Zone has supplied it's own alloc */

#define UMA_ZFLAG_INTERNAL 0x0004 /* Internal zone, no offpage no PCPU */

#define UMA_ZFLAG_MALLOC 0x0008 /* Zone created by malloc */

#define UMA_ZFLAG_NOFREE 0x0010 /* Don't free data from this zone */

#define UMA_ZFLAG_FULL 0x0020 /* This zone reached uz_maxpages */

#define UMA_ZFLAG_BUCKETCACHE 0x0040 /* Only allocate buckets from cache */

#define UMA_ZFLAG_HASH 0x0080 /* Look up slab via hash */

函数uma_zcreate的实现主要是通过调用uma_zalloc_internal函数,我们先看看函数uma_zcreate的代码:

args.name = name;

args.size = size;

args.ctor = ctor;

args.dtor = dtor;

args.uminit = uminit;

args.fini = fini;

args.align = align;

args.flags = flags;

return (uma_zalloc_internal(zones, &args, M_WAITOK));

正如我们前面讨论启动时,提及的所有的区域创建都是从zones中派生的,

现在分析uma_zalloc_internal函数,可以分为4个主体部分讨论:

if (bucketdisable && zone == bucketzone)

return (NULL);

在系统启动时,指定的UMA_BOOT_PAGES空间耗尽(bucketdisable = 1),并且指定的父区域管理器是bucketzone,则返回NULL值。

ZONE_LOCK(zone);

slab = uma_zone_slab(zone, flags);

if (slab == NULL) {

ZONE_UNLOCK(zone);

return (NULL);

}

通过调用uma_zone_slab函数,从zone中分配一个uma_slab类型结构,其指针赋给slab。

item = uma_slab_alloc(zone, slab);

ZONE_UNLOCK(zone);

通过uma_zone_slab函数,从zone中的一个slab中分配一个item,该item用于存储新的uma_zone类型的管理结构。

if (zone->uz_ctor != NULL)

zone->uz_ctor(item, zone->uz_size, udata);

if (flags & M_ZERO)

bzero(item, zone->uz_size);

return (item);

如果制定了uz_ctor,则调用回调函数对item里存储的新的uma_zone进行构造初始化。最后返回新的uma_zone地址。

函数void * uma_zalloc(uma_zone_t zone, int flags)

该函数实现了从zone中分配一个item的功能,该函数只是uma_zalloc_arg函数一个简单封装。

return uma_zalloc_arg(zone, NULL, flags);

因此,我们讨论uma_zalloc_arg函数实现的主体部分。

zalloc_restart:

cpu = PCPU_GET(cpuid);

CPU_LOCK(zone, cpu);

cache = &zone->uz_cpu[cpu];

根据当前调用者的CPU号,获得相应的uma_cache结构。

zalloc_start:

bucket = cache->uc_allocbucket;

成员uc_allocbucket记录了该item应该分配的来源。

if (bucket) {

如果bucket有值,则说明可以检查是否能从该bucket中分配item。

if (bucket->ub_ptr >-1) {

如果ub_ptr >-1,说明该bucket目前存在可以分配的item。

item = bucket->ub_bucket[bucket->ub_ptr];

bucket->ub_ptr--;

cache->uc_allocs++;

CPU_UNLOCK(zone, cpu);

if (zone->uz_ctor)

zone->uz_ctor(item, zone->uz_size, udata);

if (flags & M_ZERO)

bzero(item, zone->uz_size);

return (item);

分配该item,并做相应的初始化,返回该item。

} else if (cache->uc_freebucket) {

如果uc_allocbucket没有可用空间,检查是否存在free bucket,并且可用。

if (cache->uc_freebucket->ub_ptr >-1) {

uma_bucket_t swap;

swap = cache->uc_freebucket;

cache->uc_freebucket = cache->uc_allocbucket;

cache->uc_allocbucket = swap;

goto zalloc_start;

将该可用的uc_freebucket赋给uc_allocbucket,重新对bucket赋值,再次试图分配item。

}

}

}

以上是对如果存在bucket的情况处理。分析一下,什么情况会继续执行下面的代码。可能的情况:a. 不存在uc_allocbucket;b. uc_allocbucket已经耗尽。

ZONE_LOCK(zone);

zone->uz_allocs += cache->uc_allocs;

cache->uc_allocs = 0;

更新uz_allocs值,该值说明了该zone区域被分配的次数。比如当uc_allocbucket已经耗尽,需要将该cache的uc_allocs加入uz_allocs中,因为后面会更新uc_allocbucket。

if (cache->uc_allocbucket) {

LIST_INSERT_HEAD(&zone->uz_free_bucket, cache->uc_allocbucket, ub_link);

cache->uc_allocbucket = NULL;

}

现在uc_allocbucket是一个可以释放item的bucket,将其加入到uz_free_bucket中。

if ((bucket = LIST_FIRST(&zone->uz_full_bucket)) != NULL) {

LIST_REMOVE(bucket, ub_link);

cache->uc_allocbucket = bucket;

ZONE_UNLOCK(zone);

goto zalloc_start;

}

更新cache结构的uc_allocbucket指针,试图重新分配item。

CPU_UNLOCK(zone, cpu);

if (zone->uz_count < UMA_BUCKET_SIZE - 1)

zone->uz_count++;

if (uma_zalloc_bucket(zone, flags)) {

ZONE_UNLOCK(zone);

goto zalloc_restart;

}

ZONE_UNLOCK(zone);

如果zone确实没有可用bucket使用,因此,需要通过uma_zalloc_bucket分配新的bucket,然后完全重新开始试图分配item。

return (uma_zalloc_internal(zone, udata, flags));

什么时候会执行到这一步?比如,对于系统的某个模块的链表,需要malloc分配每个节点空间,而且该链表很长,以至于在uma_zcreate创建的slab里的item都使用完了。因此,需要再分配slab来满足需要。

函数void uma_zfree(uma_zone_t zone, void *item)

该函数实现了将item释放回zone中,同uma_zalloc类似,该函数是uma_zfree_arg的一个封装。

uma_zfree_arg(zone, item, NULL);

现在,我们分析uma_zfree_arg的实现。

if (zone->uz_flags & UMA_ZFLAG_FULL)

goto zfree_internal;

标识UMA_ZFLAG_FULL说明该区域zone已经达到峰值,直接放在后面处理。后面再讨论。

if (zone->uz_dtor)

zone->uz_dtor(item, zone->uz_size, udata);

如果该zone的解析函数指针不为空,则首先调用其解析函数处理。

zfree_restart:

cpu = PCPU_GET(cpuid);

CPU_LOCK(zone, cpu);

cache = &zone->uz_cpu[cpu];

同uma_zalloc_arg函数类似,根据当前调用者的CPU号,获得相应的uma_cache结构。

zfree_start:

bucket = cache->uc_freebucket;

if (bucket) {

if (bucket->ub_ptr < zone->uz_count) {

bucket->ub_ptr++;

bucket->ub_bucket[bucket->ub_ptr] = item;

CPU_UNLOCK(zone, cpu);

return;

} else if (cache->uc_allocbucket) {

if (cache->uc_allocbucket->ub_ptr < cache->uc_freebucket->ub_ptr) {

uma_bucket_t swap;

swap = cache->uc_freebucket;

cache->uc_freebucket = cache->uc_allocbucket;

cache->uc_allocbucket = swap;

goto zfree_start;

}

}

}

比较uma_zalloc_arg函数相应的代码,只不过bucket代表需要释放的uc_freebucket。如果该uc_freebucket还有剩余item空间,就不真正的释放该item,只是将计数器加1。如果uc_freebucket满了,并且uc_allocbucket有值,而且uc_allocbucket还有item空间,则交换这两个值,重新对bucket赋值,并再次试图对bucket计数器加1。

如果该zone原来的uc_freebucket为空,或者uc_freebucket和uc_allocbucket都满了,则需要继续执行。

ZONE_LOCK(zone);

bucket = cache->uc_freebucket;

cache->uc_freebucket = NULL;

if (bucket != NULL) {

LIST_INSERT_HEAD(&zone->uz_full_bucket, bucket, ub_link);

}

如果是uc_freebucket的item已经满了,则将uc_freebucket插入uz_full_bucket链表中。

if ((bucket = LIST_FIRST(&zone->uz_free_bucket)) != NULL) {

LIST_REMOVE(bucket, ub_link);

ZONE_UNLOCK(zone);

cache->uc_freebucket = bucket;

goto zfree_start;

}

从该zone需要释放的bucket链表uz_free_bucket(如果其不为空)中,移出第一个节点,将其赋给uc_freebucket,重新执行zfree_start。

CPU_UNLOCK(zone, cpu);

ZONE_UNLOCK(zone);

bflags = M_NOWAIT;

if (zone->uz_flags & UMA_ZFLAG_BUCKETCACHE)

bflags |= M_NOVM;

bucket = uma_zalloc_internal(bucketzone, NULL, bflags);

if (bucket) {

bucket->ub_ptr = -1;

ZONE_LOCK(zone);

LIST_INSERT_HEAD(&zone->uz_free_bucket, bucket, ub_link);

ZONE_UNLOCK(zone);

goto zfree_restart;

}

当执行到这段代码,说明以前分配给该zone的bucket都已经使用完了,因此,通过uma_zalloc_internal函数试图从bucketzone中再为zone分配新的bucket。如果成功,则插入uz_free_bucket链表中,再次试图调整该bucket的ub_ptr值。

zfree_internal:

uma_zfree_internal(zone, item, udata, 0);

return;

当执行到这段代码,说明该zone的bucket已经完全满了,需要真正释放该item返回给zone。函数uma_zfree_internal的实现在uma_zdestroy函数中讨论。

函数void uma_zdestroy(uma_zone_t zone)

该函数用于销毁一个空的区域。

uma_zfree_internal(zones, zone, NULL, 0);

函数uma_zdestroy是uma_zfree_internal的一个封装,因此,我们讨论uma_zfree_internal的实现。static void uma_zfree_internal(uma_zone_t zone, void *item, void *udata, int skip)

if (!skip && zone->uz_dtor)

zone->uz_dtor(item, zone->uz_size, udata);

如果没有指定跳过该zone的解析函数,并且该zone的解析函数指针不为空,则执行该解析函数。

ZONE_LOCK(zone);

if (!(zone->uz_flags & UMA_ZFLAG_MALLOC)) {

mem = (u_int8_t *)((unsigned long)item & (~UMA_SLAB_MASK));

if (zone->uz_flags & UMA_ZFLAG_HASH)

slab = hash_sfind(&zone->uz_hash, mem);

else {

mem += zone->uz_pgoff;

slab = (uma_slab_t)mem;

}

} else {

slab = (uma_slab_t)udata;

}

根据标识UMA_ZFLAG_MALLOC,如果uz_flags含有该标识,说明该zone是用于malloc机制,则通过udata入参获得uma_slab_t(slab)的值;如果不含有该标识,则通过计算获得该slab的值。

if (slab->us_freecount+1 == zone->uz_ipers) {

LIST_REMOVE(slab, us_link);

LIST_INSERT_HEAD(&zone->uz_free_slab, slab, us_link);

} else if (slab->us_freecount == 0) {

LIST_REMOVE(slab, us_link);

LIST_INSERT_HEAD(&zone->uz_part_slab, slab, us_link);

}

根据slab成员us_freecount不同的值,将slab移入不同队列中。

freei = ((unsigned long)item - (unsigned long)slab->us_data) / zone->uz_rsize;

slab->us_freelist[freei] = slab->us_firstfree;

slab->us_firstfree = freei;

slab->us_freecount++;

调整该slab的item指针,以及相应的统计信息。

zone->uz_free++;

调整zone的可用空间(item)计数器。

if (zone->uz_flags & UMA_ZFLAG_FULL) {

if (zone->uz_pages < zone->uz_maxpages)

zone->uz_flags &= ~UMA_ZFLAG_FULL;

wakeup_one(zone);

}

ZONE_UNLOCK(zone);

如果uz_flags含有UMA_ZFLAG_FULL,说明该zone在释放该item支前,已经达到饱和状态,因此,释放了该item,说明该zone又有了可用的item。所以,去掉标识UMA_ZFLAG_FULL,并通过wakeup_one函数,试图唤醒等待于该zone的第一个线程。

3.4 关于区域分配器的一点后话

FreeBSD5.0提供的区域分配器,为内核动态分配内存提供了全面的跟踪机制,同时通过指针的移动极大的提高了内存分配和释放的速度。区域分配器含有很多链表,分配和释放空间,主要是通过节点在链表间的转移实现。

4. 内核动态内存管理-malloc机制

内核和用户调用malloc函数的形式不一样,在这里,我们讨论内核部分的实现。首先,看看malloc的定义:“void * malloc(unsigned long size, struct malloc_type *type, int flags);”,内核在使用malloc前,需要通过宏MALLOC_DEFINE定义malloc_type的type实体,将该宏展开:

MALLOC_DEFINE(type, shortdesc, longdesc) à

struct malloc_type type[1] = {

{ NULL, 0, 0, 0, 0, 0, M_MAGIC, shortdesc, {} }

};

SYSINIT(type##_init, SI_SUB_KMEM, SI_ORDER_SECOND, malloc_init, type);

SYSUNINIT(type##_uninit, SI_SUB_KMEM, SI_ORDER_ANY, malloc_uninit, type)

因此,内核使用malloc函数的变量type都会在开机的时候通过malloc_init初始化,在reboot的时候通过malloc_uninit释放。

查看malloc_type结构:

struct malloc_type {

struct malloc_type *ks_next; /* next in list */

u_long ks_memuse; /* total memory held in bytes */

u_long ks_size; /* sizes of this thing that are allocated */

u_long ks_inuse; /* # of packets of this type currently in use */

uint64_t ks_calls; /* total packets of this type ever allocated */

u_long ks_maxused; /* maximum number ever used */

u_long ks_magic; /* if it's not magic, don't touch it */

const char *ks_shortdesc; /* short description */

struct mtx ks_mtx; /* lock for stats */

};

该结构用于对同一种申请动态分配的类型使用情况的控制。成员ks_next用于将内核所有需要动态分配空间的类型通过kmemstatistics链接。别的成员参考英文注释。

4.1 malloc子系统初始化

通过mi_startup()函数对SI_SUB_KMEM子系统初始化。

A:SYSINIT(kmem, SI_SUB_KMEM, SI_ORDER_FIRST, kmeminit, NULL)

分析kmeminit函数的主体部分:

mtx_init(&malloc_mtx, ”malloc“, NULL, MTX_DEF);

初始化全局互斥体malloc_mtx,该互斥体用于保护全局malloc_type链表kmemstatistics,该链表用于维护系统通过MALLOC_DEFINE宏定义的type。

vm_kmem_size = VM_KMEM_SIZE;

mem_size = cnt.v_page_count * PAGE_SIZE;

if ((mem_size / VM_KMEM_SIZE_SCALE) >vm_kmem_size)

vm_kmem_size = mem_size / VM_KMEM_SIZE_SCALE;

if (vm_kmem_size >= VM_KMEM_SIZE_MAX)

vm_kmem_size = VM_KMEM_SIZE_MAX;

TUNABLE_INT_FETCH(”kern.vm.kmem.size“, &vm_kmem_size);

if ((vm_kmem_size / 2) >(cnt.v_page_count * PAGE_SIZE))

vm_kmem_size = 2 * cnt.v_page_count * PAGE_SIZE;

设置、调整内核内存尺寸vm_kmem_size,其中TUNABLE_INT_FETCH宏用于从/boot/defaults/loader.conf或/boot/loader.conf文件中获得kern.vm.kmem.size值,如果设置了该值,则赋给vm_kmem_size。最后将vm_kmem_size的值限制在物理内存的两倍以内。

npg = (nmbufs * MSIZE + nmbclusters * MCLBYTES + nmbcnt *

sizeof(u_int) + vm_kmem_size) / PAGE_SIZE;

kmem_map = kmem_suballoc(kernel_map, (vm_offset_t *)&kmembase,

(vm_offset_t *)&kmemlimit, (vm_size_t)(npg * PAGE_SIZE));

kmem_map->system_map = 1;

计算用于网络通信所需缓冲的尺寸,并通过kmem_suballoc函数分配vm_map类型的结构用于管理该段内存(kmem_map)。

uma_startup2();

for (i = 0, indx = 0; kmemzones[indx].kz_size != 0; indx++) {

int size = kmemzones[indx].kz_size;

char *name = kmemzones[indx].kz_name;

kmemzones[indx].kz_zone = uma_zcreate(name, size, NULL, NULL, NULL, NULL,

UMA_ALIGN_PTR, UMA_ZONE_MALLOC);

for (;i <= size; i+= KMEM_ZBASE)

kmemsize[i >>KMEM_ZSHIFT] = indx;

}

这段代码是区域分配器初始化的一个部分。函数uma_startup2完成区域分配器所需的一些全局变量的设置。全局变量kmemzones的定义如下:

struct {

int kz_size;

char *kz_name;

uma_zone_t kz_zone;

} kmemzones[] = {

{16, ”16“, NULL},

{32, ”32“, NULL},

{64, ”64“, NULL},

{128, ”128“, NULL},

{256, ”256“, NULL},

{512, ”512“, NULL},

{1024, ”1024“, NULL},

{2048, ”2048“, NULL},

{4096, ”4096“, NULL},

{8192, ”8192“, NULL},

{16384, ”16384“, NULL},

{32768, ”32768“, NULL},

{65536, ”65536“, NULL},

{0, NULL},

};

当内核需要分配一个较小内存空间时(<65536),函数malloc通过该变量实现内存区域分配,区域分配器是通过uma_zone结构管理的,因此,在这段代码中通过uma_zcreate创建相应的区域,并将该区域指针赋给kz_zone成员,UMA_ZONE_MALLOC标识指明了该区域用于malloc调用。涉及到uma的函数系列,我们在后面的区域分配器中讨论。

B:SYSINIT(type##_init, SI_SUB_KMEM, SI_ORDER_SECOND, malloc_init, type)

和SYSUNINIT(type##_uninit, SI_SUB_KMEM, SI_ORDER_ANY, malloc_uninit, type)

该宏是MALLOC_DEFINE宏的一条语句。所有内核通过malloc动态分配的空间都需通过MALLOC_DEFINE定义,即在系统初始化时,需要malloc_init对该变量初始化,下面分析该函数主体:

mtx_lock(&malloc_mtx);

if (type->ks_magic != M_MAGIC)

panic(”malloc type lacks magic“);

if (cnt.v_page_count == 0)

panic(”malloc_init not allowed before vm init“);

if (type->ks_next != NULL)

return;

type->ks_next = kmemstatistics;

kmemstatistics = type;

mtx_init(&type->ks_mtx, type->ks_shortdesc, ”Malloc Stats“, MTX_DEF);

mtx_unlock(&malloc_mtx);

这段代码非常简单,就是在malloc_mtx互斥体的保护下,将一个新的malloc_type 类型的type变量插入kmemstatistics链表首部,并初始化malloc_type结构的ks_mtx互斥体。

同样,malloc_uninit用于在系统关闭时,将type变量从kmemstatistics抽出,并销毁其ks_mtx互斥体。

4.2 malloc接口函数的实现

函数void * malloc(unsigned long size, struct malloc_type *type, int flags)

该函数实现了内核动态分配内存的功能,FreeBSD根据其分配内存尺寸与阀值KMEM_ZMAX(64K)的关系决定如何实现该内存区域分配,其实现主体如下。

if (size <= KMEM_ZMAX) {

if (size & KMEM_ZMASK)

size = (size & ~KMEM_ZMASK) + KMEM_ZBASE;

indx = kmemsize[size >>KMEM_ZSHIFT];

zone = kmemzones[indx].kz_zone;

va = uma_zalloc(zone, flags);

mtx_lock(&ksp->ks_mtx);

if (va == NULL)

goto out;

ksp->ks_size |= 1 << indx;

size = zone->uz_size;

} else {

size = roundup(size, PAGE_SIZE);

zone = NULL;

va = uma_large_malloc(size, flags);

mtx_lock(&ksp->ks_mtx);

if (va == NULL)

goto out;

}

如果申请的内存空间尺寸小于等于阀值KMEM_ZMAX,采用2的幂的关系来分配空间,即如果申请500字节的空间,满足该尺寸的最小kmemzones是kz_size为512的项;如果申请513字节的空间,则满足该尺寸的最小kmemzones是kz_size为1024的项,因此,实际通过uma_zalloc函数分配的内存尺寸应该是512和1024字节。

如果申请的内存空间尺寸大于阀值KMEM_ZMAX,采用满足该尺寸的最小整数倍PAGE_SIZE(4K)的空间尺寸。即如果申请65537字节的内存,则需要分配的尺寸是17个PAGE_SIZE(68K),更新尺寸后,通过uma_large_malloc函数实现具体区域空间分配,其本质是通过kmem_malloc函数实现。

ksp->ks_memuse += size;

ksp->ks_inuse++;

out:

ksp->ks_calls++;

if (ksp->ks_memuse >ksp->ks_maxused)

ksp->ks_maxused = ksp->ks_memuse;

mtx_unlock(&ksp->ks_mtx);

return ((void *) va);

这段代码是malloc函数实现的剩余部分,其中略过了与流程关系不大的代码。主要用于更新申请分配内存的类型控制结构的成员信息。

函数void free(void *addr, struct malloc_type *type)

该函数释放动态分配的内存空间。

slab = vtoslab((vm_offset_t)addr & (~UMA_SLAB_MASK));

if (!(slab->us_flags & UMA_SLAB_MALLOC)) {

size = slab->us_zone->uz_size;

uma_zfree_arg(slab->us_zone, addr, slab);

} else {

size = slab->us_size;

uma_large_free(slab);

}

在malloc函数中,通过uma_large_malloc函数分配的空间起slab值的us_flags标识含有UMA_SLAB_MALLOC。正如malloc函数根据申请的空间尺寸有两种获得内存的方法,释放内存空间时,也是这两种获得空间方法对应的释放空间方法。当调用uma_large_free函数时,其实质是调用kmem_free函数实现。

mtx_lock(&ksp->ks_mtx);

ksp->ks_memuse -= size;

ksp->ks_inuse--;

mtx_unlock(&ksp->ks_mtx);

由于释放了该内存空间,因此,管理该类型的malloc_type的成员也需要更新。

函数void * realloc(void *addr, unsigned long size, struct malloc_type *type, int flags)

该函数用于改变内存空间尺寸,将先前分配的内存空间addr的尺寸改变为size。

if (addr == NULL)

return (malloc(size, type, flags));

如果没有指明内存地址addr,则就是完全分配一个size尺寸的内存空间,调用malloc函数即可。

slab = vtoslab((vm_offset_t)addr & ~(UMA_SLAB_MASK));

if (slab->us_zone)

alloc = slab->us_zone->uz_size;

else

alloc = slab->us_size;

如果原来分配的空间存在于uma_zone中(us_zone不为空),则通过uz_size获得原来空间尺寸,否则通过us_size获得原来尺寸。

if (size <= alloc

&& (size >(alloc >>REALLOC_FRACTION) || alloc == MINALLOCSIZE))

return (addr);

如果可能,尽量用原来的内存地址。

if ((newaddr = malloc(size, type, flags)) == NULL)

return (NULL);

bcopy(addr, newaddr, min(size, alloc));

free(addr, type);

return (newaddr);

重新分配一个size尺寸的内存空间,将原有空间的数据拷贝到新的内存空间,并释放原有的内存空间。

函数void * reallocf(void *addr, unsigned long size, struct malloc_type *type, int flags)

该函数是realloc函数的封装,如果调用realloc成功,则返回;不成功则释放原有地址addr的内存空间。

4.3 malloc其它说明

内核通过sysctl_kern_malloc函数,提供了查询当前内核通过malloc动态内存分配的方法,我们可以通过命令“sysctl kern.malloc”查询。

篇5:FreeBSD 5.4 桌面配置(ZZ)Unix系统

自CU BSD版面 FreeBSD 最新公布了 5.4 版本,于是,我立即下载, 以下是我的安装和配置过程。或许对你有帮助。安装完以后,KDE和GNOME都是最新的。并且中文显示非常美观和清晰。还不错。 我的电脑P4,两块网卡,rl0网卡用于ADSL, rl1网卡用于与内部网连

转载自CU BSD版面

FreeBSD 最新公布了 5.4 版本,于是,我立即下载。 以下是我的安装和配置过程。或许对你有帮助。安装完以后,KDE和GNOME都是最新的。并且中文显示非常美观和清晰。还不错。

我的电脑P4,两块网卡,rl0网卡用于ADSL, rl1网卡用于与内部网连接,液晶17显示器。注意,下文中的代码部分前边用#符号开头的为注释,如果你要使用,请根据你的情况去掉#号。我全部都使用vim作为编辑器。

我的主要步骤是:

= 1 安装系统和软件包 =

下载以后,先安装系统部分。然后,重新起动。

用如下命令,起用光盘:

代码:

mount_cd9660 /dev/acd0 /cdrom

然后,把两张光盘中的 packages 目录复制到 /usr/local/software 目录中, 放在一起,然后就可以用 pkg_add 安装你想要的软件了。这样做主要是为了不用来回换光盘,并且速度也快一些。

由于我喜欢使用 KDE ,所以,我的安装顺序是:

代码:

cd x11

pkg_add xorg-6.8.2.tbz

pkg_add kde-3.4.0.tbz

你要是喜欢使用GNOME ,也可以 pkg_add gnome2-2.10.0.tbz。随你安装。

这样桌面软件就有了,我又安装了vim,cvsup和unzip。

如果要使用 VIM ,最好先建立一个 vimrc 文件:

代码:

cp /usr/local/share/vim/vim63/vimrc_example.vim /usr/local/share/vim/vimrc

这样编辑的时候就方便多了。

= 2 基本配置 =

然后编辑 /root/.cshrc 文件,我用vim编辑加入以下代码:

代码:

alias ls ls -aGw  # 彩色显示目录文件

setenv LANG zh_CN.eucCN

setenv LC_LANG zh_CN.eucCN

#setenv LC_CTYPE zh_CN.UTF-8 # 让系统默认文件编码为utf-8

#setenv LC_CTYPE zh_CN.eucCN # 让系统默认文件编码为GB2312

setenv XMODIFIERS @im=fcitx # 我使用小企鹅输入法

然后,编辑 /etc/rc.conf 文件,修改为:

代码:

gateway_enable=”YES“

linux_enable=”YES“

moused_enable=”YES“

sshd_enable=”YES“

usbd_enable=”YES“

ifconfig_rl1=”inet 192.168.0.1  netmask 255.255.255.0“ # 修改为你的 IP 地址和网卡

hostname=”office.baow.com“ # 修改为你自己的主机名称

= 3 连接网络 =

先连接网络,我使用ADSL上网,所以我编辑 /etc/ppp/ppp.conf 文件,修改为:

代码:

default:

set log Phase tun command

set ifaddr 10.0.0.1/0 10.0.0.2/0

set timeout 100

adsl:

set device PPPoE:rl0 # 使用 rl0 网卡

set mru 1492

set authname 123456 # 修改为你的ADSL帐号

set authkey 123456  # 修改为你的密码

set dial

set login

add default HISADDR

然后在 /etc/目录下添加一个 resolv.conf 文件,用于域名解析,输入:

代码:

domain  baow.com

nameserver  202.106.0.20

# 可以按照上边格式,加入第二个域名解析服务器

然后,最好重启。再登录。用命令: ppp -auto adsl 就可以联网了。

用命令: ppp -auto -nat adsl 就可以作为网关,共享连接。

用命令: killall ppp 可以断开网络。

= 4 升级 ports =

这一步时间有点长,可以略过。主要为了安装小企鹅输入法。 当然你也可以从 www.fcitx.org 网站下载,直接安装。

如果要升级 ports,先建立 ports 文件:

代码:

cp /usr/share/examples/cvsup/ports-supfile /usr/MyPorts

cd /usr

编辑 MyPorts 文件,修改其中的:

代码:

default host=cvsup2.FreeBSDchina.org

然后用命令升级 ports:

代码:

cvsup -g -L 2 MyPports

= 5 安装输入法和中文字体 =

接下来装中文,先安装小企鹅输入法:

代码:

cd /usr/ports/chinese/fcitx

make install clean

接下来安装中文字体:

代码:

mkdir /usr/X11R6/lib/X11/fonts/TrueType/

然后到windows的系统盘下的 windowsFonts ,把simsun.ttc tahoma.ttf 这两个文件copy进去,当然也可以多复制一些字体。

通过以下命令可以连接 Windows 系统:

代码:

mkdir /mnt/WinXP

mount_msdosfs /dev/ad0s1 /mnt/WinXP # 适用于FAT

#mount_ntfs /dev/ad0s1 /mnt/WinXP # 适用于 NTFS

= 6 显示配置 =

然后,配置显示,即配置 xorg, 用如下命令开始配置:

代码:

xorgcfg -textmode

生成一个 xorg.conf 文件,保存在 /etc/X11/ 中

根据你的电脑配置,编辑修改,我的配置为:

代码:

Section ”ServerLayout“

Identifier     ”Layout0“

Screen      0  ”Screen0“ 0 0

InputDevice    ”Keyboard0“ ”CoreKeyboard“

InputDevice    ”Mouse0“ ”CorePointer“

EndSection

Section ”Files“

FontPath    ”/usr/local/share/fonts“

FontPath    ”/usr/X11R6/lib/X11/fonts“

FontPath    ”/usr/local/share/fonts/override“

FontPath    ”/usr/X11R6/lib/X11/fonts/100dpi“

FontPath    ”/usr/X11R6/lib/X11/fonts/75dpi“

FontPath    ”/usr/X11R6/lib/X11/fonts/bitstream-vera“

FontPath    ”/usr/X11R6/lib/X11/fonts/cyrillic“

FontPath    ”/usr/X11R6/lib/X11/fonts/latin2“

FontPath    ”/usr/X11R6/lib/X11/fonts/local“

FontPath    ”/usr/X11R6/lib/X11/fonts/misc“

FontPath    ”/usr/X11R6/lib/X11/fonts/PEX“

FontPath    ”/usr/X11R6/lib/X11/fonts/Speedo“

FontPath    ”/usr/X11R6/lib/X11/fonts/TTF“

FontPath    ”/usr/X11R6/lib/X11/fonts/Type1“

FontPath    ”/usr/X11R6/lib/X11/fonts/TrueType“

FontPath    ”/usr/X11R6/lib/X11/fonts/URW“

FontPath    ”/usr/X11R6/lib/X11/fonts/chinese“

EndSection

Section ”Module“

Load ”freetype“

# Load ”xtt“

Load  ”extmod“

Load  ”glx“

Load  ”dri“

Load  ”dbe“

Load  ”record“

Load  ”xtrap“

Load  ”type1“

Load  ”speedo“

EndSection

Section ”InputDevice“

Identifier  ”Mouse0“

Driver      ”mouse“

Option       ”Protocol“ ”Auto“

Option       ”Emulate3Buttons“

Option       ”Device“ ”/dev/sysmouse“

Option      ”ZAxisMapping“ ”4 5“  # 添加支持鼠标滚轮

EndSection

Section ”InputDevice“

Identifier  ”Keyboard0“

Driver      ”keyboard“

Option       ”XkbModel“ ”pc101“

Option       ”XkbLayout“ ”us“

EndSection

Section ”Monitor“

Identifier   ”Monitor0“

### Uncomment if you don't want to default to DDC:  # 我使用液晶显示器,所以注释掉

#   HorizSync    31.5 - 48.5

#   VertRefresh  50.0 - 70.0

EndSection

Section ”Device“

Identifier  ”Card0“

Driver      ”nv“

EndSection

Section ”Screen“

Identifier ”Screen0“

Device     ”Card0“

Monitor    ”Monitor0“

DefaultDepth     24

SubSection ”Display“

Viewport   0 0

Depth    24

Modes    ”1024x768“

EndSubSection

EndSection

然后在 /root 目录下 编辑 .xinitrc 文件:

代码:

exec fcitx & # 运行 fcitx

exec startkde # 使用KDE

#exec gnome-session # 使用 GNOME

然后,用以下命令,起动桌面:

代码:

startx

这样中文环境就建立起来了,

进入以后,通过调整字体为 simsun ,就可以显示清晰好看的中文界面了。我使用KDE,因此我在controlcenter 中的字体控制部分调整为simsun字体,并且不选中 Use anti-aliasing for fonts选项。这样字体就好看了。

多试试吧。

= 7 拼音输入和 VIM =

拼音输入方法比较多,我主要使用小企鹅输入法,尤其是双拼输入。

以下是我的双拼编码,可以保存到 /root/.fcitx/sp.dat 文件中,这样就可以和Windows中的智能拼音和紫光拼音一致了。

代码:

# fcitx  双拼编码方案

# 以'#'打头的行是注释。

#

# 双拼编码范围为英文字母键(A到Z)和分号键“;”。在此文件中,为了看起来醒目起见,双拼编码

# 采用大写英文字母(此处也可以用小写字母);而在实际输入过程中,只能使用小写英文字母。

[零声母标识]

=O

[声母]

# 双拼编码就是它本身的声母不必列出

ch=E

sh=V

zh=A

[韵母]

# 双拼编码就是它本身的韵母不必列出

ai=L

an=J

ang=H

ao=K

ei=Q

en=F

eng=G

er=R

ia=D

ian=W

iang=T

iao=Z

ie=X

in=C

ing=Y

iong=S

iu=R

ng=S

u=B

ua=D

uai=C

uan=P

uang=T

ue=M

ve=M

ui=M

un=N

uo=O

对于vim编辑器,可以在/usr/local/share/vim/ 下新建一个 vimrc 文件,可以从vim63/vimrc_example 复制过来。

我经过修改,对 vimrc 文件加入以下代码,用于对vim进行个性化设置:

代码:

colorscheme desert ” 选择desert颜色风格

set shiftwidth=4 “ tab 键自动调整为4个空格

set tabstop=4

set expandtab ” 自动缩进

set backupdir=/usr/local/backup “ 默认备份文件目录

set guifont=SimSun 12  ” 设置默认字体为 simsun

从 ports/editor/vim 中,安装vim,使用以下命令可以支持GTK2。

代码:

cd /usr/ports/editors/vim

make -DWITH_GTK2 install

系统默认文件编码为utf-8, vim 显示中文会好些。

关于 FreeBSD,还有更多的使用技巧,以后再写吧。

bbs.chinaunix.net/forum/viewtopic.php?t=547355&show_type=

原文转自:www.ltesting.net

篇6:freeBSD 安装Unix系统

今天 重新安装了freeBSD 首先最小化安装 重起下载nforce2网卡驱动 再下载安装 w3m mp3blaster zhcon emacs wget 不到1G的空间 可以看网页 可以 可以听音乐 可以用emacs

足够了 如果能识别网卡就更棒了 package也不用下载 只要下载boot盘

系统安装完后的第一步,是进行CVSUP,立刻同步你的src源代码和ports代码,

freeBSD 安装Unix系统

关于cvsup的详细内容,请看FREEBSD手册的cvsup部分,此处我只介绍在本例中的过程。由于cvsup需要互联网环境,所以需要接好你的网线了。进入下列目录:

#cd /usr/src/share/examples/cvsup

原文转自:www.ltesting.net

篇7:freebsd 升级Unix系统

cobrawgl.blogchina.com/blog/article_54082.198431.html www.cnfug.org/journal/archives/000056.html 本文简要介绍了我 make world 的过程, 在 /usr/share/examples/cvsup/ 下 standard-supfile 是用来升级到 current 版本的. 不建议用来做

cobrawgl.blogchina.com/blog/article_54082.198431.html

www.cnfug.org/journal/archives/000056.html

本文简要介绍了我 make world 的过程。

在 /usr/share/examples/cvsup/ 下

standard-supfile 是用来升级到 current 版本的. 不建议用来做工作平台.

stable-supfile 是用来升级到 stable 版本的. 建议选用.

首先, 编辑了 /usr/share/examples/cvsup/stable-supfile

修改 *default host=CHANGE_THIS.FreeBSD.org

为 *default host=cvsup.FreeBSD.org

当然, 也可以选择别的 mirrors. 可是,主站的 src 是最新的啊, 呵呵.

然后, 修改 tag 选项.

# change “RELENG_4” to “RELENG_3” or “RELENG_2_2” respectively.

*default release=cvs tag=RELENG_4

这里, 因为我用的是 5.2.1, 所以改成

*default release=cvs tag=RELENG_5_2

cvsup ...

# cd /usr/src/

# cvsup -g -L 2 /usr/share/examples/cvsup/stable-supfile

build world

# pwd

/usr/src

# make buildworld

build kernel

# pwd

/usr/src

# make buildkernel KERNCONF=GENERIC

当然, 你也可以自己编辑一个内核配置文件来替换 GENERIC.

现在 make installkernel, make installworld

# pwd

/usr/src

# make installkernel KERNCONF=GENERIC

# mergemaster -cv

# make installworld

# shutdown -r now

mergemaster 的时候, 你根据提示做就可以了

“d” 是删除. “i”是安装. “m”是合并. 一般, 选 “i” 就可以了.

顺带着又 cvsup 了 ports, 使用的是 ports-supfile

方法和前面一样:

修改 *default host=CHANGE_THIS.FreeBSD.org

为 *default host=cvsup.FreeBSD.org

# cd /usr/ports/

# cvsup -g -L 2 /usr/share/examples/cvsup/ports-supfile

cvsup 完了后, 要记得

# portsdb -uU

++++++++++++++++++++++++++++++++++++++

关于 proxy user 的补充

Login: proxy

Password: *

Uid [#]: 62

Gid [# or name]: 62

Change [month day year]:

Expire [month day year]:

Class:

Home directory: /nonexistent

Shell: /usr/sbin/nologin

Full Name: Packet Filter pseudo-user

#makebuildworld

#makebuildkernel

#makeinstallkernel

# reboot

您应该启动到单用户模式下(例如从启动提示符处使用 boot -s),

然后执行:

# mergemaster -p

#makeinstallworld

# mergemaster

# reboot

原文转自:www.ltesting.net

篇8:FreeBSD 使用手册Unix系统

FreeBSD 使用手册

www.freebsd.org.cn/snap/doc/zh_CN.GB2312/books/handbook/

目前最为权威、内容最全的FreeBSD用户手册,适合各类学习freeBSD阶段的人,

欢迎加入freeBSD学习的行列当中来...............

在chinaunix.net上的手册中心中有更多的不同内容BSD的用户使用手册‘

chinaunix.net的手册中心

原文转自:www.ltesting.net

篇9:打造FreeBSD桌面系统

二、图像处理软件

FreeBSD下的图形图像处理软件比较丰富,比如图像处理工具GIMP、绘图工具XPaint、图像浏览工具电子眼ee,Compupic等等,这些工具都可从Ports中直接安装,

1.图像处理工具——GIMP

FreeBSD下的图像处理工具中,最著名的要数GIMP了。GIMP是GNU图像处理程序(GNU Image Manipulation Program)的缩写,它是一个完全免费的自由软件包,适用于对图像进行各种艺术处理。GIMP的功能非常强大,它可以作为一个简单的绘图程序来使用,也可以作为一个高质量的图像处理软件来使用,它还有图像格式转换等功能。GIMP具有高度可扩展能力,它支持带插件参数的高级脚本接口,对每项工作,无论是最简单的任务,还是复杂的图像处理过程,都可以很容易地用脚本来描述。由于其功能相当强大,因为GIMP被誉为FreeBSD下图像处理的法宝,是自由的PhotoShop。

GIMP的界面要比PhotoShop简洁得多,启动时只有一个看起来和PhotoShop很相似的工具栏。只要当我们打开图像文件并用鼠标右键单击图像时才会弹出一系列的菜单选项,如图14所示。

图14

GIMP主要有如下特点:

● 全套的绘图工具,包括笔刷、铅笔、喷枪等。

●平板式的内存管理,能够处理的图像尺寸大小只受磁盘自由空间的限制。

● 支持各种常见的图形格式,包括gif、jpg、jpg、xpm、tiff、tga、mpeg、ps、pdf、pcx、bmp等,并能进行各种图形格式的相互转换。

● 过程数据库允许从外部程序调用内部的GIMP函数。

● 无限次的Undo/Redo(仅受磁盘空间的限制)。

● 支持旋转、缩放、裁剪及反转等变形工具。

● 具有多种选择工具,如矩形、椭圆、自由、模糊、曲线及智能。

● 插件功能允许用户任意增加文件格式及新的效果滤镜,并且网上有超过上百个免费的插件可供下载,这样一来就可以无限扩展GIMP的功能。

另外,GIMP、对Web图片设计也有不错的支持,同时还提供了一个抓图工具——屏幕快照,效果非常不错,本文所有图片均由该工具截取。有关更多GIMP的功能介绍及使用方法,请参考由机械工业出版社发行的《GIMP权威指南》一书。

2.绘图工具——XPaint

XPaint是X11自带的一个彩色图像编辑工具,其功能与Windows中的“画笔”程序相似,支持编辑各种图像格式,包括PPM、XBM、BMP、jpg、TIFF等。XPaint的用户界面从功能上被分成两个区域,一个是工具区,用于选择当前绘画要进行的操作;另一个是绘图区,用于创建/修改图像。每个绘图窗口允许拥有自己的调色板和模式集,如图15所示。

图15

XPaint对显示模式有所依赖,在存储图像的时候将会把图像调整为当前的显示模式(例如,在16位色环境中打开一幅32位真彩色的图像,重新存盘时会存储为16位色)。

3.图像浏览工具

与Windows一样,FreeBSD下的图像浏览工具也非常丰富,下面介绍几款比较常用的软件。

(1)GNOME之眼

“GNOME之眼”是一个在GNOME窗口环境下浏览图像文件的软件,它是随GNOME软件包一起分发的,

当正确安装GNOME后,在GNOME的主菜单中就可以找到“GNOME”快捷方式。“GNOME之眼”可以浏览bmp、tif、gif、xpm等格式的图像文件,使用方法非常简单。用户可以选择一次装入多个文件,并可预图图像。“GNOME之眼”也支持对图像进行一些基本的编辑操作,比如色彩控制、放大、缩小和旋转等,软件主界面如图16所示。

图 16

(2)GQview

GQview也是一个在GNOME窗口环境下浏览图像的软件。其功能和使用方法与电子眼相似,只是更直观一些。GQview的用户界面左边是菜单条、工具栏、目录列表及图像文件列表,右边是图像浏览区,在图像文件列表中选择文件后,就可以在右边区域进行浏览了,这点倒和ACDSee相同,如图17所示。

图17

GQview自身的图像编辑功能相对较弱,但它支持调用外部的图像处理软件,除了GIMP、电子眼等软件外,用户还可根据需要让GQview与其它图像编辑软件相关联。

三、网络工具软件

从FreeBSD诞生的那一天起,就注定了它的网络功能空前强大,使用FreeBSD的用户会发现在FreeBSD环境下上网效率会比Windows高很多,而且其安全性更是Windows所不能比拟的。FreeBSD环境下的网络工具数不胜数,下面介绍几款常用的网络工具。

1.多功能即时通讯软件——Gaim

采用开放系统架构设计的Gaim(GNU AIM)最初是作为AOL的即时通讯软件AIM的客户端进行设计的,它可以同时加载多个插件形式的即时通讯软件(Gaim本身并不具备通讯功能,而是通过插件来实现的)——不仅操作方法统一规范,而且还具有系统资源消耗小的优点,让“聊天狂人”们再不用手忙脚乱地在各个软件之间切来换去了,软件主界面如图18所示。

图18

Gaim的安装版本中附带有很多流行的通讯插件,比如Yahoo通、MSN Messenger、ICQ、AIM、IRC等等。另人遗憾的是Gaim的安装版本中没有附带国内流行的QQ插件。其实Gaim的QQ插件其实早已问世,但是受到腾讯公司的打压,最新版本的QQ插件也只能支持到Gaim 0.64(目前最新版本为0.75),并且该插件只有for Linux版本,还没有公开源代码,所以即使要在FreeBSD上运行QQ For Gaim,也只能安装老版本的Gaim,而且还要使用Linux兼容模式运行,操作复杂且效率低下。如果一定要在FreeBSD上使用QQ,笔者推荐使用Perl OICQ,这是一个运行在命令行下字符界面的QQ,虽然界面简陋一些,但基本能够满足与QQ用户相互通讯的要求,关于该软件的更详细介绍请浏览perl-oicq.sourceforge.net/。

Gaim的Port位于/usr/ports/net/gaim目录中,Perl OICQ的Port位于/usr/ports/chinese/oicq目录中。

2.GNU的FTP客户端工具——gFTP

gFTP是X Window下一个用Gtk开发的多线程FTP客户端工具。它与MS Windows环境中的CuteFTP等FTP工具极为类似,目前gFTP的最新版本是2.0.15支持并行下载、断点续传、传输任务队列、全目录下载等功能,其主界面如图19所示。

图19

该Port位于/usr/ports/ftp/gftp目录中。如果想安装其它网络工具软件,可以到/usr/ports/net目录中查找。

[转载 命令]FreeBSD 文件和目录查找Unix系统

FreeBSD几个软件的实现及遇到的一些问题(二)Unix系统

FreeBSD Ports 方式安装MySQL及注意事项linux操作系统

多内核CPU的性能测试Unix系统

Linux系统中配置构建Postfix文档linux操作系统

探讨华为交换机配置手册中有关以太信道的重要性

samba服务器的文件共享配置Unix系统

Linux系统下配置功能完善的Web服务器服务器教程

植物配置设计说明范文

各种新员工手册

FreeBSD手册――配置FreeBSD内核Unix系统(精选9篇)

欢迎下载DOC格式的FreeBSD手册――配置FreeBSD内核Unix系统,但愿能给您带来参考作用!
推荐度: 推荐 推荐 推荐 推荐 推荐
点击下载文档 文档为doc格式
点击下载本文文档