作者存档: Archangel_SDY

Windows 7 & Arch Linux U盘安装二合一

距离上次发文也一年多了, 真是时光飞逝. 这一年工作接触了很多也做了很多, 不过其实学得越多越发现自己是小白, 也不好意思写文章误导大众了><, 分享一些小经验吧.

话说最近转战Arch Linux, 不过U盘已经放了个Windows 7, 就想弄个双启动. 双启动就得给U盘重新装个引导呗, 不过折腾了半天syslinux没搞定, 启动时卡在那黑屏, 也没啥报错信息. 还是上GRUB吧. 以下是在U盘上安装GRUB并配置双启动的方法.

U盘的目录结构

由于我的U盘之前做成了Windows 7的安装盘, 所以弄成了如下的文件结构. 直接引导Windows 7的ISO谁有兴趣的话也可以尝试下.

  • Windows 7: 直接解压镜像至U盘根目录.
  • Arch Linux: 直接把ISO放于U盘/boot/isos/下.

GRUB的安装

在Ubuntu下直接用grub-install即可, 其它系统可查阅相关文档.

grub-install --boot-directory=/mnt/usb/boot /dev/sdb


其中/dev/sdb是U盘设备, 假设U盘被挂载到/mnt/usb目录下.

GRUB的配置

以下是grub.cfg, 在U盘的/boot/grub/下, 注意不要和你硬盘的/boot搞混了!

set timeout=10
set default=0

menuentry "Arch" {
 set isofile="/boot/isos/archlinux-2013.09.01-dual.iso"
 loopback loop /$isofile
 linux (loop)/arch/boot/x86_64/vmlinuz archisolabel=ARCH_201309 img_dev=/dev/disk/by-label/GSP1RMCULXF img_loop=$isofile earlymodules=loop
 initrd (loop)/arch/boot/x86_64/archiso.img
}

menuentry "Windows 7" {
 insmod part_msdos
 insmod ntfs
 insmod ntldr
 ntldr /bootmgr
}

其中Arch的引导需要指定下镜像文件所在设备的路径img_dev, 我这边是根据U盘的Label(img_dev=/dev/disk/by-label/GSP1RMCULXF)找的, 不知道有没有更好的办法.

至此从U盘启动的时候就会出现启动菜单, 当然也可以类似地加入Ubuntu等, 不过要注意的是其它发行版和Arch的ISO目录结构可能不太一样, 需要查阅下相关文档看怎么配置引导.

完结撒花><!


2014.04.16 Update

贴一个Ubuntu的配置:

menuentry "Ubuntu 13.10 Desktop amd64" {
 set isofile="/boot/isos/ubuntu-13.10-desktop-amd64.iso"
 loopback loop /$isofile
 linux (loop)/casper/vmlinuz.efi boot=casper img_dev=/dev/disk/by-label/GSP1RMCULXF iso-scan/filename=$isofile noprompt noeject
 initrd (loop)/casper/initrd.lz
}

Cisco IPSec VPN For Debian

最近倒腾了下OpenVPN, 发现虽然用起来比SSH速度快点但拨上去实在是太慢. 看到推上的巨巨们都在上Cisco IPSec, 索性决定也自己上一个. 倒腾了下发现配置也不是很复杂, 网上也有巨巨们十分详细的配置步骤, 只是自己在配的过程中也发现有几个地方需要特别注意下, 在此流水帐下吧.

服务端


racoon

服务端配置主要是racoon, 配置文件参考了这篇文章这篇文章, 在此非常感谢. 另外这篇关于如何兼容黑莓的文章讲得也非常清楚, 再次一并推荐下.

既然是Debian安装当然是apt-get:

apt-get install racoon

然后是几个配置文件:

# /etc/racoon/racoon.conf

path pre_shared_key “/etc/racoon/psk.txt”;
path certificate “/etc/racoon/certs”;

listen {
    isakmp SERVER.IP.ADDRESS [500];
    isakmp_natt SERVER.IP.ADDRESS [4500];
}
 
remote anonymous {
    exchange_mode aggressive, main, base;
    mode_cfg on;
    proposal_check obey;
    nat_traversal on;
    generate_policy unique;
    ike_frag on;
    passive on;
    dpd_delay 30;
     
    proposal {
        lifetime time 28800 sec;
        encryption_algorithm 3des;
        hash_algorithm md5;
        authentication_method xauth_psk_server;
        dh_group 2;
    }
}
 
sainfo anonymous {
    encryption_algorithm aes, 3des, blowfish;
    authentication_algorithm hmac_sha1, hmac_md5;
    compression_algorithm deflate;
}
 
