Linux下常用的dd命令
今天在微博看到了关于服务器硬盘负载的测试(原文见:使用 dd 命令进行硬盘 I/O 性能检测),前一段时间在搭建Minecraft服务器,我就经常怀疑IO爆表,但是内存可以用free,CPU和内存可以用top,可IO怎么查,我怎么知道这台服务器到底是哪里爆表了。 文章里提出用dd进行硬盘写入操作,通过写入情况分析现在负载到了一个什么程度。
但是,我并不太敢认可这种做法,这种做法就像判断瓶子满没满,再向里面注入写就知道了。但我目前并没有其他能看出磁盘当前性能的办法,暂且认可。因为如果瓶子已经满了,这些注入岂不是又增加负担。 也许我的担忧是在我无法负担更好的VPS的基础上的吧,就像好多朋友已经现在游戏服务器上玩的happy,我想看下服务器IO当前性能,结果一个dd就把服务器搞崩了,全线玩家集体掉线,也是挺无语。
继续说dd命令,之前看过一篇关于介绍新手级命令的文章(原文见:对 Linux 新手有用的 20 个命令),因为已经接触linux有一段时间了,几乎对这些命令用了不知多少遍了,唯独dd,之前就用过一次,就是帮同学写镜像,好像还写砸了。 dd的作用事把指定的输入文件拷贝到指定的输出文件中,并且在拷贝的过程中可以进行格式转换。现在就说下dd的常用用法。 先说下dd的语法和参数:
- if =输入文件(或设备名称)。
- of =输出文件(或设备名称)。
- ibs = bytes 一次读取bytes字节,即读入缓冲区的字节数。
- skip = blocks 跳过读入缓冲区开头的ibs*blocks块。
- obs = bytes 一次写入bytes字节,即写 入缓冲区的字节数。
- bs = bytes 同时设置读/写缓冲区的字节数(等于设置obs和obs)。
- cbs = bytes 一次转换bytes字节。
- count = blocks 只拷贝输入的blocks块。
- conv = ASCII 把EBCDIC码转换为ASCII码。
- conv = ebcdic 把ASCII码转换为EBCDIC码。
- conv = ibm 把ASCII码转换为alternate EBCDIC码。
- conv = blick 把变动位转换成固定字符。
- conv = ublock 把固定们转换成变动位
- conv = ucase 把字母由小写变为大写。
- conv = lcase 把字母由大写变为小写。
- conv = notrunc 不截短输出文件。
- conv = swab 交换每一对输入字节。
- conv = noerror 出错时不停止处理。
- conv = sync 把每个输入记录的大小都调到ibs的大小(用ibs填充)。
1.对硬盘(西数黑盘)进行测试:
hypo@Hypo-TP:~$ dd if=/dev/zero of=test.img bs=512M count=4 oflag=dsync
记录了4+0 的读入
记录了4+0 的写出
2147483648字节(2.1 GB)已复制,19.7395 秒,109 MB/秒
hypo@Hypo-TP:~$ dd if=/dev/zero of=test.img bs=1G count=1 oflag=dsync
记录了1+0 的读入
记录了1+0 的写出
1073741824字节(1.1 GB)已复制,9.12763 秒,118 MB/秒
可以看到写入延时和写入速度。 其中/dev/zero事unix和linux下设计的白洞,可以源源不断的提供null字节,而也有一个unix和Linux的黑洞,那就是/dev/null。 2.写iso镜像:
sudo dd if=archlinux-2015.03.01-dual.iso of=/dev/sdd && sync
记录了1218560+0 的读入
记录了1218560+0 的写出
623902720字节(624 MB)已复制,0.910181 秒,685 MB/秒
3.备份整个磁盘,比如:
dd if=/home of=home.img bs=4M
4.备份MBR 备份: 备份磁盘开始的512Byte大小的MBR信息到指定文件:
dd if=/dev/hdx of=/path/to/image count=1 bs=512
恢复: 将备份的MBR信息写到磁盘开始部分:
dd if=/path/to/image of=/dev/hdx
5.修复硬盘 当硬盘较长时间(比如一两年年)放置不使用后,磁盘上会产生magnetic flux point。当磁头读到 这些区域时会遇到困难,并可能导致I/O错误。当这种情况影响到硬盘的第一个扇区时,可能导致 硬盘报废。下面的命令有可能使这些数据起死回生。且这个过程是安全,高效的。
dd if=/dev/sda of=/dev/sda
6.销毁磁盘数据 利用随机的数据填充硬盘:
dd if=/dev/urandom of=/dev/hda1
在某些必要的场合可以用来销毁数据。执行此操作以后,/dev/hda1将无法挂载,创建和拷贝操作无法执行。
依赖倒置原则
什么是依赖倒置原则
要给依赖倒置原则下个定义,三句话足以:
- 高层模块不应该依赖低层模块,两者都应该依赖其抽象
- 抽象不应该依赖细节
- 细节应该依赖抽象
刚读这三句话的时候,我也有些恍惚,体会了好久,又想了好久才有体会。最近刚刚完成一个项目的Demo版,体会了下这个原则,然后想想不依照这个原则的后果,重写的心都有。 先解释一下这几个名词:高层模块和低层模块比较好区分,每个逻辑的实现都是由更小的原子逻辑组成,不可分割的原子逻辑就是低层模块,而原子逻辑的再组装就是高层模块。 至于抽象和细节,在Java中,可以理解为抽象就是指接口或抽象类,两者都是不能直接被实例化。而细节就是实现类,实现接口或继承抽象类而产生的类就是细节。细节是可以直接被实例化,也就是可以加上一个new之后便可以产生一个对象。 依赖倒置原则在Java语言中的表现就是:
- 模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的;
- 接口或抽象类不依赖于实现类
- 实现类依赖接口或抽象类
可以精简的定义为:面向接口编程。 采用依赖导致原则可以减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码的可读性和可维护性。 举一个栗子来具体说明下。
提高系统的稳定性
如果我们有一个司机类和一个奔驰类,来实现让司机开奔驰,我们只需要实现一个奔驰类:
package net.ihypo.dip;
public class Benz {
public void run(){
System.out.println("奔驰跑2333");
}
}
并让奔驰可以跑,就可以了。 然后我们再实现这个司机类:
package net.ihypo.dip;
public class Driver {
public void driver(Benz benz){
benz.run();
}
}
在使用的时候,把奔驰类对象传给司机类对象,这样就可以了:
package net.ihypo.dip;
public class Client {
public static void main(String[] args) {
Driver blf2 = new Driver();
Benz benz = new Benz();
blf2.driver(benz);
}
}
功能实现了,就可以兴匆匆去上线发布了,其实明显有问题嘛,其实我刚刚完成Dome版的这个项目就是这么办的,忘了仔细设计未来的功能扩充。 问题来了,如果这个司机土豪又买了一个宝马,换句话说,项目需求改了,功能要求更高了,怎么办,最直接的方法就是重载Driver中的driver函数,当然,这只是因为增加了一个宝马,如果在来几辆车呢? 所谓“危难时刻见真情”,移植到技术上就是“变更才显真功夫”,业务需求变更永无休止,技术前进就永无止境,在发生变更时才能发觉我们的设计或程序是否耦合。明显,上面的程序耦合性过高导致要修改已经完成的代码。 者明显导致系统的可维护性降低,可读性降低,稳定性降低。什么是稳定性?固话的、健壮的才是稳定的,这里只是增加一个车类就需要修改司机类,这不是稳定性,只是易变性。被依赖者的变更竟然让依赖者来承担修改的成本,这样的依赖关系谁肯承担! 设计是否具备稳定性,只要适当地“松松土”,观察“设计的蓝图”是否还可以茁壮地成长就可以得出结论,稳定性较高的设计,在周围环境频繁变化的时候,依然可以做到“我自岿然不动”。 那么这个程序应该怎么改进?这就用到了依赖倒置原则的第一条高层模块不应该依赖低层模块,两者都应该依赖其抽象。 所以应该先有一个司机的抽象:
package net.ihypo.dip;
public interface IDriver {
void driver(ICar car);
}
以及一个汽车的抽象:
package net.ihypo.dip;
public interface ICar {
void run();
}
这样,让抽象之间发生关系,让高层模块不一来低层模块。几口只是一个抽象概念,是对一类事物的最抽象描述,具体的实现代码由相应的实现类来完成。 然后分别实现IDriver和ICar来具体化司机类和汽车类,并实现功能。
package net.ihypo.dip;
public class Benz implements ICar{
@Override
public void run(){
System.out.println("奔驰跑2333");
}
}
package net.ihypo.dip;
public class Driver implements IDriver{
@Override
public void driver(ICar car) {
// TODO Auto-generated method stub
car.run();
}
}
Driver只是传入了ICar接口,至于是哪个具体的类型,需要在高层模块中声明:
package net.ihypo.dip;
public class Client {
public static void main(String[] args) {
Driver blf2 = new Driver();
ICar benz = new Benz();
blf2.driver(benz);
}
}
这样,即使需求改变了,也不需要修改即成的部分,提高系统的稳定性。
降低并行开发引起的风险
如果在开始的那个栗子中,如果甲乙两个人同时开发这个程序,甲负责司机类,乙负责汽车类,那么如果甲开发的速度比较快,那么如果甲开发完之后能不能进行测试阶段呢?并不能,因为乙还不没有开发完,并没有数据参数让你来测试。 但是用了接口就不一样了,接口在一开始就是定好的,就像一个契约,接口所定的东西就是未来参数多实现的东西,完全不用在乎乙开发的如何,因为他所写的也是符合这个契约的。 有一个JMock工具,其最基本的功能就是根据抽象虚拟出一个对象进行测试,测试类代码如下:
public class DriverTest extends TestCase{
Mockery context = new JUnit4Mockery();
@Test
public void testDriver(){
final ICar car = context.mock(ICar.class);
IDriver driver = new Driver();
context.checking(new Expectations(){{
oneOf(car).run();
}});
driver.driver(car);
}
}
我们只需要一个ICar接口就可以独立的对Driver类进行单元测试。 两个相互依赖的对象可以分别进行开发,孤立地进行单元测试,进而保证并行开发的效率和质量,TDD开发的精髓不就如此么,车市驱动开发,先写好单元测试类,然后再写实现类。 抽象是对实现的约束,对依赖者而言,也是一种契约,不仅仅约束自己,还同时约束自己与外部的关系,其目的是保证所有的细节不脱离不了这个圈圈,始终让你的对象做到“言必信,行必果”。
最佳实践
依赖倒置原则的本质就是通过抽象使各个类或模块的实现彼此独立,不相互影响,实现模块间的松耦合。怎么在项目中使用这个规则呢?只要遵循下面的几个规则就可以:
- 每个类尽量都有接口或抽象类,或者抽象类和接口两者都具备
- 变量的表面类型尽量是接口或者抽象类:当然只是尽量,比如如果使用clone方法就必须要使用实现类,这是JDK的规范
- 任何类都不应该从具体类派生:如果一个项目处于开发状态确实不应该出现从具体类派生出子类的情况,但这不是绝对的,因为人都是会犯错误的,有时候设计缺陷是在所难免的,因此只要不超过两层的继承都是可以忍受的。
- 尽量不要覆写基类方法
- 结合里氏替换原则
依赖倒置原则的优点在小型项目中很难体现出来,例如小于10人月的项目,使用简单的SSH框架,基本上不话费力气就可以完成,是否采用依赖倒置原则影像不大。 但是在一个中大型项目中,采用依赖倒置原则有非常多的有点,特别是规避一些非技术因素引起的问题,通过采用接口等抽象对实现类进行约束可以减少需求变化引起的工作量剧增的情况。 依赖倒置原则是6个设计原则中最难实现的原则,它是实现开闭原则的重要途径,依赖倒置原则没有实现,就别想实现对拓展开发,对修改关闭。 但是,依赖倒置原则不是万能的,现实世界中的确存在必须依赖细节的事物,比如法律,就必须依赖细节的定义。 我们在实际的项目中使用依赖倒置原则时需要审时度势,不要抓住一个原则不放,每个原则的邮电都是有限度的,并不是放之四海皆准的真理,所以别为了遵循一个原则而放弃了一个项目的终极目标:投产上线和盈利!
Copyright © 2020 Powered by MWeb, Theme used GitHub CSS.