Pages

2009年6月26日 星期五

花蓮行第一天

今年員工旅遊是我第一次參與,打算自行規劃去花蓮玩,這也是我第二次去,上一次主要的景點是太魯閣,所以這次打算找不一樣的地方...。

第一天大夥約在台北車站,準備搭十點多的太魯閣號,車程僅需要兩個小時(停靠站:松山、花蓮),在車上邊享受伯朗咖啡和三明治、邊觀賞外面依山傍海的景色。Carol則是把PSP當MP3用(好野人),邊聽音樂邊拍照。到了花蓮,就先拍花蓮車站,畢竟,群山矗立的景致是難得一見的。


在車站拿了市區地圖後,就到詮興租車行租車,接著去三國一曼波魚餐廳吃午餐,曼波魚吃起來的口感已經忘記了XD,但肯定得一點是,自己掏腰包就不吃,哈哈。吃飽飯就去吃一心泡泡冰,這間店之前有來過,還不錯吃拉,接著就到小熊森林登記,這間民宿實在是太可愛了,所見之處都是一堆娃娃、玩具、以及特殊的擺設,女孩子和娘泡應該會超喜歡的吧。進到房間一整個超舒適,因為冷氣早就已經打開,涼颼颼的!房間還有飲料、礦泉水、泡麵,甚至廁所都做乾濕分離,果然有他的價值\( ̄▽ ̄)/。












休息一會,就去吃炸蛋蔥油餅,這是個物美價廉的小吃,要價$20/個,吃完就前往七星潭。因為今天天氣非常好,海風吹來更是舒適,完全是一整個放鬆,躺在石頭上可以馬上就睡著XD。






七星潭附近還有原野牧場和七星柴魚博物館,我們選擇去博物館,不過這裡挺無聊的,就是擺一些展示的東西,和影片介紹,待不了太久的,離開前買了柴魚丁角回去吃。晚上我們先去吃公正包子,這家店實在太有名了,排了至少半小時,接著去吃五霸包心粉圓隔壁的包心粉圓,似乎這間才是「正宗」,吃完就去最後一站:自強夜市,這次來就真正看個仔細,嗯~好小的夜市阿!尤其今天是禮拜五,攤販沒有全部開張,人也少很多,整個覺得弱掉了。我買了一杯現榨果汁和第一家烤肉串,烤肉串還挺不錯的,不過要排20分鐘以上(還非假日哩)。疲憊的一天,回到小熊森林洗澡休息囉。


花蓮行第二天
花蓮行第三天

2009年4月28日 星期二

OpenCV 初體驗

最近安裝了 OpenCV 來試試,本來是安裝 1.1pre1,但是程式執行都會發生應用程式初始化失敗之類的訊息,網路上也有不少人有這個問題,但解法似乎不是通用(重新編譯、複製 DLL 到 system32、安裝 VS2005 SP1...),所以改成安裝 1.0 版本。

我的安裝環境是 Vista,測試環境是 Visual Studio 2005,使用前需至「Tools->Options->Projects and Solutions VC++ Directories」作設定。
  • Include files
    C:\Program Files\OpenCV\otherlibs\cvcam\include
    C:\Program Files\OpenCV\otherlibs\highgui
    C:\Program Files\OpenCV\ml\include
    C:\Program Files\OpenCV\cxcore\include
    C:\Program Files\OpenCV\cvaux\include
    C:\Program Files\OpenCV\cv\include
  • Library files
    C:\Program Files\OpenCV\lib
最後就是測試程式,這裡會開啟一個 console application 專案,該程式會開啟一個PNG檔,並且在視窗上呈現。
  #include "stdafx.h"
  #include <cxcore.h>
  #include <highgui.h>

  int _tmain(int argc, _TCHAR* argv[])
  {
    IplImage *pImage = cvLoadImage("test.png", 1);
    if(!pImage)
      fprintf(stderr, "Can not open image file\n");

    cvNamedWindow("OpenCv Win", 1);
    cvShowImage("OpenCv Win", pImage);
    cvWaitKey();
    cvDestroyWindow("OpenCv Win");
    cvReleaseImage(&pImage);
    return 0;
  }

下載點
OpenCV 1.0
OpenCV 1.0rc1
OpenCV 中文網站