mode_cfg {
    auth_source system;
    dns4 8.8.8.8;
    banner “/etc/racoon/motd”;
    save_passwd on;
    network4 10.12.0.100;
    netmask4 255.255.255.0;
    pool_size 100;
    pfs_group 2;
}

# /etc/racoon/psk.txt

# Group Name Group Secret
GROUP.NAME GROUP.SECRET

设置好psk.txt的权限:

chmod 600 /etc/racoon/psk.txt

# /etc/racoon/motd

# Banner
Welcome to Cisco IPSec!

Account

用户验证用的是系统的用户系统, 简单的新建一个用户即可, 为了安全性shell可设为/bin/false.

useradd -MN -b /tmp -s /bin/false USER
passwd USER

iptables

添加以下规则以开放相应端口和NAT转发.

iptables -A INPUT -p udp -–dport 500 -j ACCEPT
iptables -A INPUT -p udp –dport 4500 -j ACCEPT
iptables -t nat -A POSTROUTING -s 10.12.0.0/24 -o eth0 -j MASQUERADE
iptables -A FORWARD -s 10.12.0.0/24 -j ACCEPT

重启服务器后以上规则将失效, 持久化参见这篇文章.

ipv4 forward

# /etc/sysctl.conf

net.ipv4.ip_forward=1

修改后使用如下命令使之生效:

sysctl -p /etc/sysctl.conf

客户端


Linux

Linux可以使用vpnc作为客户端, 配置文件如下:

# /etc/vpnc/default.conf

IPSec gateway SERVER.DOMAIN/IP
IPSec ID GROUP.NAME
IPSec secret GROUP.SECRET
IKE Authmode psk
Xauth username USER.NAME
Xauth password USER.PASSWORD
NAT Traversal Mode cisco-udp

之后可以使用vpnc-connect/vpnc-disconnect连接和断开VPN.

PyPuzzle

前一阵子贴吧洪水泛滥, 写了个Bot过滤关键词删贴. 后来发现广告贴有改贴图的趋势, 这个过滤有压力啊. 想了想读图这种高难度还是不考虑了, 建个图片索引库然后比较相似度好了. 搜了下有现成的库libpuzzle, 不过是个C库, 而Bot是Python写的, 那就顺便包装个Extension吧, 也顺便学写下Python的C扩展, 于是就有了这个PyPuzzle. 参考了PycURL, 拿来当模板还是很不错的.

项目主页: https://github.com/ArchangelSDY/PyPuzzle

结果写完广告贴就销声匿迹了, 囧.

A*算法的Javascript实现

最近突发奇想想去实现下寻路算法, 于是就去研究了下A*, 考虑到画图方便直接拿js+canvas写了. Javascript的数组实在是不能忍啊, 于是Hack一把Array对象><. 闲话不多说, 上代码:

DEMO

Hello Octopress

啊啊啊终于跳了Octopress的, 不过好懒不想做迁移了两个齐头并进吧>///<.

顺便把Godaddy的DNS踹了换到Linode>///<.

SSD安装小记

刚开学就想败家, 想来想去还是把本子武装到牙齿吧, 于是入了SSD, Crucial M4 C400 64G, 无奈本子只有SATA Ⅱ上不了500M/s, 残念. 不过这篇文章的主题不是评测, 而是流水帐一下那蛋疼的安装过程.

原方案: HDD不动, SSD放光驱位, 引导放HDD. 因为引导本来就在一个单独的分区上, 看有人引导放SSD上引导不了, 索性引导不动.

因为U盘量产成了一个光驱一个U盘, 光驱模拟的是XP, 懒得动了, 于是考虑从硬盘装Win7. 把ISO解压到HDD, 重启F8选修复, 进去后选cmd, 熟练地切换到Win7目录(source), setup, 居然显示"无法启动找不到所需的子程序"!

Well, well, 难不倒我, 大不了换个办法引导进去开setup便是. 给拆下来的光驱插上电路板(一并买了个光驱盒, 做工奇烂, 就一塑料盒+电路板+2根线), 插上电脑随便找了张Win7 RC引导进去进cmd, 还是切换到HDD里的Win7目录setup, 这回成功了. 至于为啥不直接从光驱装Win7, 因为刻的盘都太老了, 这几年嫌从光盘装太慢都直接从硬盘开始装.

恩, 成功给SSD分完区格式化, 卧槽! 居然说"无法在此分区上安装", 无视之直接下一步, 卧槽无法安装居然可以安装了! 于是6min复制完文件重启.

