Pages

2011年12月28日 星期三

Unix-like Command

Join
範例一
$ cat a
3 aa
5 uu
4 bb
7 38
2 oo

$ cat b
uu test
38 abc
oo def

$ sort -k 2 a | sed "s/ /:/" > a.sorted  # 將主 key 做排序, 並將空白變成 ":"
7:38
3:aa
4:bb
2:oo
5:uu

Ubuntu Note

uBuntu 更換中文輸入法
  • 例如:要將 iBus 換成 gcin ##ReadMore##

    sudo update-alternatives --config xinput-zh_TW // 整個系統都切換

    或者

    im-switch -s gcin // 只切換該帳號

    設定完,登出再登入即可套用。
  • 網址:http://thaddeusnotes.blogspot.com/2010/09/ubuntu.html

apt 常用參數

apt-cache search [關鍵字] - 搜尋未安裝套件
apt-get install [套件名稱] - 安裝新套件
apt-get remove [套件名稱] - 移除套件但不刪除設定檔
apt-get remove --purge [套件名稱] - 刪除套件也刪除設定檔
apt-cache depends [套件名稱] - 顯示套件相依的相關資訊

apt-get clean - 移除所有套件的 deb 暫存檔
apt-get autoclean - 移除舊版所有套件的 deb 暫存檔
ps: apt 下載安裝過的檔案會放在 /var/cache/apt/archives/ 裡面,
apt-get clean / autoclen 會清除在裡面的檔案。所以只要沒執行過這兩個指
令,都可以到這個路徑看到已經有安裝過哪些檔案。

apt-cache show [套件名稱] - 列出套件詳細資訊
apt-cache stats - 套件庫中的資訊
apt-setup - 設定 /etc/apt/sources.list

apt-get update - 更新套件索引
apt-get upgrade - 升級已安裝套件
apt-get dist-upgrade - 考慮套件相依性進行升級
apt-get -s dist-upgrade - 列出過程中執行的動作
apt-get -d dist-upgrade - 僅下載相關套件,而不安裝
apt-get -y dist-upgrade - 用 yes 回答所有設定問答
apt-update.sh
#! /bin/bash
apt-get update
apt-get -y -d dist-upgrade
apt-get -s dist-upgrade
<來源> http://jctalk.blogspot.com/2008/01/apt.html


RPM 轉 DEB 工具 - Alien

Alien 是一個將 rpm, dpkg, stampede slp 及 slackware tgz 檔案格式間轉換的工具。

$ sudo apt-get install alien
$ sudo alien -d name-of-rpm-file.rpm

[註] 需要有 root 權限才可以轉換,當然轉換後可以依需要 chown、chgrp、chmod。


開機自動掛載 ntfs 檔案系統
/etc/fstab 可以自動掛載特定的檔案系統,特別在系統啟動的時候。在這個檔案裡,描述了檔案系統,掛載點和其他選項。下面是一個/etc/fstab檔案的內容:


/dev/hda1       /                      ext2          defaults              1 1
/dev/hda5       /home             ext2           defaults              1 2
/dev/hda6       swap              swap         defaults              0 0
/dev/fd0         /mnt/floppy     auto           sync,user,noauto,nosuid,nodev,unhide 0 0
/dev/cdrom     /mnt/cdrom     auto          user,noauto,nosuid,exec,nodev,ro 0 0
none                /proc              proc          defaults              0 0
none                /dev/pts          devpts      mode=0622        0 0


每一行的參數按照順序代表
  • 擁有檔案系統的裝置,
  • 掛載點
  • 檔案系統型態
  • 掛載選項
  • 備份程式dump的旗標
  • fsck(FileSystem Check)檢查分割區的順序
所以說,如果我想把 ntfs 的檔案系統在開機就自動掛載上去,就修改這個檔案。例如:

/dev/sda2 /media/disk ntfs defaults 0 2

然後在 /media 建立一個對應的資料夾: sudo mkdir /media/disk。至於最後一個參數通常就擺「2」,可以 man 看看。

使用 fuser 的指令
要怎麼找出是哪個程式掛在那個目錄上, 可以使用 fuser - identify processes using files or sockets

假設現在 mount 起來的目錄是 /media/share
查詢:  
               fuser -m /media/share
顯示:
               /media/share: 25023c

就代表是 process 25023(pid) 有使用到此目錄, 後面 c 代表的意思可參考下述:
c: current directory.
e: executable being run.
f: open file. f is omitted in default display mode.
F: open file for writing. F is omitted in default display mode.
r: root directory.
m: mmap'ed file or shared library.

要把這個資源釋放的話, 可以有下述做法:
1) kill -9 25023 # ps aux | grep 25023 
2) fuser -m -v -i -k /media/share # 會問你是不是要把 25023 這個 kill 掉, 選 y 就會 kill 掉
    訊息如下:
    USER PID ACCESS COMMAND
    /meida/share: root 25023 ..c.. bash
    Kill process 25023 ? (y/N) y

http://plog.longwin.com.tw/my_note-unix/2008/11/18/debian-ubuntu-linux-umount-device-busy-2008


不用密碼直接用 ssh 登入到遠端電腦
多年前 telnet 當道,但在安全意識漸漸浮上檯面之後,telnet 在登入時的安全就被大家質疑,後來 ssh (Secure Shell) 出現時,改變了當初的習慣,大家在管理 Linux 時,現在都使用 ssh 來登入,而 ssh 好處我在這也不再多做說明,因為他還可以配合 rsync 做出遠端備份,一旦設定好 ssh 之後,還會有 scp 可以使用!這樣就可以在不同電腦間 copy 檔案,並且為傳輸的資料加密了!

Shell Script & ssh

自動化的工作可以讓管理員有效率的完成目標,也不用浪費人力和時間做同樣的事情,在無人職守的情況下,要讓 script 自動連入遠端系統做事是件有些麻煩的事,因為您必需登入系統才可以繼續工作,為了不略過登入系統這個步驟,我們可以製做一個
public key 讓遠端的機器信任我們,如此就只要直接連入就可以,而不用再輸入帳號和密碼。


製作 public keys & private keys

利用 ssh-keygen 來做出公用和私有鑰匙,並傳送 public key 到遠端機器使其信任本機登入。

[steven@cute steven]$ ssh-keygen -t dsa