API 參考
OpenCV 1.0 API Reference

OpenCV 相關網誌、網站昨日
OpenCV 中文網站

2009年2月27日 星期五

驅動程式初體驗

因為需求,必須去了解 Linux Driver,所以就東問西問、看非常艱澀的驅動程式第3版、亂試一通,終於把 Dualview 給弄出來,所以趁著空檔時間,來po個文章..:)。##ReadMore##

這裡先簡單把如何寫一個驅動程式的基本程式碼列出來做說明,當然也可以從歐萊禮下載原始碼來做測試,如果官方網站掛掉還有這裡。
// 定義模組的初始函式(__init)與清理函式(__exit)
#include <linux/init.h>
// 定義許多 module_ 開頭的函式或 MODULE_ 開頭的巨集
#include <linux/module.h>

#define DRIVER_AUTHOR   "Linly"
#define DRIVER_DESC     "Hello World crap driver"
#ifndef DRIVER_VERSION
#define DRIVER_VERSION  "v0.0.1"
#endif

/*
* KERN_ALERT = <1>,代表訊息優先度,最高為 <0>
* 用法同 printf,只是不支援浮點數
*/
static int __init helloworld_init(void)
{
 printk(KERN_ALERT "Hello, world\n");
 return 0;
}

static void __exit helloworld_cleanup(void)
{
 printk(KERN_ALERT "Goodbye, cruel world\n");
}

module_init(helloworld_init);    // for insmod calls
module_exit(helloworld_cleanup); // for rmmod calls

// 模組資訊,可從 modinfo helloworld.ko 查詢
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
MODULE_VERSION(DRIVER_VERSION);
■ 說明如下:
  1. 前兩行的標頭檔通常都會加入,作用如註解所述。
  2. module_init 是模組(驅動程式)載入會執行的;module_exit 是模組(驅動程式)移除會執行的。
  3. printk 是寫 kernel module 會使用的 API,因為一般開發應用程式的 API 都不能再撰寫驅動程式時使用,只能使用 kernel API。而優先度的部份共分為七種<0>到<7>,數字越小代表優先度越高,如果你希望訊息可以再你期望的地方印出來,就讓它優先度高一點吧,我通常使用<1>,而這些訊息可以透過「dmesg」看到。
  4. MODULE_LICENSE 一定要加上所規定的一些字串,有幾種選擇,擇一即可,如:"GPL"、"GPL v2"、"GPL and additional rights"、"Dual BSD/GPL"、"Dual MPL/GPL"、"Proprietary"。
  5. 載入模組為「sudo insmod xxxx.ko」;移除模組為「sudo rmmod xxxx.ko」,如果說要重新掛載已經存在的模組,需先移除,然後再載入。  
再來就是 Makefile,它和以往所寫的不同,在於驅動程式使用的是核心提供的函式,所以編譯時必須把目錄切換至 kernel source tree 來做編譯,這個東西可以把它想成是 develop toolkit,像標頭檔就會這裡頭。如果是 Ubuntu,該路徑位於「/usr/src」;如果是 fedora,需要去下載安裝,因為作業系統裝好並不會存在。
KERNEL_VERSION    := `uname -r`
KERNEL_DIR    := /lib/modules/$(KERNEL_VERSION)/build

PWD        := $(shell pwd)

obj-m        := helloworld.o
#module-objs   := helloworld.o

all:
    echo "Building Hello World driver..."
    make -C $(KERNEL_DIR) M=$(PWD) modules

clean:
    -rm -f *.o *.ko *.mod.c Module.symvers version.h

你會看到 KERNEL_DIR 是指派成 bra bra build,不過實際到那邊看,會發現 build 其實是一個 symbolic link,指向 /usr/src/$(KERNEL_VERSION),也就是 kernel source tree。編譯後會回到 pwd,並且在程式的目錄下,產生 .o 檔案,最後再根據 obj-m 的名稱,產生 .ko 檔(如根據 helloworld.o 產生 helloworld.ko)。然後「sudo insmod helloworld.ko」,就可以再 dmesg 看到訊息囉。

2009年2月25日 星期三

