linux操作系统
关于磁盘分区表、系统分区、系统引导方面,有很多名词,目前各种资料也比较杂乱,特意整理了一下。

CHS:Cylinder-Head-Sector
最传统的 柱面-磁头-扇区 模式的磁盘寻址方式。传统只支持8GB以内的磁盘。目前磁盘早已不采用真*CHS来寻址,特别是SSD,压根没有磁头了。但依然保持兼容,支持虚拟CHS信息。

在DOS和Windows XP系统时代,分区必须要对齐柱面(原因是和bios命令INT 13h相关,这里不细展开),而引导分区在第一柱面上,所以根分区从第二个柱面开始。所以XP之前的根分区都是从63扇区开始。
http://www.pixelbeat.org/docs/disk/

DOS had the requirement that its image did not span across cylinders, and so this region was added by partition managers so that the first partition was aligned on a cylinder boundary. Therefore this region's size is determined by the number of sectors (512 bytes) per cylinder. The maximum (and usual given todays disk sizes and LBA) sectors per track is 63, which leaves 62 sectors free after the MBR (31,744 bytes).


按这里的说法,每组扇区是63个。


这里其实就有一个疑问,引导扇区到底是0号还是1号?按习惯来讲,应该是0号开始,那么0-62是第一柱面,63是第二柱面的第一个扇区,所以系统从63开始是合理的。

但CHS资料显示,扇区号范围是1-63。(111111)。这就很奇怪了。。那难道第二柱面的第一个扇区不应该是64才对么?

仔细研究了下
https://en.wikipedia.org/wiki/Cylinder-head-sector

The first LBA sector is sector # zero, the same sector in a CHS model is called sector # one.
    All the sectors of each head/track get counted before incrementing to the next head/track.
    All the heads/tracks of the same cylinder get counted before incrementing to the next cylinder.
    The outside half of a whole Hard Drive would be the first half of the drive.



这就引出了LBA:Logical Block Address。由于磁盘越来越大,CHS已经无法满足寻址需求。
所以现在采用了逻辑寻址,仍然兼容CHS,但LBA的起始扇区是0,对应CHS的起始扇区1.


CHS tuples can be mapped onto LBA addresses using the following formula:

    A = (c ⋅ Nheads + h) ⋅ Nsectors + (s − 1),

where A is the LBA address, Nheads is the number of heads on the disk, Nsectors is the maximum number of sectors per track, and (c, h, s) is the CHS address.



所以Fdisk使用LBA寻址方式的话,按0开始计算扇区号就ok。

这就引出了第二个经典问题:Windows XP分区使用SSD性能差问题。
核心原因之一就在于此。现代磁盘物理扇区都是4KB,需要4KB对齐,而起始的63显然不是4K对齐的。所以很多XP分区直接安装Windows7后,使用SSD时会报分区未对齐的问题。