Generating public/private dsa key pair.
Enter file in which to save the key (/home/steven/.ssh/id_dsa):
Enter passphrase (empty for no passphrase): <- 不用輸入
Enter same passphrase again: <- 不用輸入
Your identification has been saved in /home/steven/.ssh/id_dsa.
Your public key has been saved in /home/steven/.ssh/id_dsa.pub.
The key fingerprint is:

fa:c9:a9:e4:d5:70:52:88:cc:f3:25:fd:68:ae:c4:4b steven@cute.com.tw

[steven@cute steven]$


接著,再到 /home/steven/.ssh 裡看看,會多出 id_dsa 和 id_dsa.pub 這兩個檔案。

[steven@cute steven]$ cd .ssh
[steven@cute .ssh]$ ls
id_dsa id_dsa.pub known_hosts
[steven@cute .ssh]$


現在我們要使遠端機器 mirror.abc.com,使用 sandy 登入時不用輸入密碼,因為,我們應該複製一份 id_dsa.pub 到 sandy@mirror.abc.com 去,並加入到 authorized_keys。


[steven@cute .ssh]$ scp id_dsa.pub sandy@mirror.abc.com:~/id_dsa_steven.pub
sandy@mirror.abc.com's password:
id_dsa.pub 100% |*****************************| 607 00:00

[steven@cute .ssh]$


登入 sandy@mirror.abc.com

[steven@cute .ssh]$ ssh sandy@mirror.abc.com
sandy@mirror.abc.com's password:
-bash-2.05b$ ls id_dsa_steven.pub
id_dsa_steven.pub
-bash-2.05b$ cat id_dsa_steven.pub >> .ssh/authorized_keys
-bash-2.05b$ exit

完成後離開,回到本機,再做一次 ssh 到 mirror.abc.com

[steven@cute .ssh]$ ssh sandy@mirror.abc.com
-bash-2.05b$

如此就不用輸入密碼就直接登入了!


保護你的私有金匙

在製做 dsa key 時,會有一份私有和一份公有金匙,實務上會保留起來,並做備份,因為當 ssh 在登入時,會使用 id_dsa.pub 和本機的 id_dsa 做確認,因此如果這兩者比對不成功時就會再次要求輸入密碼。

參考資料 http://www.l-penguin.idv.tw/article/ssh-keygen.htm


Ubuntu Linux下single模式無需密碼方法
  1. 按Esc進入系統啟動功能表,選擇"Ubuntu, kernel 2.6.20-16-generic (recovery mode),然後按e。
  2. 在第二層功能表選擇"kernel /boot/vmlinux-2.6.20-16-generic root=UUID=ae424e-bod0-475c-2342433 ro single" 按下e進行編輯。 
  3. 修改啟動參數為"kernel /boot/vmlinux-2.6.20-16-generic root=UUID=ae424e-bod0-475c-2342433 rw single init=/bin/bash" 然後按回車。 
  4. 按b啟動系統就可以進入single模式而不需要密碼了。
參考資料
http://big5.ccidnet.com:89/gate/big5/linux.ccidnet.com/art/302/20080605/1467721_1.html

VMWare 的 Linux 對於 mplayer 的設定
首先要先安裝 codec,不管是不是用 VMWare 都要裝:

sudo wget http://www.medibuntu.org/sources.list.d/gutsy.list -O /etc/apt/sources.list.d/medibuntu.list &&
wget -q http://packages.medibuntu.org/medibuntu-key.gpg -O- | sudo apt-key add - && sudo apt-get update && sudo apt-get install w32codecs


接著開啟 mplayer,"按右鍵 -> Performance -> Vedio -> 將 xv 改成 x11",重開 mplayer 即可。另外,記得要"按右鍵 -> Performance -> Font",選個字型檔載入。 例如:/usr/share/fonts/truetype/arphic/ukai.ttf


在 Windows 環境下遠端 Linux
做法一
這裡用 client 是 Windows XP;server 是 Ubuntu

Server端 - 安裝VNC
% sudo apt-get install x11vnc
% sudo x11vnc -storepasswd [password] /etc/x11vnc.pass
執行VNC
% sudo x11vnc

Client端至 http://www.realvnc.com/index.html 下載 vncviewer 安裝連線即可

做法二
直接用 Windows XP 的遠端工具即可,但沒嘗試過,參考網頁 http://axp1.csie.ncu.edu.tw/~rick/blog/?p=117


安裝字型
在終端機裡以「su」指令切換到「root」,也就是管理員的身份(請注意:在Ubuntu裡切換root身份的方法是用「sudo」,在Fedora裡也可以用sudo,不過筆者偏好使用su),然後把arialuni.ttf複製到「/usr/share/fonts/」目錄下:

程式碼
cp 來源子目錄/arialuni.ttf /usr/share/fonts/
fc-cache –v –f /usr/share/fonts/

注意:新安裝的系統可能會發生檔案時間在未來,以致fc-cache不能寫入的情況,解決方法就是用touch指令「碰」一下檔案,讓檔案時間變成現在:

touch /usr/share/fonts
touch /usr/share/fonts/*
touch /usr/share/fonts/*/*
touch /usr/share/fonts/*/*/*

再執行一次「fc-cache –v –f /usr/share/fonts/」即可。
做完這兩步,系統裡就多了這個字型讓你選用。GNOME使用者可以到「系統>偏好設定>外觀與感覺>字型」,KDE使用者是到「選單>控制中心>主題與外觀>字型」裡查看,是不是多了Arial Unicode MS這一項呢?要注意除了固定寬度字型,其他都可以改成Arial Unicode MS。

fc-cache指令是什麼?
fc-cache的功能是掃描字型檔的子目錄,然後再建立包含字型資訊的快取(cache)給應用程式。簡單的說就是讓系統登錄指定目錄的字型。在我們執行fc-cache後面的「-f」參數,是強迫寫入(force)的意思,而「-v」是顯示執行資訊(verbose)的意思。




參考資料
http://tech.chinatimes.com/2007Cti/2007Cti-News/Inc/2007cti-news-Tech-inc/Tech-Content/0,4703,171701+172007122502835,00.html


備份 VMWare 檔案,之後 restore 造成網路不通的情形發生
前鎮子重灌電腦,便把 VMWare 的 .vmdk 以及 .vmx 備份下來,重灌完之後直接把檔案 restore,結果發生網路不通,檢查一下發現在 /etc/udev/rule.d 當中 70-persistent-net.rules 的這一行:

# PCI device 0x1022:0x2000 (pcnet32)
SUBSYSTEM=="net", DRIVERS=="?*", ATTRS{address}=="00:0c:29:0b:8e:62", NAME="eth1"

linux 應該以 eth0 為預設的網卡,不過這邊會是 eth1,我想應該是因為當初是在有 Windows 的情況下去安裝 Ubuntu,雖然是共享實體網卡,不過虛擬機器讓 Ubuntu 以為有另一張網卡,所以才會是 eth1。

不過現在的情形我並沒有重新安裝 Ubuntu,而是直接 restore,在沒有依正常管道安裝的情況下,Ubuntu 會以預設的 eth0,去抓網卡,導致偵測不到。所以結論就是,只要把以上用螢光筆畫下來的部份改成 eth0 即可。


安裝 ubuntu 後的基本設定
紀錄一下安裝完畢後設定網路的部分,到 /etc/network/,sudo vi interfaces
將底下內容
# The primary network interface
auto eth0
iface eth0 inet dhcp

修改成底下的固定IP
# The primary network interface
auto eth0
iface eth0 inet static
address 140.113.208.102
netmask 255.255.255.0
gateway 140.113.208.254
dns-nameservers 140.113.1.1

然後 sudo /etc/init.d/networking restart
試著 ping tw.yahoo.com,如果發現有問題,可能是DNS設定有誤,到 /etc/resolv.conf
把 nameserver 改成你要的 DNS Server,然後再用上面指令 restart 一次即可
  1. sudo apt-get install build-essential 會安裝基本套件,例如 gcc、g++、linux-library、make, etc。
  2. sudo apt-get install openssh-server 安裝 ssh serevr,然後就可以用 putty or pietty 遠端連線。
  3. 如果要修改自己的PATH,只要修改 ~/.profile 此檔案即可。


scim
搜尋 scim 會列出相關的套件,安裝 scim 、 scim-gtk2-immodule 、 scim-modules-socket (或是 scim-frontend-socket)、scim-chewing 還有 scim-tables-zh (如果原本就已經安裝就可以略過)。選好要安裝這些套件後就按下「套用」它就會開始安裝了。

然後在終端機下執行下列指令:

sudo gedit /etc/X11/Xsession.d/74scim

然後在這個檔案中加入下列內容:

export XMODIFIERS="@im=SCIM"
export GTK_IM_MODULE=SCIM
export QT_IM_MODULE=SCIM
export XIMPROGRAM="scim -d"
scim -d

接著你只需要重新啟動桌面環境就可以了,重新啟動的方法:
  1. Ctrl + Alt + F1 切到 console mode 下登入。
  2. 執行 sudo /etc/init.d/gdm restart

重新啟動 gnome

1) sudo /etc/init.d/gdm restart
2) Ctrl + Alt + backspace


Bash的環境設定說明
和Bash的環境設定有關的檔案有

/etc/profile (主要)
$HOME/.bash_profile (主要)
$HOME/.bash_login
$HOME/.profile
$HOME/.bash_logout (主要)
$HOME/.bashrc (主要)
/etc/bashrc

登入(login)時
先執行 /etc/profile,接著bash會檢查使用者的自家目錄中,是否有 .bash_profile 或者 .bash_login 或者 .profile,若有,則會執行其中一個,執行順序為:

.bash_profile 最優先
.bash_login其次
.profile 最後

這三個檔案只有在登入時,才會被bash讀取

登出(exit/logout)時
bash會檢查使用者自家目錄中是否有 .bash_logout,若有,則bash會執行其中的指令

登入後啟動一個新的shell
此時我們稱之為一個 subshell, 也就是說在命令列中鍵入 bash, 除了原先登入時的bash 之外, 又另外啟動了一個新的 bash shell.

bash 會檢查使用者的自家目錄中是否有 .bashrc,若有則予以執行,可以在在各檔中加入 echo 指令, 以觀察其執行順序。

各檔案用途說明
/etc/profile 由 root 所控管, 用來設定適合全體使用者的shell環境若使用者自己覺得 /etc/profile 的設定, 並不合意, 可以修改自家目錄中的 .bash_profile既然有了 .bash_profile。

為何要有 .bash_login 及 .profile ?這是因為有些人可能是從 Bourne shell 移轉過來的, 那麼, 只要將 Bourne shell 主要的起動檔 .profile 移到自家目錄中, 放棄使用 .bash_profile 及 .bash_login 即可繼續沿用以前的設定環境。

.bash_login 存在理由或許和 c shell 有關, 但因為 bash 和 c shell 二者語法並不完全相容, 因此, 並不建議將 c shell 的啟動檔直接移過來使用。

.bashrc 則是用來設定 subshell 的環境的, 之所以要有這個 .bashrc 是為免 subshell產生時, 又重覆將 /etc/profile 執行一次. 我們發現 .bashrc 中已預先會去執行 /etc/bashrc 的指令, 這表示, 或許 root 會將產生 subshell 時的環境設好了,使用者只要沿用 /etc/bashrc 的內容, 應該不會有任何問題.

.bash_logout 是使用者登出主機之前, 會去執行的設定檔, 如果使用者希望在他登出系統之後, 能幫他自動處理一些瑣事, 比如: 清除暫存檔, 清除螢幕等, 可以在這個檔案中加以設定.

http://linux.tnc.edu.tw/techdoc/Bash-env.htm


vim相關設定
若出現以下語法不支援之類的訊息,就去確認一下是否有安裝 vim,有可能只有安裝 vim-tiny 或者 vim-common,導致以下的設定檔無法使用,可以利用 sudo dpkg -l | grep vim 檢查。

/etc/vim/vimrc
-----------------------------------------------------------------
set nocompatible " use vim defaults
set ls=2 " allways show status line
set tabstop=4 " numbers of spaces of tab character
set shiftwidth=4 " numbers of spaces to (auto)indent
set scrolloff=3 " keep 3 lines when scrolling
set cindent " cindent
set smartindent " smart indent
set autoindent " always set autoindenting on
set showcmd " display incomplete commands
set hlsearch " highlight searches " 將搜尋所符合的關鍵字全部 highlight
set incsearch " do incremental searching
set ruler " show the cursor position all the time " 等同於 set ru,會在右下角顯示游標所在的行數及列數
set visualbell t_vb= " turn off error beep/flash
set novisualbell " turn off visual bell
set nobackup " do not keep a backup file
set number " show line numbers
set ignorecase " ignore case when searching
"set noignorecase " don't ignore case
set title " show title in console title bar
set ttyfast " smoother changes
"set ttyscroll=0 " turn off scrolling, didn't work well with PuTTY
set modeline " last lines in document sets vim mode
set modelines=3 " number lines checked for modelines
set shortmess=atI " Abbreviate messages
set nostartofline " don't jump to first character when paging
set whichwrap=b,s,h,l,<,>,[,] " move freely between files