重启后, 进不去装到一半的系统, 找不到所需驱动, 好吧, 果然"无法在此分区上安装"= =, 莫非是因为SSD放在了光驱位? 好吧好吧, 咱调换一下便是, 反正原来光驱面板拆不下来现在光驱位用普通面板好像相性不是很好的样子留了个小洞, 应该不用太担心散热= =.

调换后, 再次重装系统, 这次不提示不能装了, 恩, 这次复制完重启后压根连引导界面都看不见了直接提示找不到Device了! 靠, Acer这破BIOS果然引导时候是不给光驱位设备加电的! 于是HDD上的引导读不到了!

很好很好, 全部推倒重来吧. 为了避免转Win7的时候自动把引导加到HDD上去直接把HDD拔了开始装, 不过把HDD拔了如何开始装?! 好吧好吧, 还有移动硬盘, 用Win7 RC的光盘引导进PE, cd到移动硬盘里的新Win7开始装.

安装一切顺利, 装完关机后插上HDD, 进入系统后正常使用. 使用大半天后温度保持在33°C, 问题不大吧, 不知夏天如何.

至此系统安装完毕, 开机至进入桌面用时25s.

感想1: 以后买本子先看BIOS, 这Insyde的BIOS弱爆了啊毛都不能调啊喂= =.
感想2: Acer乃给螺丝打蜡也就算了你还给光驱面板扣子里塞胶水!!!

感想3: 拆机必备STANLEY><.

imageInstant – 图片即时搜索脚本

现在以图搜图的搜索引擎也不少了, 一般都支持通过URL查找或通过上传文件查找. 不过每次想找图时都要另开一页把图片地址复制进去颇为不便, 于是就做了这么一个脚本以Bookmarklet的形式调用, 在图片上生成一个菜单, 直接选择搜索引擎搜索即可.

imageInstant

其实这个脚本还是上个学期末写的, 寒假看了本《Javascript语言精粹》, 受益匪浅, 可谓字字精华没有一句废话, 于是这几天就把这个脚本重写了一遍, 感觉正如书中所说, 如果除去那些糟粕, Javascript确实是门非常优雅的语言.

项目托管在GitHub.

主页: http://archangelsdy.github.com/iqdbInstant/
源码: https://github.com/ArchangelSDY/iqdbInstant

32-bit BMP转PNG之C#版

.Net库中默认的Bitmap类是不支持读取32-bit BMP的, 注意是不支持直接读, 本身是可以处理ARGB结构的. 既然这样那就自己一个个字节读吧, 反正BMP又不需要解码一个一个像素挨个读取便是.

不过读取了每个像素的各个分量值后如果挨个setPixel()的效率是不可忍受的. 且不说双层循环本来效率就很差, setPixel()本来就不快. 还好微软自己也觉得这速度太不靠谱, 留下了用指针直接操作的方法.

主要代码如下:

using (FileStream fs = new FileStream(file, FileMode.Open))
{
    //判断像素深度, 仅对32-bit BMP操作
    fs.Position = 0x1C;
    byte[] byteDepth = new byte[4];
    fs.Read(byteDepth, 0, 4);
    int depth = BitConverter.ToInt32(byteDepth, 0);
    if (depth != 32)
    {
        Console.Write("Not 32-bit bmp file.\n");
        continue;
    }
    else
    {
        DateTime dt = DateTime.Now;
    
        //Pixel数据区开始位置
        fs.Position = 0xA;
        byte[] bytePixelArrayStart = new byte[4];
        fs.Read(bytePixelArrayStart, 0, 4);
        int posPixelArrayStart = BitConverter.ToInt32(bytePixelArrayStart, 0);
    
        //图片宽度
        fs.Position = 0x12;
        byte[] byteWidth = new byte[4];
        fs.Read(byteWidth, 0, 4);
        int width = BitConverter.ToInt32(byteWidth, 0);
    
        //图片高度
        fs.Position = 0x16;
        byte[] byteHeight = new byte[4];
        fs.Read(byteHeight, 0, 4);
        int height = BitConverter.ToInt32(byteHeight, 0);

        fs.Position = posPixelArrayStart;
        //建立一个与原图等大的Bitmap对象, 设置像素格式为32-bitARGB
        using (Bitmap bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb))
        {
            //获取该Bitmap对象的BitmapData数据
            BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, bmp.PixelFormat);
            //获取数据开始的指针
            IntPtr ptr = bmpData.Scan0;
            //计算数据区的长度
            int imgLen = Math.Abs(bmpData.Stride) * bmp.Height;
            byte[] pixels = new byte[imgLen];
            
            //逐个Byte读取值并写入对应像素的各个通道
            for (int counter = 0; counter < pixels.Length; counter++)
            {
                byte[] color = new byte[1];
                fs.Read(color, 0, 1);
                pixels[counter] = color[0];
            }
            
            //直接将新的Pixel数据复制入BitmapData对象, 覆盖原有数据
            System.Runtime.InteropServices.Marshal.Copy(pixels, 0, ptr, imgLen);
            bmp.UnlockBits(bmpData);
    
            //这样生成的图时y方向上下颠倒的, 需要做一次翻转
            //这样的翻转不会导致图片质量的损失
            bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
    
            //转换成PNG格式并写入新文件
            string pngFile = string.Format(@"{0}\{1}.png", Path.GetDirectoryName(file), Path.GetFileNameWithoutExtension(file));
            bmp.Save(pngFile, ImageFormat.Png);
    
            Console.Write("Used: {0}ms.\n", (DateTime.Now - dt).TotalMilliseconds);
        }
    }
}

