2013年3月29日 星期五

強力膠程式

基於俗話說:「正規表示式用的好,下班比較早。」的名言,又用了強力膠搞程式。

最近因為在搞NTP需要檢查輸入變數的IP或者是HOST NAME,需要用到正則表示式幫忙檢查。可是又不太想老老實實的寫程式,一心只想要早點下班,老實說,總歸一句話就是人懶。


sprintf(szCmd, "echo '%s' | grep -q '^\\(\\w\\+\\.\\)\\+\\w\\+$'", strIpHostName);
if(system(szCmd))       {ret=d_NTP_IPURL_INVALID_FORMAT; goto CNCS_EXIT;}


兩行寫完,就下班了~~~XD

這主要還是利用正則表示式幫忙檢查樣式,grep -q 會回應成功或失敗,要system會回0表示樣式對了,當然啦~用system是會比較慢一點,幸好這程式是不太在意效率問題,而我也剛好比較在乎下班時間,所以就這樣搞了。~~XD

原來的shell命令大概像下面這行一樣
echo "www.google.com.tw" | grep -q '^\(\w\+\.\)\+\w\+$'
執行成功或失敗,這樣看不太出來,加上後面那一段,格律對了,就顯示IP/HOST NAME OK
echo "www.google.com.tw" | grep -q '^\(\w\+\.\)\+\w\+$' && echo 'IP/HOST NAME OK'

搭一些牙籤上去讓C語言做出來的程式跑起來,可以運作就收工了,就看起來像是用強力膠黏出來的程式風格,也證明了俗話說的好。

PS.在GNU C上正途是用regcomp這函式,只是這樣程式寫起來就會落落長。。。

2013年3月28日 星期四

sed 與 awk

在script執行過程當中想要改東改西的這種搞法,被稱為串流編輯,而有這種能力的工具,被稱為串流編輯器(stream editer),這當中有兩個工具很好用,就是sed與awk。

這兩個工具在用之前,需要有正規表示式(Regular Expression)的知識,俗話說:「正規表示式用的好,下班就會早。」,它是用來描述字串共同的模式,利用樣式來描述字串的規則,再利用這規則來搜尋、修改等動作。

sed是一種非交談式的串流編輯器,可動態編輯資料,主要是比對樣式,符合就做指定動作。

awk是一種可以處理資料、產生格式化報表的語言。主要是拿來讀取檔案,逐列取出當成紀錄(record),每筆紀錄用欄位分隔符號切開,然後輸出每個欄位的值。

這兩種神兵,用的好,可以讓事情少很多,有些用手敲個老半天,手工打造的表格,用這兩個兵器搞下去,往往一行之內就搞定了。

參考wiki : http://zh.wikipedia.org/wiki/AWK
參考文件:http://www.delightpress.com.tw/bookRead/skns00004_read.pdf
參考網頁:http://linux.vbird.org/linux_basic/0330regularex.php

最近用awk的小心得

$find . -name '*.ko' | awk -F "/" '{printf("%s\t\t->\t\t",$NF)};{for(i=1; i <= NF-1; i++) printf("%s/",$i)};{printf("\r\n")}'

awk的$NF指的是最後一個欄位,這一段命令事先找到資料夾下的.ko檔案,把路徑及檔名丟給awk處理,先印出最後一個欄位,然後加上->符號隔開檔名,最後再用for迴圈組合路徑出來。

結果就是像大概下面這個表一樣。

w1_ds2431.ko -> ./target/lib/module/2.6.32.9/kernel/drivers/w1/slaves/
mmc_core.ko -> ./target/lib/module/2.6.32.9/kernel/drivers/mmc/core/
sdhci.ko -> ./target/lib/module/2.6.32.9/kernel/drivers/mmc/host/
sdhci-platform-bcm5892.ko -> ./target/lib/module/2.6.32.9/kernel/drivers/mmc/host/
sdhci-pltfm.ko -> ./target/lib/module/2.6.32.9/kernel/drivers/mmc/host/
mmc_test.ko -> ./target/lib/module/2.6.32.9/kernel/drivers/mmc/card/
mmc_block.ko -> ./target/lib/module/2.6.32.9/kernel/drivers/mmc/card/
scsi_wait_scan.ko -> ./target/lib/module/2.6.32.9/kernel/drivers/scsi/
做這個表,主要是把kernel module拷貝到rootfs當中,之前寫的script可以使用這個表列出來的檔案拷貝到rootfs當中,一個一個手工keyin製表,是很勤勞的人在做的事情,實在不符合我的風格,對~我就是個懶人,所以就用了awk把表搞出來,當然使用perl一樣做的到,只是剛好今天換了工具而已。

2013年2月21日 星期四