"set autowrite " auto saves changes when quitting and swiching buffer
"set expandtab " tabs are converted to spaces, use only when required
"set sm " show matching braces, somewhat annoying...
"set nowrap " don't wrap lines
set mouse=a
set backspace=2

if version>=600
syntax on " syntax highlighing

"---- Options for Windows
if has("gui_running")
set guifont=Courier " use this font
set lines=50 " height = 50 lines
set columns=100 " width = 100 columns
set background=light " adapt colors for background
set selectmode=mouse,key,cmd
set keymodel=
else
set background=dark " adapt colors for dark background
colorscheme elflord " use this color scheme
endif

" Filetypes (au = autocmd)
au filetype help set nonumber " no line numbers when viewing help
au filetype help nnoremap <buffer><cr> <c-]> " Enter selects subject
au filetype help nnoremap <buffer><bs> <c-T> " Backspace to go back

" When using mutt, text width=72
"au filetype mail set textwidth=72
"au filetype tex set textwidth=72
"au BufRead mutt*[0-9] set tw=72

" Automatically chmod +x Shell and Perl scripts
au BufWritePost *.sh !chmod +x %
au BufWritePost *.pl !chmod +x %

" File formats
au BufNewFile,BufRead *.pls set syntax=dosini
au BufNewFile,BufRead modprobe.conf set syntax=modconf
endif

" Keyboard mappings
map <F1> :previous<CR> " map F1 to open previous buffer
map <F2> :next<CR> " map F2 to open next buffer
map <silent> <C-N> :silent noh<CR> " turn off highlighted search
map ,v :sp ~/.vimrc<cr> " edit my .vimrc file in a split
map ,e :e ~/.vimrc<cr> " edit my .vimrc file
map ,u :source ~/.vimrc<cr> " update the system settings from my vimrc file
"----- write out html file
map ,h :source $VIM/vim70/syntax/2html.vim<cr>:w<cr>:clo<cr>:n<cr>,h

" Common command line typos
"cmap W w
"cmap Q q

" Keyboard mapping for numeric keypad " 對應鍵盤右邊的按鍵
imap <Esc>OM <c-m>
map <Esc>OM <c-m>
imap <Esc>OP <nop>
map <Esc>OP <nop>
imap <Esc>OQ /
map <Esc>OQ /
imap <Esc>OR *
map <Esc>OR *
imap <Esc>OS -
map <Esc>OS -

imap <Esc>Ol +
imap <Esc>Om -
imap <Esc>On ,
imap <Esc>Op 0
imap <Esc>Oq 1
imap <Esc>Or 2
imap <Esc>Os 3
imap <Esc>Ot 4
imap <Esc>Ou 5
imap <Esc>Ov 6
imap <Esc>Ow 7
imap <Esc>Ox 8
imap <Esc>Oy 9
imap <Esc>Oz 0
" All system-wide defaults are set in $VIMRUNTIME/debian.vim (usually just
" /usr/share/vim/vimcurrent/debian.vim) and sourced by the call to :runtime
" you can find below. If you wish to change any of those settings, you should
" do it in this file (/etc/vim/vimrc), since debian.vim will be overwritten
" everytime an upgrade of the vim packages is performed. It is recommended to
" make changes after sourcing debian.vim since it alters the value of the
" 'compatible' option.

" This line should not be removed as it ensures that various options are
" properly set to work with the Vim-related packages available in Debian.
runtime! debian.vim

" Uncomment the next line to make Vim more Vi-compatible
" NOTE: debian.vim sets 'nocompatible'. Setting 'compatible' changes numerous
" options, so any other options should be set AFTER setting 'compatible'.
"set compatible

" Vim5 and later versions support syntax highlighting. Uncommenting the next
" line enables syntax highlighting by default.
syntax on

" If using a dark background within the editing area and syntax highlighting
" turn on this option as well
set background=dark

" Uncomment the following to have Vim jump to the last position when
" reopening a file
if has("autocmd")
au BufReadPost * if line("'\"") > 0 && line("'\"") <= line("$")
\| exe "normal g'\"" | endif
endif

" Uncomment the following to have Vim load indentation rules according to the
" detected filetype. Per default Debian Vim only load filetype specific
" plugins.
if has("autocmd")
filetype indent on
endif

" The following are commented out as they cause vim to behave a lot
" differently from regular Vi. They are highly recommended though.
set showcmd " Show (partial) command in status line.
set showmatch " Show matching brackets.
"set ignorecase " Do case insensitive matching
"set smartcase " Do smart case matching
"set incsearch " Incremental search
"set autowrite " Automatically save before commands like :next and :make
"set hidden " Hide buffers when they are abandoned
"set mouse=a " Enable mouse usage (all modes) in terminals

" Source a global configuration file if available
" XXX Deprecated, please move your changes here in /etc/vim/vimrc
if filereadable("/etc/vim/vimrc.local")
source /etc/vim/vimrc.local
endif
http://packages.ubuntu.com/edgy-backports/editors/vim-common


USB 無法自動讀入
錯誤訊息:
Cannot mount volume
Unable to mount the volume 'IBM_PRELOAD'

Details:
Volume is scheduled for check. Please boot into Windows TWICE, or use the 'force' mount option. For example type on the command line:

mount -t ntfs-3g /dev/sdb1 /media/IBM_PRELOAD -o force

Or add the option to the relevant row in the /etc/fstab file: /dev/sdb1 /media/IBM_PRELOAD ntfs-3g defaults,force 0 0

手動掛載吧!
sudo mkdir /media/windows
sudo mount -t ntfs-3g -o force /dev/sdb1 /media/windows

看不到中文的話,執行 sudo mount -o iocharset=utf8 /dev/sdb2 /media/USB/

.NET Framework Note&Environment

MessageBox::Show