测试表明使用指针直接操作Pixel数据后速度有显著提升.

另有个小工具叫AlphaConv也可以实现32-bit BMP和PNG互转, Delphi写的, 调的libpng, 效率应该不错, 可惜只有图形界面.

有空来试试直接用C调libpng来实现一下, 看能比.Net快多少><.

Dnsmasq设置简介

说是简介其实也不想详细介绍, 具体官网上都有, 一搜便知. 这里只是简单地罗列下常用配置, 以供参考.

#不读取/etc/resolv.conf文件
no-resolv

#不扫描/etc/resolv.conf和/etc/dnsmasq.conf文件的改动,如果有改动直接重启程序即可
no-poll

#设置dns服务器
server=8.8.8.8

#指定*.domain.name用8.8.8.8解析
server=/domain.name/8.8.8.8
注意不是server=/*.domain.name/8.8.8.8

#解析*.domain.name到127.0.0.1
address=/domain.name/127.0.0.1

#无效的DNS服务器, 用于反DNS劫持
bogus-nxdomain=127.0.0.1

下列一个典型的配置文件, 用于tomato, 要使其生效请开启截获DNS端口.

#覆盖掉原有DNS设置
no-resolv

#对baidu.com及其子域名使用中国电信的DNS
server=/baidu.com/218.2.135.1

#对其它的域名使用Google DNS
server=8.8.8.8

C#控制台输出实时重定向

.Net Framework中的Process类和ProcessStartInfo类可以方便地控制进程的启动, 还可以方便地将控制台程序的输出重定向到Stream中, 然后显示到TextBox中或者记录到文件中. 不过要实时显示每一行的输出还得用点技巧, 方法如下.

启动进程前的准备.

//设置待启动程序的路径和要传递的参数
ProcessStartInfo info = new ProcessStartInfo(path, args));

//将标准输出流重定向, 需同时把UseShellExecute设置为false
info.RedirectStandardOutput = true;

//不使用操作系统外壳程序启动
info.UseShellExecute = false;

//不创建窗体
info.CreateNoWindow = true;

为了做到实时捕获, 需在进程结束前一直搜索输出流, 如有新的一行便读取并引发一个事件以让主程序窗体能够响应并将这行添加至TextBox.

if (process.Start())
{
    while (!process.HasExited)
    {
        string line = process.StandardOutput.ReadLine();
        if (line != null)
            OutputLineGet(line);
    }
    process.WaitForExit();
}

为了不让窗体假死, 将调用上述循环放至另一个的单独线程中.

public void Start()
{
    new System.Threading.Thread(new System.Threading.ThreadStart(this.StartConsoleProgram)).Start();
}


然后便是窗体响应该事件, 注意到是跨线程调用GUI, 需用委托.

public MainForm()
{
  InitializeComponent();

  //注册事件
  this.OutputLineGet += new OnOutputLineGetDelegate(MainForm_OutputLineGet);
}

public delegate void OnOutputLineGetDelegate(string line);
public event OnOutputLineGetDelegate OutputLineGet;

private void MainForm_OutputLineGet(string line)
{
    if (this.InvokeRequired)
    {
        this.Invoke(new OnOutputLineGetDelegate(MainForm_OutputLineGet), line);
    }
    else
    {
        txtLog.AppendText(line);

        //添加换行符
        txtLog.AppendText("\r\n");
    }
}

这样便能一行一行实时捕获输出了, 而且主程序也可以响应窗体消息不至于假死.