(转)解决ARP攻击的方法和原理

(转)解决ARP攻击的方法和原理

【故障原因】

  局域网内有人使用ARP欺骗的木马程序(比如:传奇盗号的软件,某些传奇外挂中也被恶意加载了此程序)。

【故障原理】

  要了解故障原理,我们先来了解一下ARP协议。

  在局域网中,通过ARP协议来完成IP地址转换为第二层物理地址(即MAC地址)的。ARP协议对网络安全具有重要的意义。通过伪造IP地址和MAC地址实现ARP欺骗,能够在网络中产生大量的ARP通信量使网络阻塞。

  ARP协议是“Address Resolution Protocol”(地址解析协议)的缩写。在局域网中,网络中实际传输的是“帧”,帧里面是有目标主机的MAC地址的。在以太网中,一个主机要和另一个主机进行直接通信,必须要知道目标主机的MAC地址。但这个目标MAC地址是如何获得的呢?它就是通过地址解析协议获得的。所谓“地址解析”就是主机在发送帧前将目标IP地址转换成目标MAC地址的过程。ARP协议的基本功能就是通过目标设备的IP地址,查询目标设备的MAC地址,以保证通信的顺利进行。

  每台安装有TCP/IP协议的电脑里都有一个ARP缓存表,表里的IP地址与MAC地址是一一对应的,如下所示。

主机             IP地址              MAC地址
A             192.168.16.1    aa-aa-aa-aa-aa-aa
B             192.168.16.2    bb-bb-bb-bb-bb-bb
C             192.168.16.3    cc-cc-cc-cc-cc-cc
D             192.168.16.4    dd-dd-dd-dd-dd-dd

  我们以主机A(192.168.16.1)向主机B(192.168.16.2)发送数据为例。当发送数据时,主机A会在自己的ARP缓存表中寻找是否有目标IP地址。如果找到了,也就知道了目标MAC地址,直接把目标MAC地址写入帧里面发送就可以了;如果在ARP缓存表中没有找到相对应的IP地址,主机A就会在网络上发送一个广播,目标MAC地址是“FF.FF.FF.FF.FF.FF”,这表示向同一网段内的所有主机发出这样的询问:“192.168.16.2的MAC地址是什么?”网络上其他主机并不响应ARP询问,只有主机B接收到这个帧时,才向主机A做出这样的回应:“192.168.16.2的MAC地址是bb-bb-bb-bb-bb-bb”。这样,主机A就知道了主机B的MAC地址,它就可以向主机B发送信息了。同时它还更新了自己的ARP缓存表,下次再向主机B发送信息时,直接从ARP缓存表里查找就可以了。ARP缓存表采用了老化机制,在一段时间内如果表中的某一行没有使用,就会被删除,这样可以大大减少ARP缓存表的长度,加快查询速度。

  从上面可以看出,ARP协议的基础就是信任局域网内所有的人,那么就很容易实现在以太网上的ARP欺骗。对目标A进行欺骗,A去Ping主机C却发送到了DD-DD-DD-DD-DD-DD这个地址上。如果进行欺骗的时候,把C的MAC地址骗为DD-DD-DD-DD-DD-DD,于是A发送到C上的数据包都变成发送给D的了。这不正好是D能够接收到A发送的数据包了么,嗅探成功。

  A对这个变化一点都没有意识到,但是接下来的事情就让A产生了怀疑。因为A和C连接不上了。D对接收到A发送给C的数据包可没有转交给C。

  做“man in the middle”,进行ARP重定向。打开D的IP转发功能,A发送过来的数据包,转发给C,好比一个路由器一样。不过,假如D发送ICMP重定向的话就中断了整个计划。

  D直接进行整个包的修改转发,捕获到A发送给C的数据包,全部进行修改后再转发给C,而C接收到的数据包完全认为是从A发送来的。不过,C发送的数据包又直接传递给A,倘若再次进行对C的ARP欺骗。现在D就完全成为A与C的中间桥梁了,对于A和C之间的通讯就可以了如指掌了。

【故障现象】

  当局域网内某台主机运行ARP欺骗的木马程序时,会欺骗局域网内所有主机和路由器,让所有上网的流量必须经过病毒主机。其他用户原来直接通过路由器上网现在转由通过病毒主机上网,切换的时候用户会断一次线。

  切换到病毒主机上网后,如果用户已经登陆了传奇服务器,那么病毒主机就会经常伪造断线的假像,那么用户就得重新登录传奇服务器,这样病毒主机就可以盗号了。

  由于ARP欺骗的木马程序发作的时候会发出大量的数据包导致局域网通讯拥塞以及其自身处理能力的限制,用户会感觉上网速度越来越慢。当ARP欺骗的木马程序停止运行时,用户会恢复从路由器上网,切换过程中用户会再断一次线。

【HiPER用户快速发现ARP欺骗木马】

  在路由器的“系统历史记录”中看到大量如下的信息(440以后的路由器软件版本中才有此提示):

  MAC Chged 10.128.103.124
   MAC Old 00:01:6c:36:d1:7f
   MAC New 00:05:5d:60:c7:18

  这个消息代表了用户的MAC地址发生了变化,在ARP欺骗木马开始运行的时候,局域网所有主机的MAC地址更新为病毒主机的MAC地址(即所有信息的MAC New地址都一致为病毒主机的MAC地址),同时在路由器的“用户统计”中看到所有用户的MAC地址信息都一样。
   如果是在路由器的“系统历史记录”中看到大量MAC Old地址都一致,则说明局域网内曾经出现过ARP欺骗(ARP欺骗的木马程序停止运行时,主机在路由器上恢复其真实的MAC地址)。