<<範例>>##ReadMore##
String ^message = gcnew String(L"Your background is complicated. Pls move to another place!");
String ^caption = gcnew String(L"Check Result");
::DialogResult result;

// Displays the MessageBox
result = MessageBox::Show(this, message, caption, MessageBoxButtons::OK, MessageBoxIcon::Information);

if(result == ::DialogResult::OK)
{
    this->Close();
}



Dialog Used

  • 如果想在按下按鈕後,彈出 Dialog,就需要在按鈕按下的事件加入以下內容
    if(m_OpenImageDialog->ShowDialog() == System::Windows::Forms::DialogResult::OK)
    {
    char *filename = (char*)(void*)Marshal::StringToHGlobalAnsi(m_OpenImageDialog->FileName);
    // Add anything what you want
    }

  • 如果想加入副檔名的 filter,就加在 Dialog::Filter (記得從 Designer 的屬性欄修改)

    bmp files (*.bmp)|*.bmp|jpg files (*.jpg)|*.jpg|All files (*.*)|*.*

  • 也可以設定每次開始的起始目錄 (Dialog::InitialDirectory)


    c:\\
KeyBoard Hook (DllImport)

<<宣告>>
bool isHide;
IntPtr m_HookHandle;
 
// 利用函式物件封裝 Hook Procedure
delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam); 

[DllImport("user32.dll", CharSet = CharSet::Ansi, CallingConvention = CallingConvention::StdCall)]
static IntPtr SetWindowsHookEx(int idHook, HookProc ^lpfn, IntPtr hInstance, int threadId);

[DllImport("user32.dll", CharSet = CharSet::Auto, CallingConvention = CallingConvention::StdCall)]
static int CallNextHookEx(IntPtr idHook, int nCode, IntPtr wParam, IntPtr lParam);

[DllImport("user32.dll", CharSet = CharSet::Auto, CallingConvention = CallingConvention::StdCall)]
static bool UnhookWindowsHookEx(IntPtr idHook);

<<定義>>
int KeyBoardHookProc(int nCode, IntPtr wParam, IntPtr lParam)
{
     PKBDLLHOOKSTRUCT keyInfo; // KBDLLHOOKSTRUCT Pointer
     keyInfo = (PKBDLLHOOKSTRUCT)lParam.ToPointer();

     DWORD code = keyInfo->scanCode;

     bool isPressed = ((keyInfo->flags & 0x80) == 0);
 
     if(isPressed && code == 0x20) // If D is pressed
    {
         /*
          * do what you want to do
          */
          return CallNextHookEx(IntPtr(0), nCode, wParam, lParam); 
    }
}

<<使用>>
1. 建立Hook
      HookProc ^hook = gcnew HookProc(this, &MainForm::KeyBoardHookProc);
     m_HookHandle = SetWindowsHookEx(WH_KEYBOARD_LL, hook, IntPtr(0), 0);
 

2. 釋放Hook
    UnhookWindowsHookEx(m_HookHandle);
 

<<注意事項>> 
  1. 一次押放,會收到兩次相同的 scan code,但 flag 不同 (0x00 & 0x80)。所以,若只想要處理一次,就需要去判斷 flag。 
  2. 若不使用 Hook,記得釋放該資源。 
  3. Hook Keyboard Tool .. (see)。 
  4. 記得在 stdafx.h 補上#define _WIN32_WINNT 0x0600,要寫在其他檔案也行,數值必須大於 0x0400。我寫 0x0600 是 Vista 定義的。

Timer Type
我目前使用的 Timer,是定義在 System::Windows::Forms 的類別,它是 Single-thread;如果要使用 Multi-thread Timer,則要使用 System::Timer。


Convert Class

<<例如>>
int a = Convert::ToInt32(gcnew String(L"123"));   // a = 123


The purpose of  /D

在「Project 右鍵 -> Properties -> Configuration Properties -> C/C++ -> Command Line」可以看到這個「/D」參數,它的目的就是幫你「#define」。如果你有一些 Debug 視窗,希望不要在 Release 版本出現,就需要在編譯的時候告知編譯器。VS2005 C++ Debug 版本的編譯器參數如下:
/Od /D "WIN32" /D "_DEBUG" /D "_UNICODE" /D "UNICODE" /FD /EHa /MDd /Fo"Debug\\" /Fd"Debug\vc80.pdb" /W3 /nologo /c /Zi /clr /TP /errorReport:prompt /FU "c:\Windows\Microsoft.NET\Framework\v2.0.50727\System.dll" /FU "c:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Data.dll" /FU "c:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Drawing.dll" /FU "c:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Windows.Forms.dll" /FU "c:\Windows\Microsoft.NET\Framework\v2.0.50727\System.XML.dll"


Get Specific Folder Path

<<網址>>
Environment::SpecialFolder 列舉型別

<<例如>>
Environment::GetFolderPath(Environment::SpecialFolder::Desktop);

參考資料


在 Visual C++ .NET 中將 System::String* 轉換為 Char*
<<方法一>>

http://support.microsoft.com/kb/311259

<<方法二>>
//using namespace System::Runtime::InteropServices;System::String * str = S"Hello world\n";
char *str2 = (char*)(void*)Marshal::StringToHGlobalAnsi(str);
printf(str2);
Marshal::FreeHGlobal(str2); => 會有錯誤訊息,改成 Marshal::FreeHGlobal(Intptr(str2));


利用滑鼠事件框出ROI


MyClass(void)
{
   InitializeComponent();
   m_Width = 640;
   m_Height = 480;

   m_Pressed = false;
   m_ROIPoint = System::Drawing::Point(0, 0);
   m_ROIRect = System::Drawing::Rectangle(0, 0, m_Width, m_Height);
   m_OriginBMP = gcnew Bitmap(m_Width, m_Height);
}

int m_Width;

int m_Height;
bool m_Pressed;
System::Drawing::Point m_ROIPoint;
System::Drawing::Rectangle m_ROIRect;
System::Drawing::Bitmap ^m_OriginBMP;

....


void FormMouseDown(System::Object ^sender, System::Windows::Forms::MouseEventArgs ^e)

