本文同步自(如浏览不正常请点击跳转):https://zohead.com/archives/raid5-layout-performance 本文主要介绍 RAID5 内部的数据布局方式,不对 RAID5 的基本原理做过多介绍。磁盘阵列中的 RAID5 类型是通过 XOR 奇偶校验算法来实现阵列中的数据冗余的,可以允许在坏一块磁盘的情况下继续使用,虽然在正常使用过程中写的额外开销比读要多很多,但对于一般企业来说还是很不错的选择。 布局方式: RAID5的校验和信息是平均分布到 RAID5 的每个硬盘上的,实际使用中有以下4种分布方式: 1、左对称(Left-symmetric); 2、左不对称(Left-asymmetric); 3、右对称(Right-symmetric); 4、右不对称(Right-asymmetric) 说这几个名词肯定不好理解,下面是一个4个硬盘组成的 RAID5 内部数据和校验和(parity)在不同布局方式下的分布图(每一行表示是一个条带 stripe): RAID5 几种布局方式中的“左”、“右”表示校验和(也就是图中的红色的P)如何分布,如图中,“左”的校验和都是从最后一个磁盘开始,依次前推,“右”的校验和从第一个磁盘开始,依次后推。 “对称”、“不对称”标识数据(也就是图中的 D0、D1、D2…)的分布方式,“不对称”方式都是直接将数据按照磁盘的数据直接排下来的(图中的左不对称和右不对称的前后数据明显不对称),而“对称”方式则将每个 stripe 中的第一个数据放在校验和的后面,然后往后排列,需要时再绕回第一个磁盘,这样“对称”的数据排列方式就能始终按照磁盘的顺序依次下来。 性能影响: 在实际使用测试中,校验和的左右布局方式对 RAID5 性能没有太大影响。由于实际读写 RAID5 时命令时均匀发到每个磁盘上的,从图中也可以推断出“对称”方式在处理大块值的连续 I/O 读时有更好的性能,实际测试中对随机 I/O 性能也有提升。 需要注意的是上面所说的性能影响相对于 RAID5 中的磁盘个数以及 stripe 条带大小参数来说是比较细微的。 其它说明: 现在很多的RAID卡中都使用了 左不对称 这种 RAID5 布局方式,另外 Linux MD RAID、Windows动态磁盘(LDM)、Veritas Volume Manager(VxVM)等实现中都默认使用了 左不对称 方式。了解 RAID5 的布局方式对 RAID 的性能调试之类有一定帮助哦,HOHO。 除了 Linux MD RAID,其它 RAID5 实现中的布局方式似乎没见到可以让用户选择,Linux MD RAID 可以在使用 mdadm 命令创建时指定 --layout 参数指定布局方式,详细请参考 man mdadm。 如果上面还是看不明白,可以参考这些E文的说明,特别第一篇画的很详细: http://www.accs.com/p_and_p/RAID/LinuxRAID.html http://www.z-a-recovery.com/art-raid5-variations.htm http://sfdoccentral.symantec.com/sf/5.0MP3/solaris/html/vxvm_admin/ch01s04s09s03.htm
Tag: Linux
刚迷上screen就不得不马上移情tmux
本文同步自(如浏览不正常请点击跳转):https://zohead.com/archives/terminal-screen-and-tmux 一个星期之前调试代码时用上 GNU screen,这个多终端模拟器,对我而言很大程度上摆脱了调试时必须开N个 SecureCRT 远程登录标签的郁闷,而且很多 Linux 发行版上都已经自带 screen 包,直接安装即可,也可以在这下载自己编译: http://www.gnu.org/software/screen/ screen 的快捷键操作非常简单(文本模式下的 Linux 必须要N多快捷键的说),安装之后,在一个终端里运行 screen 命令就可以启动,可以在一个终端里再创建新的 shell,使用快捷键在不同的 shell 之间切换,使用 screen 的 detach shell 可实现更好的后台任务处理,用起来还是比较 happy,无奈 GNU screen 也有一些缺陷: 1、不支持在一个 shell window 里再拆分子窗口,也就是像 vim 的多窗口编辑效果; 2、对 xterm 终端支持一般; 3、不能自己根据启动 screen 时的窗口大小调节新 shell 的大小,这个小郁闷; …… 今天看到一哥们分享的 tmux 终端复用工具,立马感觉我要不淡定的移情了,在此下载: http://sourceforge.net/projects/tmux/ tmux 对比 screen 的优点:单个 shell window 拆分很强大,移动切换 shell pane 很强大,完美支持 xterm 终端和集成原 shell 大小,BSD license,命名 shell window,OOO,受不了先上图: 图中是我实际使用的一个环境,pane 0 查看 man 帮助,pane 1 修改代码,pane 2 命令操作,pane 3 监控系统。 tmux 的快捷键和 screen 的有点类似,只是初始快捷键改为了 Ctrl+B: Ctrl-B-C :创建新窗口; Ctrl-B-D :detach窗口,和 screen 的类似; Ctrl-B-” :将当前窗口拆分为上下两行; Ctrl-B-%:将当前窗口拆分为左右两列; Ctrl-B-上下左右:切换 shell pane …… 具体请看 man tmux,果断强烈推荐了,tmux现在除了不是在每个 Linux 发行版中默认就附带这点似乎没有什么相比 screen 的缺点了(不过我还是下到了编译好的 RHEL6 上的 RPM 包,哈哈)。 tmux得在以后使用时继续研究咯,screen暂时抛弃,玩的开心~~~ ^_^
解决socket在execl之后被自动继承的问题
本文同步自:https://zohead.com/archives/close-socket-after-execl/ Linux下的进程在创建子进程(fork)后,子进程会自动继承父进程的文件描述符,这种机制在没有 execl 运行时一般都没有问题,而且也比较方便在子进程中直接使用父进程中打开的文件描述符,但如果子进程通过 execl 类函数运行新的程序时,这些继承的文件描述符却没有被关闭,导致 execl 之后的程序中也有父进程中文件描述符的拷贝,有些情况就会出现错误。 由于 socket 也在这些文件描述符范围内,本文简单说下解决 socket 被自动继承的几种方法。 首先是测试程序,头文件就没列出来了。 父进程和子进程分别 sleep 10秒钟,方便外部查看打开的文件描述符,编译运行程序,使用 lsof 命令就可以看到子进程在 execl 之后的 sleep 进程中也打开了父进程的 socket 描述符(UDP连接),这不是我们想要的效果滴: [root@localhost ~]# lsof -c sleep COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME sleep 5750 root cwd DIR 8,1 4096 8145 /root sleep 5750 root rtd DIR 8,1 4096 2 / sleep 5750 root txt REG 8,1 27880 456104 /bin/sleep sleep 5750 root mem REG 8,1 155696 301801 /lib64/ld-2.12.so sleep 5750 root mem REG 8,1 1912928 301802 /lib64/libc-2.12.so sleep 5750 root mem REG 8,1 99158752 391655 /usr/lib/locale/locale-archive sleep 5750 root 0u CHR 136,1 0t0 4 /dev/pts/1 sleep 5750 root 1u CHR 136,1 0t0 4 /dev/pts/1 sleep 5750 root 2u CHR 136,1 0t0 4 /dev/pts/1 sleep 5750 root 3u IPv4 15022 0t0 UDP *:5555 解决办法有如下几种: 1、比较死板的方法,在子进程 fork 之后关闭所有的 socket 描述符,在 execl 之前加上: int fd = 0, fdtablesize = 0; for (fd = 0, fdtablesize = getdtablesize(); fd < fdtablesize; fd++) { // close all socket handle inherited from parent process if (isfdtype(fd, S_IFSOCK)) { close(fd); } } 这种处理比较彻底,即使子进程没有 execl 也会关闭继承的 socket 句柄,如果要关闭从父进程继承的所有句柄,去掉 isfdtype 判断即可。 2、使用 fcntl 为父进程 socket 设置 O_CLOEXEC 标志,表示此 socket 在 execl 之前自动关闭(注意并非在 fork 之后),在 socket 创建完成之后加上: ret = fcntl(sock, F_GETFD); ret = fcntl(sock, F_SETFD, ret | FD_CLOEXEC); 3、Linux 2.6.27 之后创建 socket 时开始支持 SOCK_CLOEXEC 参数,与上面的效果相似,只要改创建 socket 的地方: sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP); 另外 Linux 2.6.27 之后创建 socket 另外支持 SOCK_NONBLOCK 参数,如果设置此参数,socket 自动创建为非阻塞式。 以上三种办法均在 RHEL 6.1 64位系统上编译测试通过,有任何问题欢迎交流,玩的开心 ^_^
rsync在 Linux/cygwin/msys 环境下的备份性能对比
本文博客链接:https://zohead.com/archives/rsync-performance-linux-cygwin-msys/ rsync是一个开源免费的文件同步和备份工具,可用于本地备份,本地与远程服务器之间的备份,可以实现增量和差异备份,而且由于比较好的算法,在文件备份速度上也相对其它一些文件备份工具有明显的优势。 但 rsync 一直以来没有 Windows 下的原生客户端,都是基于 cygwin 环境实现,实际备份性能会受一些影响,近日看到 rsync 的 基于 MSYS 的 Win32 原生客户端已经被 port 出来,故简单做下性能对比测试。 测试环境: rsync服务器为 RHEL5 Linux 64bit,8个SATA盘的RAID0做下层存储,采用单千兆网络和千兆交换机 rsync客户端为:RHEL5 Linux 64bit,Windows 2003 Enterprise 32bit 测试时 rsync 均通过匿名方式访问,不经过SSH做用户验证,由于考虑到测试的 rsync 客户端的系统盘速度有瓶颈,客户端文件读写都通过内存文件系统来实现(Linux 上使用 tmpfs,Windows 上使用 ImDisk 模拟内存盘)。 使用同样的客户端主板分别在 Linux 和 Windows 内存中产生 1.5GB 的测试文件,然后通过 rsync 客户端进行备份到服务器(写操作)和从服务器上恢复(读操作)的操作。 备份命令示例: rsync -hv x.dat 192.168.1.125::rsync0/ 测试软件列表: 标准 Linux rsync 客户端(RHEL 5 系统自带) cygwin rsync 客户端 MSYS rsync 客户端 http://sourceforge.net/projects/mingw/files/MSYS/Extension/rsync/ RsyncWin32 客户端 http://sourceforge.net/projects/rsyncwin32/ 测试结果: 测试软件 写性能(MB/s) 读性能(MB/s) Linux rsync 105.27 105.28 cygwin rsync 76.22 64.49 MSYS rsync 7.98 8.14 RsyncWin32 76.72(出现错误) 38.50(出现错误) 从测试结果看,由于 rsync 本身面向类 Linux 环境开发,在 Linux 系统中有着非常好的性能,cygwin rsync 与 Linux 相比有一定差距,但实际使用中还是比较稳定的,而 MSYS rsync 还处于测试阶段,虽然没有出现备份错误,但在千兆网络环境下性能非常差,RsyncWin32 则相对而言问题比较多,备份过程中甚至会出现备份错误。 综上看来,目前在 Windows 上使用 cygwin rsync 做备份客户端仍然算是比较好的解决方案,MSYS rsync 的问题可以啥时候有空再看看咯。
Linux下出现 pam_get_item 的原因分析
本文博客链接:https://zohead.com/archives/linux-pam-get-item-error-library/ 近日在将一个 RHEL6 上的服务程序移植到老的 Fedora Core 2 Linux 上时出现了一些问题,该程序的功能为预先加载几个动态库,这几个动态库中再需要根据不同的需求做 PAM 用户验证操作,程序在 RHEL6 上编译和使用都没有问题,换到 Fedora Core 2 环境之后,编译时一切正常,但在运行时出现比较奇怪的 pam_get_item 错误: pam: PAM [dlerror: /lib/security/../../lib/security/pam_env.so: undefined symbol: pam_get_item] pam: PAM [dlerror: /lib/security/../../lib/security/pam_unix.so: undefined symbol: pam_get_item] pam: PAM [dlerror: /lib/security/../../lib/security/pam_succeed_if.so: undefined symbol: pam_get_item] … 程序所加载的动态库中使用的 PAM 配置文件 /etc/pam.d/system-auth 里就用到了 pam_env.so、pam_unix.so、pam_succeed_if.so 等几个 PAM 模块。 但实际系统中还有很多服务都使用 /etc/pam.d/system-auth 这个 PAM 配置来进行用户验证,都能正常工作,因此做下简单分析。 由于此服务程序太复杂,就打定写个简单的模拟程序来进行分析。 1、动态库部分(checkpam.c): PAM 验证的实现,调用 PAM 函数进行验证,具体 PAM 函数的调用就不详细写出了。 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <security/pam_appl.h> int check_pam(const char * user, const char * pass) { int ret = -1; pam_start("system-auth", user, …, …); … // 实际 PAM 代码省略,成功返回0,失败返回1 // … pam_end(…); return ret; } 和实际服务程序一致,通过 system-auth PAM 配置进行验证,编译生成动态库: cc -c checkpam.c cc -shared -fPIC -lpam -o libcheckpam.so checkpam.o ldd libcheckpam.so,查看此动态库的依赖列表: [root@linux root]# ldd libcheckpam.so linux-gate.so.1 => (0x00c67000) libpam.so.0 => /lib/libpam.so.0 (0x0025b000) libc.so.6 => /lib/tls/libc.so.6 (0x0041d000) libdl.so.2 => /lib/libdl.so.2 (0x00153000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x00876000) 2、主程序部分(pamtest.c): 加载动态库,调用 PAM 进行用户验证,比较丑陋,只需能验证,HOHO。 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <dlfcn.h> int main(int argc, char ** argv) { int ret = 0; void * lpso = NULL; int (* lpfunc)(const char *, const char *) = NULL; lpso = dlopen("./libcheckpam.so", RTLD_LAZY); if (lpso == NULL) { printf("Library load failed: %s.\n", dlerror()); return 1; } lpfunc = dlsym(lpso, "check_pam"); if (lpfunc == NULL) { printf("Load symbol failed: %s.\n", dlerror()); dlclose(lpso); return 2; } ret = lpfunc(argv[1], argv[2], NULL); dlclose(lpso); printf("pam ret: %d.\n", ret); return ret; } 功能很简单,dlopen 加载刚才生成的 libcheckpam.so,然后 dlsym 找到 check_pam 函数,接着调用函数进行用户验证。 编译程序: cc -o pamtest pamtest.c -ldl ldd pamtest,查看程序的依赖列表,可以看到此时已不需要 pam 库,和实际使用的服务程序的实现方式一致了: [root@linux root]# ldd pamtest linux-gate.so.1 => (0x00631000) libdl.so.2 => /lib/libdl.so.2 (0x009ac000) libc.so.6 => /lib/tls/libc.so.6 (0x0088f000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x00876000) […]
Linux advisory and mandatory lock with fcntl
本文博客链接:https://zohead.com/archives/linux-fcntl-advisory-mandatory-lock/ 近日小温下APUE,发现Linux下的 fcntl 实现强制锁的功能好像都没试验过,简单做个测试。 首先用 fcntl 实现建议锁(Advisory locking),比较简单,贴个最简单的代码: 关键的几句已高亮显示,用 F_SETLKW 等待写的锁,很好测试,分别开一个终端运行一次就可以测试出来。 备注: Linux 同时实现了 POSIX 的 fcntl 锁函数,BSD 的 flock 函数,SVR4 的 lockf 函数,这些默认都是建议锁。 这个简单的实现只是建议锁,需要每个对文件的操作的进程都遵循同样的锁操作才能起作用,这些进程称为合作进程(Cooperative processes),但实际使用时会有例外的情况,如果另外开一个终端窗口直接写 test.file 文件,会发现可以直接写,因为这种写没有锁操作,不是合作进程,这种情况就需要用强制锁(Mandatory locking)了。 强制锁的实际代码和上面的完全一样,不过在 Linux 上需要做一些改动: 1、挂载文件系统时需要加 mand 参数在文件系统上启用强制锁支持,比较新的 Linux kernel 里已经基本在所有文件系统上都实现了; 2、去掉程序的组执行权限; 3、增加程序的设置组ID权限。 第二项和第三项在普通情况下实际上是自相矛盾的,所以 Linux 就用这种特殊情况就表示启用强制锁(Mandatory locking)了。 备注: 1、使用 BSD 的 flock 函数时不能使用强制锁,见 Linux kernel 源码下 mandatory-locking.txt 中的说明: Mandatory locks can only be applied via the fcntl()/lockf() locking interface - in other words the System V/POSIX interface. BSD style locks using flock() never result in a mandatory lock. 2、如果一个文件被某进程强制锁住,另一个进程通过 creat 函数或者 open 时加 O_TRUNC 参数,都会导致函数调用失败; 3、强制锁在不同的 UNIX 及 类UNIX 系统中实现不同,第二个备注中的 O_TRUNC 参数便是 Linux kernel 和 HP-UX 不同的地方; 4、tmpfs、nfs 也支持强制锁,可以用这两个文件系统方便测试; 5、删除文件(unlink操作)再重新创建文件,会导致强制锁失效; 6、Linux kernel 的强制锁的实现并不完全可靠(感觉白写了,哈哈,实际情况还要做处理),见 man fcntl 的说明。 强制锁的实际操作: mount -t tmpfs -o size=10m,mand tmpfs /mnt cp test /mnt cd /mnt chmod g-x test chmod g+s test 运行 test 程序,同时开另一个终端,直接用 echo 读写 test 文件,会发现强制锁已经起作用。