目前Windows的默认起始扇区都是2048了,即首个分区前面空出来1MB的空间。这样就没有了4KB对齐的问题。
(1MB原因:Using a 1-MB alignment boundary allows safer editing of the partition table with Vista Disk Management. 详情可见https://en.wikipedia.org/wiki/Logical_Disk_Manager   主要还是空出来足够多的空间为了兼容一下历史的各种分区工具)



综上,CHS和LBA主要讲的是磁盘寻址方式。目前主要是LBA寻址了。历史上的4K对齐问题,就是因为寻址方式切换带来的问题。由于目前均已经采用了LBA模式,所以这块已经比较清爽了。



然后比较杂乱的就是MBR/EFI/GPT这一大堆了。

主要原因在于MBR在不同语境下具有了多种通俗含义。下面分场景来梳理一下。

在磁盘分区表场景,传统的分区表经常被叫做MBR分区表,与之对应的叫GPT分区表。在这里,MBR是分区表类型名。

MBR分区表信息是存在MBR分区里的,限于512容量限制,只支持4个分区槽位,所以就有MBR最大只支持4个主分区的限制。另外由于扇区号为32bit,所以在512扇区场景下,只能寻址2TB,所以也有2TB容量限制。


GPT分区表是UEFI标准的一部分,主要是支持更大磁盘和更新的引导模式,主要是使用扇区1存储主元信息,存储磁盘空间,uuid等信息,扇区2-33存储分区信息(这里有个有趣的点,就是GPT最大到底支持多少个分区,理论推算应该是32扇区 * 512扇区大小 / 128每个分区表项大小= 128,很多文章说gpt支持无限个分区,推测应该是有一些扩展实现。不过OS层面,windows只支持128个分区,linux只支持256个,所以开多了也并没有用)
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/include/linux/genhd.h?h=v5.0.12#n63


这里就需要注意了:在使用GPT分区表的磁盘上,依然可以有MBR引导扇区。因为GPT是从扇区1开始的。

目前实践上,UEFI模式启动必须要用GPT分区。BIOS/MBR/legacy模式(后面统称传统模式)可以启动MBR分区/GPT分区磁盘。
注意:支持UEFI模式的硬件一般也向下兼容legacy模式

Case1:传统模式启动MBR分区:

传统模式,grub/老的Windows都是如此,差不多从Windows8开始,默认已经都改成UEFI启动了。但依然支持传统引导模式(Windows 11是否依然支持暂时没调研)。

对于grub来讲,大概分为stage1,stage1.5,stage2。
stage1放在MBR中,用来加载第二个扇区的stage1.5(MBR容量有限)
stage1.5加载后面连续n个扇区,用来加载文件系统,进入stage2
stage2进入主分区,启动系统内核

由于使用的都是63扇区之前的内容,所以并不会干扰正常分区中的内容


Case2:传统模式启动GPT分区:

grub2是支持该模式的。由于分区1-33都被gpt分区表占了。所以一般需要一个BIOS Boot Partition
https://en.wikipedia.org/wiki/BIOS_boot_partition
这个分区需要30KB以上,可以在任意位置,part entry type是固定的21686148-6449-6E6F-744E-656564454649 (ASCII string "Hah!IdontNeedEFI",blkid -p可查看)
但由于上面提到,主流分区工具都会预留一个1MB的空间在头部,所以这个分区就可以缩在这里,即扇区34-2047这个区间。目前看debian系是放在这里的。redhat系似乎是放在了2048-4095处(只看了一个样本)。虽然没有对齐之类的,但这个区域本身也基本不读写。所以还ok。


Case3:UEFI启动MBR分区:

Compatibility Support Module (CSM) 兼容模式,用传统模式启动。

Case4:UEFI启动GPT分区:

一般使用EFI system partition (also called ESP),推荐512MB,存放系统内核等文件。类型是EFI System。
文件系统推荐采用FAT32,UEFI默认是支持FAT12/16/32,但是可以扩展。比如apple就支持HFS+。



由上可知。legacy和efi模式启动其实是可以共存的,只要同时有BIOS Boot Partition和ESP,就可以同时支持两种模式启动。当然,更新内核时需要注意同时更新/boot和ESP中的内核。这样才不会产生不一致的问题。


Tags: , , , , ,
    最近搞了个机器。想搞成同时支持openvz和kvm虚拟化技术的host。从原理上讲认为问题不大,因为两者分别是不同层面的虚拟化技术,一个是硬件级别虚拟化,一个是cgroup水平的进程级虚拟化(对这块了解不深,说错勿怪)。所以还是可能同时安装的。

    搜了下方案,果然有个proxmox的发行版是解决这个需求的。看了下文档,集成了一堆面板啥的,东西越多bug就越多。还是自己diy一个。

    由于openvz是需要打过patch的内核运行,而kvm则需要kvm和kvm_intel内核模块的加载(amd的就是kvm_amd)。所以难点就是这个的兼容。其他用户态的进程如果有冲突啥的都好解决。

    首先选用debian7(我喜欢debian),发现需要用openvz的源,源里使用的openvz内核是2.6.32。而debian7的内核目前是3.2。风险比较大。从源里拖了openvz内核deb包下来看,发现居然32位的内核不带kvm模块,而64位的才有。哭了。不知道他们咋搞的。于是64位系统搞起。结果发现kvm_intel加载不进去。报input/output err。用-f强制加载后报签名被拒绝。估计编译的时候哪里错了,虽然有内核源码和.config,不过还是不自己编译了,太折腾。

    还是用debian6。debian6的官方源里已经包含openvz了。内核版本也是2.6,32系列。跟debian6默认内核版本相同。这个感觉靠谱。装上重启进入openvz内核。modprobe加载kvm内核模块。ok,成功了。



     下一步就是进程和网络上的调整了。都不是大问题。



     结论是:openvz目前还是建议用debian6官方源里的包。兼容性好。
Tags: , ,
    最近发现很多同学不知道线上操作替换文件的要点。所以又整理了一下。


    线上替换一个正在运行进程的文件时(包括二进制、动态库、需要读取的资源文件等)。应避免使用cp/scp操作。而需要使用mv/rsync作为替代。

    原因:cp是将源文件截断然后写入新内容。也就是说正在打开这个文件的进程可以立刻感知到修改。修改文件内容很可能导致程序逻辑错误甚至崩溃。而mv则是标记”删除“老文件,然后放一个新的同名文件过去。也就是说老文件和新文件其实是两个不同文件(inode不同),只是名字一样而已。正在打开老文件的进程不会受到影响。如果进程使用了mmap打开某文件(比如载入so),如果目标文件被使用cp覆盖并且长度变小。那么读取差额部分的地址时(在新文件中其实已经不存在了),会导致SIGBUS信号。使进程崩溃。




    至于可执行文件本身。倒是不怕cp导致崩溃。。因为cp时会报”text file busy"。压根cp不了。这时候也应该使用mv类操作。替换完成后重启进程。执行的就是新的可执行文件了。





Tags:
    最近arm盒子想用下tun功能。结果发现内核编译时没开tun,所以决定编译一个。

    先去找到了当前内核的config文件。打开了tun支持。下一步就是去找内核的源码。找了半天。发现没有完全一样的。于是找了个版本相近的,编译完载入。显然加载不了。提示insmod: error inserting 'tun.ko': -1 Invalid module format。在dmesg里提示tun: no symbol version for module_layout。

    很多资料在这里提示没有Module.symvers云云。但我的编译目录是有这个文件的。所以不是这个问题。

    由于内核源码版本和当前内核版本并不完全一致。所以尝试关闭CONFIG_MODVERSIONS。编译后加载。

    insmod依旧提示invalid module format。dmesg里改为提示version magic 'xxxxxxxxxxxxxxxxxxxxxx' should be 'xxxxx'。


    modprobe是可以强制忽略version magic的。即--force-vermagic参数,使用后成功载入tun.ko。由于内核源码非常相近。故工作正常。
    查阅了一下2.4内核源码中对CONFIG_MODVERSIONS的说明,该参数有四种可能。即内核是否开启与要加载的扩展是否开启2*2。经观察发现在3时代内核上这个特性跟之前的文档不太一致。由于时间关系没有详细研究。

    modprobe还有个参数,即--force-modversion。是在编译时开启了CONFIG_MODVERSIONS的情况下忽略接口不一致的。这个参数没有实验。


    也就是说如果想给一个内核编译扩展。最好的方式当然是找到当时编译的环境。或者编译一个新内核+扩展给系统装上。但现实中往往无法这样做。这时找到相近的源码编译。最后强制载入也就行了。(生产环境不推荐)



    happy,tun可以用了。
Tags: ,

top/vmstat等cpu iowait值含义

[| 不指定 2013/06/05 23:03]
    今天发现了一个现象。有一台io压力比较大的机器,基本iowait百分之七十左右。idle接近0。按我的理解是百分之七十的cpu都在等待或处理io。没有空闲的时间片了。

    但开启了一个视频转码服务后,iowait降到很低水平,usr和sys飙高,idle还是接近0。但此时发现视频转码和原io操作的服务均正常运行,未发生性能波动。

    马上感觉到其中的矛盾。cpu不是用完了么?为啥还能承受一个视频转码这种cpu密集的服务呢?

    
    仔细查看了一下iowait的解释。原来它的真实含义是:cpu空闲并且有进程在等待io就绪的时间。

    也就是说如果iowait很高。那么磁盘压力较大。但此时cpu是较为空闲的。此时如果运行诸如视频转码这种cpu密集型操作。是可以提高cpu利用率的。这一点在服务混布提高利用率上可以做文章。




Tags: ,
    今天发现这样一种部署情况:
   a的文件放在b的目录下,a不想让b看到,所以设置了700权限。

   其实这样也是不安全的。b虽然看不到a的文件,但可以把a的文件删掉。

   因为linux里,目录也是一个“文件”,里面记录了它里面有哪些文件,这些文件的inode。b的目录里可以放啥,b说了算,也就是说,只要将那个目录“文件”里不顺眼的文件项删掉,就能删掉文件,而不管对那个文件本身有没有权限。

   至于文件是不是真的被删掉了。取决于文件的引用数,如果没有其他硬链存在。文件就真的被删了。

  
   这里有一个特殊的目录:tmp。大家都可以往里面写,但只有属主可以删掉自己的。
   可以看下tmp目录的权限,它是root所有,所以对于普通用户来说,生效的是最后三位:rwt。其实应该是rwxt。t是特殊权限,是建立在x权限上的,如果删掉x权限,可以看到t变成了T。即失效了。

   t:SBIT(Sticky Bit)目前只针对目录有效,对于目录的作用是:当用户在该目录下建立文件或目录时,仅有自己与 root才有权力删除。
最具有代表的就是/tmp目录,任何人都可以在/tmp内增加、修改文件(因为权限全是rwx),但仅有该文件/目录建立者与 root能够删除自己的目录或文件。




  
Tags:

Linux服务器安全操作技巧

[| 不指定 2013/03/13 11:52]
      在服务器操作系统市场上,Linux因其开源、自由的特性赢得了很多人的亲睐。但相比于Windows系统简单直观的GUI界面,Linux的操作很大一部分是使用命令行方式来进行,这对于一部分初学者来讲是一个不小的挑战。操作失误轻则影响服务,重则丢失数据。那么,究竟怎样才能降低风险,更安全的使用Linux呢?

1,  vi的使用
vi是使用很广泛的文本编辑器,可以让双手在基本不离开字母区的情况下完成各种操作。应注意在查看超大文件时,不能使用vi。因为vi会将文件整体载入内存,打开大文件很容易造成服务器内存耗尽、IO占用率飙升。在这种场景下,推荐使用less命令。
2,  慎用rm
在日常操作用,应谨慎使用rm,尤其是-rf参数。对于不需要的文件应先mv到其他位置保留一段时间,待磁盘空间占用率较高需要清理文件时再进行清理。
3,  替换文件使用mv而不是cp
在替换文件时,应先备份原有文件,再使用mv操作将文件覆盖。对于正在使用该文件的进程来讲,mv操作不会在本次使用周期内被感知到,只有重新打开文件时才会生效。而cp则不然。使用cp覆盖文件,特别是覆盖动态链接库,很容易导致一些进程coredump。
4,  编写重要命令时前面加符号#
在编写重要/批量命令时,应在命令开头处先输入符号#(#在shell中为注释符)。确认无误后再去掉#,避免误按回车键导致不完整命令执行造成破坏。
5,  确保操作所依赖条件成立
对于依赖前面执行结果的操作来讲,可以使用&&来连接各条命令,确保前面执行都成功后才进行后面操作,避免前一步执行失败仍继续下一步造成破坏。
6,  使用rsync而不是scp
在拷贝大量文件时,scp无法查看具体进度,并且一但中断就要从头再来。相比之下rsync功能更为强大一些,支持增量传输,并且还可以随时查看进度。
7,  网络操作注意限速
对于rsync或wget等网络操作,应注意限速,避免耗尽带宽影响正常服务的通信。
8,  操作前设计好回滚方案
操作结果不符合预期时,需要将之前的操作回滚。应提前设计好回滚方案,避免到时候手忙脚乱导致操作失误,或操作时未注意备份文件和数据导致回滚失败。








    很久之前就有一个问题在困扰,就是明显感觉svn操作完后要顿一顿才结束。一直以为是svn机器性能问题,后来上了ssd也没效果。

   最近抽时间看了下。

   先strace一下看看时间,发现在操作末尾两个gettimeofday之间夹了个select操作。会阻塞几百ms到1s不等,然后超时。

   第一感觉是最后处理完了可能给服务器发回个什么数据之类的,服务器那里哪个规则配的不对,客户端连不上然后超时了。

   看svn源码,看不出来末尾做了啥网络操作,封装了好多层,不确定是不是哪一层注册了什么奇奇怪怪的回调函数,在最后析构时搞一把。

   用gdb单步跑了一下。

   发现svn_io_sleep_for_timestamps函数很可疑。

   查看代码,发现这是个延时函数。svn通过mtime获取文件修改信息,所以每次svn co操作末尾,svn会等待一小段时间再返回,svn客户端判断apr_stat返回的文件mtime是不是1000000整倍数(stat看到的mtime小数点后面是否全是0),如果是0,那么svn认为该文件系统不支持毫秒级粒度的mtime记录,就会等待系统时间秒的跳变,即等待平均0.5s,最长1s。如果不是0,那么只等待1ms。
  
   由于svn使用的apr_sleep为了实现较高精度的sleep延时,使用了select来做延时。这就是strace中select系统调用的由来。


   这里还有一个问题,就是如果文件mtime恰好是整数,那么svn就会判断失误,进而等待较长的时间。作者认为总比每次都等较长的时间好。

   之前用的文件系统是ext2,它只支持秒级精度的mtime,而ext4支持更高精度。

   找了一台机器,划了两个同样大小的分区,一个格式化为ext2,一个格式化为ext4,使用svn co向两个分区中checkout代码,前者耗时0.3到1s不等,后者稳定在100ms。性能提升非常明显。





Tags:
    最近需要做一个基于php的多进程server。为了优雅重启,需要捕获一下kill发出的SIGTERM信号,于是用到了pcntl_signal。

    刚开始发现捕获信号无效,无法进入信号处理函数。研究了一下文档,发现要运行一下declare(ticks = 1);看解释说是为了产生时钟云云,我的理解就类似于晶振一样。添加后父进程可以收到信号了。

    但是子进程还是无法收到信号,排查了很久,没看到什么异常,子进程是平时阻塞噻socket_accept的,怀疑是不是系统调用在这里无法中断,于是改成while 1循环,可以顺利进入中断处理函数。

    然后搜索了一下,原来pcntl默认都是会自动重新启动被中断的系统调用的,对外表现则是无法中断系统调用的阻塞。

    这时回头看手册,原来pcntl_signal还有第三个参数,也是一个可选参数,就是让我们决定是否自动重启系统调用,选择不重启即可。




Tags:
    自用开发机的ubuntu还是9.10版本,不在支持队列了,最近要编译一些东西,版本支持不足,于是决定升级到12.04。

    源列表直接更新成12.04的,然后dist-upgrade,结果出了很多奇奇怪怪问题,主要是python的依赖问题,原来的2.6升级2.7,而装2.7又依赖自身,自己编译了一个跑起来了,最后遇到了一个终极错误:Could not perform immediate configuration python2.7-minimal。 搞了半天解决不了,

    后来发现原来在apt-get命令里加上-o APT::Immediate-Configure=0暂时禁止检查即可。

    另外ubuntu升版本确实问题多多,还遇到很多相关检查版本失败的问题。可以通过修改
   /var/lib/dpkg/info/packagename.*相关文件来使其返回0让相关检查强制通过


Tags:
分页: 1/3 第一页 1 2 3 下页 最后页 [ 显示模式: 摘要 | 列表 ]