Daemon

  Daemon 似乎不需要像 windows service 一樣去註冊,只要讓他開機自動執行即可,而他滿足幾項特性:
  1. 開機後即不斷執行。
  2. 父程序為 init。
  3. 不具有 tty (所以printf是幫不了你)。
  4. 執行者為 root (所以和他通訊的程序也必須是 superuser 所執行)。##ReadMore##

  在「Advanced Programming in the UNIX Environment」一書中提到撰寫 daemon 的規則,不過我也沒遵守,因為不了解這些規則的重要性,不過還是簡單列出該書所提到的部份:
  1. 需呼叫 fork,且父程序直接 exit。
  2. 呼叫 setsid。
  3. 改變工作目錄為「/」。
  4. 把 umask 設定為「0」,避免繼承來的遮罩導致檔案權限不如預期。
  5. 關閉無用的 file descriptor。
#include <sys/type.h>
#include <sys/stat.h>
#include <fcntl.h>

int daemon_init(void)
{
pid_t pid;
if((pid = fork()) < 0)
return -1;
else if(pid != 0)
exit(0);

setsid();
chdir("/");
umask(0);
return 0;
}
  
  以上是本書的初使化範例,不論如何,都需要寫一個 script 讓開機自動啟動 daemon,這裡的作法是在 /etc/init.d/rc.local 去執行我寫的一個 script,當然只要在 rc2.d 中會執行到的 script 中加入都是可以的 [註]。以下列出 script 內容;
#!/bin/sh

[ -x /home/linly/DualView/daemon/Dualview-Daemon ] || exit 0
#filename=/home/linly/DualView/daemon/Dualview-Daemon
#test ! -e $filename && echo "The filename '$filename' DO NOT exist" && exit 0

case "$1" in
start)
# Start daemons.
cd /home/linly/DualView/daemon
./Dualview-Daemon &
;;
stop)
# Stop daemons.
chk=`/usr/bin/pgrep -x Dualview-Daemon`
if [ "$chk" ]; then
echo -n "Shutting down Dualview Daemon "
kill -9 $chk
fi
echo ""
;;
restart)
$0 stop
$0 start
;;
*)
echo "Usage: Dualview-Daemon {start|stop|status|restart}"
exit 1
esac

cd /home/linly/DualView/uvcvideo
/sbin/rmmod uvcvideo
/sbin/insmod uvcvideo.ko

exit 0

開機後輸入 ps -ajx | grep "Dualview-Daemon" 會看到以下內容:
PPID   PID    PGID   SID    TTY    TPGID  STAT   UID   TIME   COMMAND
1 6038 5067 5067 ? -1 S 0 0:00 ./Dualview-Daemon

  以我的 daemon 為例,由於 shared memory 擁有者是 root,所以權限不足是砍不掉的。而且需要用 signal 來通知 daemon 的應用程式,也必須是 superuser 所執行的應用程式。還有一點,如果需要印訊息,必須透過 syslog 這個 daemon 來達成(因為沒有終端機),以下列出相關的函式:
#include <syslog.h>
void openlog(char *ident, int option, int facility);
void syslog(int priority, char *format, ...);
void closelog(void);

  執行時發生一個問題,就是 gtk_init 無法順利初始化,導致程式被中斷,嘗試很久後,採用 gtk_init_check,該函式即使出始化失敗也不會中斷程式,且能夠順利初使 gdk 的元件,有達到我的需求。(初使做了很多動作,也不知道掛在哪裡,反正我要用的東西有初始好就好拉:P)


注意事項:
  • rc2.d 是一般開機進入圖形介面的過程中,會執行的腳本,像 rc0.d 就是關機會執行的;而 rc6.d 是重新開機會執行的。

2009年2月19日 星期四

Camera Preview with V4l2

很久之前做的範例,一直沒放上來,所以就放上來分享一下,這是用 C 和 GTK+ 寫的,一個很單純的 preview,沒有其他額外的功能,且需安裝 gtk+-2.0...

範例:Camera Preview
投影片介紹:V4L2 Introduction

2009年2月18日 星期三

GTK+ 也支援 atomic operation

  本來期望C語言的API有支援,不過搜尋一下/usr/include的標頭檔卻找不到,倒是找到C++和GTK+有支援,所以就去翻一下/usr/include/glib-2.0/glib/gatomic.h,以下是簡單的使用範例:
##ReadMore##
#include <gtk/gtk.h>
#include <stdio.h>

gint atomic;

gboolean test(gpointer data)
{
g_atomic_int_add(&atomic, 1);
gint get_atomic = g_atomic_int_get(&atomic);

if(get_atomic <= 10)
{
g_print("atomic val = %d\n", get_atomic);
return TRUE;
}
else
gtk_main_quit();
}


int main(int argc, char *argv[])
{
gtk_init(&argc, &argv);
g_timeout_add(1000,test,NULL);

g_atomic_int_add(&atomic, 1);
g_print("atomic val = %d\n", g_atomic_int_get(&atomic));

gtk_main();
return 0;
}

  提醒一下,編譯時記得加上`pkg-config gtk+-2.0 --libs --cflags`,否則會找不到相關的函式庫與標頭檔。

2009年2月9日 星期一

Shared memory and Semaphore 的使用

形成通訊是蠻常會遇到的問題。當兩個 process 在執行過程中需要溝通,可以使用許多方式,例如:
  • socket(by AF_UNIX)
  • pipe or fifo
  • file
  • shared memory
  • mmap (我覺得可以歸類到file)
  • message queue (不會用XD)
這裡我所要舉得例子是:shared memory + semaphore。畢竟是共享資源,就要考慮同步,否則會發生 race condition。由於程式碼貼上來過於累贅,我就放在 google doc,程式碼的內容是 P1(write)會一直寫進去,而 P2(read)會去讀取並列印再終端機。[註1]
[註1] 若沒有使用 semaphore,印出來的結果會有重複的內容出現。
[註2] semaphore 是另外包裝的一組 API,提供七個函式,如下:
  1. sem_create - 產生 semaphore,並設定初值。
  2. sem_open - 開啟 semaphore,前題以執行過。
  3. sem_rm - 刪除 semaphore,給 sem_close 使用。
  4. sem_close - 關閉 semaphore,並做刪除(sem_rm)。
  5. sem_op - 給 sem_wait、sem_signal 使用。
  6. sem_wait - semaphore--。
  7. sem_signal - semaphore++。

2009年1月10日 星期六

Debian Package Roadmap

  gnowledge network 這個網站會將 debian package (deb) 的相依性,以圖示的方式顯示出來,讓使用者可以更清楚了解到套件之間的關係,我隨便輸入一個 "gcin",它的 roadmap 真他媽的複雜XDD。以下是 vim-common 的 roadmap:


  
  不過大部份套件的圖都超複雜,而且網頁的反應時間並不是很好,所以玩玩就好 :P。

2009年1月7日 星期三

Linux PAM

PAM 全名為「Pluggable Authentication Module」,是用來做認証的模組。許多程式,如: login、rlogin、sshd、ftpd、su、passwd...等都有支援 PAM,當然你也可以利用 ldd 指令來查詢某程式是否支援 PAM。
##ReadMore##
linly@linly-laptop:~$ ldd /usr/bin/passwd
...
libpam.so.0 => /lib/libpam.so.0 (0xb800f000)
libpam_misc.so.0 => /lib/libpam_misc.so.0 (0xb800b000)
...
PAM 有許多有趣的應用,例如:利用指紋辨識或臉部辨識登入電腦、限制某項服務的使用時間,資源,次數、設定密碼時檢查密碼強度等等。在陳柏菁的 PAM 認証模組 網頁中有詳細介紹 PAM 是什麼?該如何設定?在此就不加贅述,不過要提醒讀者幾件事情:
  1. 認証模組有堆疊特性:設定時可以使用多種認証方式。
  2. control flag 的先後次序:例如 sufficient 放在 required 之後來使用,那 sufficient 模組的成功與否就會被忽略。
  3. 要了解每種 control flags 的意義:例如 required 與 requisite 的差異。