【在局域网内查找病毒主机】

  在上面我们已经知道了使用ARP欺骗木马的主机的MAC地址,那么我们就可以使用NBTSCAN(下载地址:http://www.utt.com.cn/upload/nbtscan.rar)工具来快速查找它。

  NBTSCAN可以取到PC的真实IP地址和MAC地址,如果有”传奇木马”在做怪,可以找到装有木马的PC的IP/和MAC地址。

  命令:“nbtscan -r 192.168.16.0/24”(搜索整个192.168.16.0/24网段, 即192.168.16.1-192.168.16.254);或“nbtscan 192.168.16.25-137”搜索192.168.16.25-137 网段,即192.168.16.25-192.168.16.137。输出结果第一列是IP地址,最后一列是MAC地址。

  NBTSCAN的使用范例:

  假设查找一台MAC地址为“000d870d585f”的病毒主机。

  1)将压缩包中的nbtscan.exe 和cygwin1.dll解压缩放到c:下。

  2)在Windows开始—运行—打开,输入cmd(windows98输入“command”),在出现的DOS窗口中输入:C: btscan -r 192.168.16.1/24(这里需要根据用户实际网段输入),回车。

  3)通过查询IP–MAC对应表,查出“000d870d585f”的病毒主机的IP地址为“192.168.16.223”。

【解决思路】

  1、不要把你的网络安全信任关系建立在IP基础上或MAC基础上,(rarp同样存在欺骗的问题),理想的关系应该建立在IP+MAC基础上。

  2、设置静态的MAC–>IP对应表,不要让主机刷新你设定好的转换表。

  3、除非很有必要,否则停止使用ARP,将ARP做为永久条目保存在对应表中。

  4、使用ARP服务器。通过该服务器查找自己的ARP转换表来响应其他机器的ARP广播。确保这台ARP服务器不被黑。

  5、使用"proxy"代理IP的传输。

  6、使用硬件屏蔽主机。设置好你的路由,确保IP地址能到达合法的路径。(静态配置路由ARP条目),注意,使用交换集线器和网桥无法阻止ARP欺骗。

  7、管理员定期用响应的IP包中获得一个rarp请求,然后检查ARP响应的真实性。

  8、管理员定期轮询,检查主机上的ARP缓存。

  9、使用防火墙连续监控网络。注意有使用SNMP的情况下,ARP的欺骗有可能导致陷阱包丢失。

【HiPER用户的解决方案】

  建议用户采用双向绑定的方法解决并且防止ARP欺骗。

  1、在PC上绑定路由器的IP和MAC地址:

  1)首先,获得路由器的内网的MAC地址(例如HiPER网关地址192.168.16.254的MAC地址为0022aa0022aa)。

  2)编写一个批处理文件rarp.bat内容如下:

  @echo off
   arp -d
   arp -s 192.168.16.254 00-22-aa-00-22-aa

  将文件中的网关IP地址和MAC地址更改为您自己的网关IP地址和MAC地址即可。

  将这个批处理软件拖到“windows–开始–程序–启动”中。

  3)如果是网吧,可以利用收费软件服务端程序(pubwin或者万象都可以)发送批处理文件rarp.bat到所有客户机的启动目录。Windows2000的默认启动目录为“C:\Documents and SettingsAll Users「开始」菜单程序启动”。

  2、在路由器上绑定用户主机的IP和MAC地址(440以后的路由器软件版本支持):

  在HiPER管理界面–高级配置–用户管理中将局域网每台主机均作绑定。

 

另一篇:

对付ARP欺骗现在使用最多就是双向IP_MAC绑定

具体操作是在所有电脑上用批处理绑定或间隔一定时间自动绑定路由的MAC, 假如路由IP为192.168.0.1 MAC为00-AA-BB-CC-DD-EE, 则在每个电脑上运行arp -s 192.168.0.1 00-AA-BB-CC-DD-EE
然后再在路由器上绑定所有电脑的IP-MAC

以上方法可以防止ARP欺骗, 游戏帐号的就不会被盗, (在有些操作系统中这样操作也还会被欺骗)
另外你如果要防止内部游戏服务器、电影服务器以及收银机能不被ARP欺骗,则你还要把这些机器的IP-MAC与所有电脑以及路由器绑定。

但是这样一样不能阻止ARP攻击的发生,
有些软件通过限制某些文件安装的方式来限制ARP攻击软件的安装, 实际上一方面你限制的范围很有限, 另外一个方面病毒软件完全可以破你这个限制, 病毒可以把你的限制去掉, 还可以简单的修改调用的dll的文件名, 你就没戏了,  所以说这个方法就好比小孩子办家家.