【轉】qt觸摸屏隱藏鼠標指針


方法1:運行加參數-nomouse

鼠標指針是沒了,觸摸屏也失效了。

方法2:QWidget::setCursor(QCursor(Qt::BlankCursor)

例:this->setCursor(Qt::BlankCurror);
只希望在某個QWidget(或QDialog等)控件上不出現鼠標指針。其他窗口仍會顯示鼠標指針。

方法3:main函數中調用QApplication::setOverrideCursor(Qt::BlankCurror);


方法4:main()函數加入 #include <QWSServer>,實例化QApplication後,添加QWSServer::setCursorVisible(false);


注意:方法2和3只有在動一下觸摸屏後鼠標才會消失,第4種在整個程序啟動到運行都不會出現鼠標指針。

終於去掉這討厭的鼠標了。

引用: http://www.myexception.cn/program/660869.html

2012年8月1日 星期三

INTEL HEX 格式

Intel HEX是一個文字檔,用來用來紀錄相對應的二進制碼。

格式:
:llaaaatt[dd...]cc
||||||||||||||||`` CHECKSUM (Zero checksum)
|||||||||```````- DATA
|||||||``- TYPE
|||||||     `- 00 : data record
|||||||     `- 01 : end of file record
|||||||     `- 02 : expended segment address record
|||||||     `- 04 : expended linear address record
|||````- ADDRESS
|``- LENGTH
`-START - colon

範例:

:10010000214601360121470136007EFE09D2190140
:100110002146017EB7C20001FF5F16002148011988
:10012000194E79234623965778239EDA3F01B2CAA7
:100130003F0156702B5E712B722B732146013421C7
:00000001FF



  Start code
  Byte count
  Address
  Record type
  Data
  Checksum




參考資料:
http://en.wikipedia.org/wiki/Intel_HEX
http://www.keil.com/support/docs/1584/

2012年7月26日 星期四

再論Linux開機

從電腦上電到出現命令提示符號間,系統經過了許多血汗的努力,這過程在使用者角度上不太重要,但,如果想要定做一個小型的Linux系統,就需要了解Linux整個啟動的過程。

當電腦上電之後首先是執行BIOS的程式,接著由BIOS交接到磁碟機上的MBR,通常OS Loader會載到MBR,這Loader負責把OS 核心(Kernel)與rootfs檔案initrd.img載入到記憶體中,並把控制權交接給核心運作,之後會解開initrd.img到記憶體上,成為暫時的rootfs但是是在RAM上,被稱為initrd (INITial Ram Disk),解開的rootfs之中有init這檔案,核心會去執行它,init(or linuxrc)必須要可以執行,可以是Script,有人稱它為Application Manager,這程式接手之後就得負責把磁碟機驅動起來,之後要掛載檔案系統,建立一些必要的設備節點,最後是根目錄切換,切換之後再去執行新的目錄中的init,把完整的系統驅動起來。

  1. bootloader載入kernel及initrd
  2. kernel解開initrd,並釋放initrd所佔用的記憶體空間
  3. 將rootfs掛載到ram disk上
  4. 執行 init (initialization)
  5. 掛載真正的rootfs
  6. chroot執行/sbin/init
所以第一次掛載ramfs時候如果需要開機到RAID上,就需要修改initrd.img這檔案,驅動起RAID並且掛載上再切換到RAID上,或是需要掛載iSCSI的磁碟當成系統碟也是需要修改它,如果不夠了解整個開機過程,相信沒辦法做到這一點,如果不需要太多的程序來搶資源,也可以讓自己寫的程式去領PID 1來用,整個系統就幾乎是你的程式獨占資源,所以對這些程序流程理解的愈深,相對的擁有的自由度就愈高。

當我在做小型的Linux系統的時候,我只需要開機到ramfs,所以不再往下交到目標磁碟機手上,而是直接用ramfs就好,一般這種作法搞出來的嵌入式系統是整個運作在記憶體上,好處是掛掉隨便使用者亂關都沒事,反正下一次要用還是得重新載入到記憶體上,只要原始的檔案沒毀損就沒事,缺點就是要等載入跟解壓縮,如此一來開機時間就會拉長,在很多的網通設備、多媒體播放器上都是這樣搞,只是他們也得面臨到開機速度的挑戰,所以就會看到各家奇奇怪怪的加速手段。

 在kernel版本2.6之後,initrd改用cpio製作,可以稱做cpio-initrd,它不需要特殊的block device或loop back device,製作過程比較簡單,而2.6與2.4的差異可以參考這一篇文章寫的。

上頭說的流程是Kernel 2.6以後的版本,而對於開機流程相信查到的文件常常是叫你去改inittab這檔案,如果你跟我一樣是用ubuntu/Debian就會發現事情並不單純(李組長語調),傳統的作法繼承於Unix-Like底下System-V init的作法,但是之後因為一堆隨身碟、硬碟、、、等隨插即用的東西出現,這個系統變得不太能夠適應,新的需求對於軟硬體的啟動、停止、掛載、移除,需要服務可以動態的啟動,傳統的sysVinit是基於runlevel的系統,使用runlevel(單、多使用者以及其它level)和鏈接(於/dec/rc?.d目錄,分別鏈接到/etc/init.d的init script)來啟動和關閉服務,而Ubuntu使用的是UPStart-Init則是基於事件的系統,它使用事件來啟動和關閉系統服務,傳統的SysVinit還有一個缺點,就是啟動需要跑一堆shell script,好處是很容易改,但是缺點是「慢」,這個缺點是從MacOS學到的教訓,在相同的位置上Solaris使用SMF(Service Management Facility),而Mac OS則使用Launchd,而Ubuntu則是傾向集這些軟體的優點於一身。

使用上也真的比較快,開機真的快多了,而且對於裝置服務的反應以比較靈敏,新的CPIO省去一次載入核心的時間,而init省去shell script運作產生的浪費,目前最耗時間的還是載入rootfs的時間(壓縮比加到頂也沒小多少,但是就明顯感覺核心解壓縮有點燒到點時間)。

sysVinit
  • initScript /rc script
  • rc => run commands
  • /etc/rc.d/init.d
  • /etc/inittab
    • id:5:initdefault:
  • /etc/rc.d/rc5.d
    • exp:
    • /etc/rc.d/rc5.d/K36mysqld
    • link to ../init.d/mysqld
    • K36mysqld
      • K : Kill/Start
      • 36: boot sequence
      • mysqld: service name
  • runlevel設定相互獨立
UPStart
  • event-driven/-based
  • 理順服務依賴關係以便實現平行啟動
  • 按需求啟動
  • Upstart job script
  • 有關UPStart相關的說明可以參考這一篇文章
也就是init最後不要交到真的磁碟機的/sbin/init而是交到ramfs裏面的/sbin/init,如果是用UPStart的服務,則是要搬相對的資料給他用,/etc/init目錄下的東西,底下的東西是.conf只是設定檔而已,不是shell script,而也要順便拷貝/etc/init.d目錄下的東西,除此之外還有相對應的設定檔也要拷貝,比如說ssh,除了init或init.d之外,還放了一個/etc/ssh/的目錄,也要順便拷貝走,與DNS相關的是/etc/hosts、/etc/resolv.conf、/etc/nsswitch.conf這3個設定檔,與帳號、密碼相關的設定檔是/etc/passwd、/etc/shadow、/etc/group這3個,如果說認証上有問題要注意/etc/pam.d/,PAM(Pluggable Authentication Modules),要順便注意/etc/nologin、/etc/securetty、/etc/security/*這堆。



2012年7月12日 星期四

我真的很懶嗎?


我一直覺得,我還蠻懶惰的,不喜歡做一堆重複的工作,寧願認真做完一次,寫個code,叫電腦自動做,也不願意重複的命令一直打,感覺這種行為很浪費生命,明明自動程序在跑幾秒鐘就沒事了,要燒個一天去做就感覺很白痴。

底下兩段CODE是把當下的UBUNTU或DEBIN系統抽出來做一個ROOTFS,也就是自動做出initrd.img的腳本,用Perl寫這種腳本還蠻快的,而且很好寫,主要是autofs.pl在運作,poCmd.pl是讀取cmd.list內容,讓autofs.pl去呼叫,poCmd.pl會按照cmd.list內列出來的命令,逐一拷貝到/bin底下,順便把需要的動態庫檔案也拷貝進去,後面要把busybox支援的命令在/bin底下做一個soft link,讓系統起來之後有一些基本的命令可以用。

原理就上一篇講的那些做一個自動腳本,把目前的系統抽一個initrd.img的小rootfs出來用,讓系統開機使用RAM DISK的ROOTFS運作,也就是有一些系統商做的小LINUX系統的搞法。

這種系統就是比誰做的小,也就是要做剛好的系統,這有兩個大原則,一個是只載入要用的模組,另外一個是只放需要的命令,所以不要白痴的把所有的模組都放在裏面,也不需要把沒用到的動態函式庫放進去,這樣會讓你做出來的ROOTFS會肥到百M的尺寸,要小,這就需要挑,挑的方法也很簡單,就先看目前系統需要哪些模組,lsmod這個命令可以列出目前系統載入了哪些模組,之後靠modinfo -n去看那個檔案放哪,只抓要用到的東西,另外有些命令用到動態庫,要調用ldd -v 命令去看用了哪些動態庫,只需要拷貝要用到的動態庫就好,這樣才能夠做出比較小的系統,我做出來的initrd.img大概就16M左右,事實上還好沒很肥,反正是自動做的,這大小還可以接受。

本來只是要我做個小系統,讓測試人員可以直接開進去使用,要穩、好拷貝,所以才要我搞這個,我也是努力的花了一星期(含其他案子同時進行)搞出來,下次就開腳本去抽就有了,不需要再來煩我~~XD。

當然不免俗的這程式有BUG(我知道有個問題),有看出來的可以留言,我們討論一下~~XD


poCmd.pl

#!/usr/bin/perl -w

open FH,"<../cmd.list";
foreach my $line(< FH >){
 chomp($line);
 if($line ne ""){
 my $limitCounter=0;
 DOAGAIN:
  my @paths=("/bin/","/sbin/","/usr/bin/","/usr/sbin/");
  print "$line";
  my $found=0;
  foreach my $path(@paths){
   if(-e "$path$line"){
    print "\t- $path$line\n";
    system "cp $path$line bin/.";
    my @vars=`ldd -v $path$line | grep lib | grep :\$ | cut -d ':' -f 1`;
    foreach my $var(@vars){
     chomp($var);
     $var =~ s/\t//g;
     $var =~ s/\ //g;
     print "\t$var\n";
     my $libPath="lib/.";
     #$libPath = "lib64/." if($var =~ /x86\_64/i);
     system "cp $var $libPath";
    }
    $found = 1;
    $limitCounter=0;
    last;
   }
  }
  if($found == 0){
   print "Can't found command exist!";
   system "sudo apt-get install $line";
   exit 1 if($limitCounter >= 3);
   $limitCounter++;
   goto DOAGAIN;
  }
 }
}

close FH;

#process busybox softlink

print "make busybox command list\n";
chdir "bin/";
my @vars = `busybox | grep \,\ `;
foreach my $var (@vars){
 if($var !~ /copyright/i){
  chomp($var);
  $var =~ s/\ //g;
  $var =~ s/\t//g;
  my @cmds = split /\,/,$var;
  foreach my $cmd(@cmds){
   if($cmd !~ /\[/){
    print "[busybox command]\t$cmd\n";
    system "ln -s busybox $cmd";
   }
  }
 }
}

autofs.pl

#!/usr/bin/perl -w

use File::Basename;
#Auto make rootfs on Debin/Ubuntu
#Author Jones
print "Auto make rootfs for Debin/Ubuntu\n";
print "Author Jones Lai\n";

my $var = `uname -a`;

if(($var !~ /ubuntu/i)&&($var !~ /debin/i)){
 print "This script just made for Debin/Ubuntu\n";
 exit 1;
}

$var = `dpkg -l`;

if($var !~ /build\-essential/){
 print "Require build-essential\r\n";
 system "sudo apt-get install build-essential";
}

my @kernels = `ls /lib/modules/`;

@kernels = sort {$b cmp $a} @kernels;

my $lastKernel = $kernels[0];

chomp($lastKernel);

print "Last kernel version is $lastKernel\n";

if(!(-e "vmlinuz")){
 print "copy kernel to local\n";
 system "cp /boot/vmlinuz-$lastKernel ./vmlinuz";
}
if(!(-e "initrd.img")){
 system "mkinitramfs -o initrd.img $lastKernel";
}
if(!(-e "tmp")){
 system "mkdir tmp";
}else{
 system "rm tmp/ -fr";
 system "mkdir tmp";
}

chdir "tmp/";
system "gunzip < ../initrd.img | cpio --extract --preserve --verbose --no-absolute-filenames";
my @modules = `lsmod`;

open(FH,">conf/modules");

foreach my $module(@modules){
 chomp($module);
 next if($module =~ /module/i);
 my ($MOD,$others) = split /\ +/,$module;
 print FH "$MOD\n";
 my $var = `modinfo -n $MOD`;
 chomp($var);
 my $dirname  = dirname($var);
 system "mkdir -p .$dirname/\n";
 system "cp -p $var .$var\n";
 print "[copy module]: $var\n";
}
close(FH);

system "perl ../poCmd.pl";

system "cp ../init init";
system "chmod +x init";

$var = `find . | cpio --create --format='newc' > ../newinitrd`;
print "$var\n";
$var = `gzip ../newinitrd`;
print "$var\n";
$var = `mv ../newinitrd.gz ../newinitrd.img`;
print "$var";
$var = `rm ../initrd.img` if(-e "../initrd.img");
$var = `mv ../newinitrd.img ../initrd.img`;

chdir "../";