接下來要做的事情就不是上面提到的「如何設定」,而是自己寫一個簡單的 PAM 應用程式和 PAM 模組。http://www.kernel.org/pub/linux/libs/pam/ 有提供管理、開發 PAM 的手冊。至於我要做的事情是:寫一個程式讓使用者輸入帳號密碼,然後進行驗證,成功與否都會出現提示訊息。有興趣的人可以從 PAM Application 與 PAM Module 的範例做修改,至於編譯的方式如下:
gcc -fPIC -c pam_module.c
gcc -shared -o pam_module.so pam_module.o -lpam
sudo cp pam_module.so /usr/lib/
gcc -o pam_app pam_app.c -lpam -lpam_misc -lpam_module
  • Hint:撰寫 pam application 時,需要做 pam_set_item,而 item type 不能使用 PAM_AUTHTOK 及 PAM_OLDAUTHTOK,所以就隨便設定一個。
  • Hint:撰寫 pam module 時,需要引用 當中的 getspnam 函式來取得加密過的密碼,至於為什麼要自己寫一個 module,因為 PAM 模組的帳密認証方式只能先輸入帳號,等他確認有這個使用者,才會在要求輸入密碼,既然說要改變他的行為,模組只好自己寫。
  最後,我把我寫好的程式碼放在 google docs,有興趣的讀者可以去看看,MY_PAM_APP、MY_PAM_MODULE。

2008年12月31日 星期三

mmap()

mmap 函式是用來將檔案映射到記憶體,video for linux two 就是透過這種方式,將 device memory mapping into user space,而 uvcvideo driver 會實作這個 file operation,來幫 camera application 建立 VMA 區段。mmap() 的原型如下:##ReadMore##

void *mmap(void *addr, size_t len, int prot,
      int
flags,int fildes, off_t off);
  • addr:用來告知從哪個記憶體位址開始映射,不過通常是擺 NULL,直接給作業系統決定,否則還要考慮對齊問題(alignment)
  • len:映射的大小
  • prot:對此記憶體的操作限制,如:PROT_READ、PROT_WRITE、PROT_EXEC、PROT_NONE
  • flags:對此記憶體的存取方式,如:MAP_SHARED、MAP_PRIVATE、MAP_FIXED,使用 MAP_SHARED 時,如果某一個行程有對 mmap 的記憶體做修改,那其他同樣有做 mmap 的行程會看到相同結果。(對同一個檔案)
  • fildes:open file 所得到的 fd。
  • off:指定檔案從哪裡開始對映
在此用 mmap 做個小實驗。首先建立一般的實體檔案,然後關閉它,接著透過映射到的記憶體來間接寫入字串到該檔案中。
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#define FILE_LENGTH 0x400 /* 1024 */

int main(int argc, char *argv[])
{
    int fd;
    void *map_memory;

    if(argc < 2) {
        printf("Usage: ./a.out <string>\n");
        return 1;
    }

    /*
     * Open a file to be mapped.
     * S_IRUSR: read permission, owner
     * S_IWUSR: write permission, owner
     */
    fd = open("/tmp/shared_file", O_RDWR | O_CREAT,
        S_IRUSR | S_IWUSR);
        lseek(fd, FILE_LENGTH-1, SEEK_SET);
        write(fd, "", 1);
        lseek(fd, 0, SEEK_SET);

    /* Create map memory. */
    map_memory = mmap(0, FILE_LENGTH,
        PROT_WRITE, MAP_SHARED,
        fd, 0);
    
    close(fd);

    /* Write to mapped memory. */
    if(strlen(argv[1]) < FILE_LENGTH)
        sprintf((char*)map_memory, "%s", argv[1]);

    munmap(map_memory, FILE_LENGTH);
    return 0;
}
這樣的作法還是可以將內容寫入檔案,而且速度比單純寫入檔案還要快,但似乎 mmap有大小的限制,因為我設定到 0x40000000,程式執行完後,系統變得怪怪的 =.=?。以下是該測試程式的 VMA 區段,位於 /proc/xxxx/maps,其中 xxxx 是 process id,可以在執行程式時輸入 &,如:./mmaptest input_string &。
08048000-08049000 r-xp 00000000 08:06 606289 /home/linly/Sample/mmap_write
...
b7df2000-b7f4a000 r-xp 00000000 08:06 1497227 /lib/tls/i686/cmov/libc-2.8.90.so
...
b7f61000-b7f62000 -w-s 00000000 08:06 686791 /tmp/shared_file
...
b7f80000-b7f81000 rw-p 0001b000 08:06 1479875 /lib/ld-2.8.90.so
bff6c000-bff81000 rw-p bffeb000 00:00 0 [stack]
 
Blogger Templates