{
   m_ROIPoint.X = e->X;
   m_ROIPoint.Y = e->Y;
   m_Pressed = true;
}
void FormMouseMove(System::Object ^sender, System::Windows::Forms::MouseEventArgs ^e)
{
  if(m_Pressed)
  {
    System::Drawing::Rectangle cloneRect = System::Drawing::Rectangle(0, 0, m_Width, m_Height);
    System::Drawing::Imaging::PixelFormat format = m_OriginBMP->PixelFormat;
    Bitmap ^clone = m_OriginBMP->Clone(cloneRect, format);

    System::Drawing::Rectangle rect = System::Drawing::Rectangle(0, 0, 0, 0);
    rect.X = min(e->X, m_ROIPoint.X);
    rect.Y = min(e->Y, m_ROIPoint.Y);
    rect.Width = max(e->X, m_ROIPoint.X) - rect.X;
    rect.Height = max(e->Y, m_ROIPoint.Y) - rect.Y;

    Graphics ^bmpGraphics = Graphics::FromImage(clone);

    bmpGraphics->DrawRectangle(Pens::Red, rect);
    delete bmpGraphics;
    Graphics ^g = this->CreateGraphics();
    g->DrawImage(clone, Point(0,0));
    delete clone;
    //g->DrawString(String::Format("x={0}, y={1}", e->X.ToString(), e->Y.ToString()), this->Font,    Brushes::Black, 320, 20);
    //g->DrawString(String::Format("x={0}, y={1}", rect.X.ToString(), rect.Y.ToString()), this->Font, Brushes::Black, 320, 30);
    //g->DrawString(String::Format("w={0}, h={1}", rect.Width.ToString(), rect.Height.ToString()), this->Font, Brushes::Black, 320, 40);
    delete g;
  }
}
void FormMouseUp(System::Object ^sender, System::Windows::Forms::MouseEventArgs ^e)
{
   System::Drawing::Rectangle cloneRect = System::Drawing::Rectangle(0, 0, m_Width, m_Height);
   System::Drawing::Imaging::PixelFormat format = m_OriginBMP->PixelFormat;
   Bitmap ^clone = m_OriginBMP->Clone(cloneRect, format);
   m_ROIRect.X = min(e->X, m_ROIPoint.X);
   m_ROIRect.Y = min(e->Y, m_ROIPoint.Y);
   m_ROIRect.Width = max(e->X, m_ROIPoint.X) - m_ROIRect.X + 1;
   m_ROIRect.Height = max(e->Y, m_ROIPoint.Y) - m_ROIRect.Y + 1;
   m_Pressed = false;

   Graphics ^bmpGraphics = Graphics::FromImage(clone);

   bmpGraphics->DrawRectangle(Pens::Blue, m_ROIRect);
   delete bmpGraphics;

   Graphics ^g = this->CreateGraphics();

   g->DrawImage(clone, Point(0,0));
   delete clone;
   delete g;

如果來源影像和繪圖範圍不一致,那框出來的區域就需要線性調整。


pin_ptr<type>
[註] 會需要 pin,是因為要傳入指標的位址, 而 cli::interior_ptr 是 managed code,非原生,像「cvSetCaptureProperty(m_Capture, CV_CAP_PROP_FRAME_WIDTH, m_Width);」的第一個參數就不用 pin。
  • 另外有一種方式是 Interop(沒用過);如果沒有 source code,可以使用 DllImport。
Bitmap 相關

想使用 1-byte 表示顏色,需要修改調色盤,但這不是真正的灰階(76KB),因為這種做法檔案大小僅(44KB)好像只透過 256 種色彩來表示,另外存檔的檔頭,開頭也不是「BM」


就是要把調色盤的「rgb」各分量設定成一樣,以下是 C# 程式碼:(附帶一提,unsafe 的出現,意味可以讓 C# 使用指標「*」)

static unsafe Bitmap ConvertToMonochrome(Bitmap bmColor)
{
     int cx = bmColor.Width;
     int cy = bmColor.Height;

     // Create a new 8-bit indexed bitmap of the same size 
    Bitmap bmMono = new Bitmap(cx, cy,    PixelFormat.Format8bppIndexed); 
    bmMono.SetResolution(bmColor.HorizontalResolution,  bmColor.VerticalResolution); 
     // Set the palette for gray shades 
     ColorPalette pal = bmMono.Palette; 
     for (int i = 0; i < pal.Entries.Length; i++) 
         pal.Entries[i] = Color.FromArgb(i, i, i); 
     bmMono.Palette = pal; 
     // Because SetPixel won't work with index bitm aps, we need 
     // direct access to the bits
     BitmapData bmd = bmMono.LockBits(new Rectangle(Point.Empty, bmMono.Size), 
         ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);


     // Scan through the pixels, converting color to B&W
     Byte *pPixel = (Byte *) bmd.Scan0;

     for (int y = 0; y < cy; y++)
    {
         for (int x = 0; x < cx; x++)
        {
            Color clr = bmColor.GetPixel(x, y);
            Byte byPixel = (byte) ((30 * clr.R + 59 * clr.G + 11 * clr.B) / 100); 
            pPixel[x] = byPixel; } pPixel += bmd.Stride;
         } 
         bmMono.UnlockBits(bmd);
         return bmMono;
    }

OpenCV 一個 channel、位元深度 8,似乎也是索引色 (由 photoshop 顯示)


Contruct Bitmap via row data
// 需要這樣建構應該和取得的影像正反有關
Bitmap^ GetBitmapObj(void* scan0, int width, int height)
{
int stride = width*3;
Byte *ptr = (Byte*)scan0;
ptr += (height-1)*stride;

Bitmap ^capturedImage = gcnew Bitmap(width, height, -stride,
System::Drawing::Imaging::PixelFormat::Format24bppRgb, (IntPtr)ptr);

return capturedImage;
}

<<注意事項>>

  1. 建構出來的 Bitmap,LockBit 後的 BitmapData->Stride 會是負的。如果用 Graphics::FromImage 把這個 Bitmap 畫到另一個 Bitmap2,則 Bitmap2 的 Stride 就會是正的。而且存取他無法用以下方式:

    // Remember UnlockBits BitmapData ^ProcessImageData =
    ProcessImage->LockBits(
    System::Drawing::Rectangle(0, 0, width, height),
    ImageLockMode::ReadWrite, PixelFormat::Format24bppRgb);
    Byte *ProcessImageDataPtr = (Byte*)ProcessImageData ->Scan0.ToPointer();

    for(i = 0; i < height; ++i)
    {
    for(j = 0; j < width; ++j, ProcessImageDataPtr +=3)
    {
    ....
    }
    }

    會造成非法記憶體存取,所以還是得先畫到另一張 bitmap 上面

    System::Drawing::Rectangle region = System::Drawing::Rectangle(0, 0, width, height);
    Bitmap ^regionImage = (gcnew System::Drawing::Bitmap(width, height));
    Graphics ^regionImageGraphics = Graphics::FromImage(regionImage);
    GraphicsUnit units = GraphicsUnit::Pixel;
    regionImageGraphics->DrawImage(m_ProcessImage, region, *rect, units);

  2. Bitmap 的建構,實際存放像素的記憶體就是第一個參數 void* scan0,也就是直接參考這一塊,如果要釋放 Bitmap,記得先把 scan0 釋放掉。

LockBits && UnlockBits 解釋 (for Bitmap)

LockBits and UnlockBits must be used as a pair. A call to the LockBits method of a Bitmap object establishes a temporary buffer that you can use to read or write pixel data in a specified format. After you write to the temporary buffer, a call to UnlockBits copies the pixel data in the buffer to the Bitmap object. If the pixel format of the temporary buffer is different from the pixel format of the Bitmap object, the pixel data is converted appropriately.


(UnlockBits 後,資料才會寫回去 Bitmap;如果沒做這個動作,還去對 Bitmap 存取,會發生 exception)
 

BitmapData ^imageData = image->LockBits( Rectangle(0, 0, width, height),
ImageLockMode::ReadOnly, PixelFormat::Format24bppRgb);
image->UnlockBits(imageData);


.NET 注意事項

  1. 「ref class」裡頭不能有非「ref class」或者「unmanage type」的成員變數。(基本型別不在此限)
    指標變數代替實體變數的宣告(此時CLR會把它解讀成 cli::interior_ptr),但使用這個還是有限制,就是需要傳指標的位址給函式,就會有錯誤訊息,所以這時必須要使用 pin 的機制。
  2. 「ref class」的成員函式,使用區域變數時,允許使用非「ref class」或者「unmanage type」的成員變數,就連指標也不用 pin。
    無。
  3. 「ref class」的成員函式不能用 const type 做為回傳值。
    ^ 回傳 manage object handle
  4. 利用「Marshal」配置「unmanaged memory」的語法。

    IntPtr m_Scan0 = Marshal::AllocHGlobal(bufSize);

    Marshal::FreeHGlobal(m_Scan0);

  5. Managed class or struct 不能給建構函式的參數預設值。
    無。

  6. 使用「native static library」,「Common Language Runtime support」若維持預設的「/clr:pure」,會造成「link error」。
    參數改成「/clr」即可。

  7. 在「.NET」的環境下,要讓managed & unmanaged code存在同一個專案,可以擺放在不同檔案中:
    a.cpp是一個純c++的檔案;b.cpp「ref class」的檔案
    當「b.cpp
    需要靠a.cpp做某些事情,可以在a.cpp開一個介面給b.cpp」去呼叫,但我猜測不能在介面參數中含有unmanaged type」,不然像 1. 的情況,我想還是會發生
  8. 「ref class」的成員變數,如果當成參數丟給「unmanaged function」,會有錯誤訊息發生,例如:
    MyFun(int &count);

    ref class MyClass {
         ...
         public:
         int m_Count;
    };

    ...;

    MyFunc(count); // 發生 can not convert int to int&

    先用區域變數丟進去接值,爾後再指派給成員變數。
IDE 設定
Precompilered Header 的設定可以讓編譯過程加快,但有時候它會 cache 到舊的東西,反而造成困擾,所以可以選擇關掉它,可以在
Properties -> Configuration Properties -> C/C++ -> Precompilered Headers -> Create/Use Precompilered Header,改成 No Using Precompilered Headers」。

Linux Driver Build & Common Struct/API

msleep

說明:對 msleep() 的調用是不可中斷的,能確保進程睡眠至少給定的毫秒數。
 

     void msleep(unsigned int msecs)
     {
         unsigned long timeout = msecs_to_jiffies(msecs) + 1;
         while (timeout)
             timeout = schedule_timeout_uninterruptible(timeout);
     } ##ReadMore##

參考



dmesg
  • 在 kernel module 中 printk,可以透過 dmesg 印出訊息,至於訊息的內容,<x> 是優先權[xxxx.xxxxxx] 是時間,單位是秒
<4>[ 8544.290000] 14: get interrupt
<4>[ 8544.290000] [x,y]=(583,259)14: input_sync
<4>[ 8544.320000] [x,y]=(583,259)14: input_sync



Device Node

  • MKDEV (引入標頭檔 #include <linux/cdev.h>)mudule_init 的 callback function 中加入
    -----------------------------------------------------
    struct cdev * Videocdev;
    int ret;
    dev_t devNum;
    devNum = MKDEV(88, 0);
    ret = register_chrdev_region(devNum, 2, "/dev/video1" );
    Videocdev = cdev_alloc(); // struct cdev *cdev_alloc(void);
    cdev_add( Videocdev , devNum , 1 ); // int cdev_add(struct cdev *, dev_t, unsigned);

    Videocdev->owner = THIS_MODULE; // #define THIS_MODULE (&__this_module)
    Videocdev->ops = &uvc_fops;
    mudule_exit 的 callback function 中加入
    -----------------------------------------------------
    cdev_del(Videocdev); // void cdev_del(struct cdev *);
    unregister_chrdev_region(MKDEV(88, 0) , 2); // extern void unregister_chrdev_region(dev_t, unsigned);
    另外在程式中用 mknod 建立裝置節點。


Copy Function

  • copy_to_user
    當應用程式利用 read 讀取某裝置的資料,例如 camera,整個呼叫過程是:read -> ... -> sys_read -> ... -> read file operation,然後 uvcvideo driver 裡頭實作 read file operation,就會透過 copy_to_user,把資料複製給應用程式。
  • copy_from_user
    當應用程式利用 write 寫入資料到某裝置,就會呼叫這支 API。

Task Struct

  • task_struct這個結構定義在 #include/linux/sched.h,他就是我們熟知的 Process Control Block,他紀錄著一個行程的一大堆資訊,參考網址。如果我們想要知道目前取得 cpu 執行的程式名稱、process id 等等,可以透過巨集 current 來得到。
char process_name[] = current->comm; // 目前執行的應用程式名稱
pid_t process_id = current->tgid; // process id (thread group id)
pid_t thread_id = current->pid // thread id

[註] 若沒有使用執行緒,則 pid = tgid。


Atomic Operation

  • 連動運算(Atomic Operation)
    我們平常做的 ++a 其實是兩個動作:加法與指派。為了確保這兩個指令連續執行,所衍生的產物。若沒有這種東西,而程式開發者也未對資料做同步,就會因為 race condition 造成資料上的問題。

#include <asm/atomic.h> // typedef struct { int counter; } atomic_t;

atomic_t count = ATOMIC_INIT(0);

atomic_dec(&
count); // 將值遞減1

atomic_inc_return(&
count); // 將值遞增1,並回傳目前的值(回傳整數)

atomic_inc(&
count); // 將值遞增1

atomic_read(&
count);
// 讀取目前的值(回傳整數)

...


Asynchronous Notification

  • 異步通知(Asynchronous Notification)
    這是驅動程式用來發送 signal 給應用程式的方式,而發送的訊號類型為 SIGIO原始碼
For Kernel Space(device driver)

#include <linux/fs.h>

static struct fasync_struct async_queue; // 資料結構

kill_fasync(&async_queue, SIGIO, POLL_IN); // 發送通知給 file owner

static int uvc_v4l2_fasync(int fd, struct file* filp, int mode)
{
     int ret = fasync_helper(fd, filp, mode, &async_queue); // 加入異步通知的名單
     return ret;
}

uvc_v4l2_fasync(-1, file, 0); // 從異步名單中移除

struct file_operations uvc_fops = {
     .owner = THIS_MODULE,
     .open = uvc_v4l2_open,
     .release = uvc_v4l2_release,
     ....
     .fasync = uvc_v4l2_fasync, // 新增 file operation
};

For User Space(application)

#include <signal.h>
#include <fcntl.h>

// 利用 F_SETFL 請求加入通知名單,這會對應到 .fasync operation
void set_fcntl(int fd) 
{
     int ori_flags;
     fcntl(fd, F_SETOWN, getpid());
     ori_flags = fcntl(fd, F_GETFL);
     fcntl(fd, F_SETFL, ori_flags|FASYNC); // 加入通知名單
}

int main(int argc, char* argv[])
{
     ...
     int fd = open("test.txt");
    
set_fcntl(fd);
     ...
     return 0;
 
}



IPC

因為 driver 是 kernel module 的一部分,是用來提供給許許多多應用程式使用,所以函式大多定義成 reentrant,這時候如果有共享資料,就必須考慮的同步。
  • 同步控制(一):Semaphore
#include <asm/semaphore.h>
static struct semaphore sem;


sema_init(&sem, 1); // 可自行設定初值

init_MUTEX_LOCKED(&sem); // 初值為0
init_MUTEX_UNLOCKED(&sem);
// 初值為1

if(down_interruptible(&sem)) return -ERESTARTSYS; // sem--,可中斷的版本

Critical Section
up(&sem);
  • 同步控制(二):Mutex
#include <linux/mutex.h>
struct mutex mymutex;

mutex_init(&mymutex);
// 設定初值,且值為1



mutex_lock(&mymutex);
Critical Section
mutex_unlock(&mymutex);

不論使用何種同步控制,一定要把值恢復,否則一堆行程都會被卡死,造成系統當機。另外還有一種叫作「spinlock」,和上述兩種的差異是,spinlock 是 busy-waiting 的,所以,該同步控制的 Critical Section 絕對不能休眠(ready->sleep)。
  • 同步控制(三):Spinlock
#include <linux/spinlock.h>

static spinlock_t rw_lock;

spin_lock_init(&rw_lock);

spin_lock_irqsave(&rw_lock, irq_flags);
spin_unlock_irqrestore(&rw_lock, irq_flags);

spin_lock(&rw_lock);
spin_unlock(&rw_lock);


參考資料:http://www.lslnet.com/linux/f/docs1/i34/big5261423.htm



Build Kernel/Driver

驅動程式編譯的方式有兩種,一種是透過 /usr/src/ 底下的 kernel source tree(kernel header);另一種就是直接和 kernel source code 一起編譯。以下要說明若使用後者時,應該如何設定 Kconfig 和 Makefile:

假設要將 EC driver 加入,先放置到相關的資料夾中(應該是 xxx/driver/ 之類的),然後需修改四個檔案:
  1. driver/Kconfig 
  2. driver/Makefile
  3. driver/compalec/Kconfig 
  4. driver/compalec/Makefile

底下簡單截取片段...

driver/Kconfig
# drivers/Kconfig
menu "Device Drivers"
...
source "drivers/auxdisplay/Kconfig"
source "drivers/uio/Kconfig"
source "drivers/android/Kconfig"
source "drivers/uvcvideo/Kconfig"
source "drivers/compalec/Kconfig"
endmenu

driver/Makefile
obj-y += android/
obj-$(CONFIG_SSB) += ssb/
obj-$(CONFIG_VIRTIO) += virtio/

obj-$(CONFIG_UVCVIDEO) += uvcvideo/
obj-$(CONFIG_COMPAL_EC) += compalec/

driver/compalec/Kconfig
#
# Block device driver configuration
#
menuconfig COMPAL_EC_DRIVER
bool "Compal Ec"
default y
---help---
Say Y here to get to see options for Compal Ec driver.
If you say N, all options in this submenu will be skipped and disabled;
only do this if you know what you are doing.
if COMPAL_EC_DRIVER # if choosing menuconfig item, keep going config COMPAL_EC
tristate "Compal Ec driver"
---help---
This is the Compal Ec driver. Say Y or M.
endif # COMPAL_EC_DRIVER

driver/compalec/Makefile
EcIO-objs := EcIOMain.o EcLowerApi.o EcIndexMode.o EcPortCtrl.o Ec686C.o
obj-$(CONFIG_COMPAL_EC) += EcIO.o

[註] 下 make menuconfig 去重編 kernel,可以設定要將驅動程式 build-in(*) 或者 module(M),前者就讓選項空白;後者就讓選項顯示 M。
前者是開機會自動載入;後者用 lsmod 可以看到。


[註] 使用「make menuconfig」或者「make bzImage」前,須先把 gcc(build-essential)、make、以及 libncurses5-dev 安裝完畢。

 
Blogger Templates