由于ARP攻击启动时发送大量ARP数据包, 所有的交换机下的网络设备无论是否在相同网断都会收到这些数据报, 并且都会对这些数据报作出响应, 发送ARP回应数据报, 就好比一个电脑在调用所有电脑同时互相攻击, 而局域网数据通讯对带宽的利用是有多少用多少, 没有限制, 这样, 网络中就不断爆发ARP数据流量, 你的网络肯定会瘫痪,  也就是说ARP攻击一启动, 即使你的电脑不被欺骗, 也一样会掉线, 因为ARP流量堵塞了整个网络, 你的网络就瘫痪了
所以说绑定没用

vim 中的backspace问题

1. 经常遇到vim中backspace不能删除字符的问题,下面是解决办法:

在vimrc中添加如下内容即可:

se nocompatible
se bs=2

具体含义,参看
:help
compatible

vi中就不要这么弄了,不支持

2. vim中方向键不好使的解决办法

set t_ku=^[OA
set t_kd=^[OB
set t_kr=^[OC
set t_kl=^[OD

不过可能和term有关系,set term=xterm 试试

国际风云

美国从来没有担心过伊朗的军力, 中俄也从来没有寄希望过伊朗的军力. 美国的军事力量在伊朗能否成功, 只与中俄有关, 与伊朗无关.


伊朗只是棋子, 这个世界在较量的, 就是美国(+欧盟) <—– >中+俄. 这个角力是全方位的, 从经济文化政治到军事. 美国要取得更高的战略地位对付中俄, 中俄要打乱美国的战略步骤(美攻,中俄守). 除了欧盟自己都搞不清自己的目标是什么外,美俄中的目标是一致的:想尽办法削弱敌人的实力, 直至掐死敌人. 谁也不会让对方舒服!
  
  一切政治都是可以谈判和交易的, 就看以实力为后盾的价钱是多少. 什么gou pi "民主自由人权", 都是幌子而已. 自越战后, 美国确实是好好钻研过孙子兵法的, 不像以前那么鲁莽了.

在 Vim 中使用 Tab 键自动完成 PHP 函数

说一下如何使用 Tab 键自动完成,按照习惯,Tab 键通常是用来缩进行的,如输入 4 个空格如果要把 Tab 映射为自动完成,可能使用上稍微有点麻烦,下面会讲如何解决这个问题。

首先需要为 Vim 提供一个 PHP 的函数列表文件,这样 Vim 才能知道如何自动完成一个 PHP 函数。函数列表不需要自己做,PHP 网站上有现成的,拿来用就可以了:

http://cvs.php.net/viewvc.cgi/phpdoc/funclist.txt

得到这个文件,直接拷贝到 Vim 目录中,比如根目录下面,然后在 vimrc 中加入如下代码,告知 vim 在自动完成时,需要扫描这个文件来分析关键字。

"You can obtain the completion dictionary file from:

" http://cvs.php.net/viewvc.cgi/phpdoc/funclist.txt

set dictionary-=$VIM/funclist.txt dictionary+=$VIM/funclist.txt

"Use the dictionary completion

set complete-=k complete+=k

那么如何解决缩进的问题呢?我们肯定不愿意在映射另外一个键来做缩进用,感觉会很别扭。下面的函数会解决这个问题。

"Auto completion using the TAB key

"This function determines, wether we are on

"the start of the line text(then tab indents)

"or if we want to try auto completion

function! InsertTabWrapper()

let col=col(‘.’)-1

if !col || getline(‘.’)[col1] !~ ‘\k’

return "\<TAB>"

else

return "\<C-N>"

endif

endfunction

"Remap the tab key to select action with InsertTabWrapper

inoremap <TAB> <C-R>=InsertTabWrapper()<CR>

 

这个函数的作用是判断当前光标前的一个字符是否为一个 Keyword 字符(:help iskeyword),vim 中用 \k 表示,这个字符的范围大约是所有非空白的可打印的字符,但除开某些特殊字符,比如 @ * " 等等。

 

于是效果就出来了,当前导字符为空白或者 @ * 等字符时,Tab 就映射为 成为缩进键。否则就映射为 ,也就是 CTRL+N 调用自动完成功能。

VIM显示文件中的函数列表

首先安装taglist
1.到
http://www.vim.org/scripts/script.php?script_id=273下载taglist
2.把解压出来的plugin里面的文件,放到D:\Program Files\Vim\vimfiles\plugin里面
3.在 Vim 中运行 :helptags D:\Program Files\Vim\vimfiles\doc 安装文档
4.输入Tlist 即可启动函数列表功能
注意:如果启动VIM时跳出「Taglist: Exuberant ctags (
http://ctags.sf.net) not found in PATH. Plugin is not loaded.」,则需要下载ec57w32.zip (Exuberant Ctags),將其中的ctags.exe放到VIM安装时的根目录D:\Program Files\Vim\Vim70。

下面是在Tlist窗口下的按键功能介绍
Taglist window key list~
The following table lists the description of the keys that can be used
in the taglist window.

Key Description~

<CR> Jump to the location where the tag under cursor is
defined.
o Jump to the location where the tag under cursor is
defined in a new window.
P Jump to the tag in the previous (Ctrl-W_p) window.
p Display the tag definition in the file window and
keep the cursor in the taglist window itself.
t Jump to the tag in a new tab. If the file is already
opened in a tab, move to that tab.
Ctrl-t Jump to the tag in a new tab.
<Space> Display the prototype of the tag under the cursor.
For file names, display the full path to the file,
file type and the number of tags. For tag types, display the
tag type and the number of tags.
u Update the tags listed in the taglist window
s Change the sort order of the tags (by name or by order)
d Remove the tags for the file under the cursor
x Zoom-in or Zoom-out the taglist window
+ Open a fold
– Close a fold
* Open all folds
= Close all folds
[[ Jump to the beginning of the previous file
<Backspace> Jump to the beginning of the previous file
]] Jump to the beginning of the next file
<Tab> Jump to the beginning of the next file
q Close the taglist window
<F1> Display help

top 命令详解

1.作用

top命令用来显示执行中的程序进程,使用权限是所有用户。

 

2.格式

top [-] [d delay] [q] [c] [S] [s] [n]

 

3.主要参数

d:指定更新的间隔,以秒计算。

q:没有任何延迟的更新。如果使用者有超级用户,则top命令将会以最高的优先序执行。

c:显示进程完整的路径与名称。

S:累积模式,会将己完成或消失的子行程的CPU时间累积起来。

s:安全模式。

i:不显示任何闲置(Idle)或无用(Zombie)的行程。

n:显示更新的次数,完成后将会退出top。

 

4.说明

top命令是Linux系统管理的一个主要命令,通过它可以获得许多信息。这里我们结合图1来说明它给出的信息。

 

top命令的显示 (图略)

 

第一行表示的项目依次为当前时间、系统运行时间、当前系统登录用户数目、1/5/10分钟系统平均负载(一般来说,这个负载值应该不太可能超过 1 才对,除非您的系统很忙碌。 如果持续高于 5 的话,那么…..仔细的看看到底是那个程序在影响整体系统吧!)。

 

第二行显示的是所有启动的进程、目前运行、挂起 (Sleeping)的和无用(Zombie)的进程。(比较需要注意的是最后的 zombie 那个数值,如果不是 0 ,嘿嘿!好好看看到底是那个 process 变成疆尸了吧?!)(stop模式:与sleep进程应区别,sleep会主动放弃cpu,而stop是被动放弃cpu ,例单步跟踪,stop(暂停)的进程是无法自己回到运行状态的)

 

第三行显示的是目前CPU的使用情况,包括us用户空间占用CPU百分比、sy 内核空间占用CPU百分比、ni 用户进程空间内改变过优先级的进程占用CPU百分比(中断处理占用)、id 空闲CPU百分比、wa 等待输入输出的CPU时间百分比、hi,si,st 三者的意思目录还不清楚 :)

 

第四行显示物理内存的使用情况,包括总的可以使用的内存、已用内存、空闲内存、缓冲区占用的内存。

 

第五行显示交换分区使用情况,包括总的交换分区、使用的、空闲的和用于高速缓存的大小。

 

第六行显示的项目最多,下面列出了详细解释。

PID(Process ID):进程标示号 ( 每个 process 的 ID )

USER:进程所有者的用户名 ( 该 process 所属的使用者 )

PR:进程的优先级别 ( Priority 的简写,程序的优先执行顺序,越小越早被执行 )

NI:进程的优先级别数值 ( Nice 的简写,与 Priority 有关,也是越小越早被执行 )

VIRT:进程占用的虚拟内存值。

RES:进程占用的物理内存值。

SHR:进程使用的共享内存值。

S:进程的状态,其中S表示休眠,R表示正在运行,Z表示僵死状态,N表示该进程优先值是负数。

%CPU:该进程占用的CPU使用率。

%MEM:该进程占用的物理内存和总内存的百分比。

TIME+:该进程启动后占用的总的CPU时间 ( CPU 使用时间的累加 )

Command:进程启动的启动命令名称,如果这一行显示不下,进程会有一个完整的命令行。

 

top命令使用过程中,还可以使用一些交互的命令来完成其它参数的功能。这些命令是通过快捷键启动的。

<空格>:立刻刷新。

P:根据CPU使用大小进行排序。

T:根据时间、累计时间排序。

q:退出top命令。

m:切换显示内存信息。

t:切换显示进程和CPU状态信息。

c:切换显示命令名称和完整命令行。

M:根据使用内存大小进行排序。

W:将当前设置写入~/.toprc文件中。这是写top配置文件的推荐方法。

 

o 快捷键可以调整各字段的位置,按o快捷键显示如下:


Current Fields: AEHIOQTWKNMbcdfgpjlrsuvyzX for window 1:Def
Upper case letter moves field left, lower case right
* A: PID = Process Id 0x00000400 PF_SIGNALED
* E: USER = User Name 0x00000800 PF_MEMALLOC
* H: PR = Priority 0x00002000 PF_FREE_PAGES (2.5)
* I: NI = Nice value 0x00008000 debug flag (2.5)
* O: VIRT = Virtual Image (kb) 0x00024000 special threads (2.5)
* Q: RES = Resident size (kb) 0x001D0000 special states (2.5)
* T: SHR = Shared Mem size (kb) 0x00100000 PF_USEDFPU (thru 2.4)
* W: S = Process Status
* K: %CPU = CPU usage
* N: %MEM = Memory usage (RES)
* M: TIME+ = CPU Time, hundredths
b: PPID = Parent Process Pid
c: RUSER = Real user name
d: UID = User Id
f: GROUP = Group Name
g: TTY = Controlling Tty
p: SWAP = Swapped size (kb)
j: #C = Last used cpu (SMP)
l: TIME = CPU Time
r: CODE = Code size (kb)
s: DATA = Data+Stack size (kb)
u: nFLT = Page Fault count
v: nDRT = Dirty Pages count
y: WCHAN = Sleeping in Function
z: Flags = Task Flags <sched.h>
* X: COMMAND = Command name/line

 

其中,AEHIOQTWKNMbcdfgpjlrsuvyzX 表示当前显示的可以调整的列,也就是 × 标识的列,每列都对应一个字母,现在,按‘E’ ,USER列就先下移,按‘e’ USER列就像下移,以此类推。

f 快捷键调整是否显示某列,按 f 键显示如下:


Current Fields: AEHIOQTWKNMbcdfgpjlrsuvyzX for window 1:Def
Toggle fields via field letter, type any other key to return
* A: PID = Process Id 0x00000400 PF_SIGNALED
* E: USER = User Name 0x00000800 PF_MEMALLOC
* H: PR = Priority 0x00002000 PF_FREE_PAGES (2.5)
* I: NI = Nice value 0x00008000 debug flag (2.5)
* O: VIRT = Virtual Image (kb) 0x00024000 special threads (2.5)
* Q: RES = Resident size (kb) 0x001D0000 special states (2.5)
* T: SHR = Shared Mem size (kb) 0x00100000 PF_USEDFPU (thru 2.4)
* W: S = Process Status
* K: %CPU = CPU usage
* N: %MEM = Memory usage (RES)
* M: TIME+ = CPU Time, hundredths
b: PPID = Parent Process Pid
c: RUSER = Real user name
d: UID = User Id
f: GROUP = Group Name
g: TTY = Controlling Tty
p: SWAP = Swapped size (kb)
j: #C = Last used cpu (SMP)
l: TIME = CPU Time
r: CODE = Code size (kb)
s: DATA = Data+Stack size (kb)
u: nFLT = Page Fault count
v: nDRT = Dirty Pages count
y: WCHAN = Sleeping in Function
z: Flags = Task Flags <sched.h>
* X: COMMAND = Command name/line

Flags field:
0x00000001 PF_ALIGNWARN
0x00000002 PF_STARTING
0x00000004 PF_EXITING
0x00000040 PF_FORKNOEXEC
0x00000100 PF_SUPERPRIV
0x00000200 PF_DUMPCORE

其中,AEHIOQTWKNMbcdfgpjlrsuvyzX 表示当前显示的可以调整的列,也就是 × 标识的列,每列都对应一个字母,现在,按对应列的字母,即可调整是否显示对应列,大写字母(×)标识为显示,小写(无×)标识为不显示

k 快捷键,杀死指定进程,按k,提示输入pid
        r 快捷键,调整某进程的nice值,按r,提示输入pid

F/O 指定排序列 ,用法类似f

可以看到,top命令是一个功能十分强大的监控系统的工具,对于系统管理员而言尤其重要。但是,它的缺点是会消耗很多系统资源。

Linux 系统中的buffer 与cache

一、Buffer cache简介
1、名称:buffer cache,又称bcache,其中文名称为缓冲器高速缓冲存储器,简称缓冲器高缓。另外,buffer cache按照其工作原理,又被称为块高缓。

  2、功能:在linux读写文件时,它用于缓存物理磁盘上的磁盘块,从而加快对磁盘上数据的访问。

3、大小:buffer cache的内容对应磁盘上一个块(block),块通常为1K,都是连续的。
       在linux下,为了更有效的使用物理内存,操作系统自动使用所有空闲内存作为Buffer Cache使用。当程序需要更多内存时,操作系统会自动减小Cache的大小
                       如果cache占用的内存过多了,影响正常运行程序需要的内存,那么会释放掉一部分cache内存,但是总量会保持一个很高的值,所以,linux总是能最大限度的使用内存,就算加到16G,32G内存,也会随着不断的IO操作,内存的free值会慢慢减少到只有几M,想要内存不发生这种情况,只有一个办法:把内存加到比硬盘大。

       在linux下,可通过命令cat /proc/meminfo和free -m查看buffer cache的内存使用情况。

二、Buffer cache的功能详解
在从外存的一页到内存的一页的映射过程中,page cache与buffer cache、swap cache共同实现了高速缓存功能,以下是其简单映射图,

外存的一页(分解为几块,可能不连续)
|
|
物理磁盘的磁盘块
|
|
内存的buffer Cache
|
|
内存的一页(由一个页框划分的几个连续buffer cache构成)
|
|
页高缓系统

在这个过程中,物理文件系统与Buffer Cache交互,负责在外围存储设备和Buffer Cache 之间交换数据。
由于bcache位于物理文件系统和块设备驱动程序之间,因此,当物理文件系统需要从块设备上读取数据时,它首先试图从bcache中去读。如果命中,则内核就不必在去访问慢速的块设备。否则如果命中失败,也即数据不在bcache中,则内核从块设备上读取相应的数据块,并将其在bcache中缓存起来,以备下次访问之用。
类似地,但物理文件系统需要向块设备上写数据时,也是先将数据写到相应的缓冲区中,并将这个缓冲区标记为脏(dirty),然后在将来的某些时候将buffer cache中的数据真正地回写到块设备上,或者将该缓冲区直接丢弃。从而实现减少磁盘写操作的频率

三、Buffer Cache的数据结构
  1、缓冲区头部对象buffer_head
  每一个缓冲区都有一个缓冲区头部来唯一地标识与描述该缓冲区。Linux通过数据结构buffer_head来定义缓冲区头部。如下所示(include/linux/fs.h)

struct buffer_head {
/* First cache line: */
struct buffer_head *b_next; /* Hash queue list */
unsigned long b_blocknr; /* block number */
unsigned short b_size; /* block size */
unsigned short b_list; /* List that this buffer appears */
kdev_t b_dev; /* device (B_FREE = free) */

atomic_t b_count; /* users using this block */
kdev_t b_rdev; /* Real device */
unsigned long b_state; /* buffer state bitmap (see above) */
unsigned long b_flushtime; /* Time when (dirty) buffer should be written */

struct buffer_head *b_next_free;/* lru/free list linkage */
struct buffer_head *b_prev_free;/* doubly linked list of buffers */
struct buffer_head *b_this_page;/* circular list of buffers in one page */
struct buffer_head *b_reqnext; /* request queue */

struct buffer_head **b_pprev; /* doubly linked list of hash-queue */
char * b_data; /* pointer to data block (512 byte) */
struct page *b_page; /* the page this bh is mapped to */
void (*b_end_io)(struct buffer_head *bh, int uptodate); /* I/O completion */
void *b_private; /* reserved for b_end_io */

unsigned long b_rsector; /* Real buffer location on disk */
wait_queue_head_t b_wait;

struct inode * b_inode;
struct list_head b_inode_buffers; /* doubly linked list of inode dirty buffers */
};
各字段的含义如下:
1b_next指针:指向哈希链表中的下一个buffer_head对象。
2.b_blocknr:本缓冲区对应的块号(block number)。
3.b_size:以字节计掉的块长度。合法值为:512、1024、2048、4096、8192、16384和32768。
4.b_list:记录这个缓冲区应该出现在哪个链表上。
5.d_dev:缓冲区对应的块所在的块设备标识符(对于位于free_list链表中的缓冲区,b_dev=B_FREE)。
6.b_count:本缓冲区的引用计数。
7.b_rdev:缓冲区对应的块所在的块设备的「真实」标识符。
8.b_state:缓冲区的状态,共有6种:
/* bh state bits */
#define BH_Uptodate 0 /* 1 if the buffer contains valid data */
#define BH_Dirty 1 /* 1 if the buffer is dirty */
#define BH_Lock 2 /* 1 if the buffer is locked */
#define BH_Req 3 /* 0 if the buffer has been invalidated */
#define BH_Mapped 4 /* 1 if the buffer has a disk mapping */
#define BH_New 5 /* 1 if the buffer is new and not yet written out */
#define BH_Protected 6 /* 1 if the buffer is protected */
9.b_flushtime:脏缓冲区必须被回写到磁盘的最后期限值。
10.b_next_free指针:指向lru/free/unused链表中的下一个缓冲区头部对象。
11b_prev_free指针:指向lru/free/unused链表中的前一个缓冲区头部对象。
12b_this_page指针:指向同属一个物理页帧的下一个缓冲区的相应缓冲区头部对象。同属一个物理页帧的所有缓冲区通过这个指针成员链接成一个单向循环链表。
13b_reqnext指针:用于块设备驱动程序的请求链表。
14b_pprev:哈希链表的后向指针。
15b_data指针:指向缓冲区数据块的指针。
16b_page指针:指向缓冲区所在物理页帧的page结构。
17b_rsector:实际设备中原始扇区的个数。
18b_wait:等待这个缓冲区的等待队列。
19b_inode指针:如果缓冲区属于某个索引节点,则这个指针指向所属的inode对象。
20b_inode_buffers指针:如果缓冲区为脏,且又属于某个索引节点,那么就通过这个指针链入inode的i_dirty_buffers链表中。

缓冲区头部对象buffer_head可以被看作是缓冲区的描述符,因此,对bcache中的缓冲区的管理就集中在如何高效地组织处于各种状态下的buffer_head对象上。

  2、buffer_head对象的SLAB分配器缓存
  缓冲区头部对象buffer_head本身有一个叫做bh__cachep的slab分配器缓存。因此对buffer_head对象的分配与销毁都要通过kmem_cache_alloc()函数和kmem_cache_free()函数来进行。
  注意不要把bh_cachep SLAB分配器缓存和缓冲区本身相混淆。前者只是buffer_head对象所使用的内存高速缓存,并不与块设备打交道,而仅仅是一种有效管理buffer_head对象所占用内存的方式。后者则是块设备中的数据块所使用的内存高速缓存。但是这二者又是相互关联的,也即缓冲区缓存的实现是以bh_cachep SLAB分配器缓存为基础的。而我们这里所说的bcache机制包括缓冲区头部和缓冲区本身这两个方面的概念。
  bh_cachep定义在fs/dcache.c文件中,并在函数vfs_caches_init()中被初始化,也即通过调用kmem_cache_create()函数来创建bh_cachep这个SLAB分配器缓存。
  注:函数vfs_caches_init()的工作就是调用kmem_cache_create()函数来为VFS创建各种SLAB分配器缓存,包括:names_cachep、filp_cachep、dquot_cachep和bh_cachep等四个SLAB分配器缓存。

3、bcache中的缓冲区头部对象链表
  一个缓冲区头部对象buffer_head总是处于以下四种状态之一:
    1未使用(unused)状态:该对象是可用的,但是其b_data指针为NULL,也即这个缓冲区头部没有和一个缓冲区相关联。
    2空闲(free)状态:其b_data指针指向一个空闲状态下的缓冲区(也即该缓冲区没有具体对应块设备中哪个数据块);而b_dev域值为B_FREE(值为0xffff)。
    3正在使用(inuse)状态:其b_data指针指向一个有效的、正在使用中的缓冲区,而b_dev域则指明了相应的块设备标识符,b_blocknr域则指明了缓冲区所对应的块号。
    4异步(async)状态:其b_data域指向一个用来实现page I/O操作的临时缓冲区。
    
  为了有效地管理处于上述这些不同状态下的缓冲区头部对象,bcache机制采用了各种链表来组织这些对象(这一点,bcache机制与VFS的其它cache机制是相同的):
    1哈希链表:所有buffer_head对象都通过其b_next与b_pprev两个指针域链入哈希链表中,从而可以加快对buffer_head对象的查找(lookup)。
    2最近最少使用链表lru_list:每个处在inuse状态下的buffer_head对象都通过b_next_free和b_prev_free这两个指针链入某一个lru_list链表中。
    3空闲链表free_list:每一个处于free状态下的buffer_head对象都根据它所关联的空闲缓冲区的大小链入某个free_list链表中(也是通过b_next_free和b_prev_free这两个指针)。
    4未使用链表unused_list:所有处于unused状态下的buffer_head对象都通过指针域b_next_free和b_prev_free链入unused_list链表中。
    5inode对象的脏缓冲区链表i_dirty_buffers:如果一个脏缓冲区有相关联的inode对象的话,那么他就通过其b_inode_buffers指针域链入其所属的inode对象的i_dirty_buffers链表中。
    (更详细的介绍请见参考资料二)

四、buffer cache的回写
  有些是直接写(write-through):数据将被立刻写入磁盘,当然,数据也被放入缓存中。如果写操作是在以后做的,那么该缓存被称为后台写(write-back)。后台写比直接写更有效,但也容易出错:如果机器崩溃,或者突然掉电,缓冲中改变过的数据就被丢失了。如果仍未被写入的数据含有重要的薄记信息,这甚至可能意味着文件系统(如果有的话)已不完整。
  针对以上的原因,出现了很多的日志文件系统,数据在缓冲区修改后,同时会被文件系统记录修改信息,这样即使此时系统掉电,系统重启后会首先从日志记录中恢复数据,保证数据不丢失。当然这些问题不再本文的叙述范围。
  由于上述原因,在使用适当的关闭过程之前,绝对不要关掉电源,sync命令可以清空(flushes)缓冲,也即,强迫所有未被写的数据写入磁盘,可用以确定所有的写操作都已完成。在传统的 UNIX系统中,有一个叫做update(kupdate)的程序运行于后台,每隔30秒做一次sync操作,因此通常无需手工使用sync命令了。Linux另外有一个后台程序,bdflush,这个程序执行更频繁的但不是全面的同步操作,以避免有时sync的大量磁盘I/O操作所带来的磁盘的突然冻结。

五、Buffer Cache和Page Cache及其它
  page不会同时存在于buffer cache和page cache。add_page_to_hash_queue将此思想显露无余。buffer_head 定义在fs.h,和文件系统有着更为紧密的关系。从文件读写角度看buffer cache缓存文件系统的管理信息像root entry, inode等,而page cache缓存文件的内容。
  注意函数block_read_full_page,虽然位于buffer.c,但并没有使用buffer cache. 但是确实使用了buffer:只是再指定page上创建buffer提交底层驱动读取文件内容.这个流程有两个值得注意的地方:
  一是普通file的read通过page cache进行
  二是page cache读取的时候不和buffer cache进行同步
  三是page cache的确使用了buffer,不过注意,buffer 不是buffer cache。

  2.4的改进:page cache和buffer cache耦合得更好了。在2.2里,磁盘文件的读使用page cache,而写绕过page cache,直接使用buffer cache,因此带来了同步的问题:写完之后必须使用update_vm_cache()更新可能有的page cache。2.4中page cache做了比较大的改进,文件可以通过page cache直接写了,page cache优先使用high memory。而且,2.4引入了新的对象:file address space,它包含用来读写一整页数据的方法。这些方法考虑到了inode的更新、page cache处理和临时buffer的使用。page cache和buffer cache的同步问题就消除了。原来使用inode+offset查找page cache变成通过file address space+offset;原来struct page 中的inode成员被address_space类型的mapping成员取代。这个改进还使得匿名内存的共享成为可能(这个在2.2很难实现,许多讨论过)。

free 命令详解

  free 命令相对于top 提供了更简洁的查看系统内存使用情况:

  $ free

  # free
   total used free shared buffers cached
  Mem: 255988 231704 24284 0 6432 139292
  -/+ buffers/cache: 85980 170008
  Swap: 746980 0 746980

   可用选项:
     -b 以Byte为单位显示
     -k 以KB为单位显示 【默认的】
     -m 以MB为单位显示
     -s delay:显示每隔多少秒数来显示一次内存使用情况
     -t 显示内存总和列
     -o 不显示缓冲区调节列


  Mem:表示物理内存统计

  -/+ buffers/cached:表示物理内存的缓存统计

  Swap:表示硬盘上交换分区的使用情况,这里我们不去关心。

  系统的总物理内存:255268Kb(256M),但系统当前真正可用的内存b并不是第一行free 标记的 16936Kb,它仅代表未被分配的内存。

  我们使用total1、used1、free1、used2、free2 等名称来代表上面统计数据的各值,1、2 分别代表第一行和第二行的数据。

  total1:表示物理内存总量。
  used1:表示总计分配给缓存(包含buffers 与cache )使用的数量,但其中可能部分缓存并未实际使用。
  free1:未被分配的内存。
  shared1:共享内存,一般系统不会用到,这里也不讨论。
  buffers1:系统分配但未被使用的buffers 数量。
  cached1:系统分配但未被使用的cache 数量。buffer 与cache 的区别见后面。
  used2:实际使用的buffers 与cache 总量,也是实际使用的内存总量。
  free2:未被使用的buffers 与cache 和未被分配的内存之和,这就是系统当前实际可用内存

  可以整理出如下等式:

  total1 = used1 + free1total1 = used2 + free2used1 = buffers1 + cached1 + used2free2 = buffers1 + cached1 + free1

  buffer 与cache 的区别

  A buffer is something that has yet to be "written" to disk. A cache is something that has been "read" from the disk and stored for later use.

更详细的解释参考:Difference Between Buffer and Cache

摘自:http://space.itpub.net/?uid-33600-action-viewspace-itemid-266415

内存文件系统【Linux下使用内存文件系统】

这里主要介绍了内存文件系统的两种形式:ramfs tmpfs

在Linux中可以将一部分内存当作分区来使用,我们称之为RamDisk。对于一些经常被访问的文件,而它们又不会被更改,可以将它们通过 RamDisk放在内存中,即可明显地提高系统的性能。当然你的内存可要足够大了。RamDisk有两种,一种可以格式化,加载,在Linux内核 2.0/2.2就已经支持,其不足之处是大小固定。另一种是内核2.4才支持的,通过Ramfs或者tmpfs来实现,它们不能被格式化,但是用起来灵 活,其大小随所需要的空间而增加或减少。这里主要介绍一下Ramfs和Tmpfs。

Ramfs顾名思义是内存文件系统,它工作于虚拟 文件系统(VFS)层。不能格式化,可以创建多个,在创建时可以指定其最大能使用的内存大小。如果你的Linux已经将Ramfs编译进内核,你就可以很 容易地使用Ramfs了。创建一个目录,加载Ramfs到该目录即可。

# mkdir -p /RAM1
# mount -t ramfs none /RAM1

缺省情况下,Ramfs被限制最多可使用内存大小的一半。可以通过maxsize(以kbyte为单位)选项来改变。

QUOTE:
# mkdir -p /RAM1
# mount -t ramfs none /RAM1 -o maxsize=10000
以上即创建了一个限定了最大使用内存大小为10M的ramdisk。

Tmpfs 是一个虚拟内存文件系统,它不同于传统的用块设备形式来实现的ramdisk,也不同于针对物理内存的Ramfs。Tmpfs可以使用物理内存,也可以使 用交换分区。在Linux内核中,虚拟内存资源由物理内存(RAM)和交换分区组成,这些资源是由内核中的虚拟内存子系统来负责分配和管理。Tmpfs就 是和虚拟内存子系统来"打交道"的,它向虚拟内存子系统请求页来存储文件,它同Linux的其它请求页的部分一样,不知道分配给自己的页是在内存中还是在 交换分区中。Tmpfs同Ramfs一样,其大小也不是固定的,而是随着所需要的空间而动态的增减。使用tmpfs,首先你编译内核时得选择"虚拟内存文 件系统支持(Virtual memory filesystem support)" ,然后就可以加载tmpfs文件系统了。

# mkdir -p /mnt/tmpfs
# mount tmpfs /mnt/tmpfs -t tmpfs

为了防止tmpfs使用过多的内存资源而造成系统的性能下降或死机,可以在加载时指定tmpfs文件系统大小的最大限制。

# mount tmpfs /mnt/tmpfs -t tmpfs -o size=32m

以上创建的tmpfs文件系统就规定了其最大的大小为32M。不管是使用ramfs还是tmpfs,必须明白的是,一旦系统重启,它们中的内容将会丢失。所以那些东西可以放在内存文件系统中得根据系统的具体情况而定。

再补充一下:ramfs只会在物理内存中被创建,而tmpfs可能在物理内存中创建,也可能在交换分区中被创建。对于想利用内存的高速IO来提高效能的应用,最好是使用ramfs。对于只是想存放临时缓存的应用,最好是用tmpfs,以提前内存的利用率。