Pages

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]

2008年12月17日 星期三

如何使用 GtkProgressBar

  進度條(ProgressBar)是一個和使用者互動必備的元件,因為你必須讓使用者了解目前處理進度,若等不下去就可以趕緊取消。至於使用他的方式我覺得可以採取「Timer」或「Thread」。下面就以計時器的方式來介紹進度條的使用。但礙於程式太佔版面,所以只會顯示部份程式碼。
  
  一個進度條的產生是透過 gtk_progress_bar_new,進度條的範圍是[0,1],且以浮點數儲存。如果要讀取或修改進度條的數值利用 gtk_progress_bar_set(get)_fraction。了解這些之後,咱們就來看看程式碼。
/*
 以下程式執行會產生一個主視窗+按鍵
 當按下按鍵就建立另一個視窗+進度條
 並且透過計時器每次增加0.01,直到1.0為止
*/
#include<gtk/gtk.h>

gdouble new_val;
guint timer_id = 0;

static gboolean flush_progressbar(gpointer progressbar)
{
new_val= gtk_progress_bar_get_fraction(GTK_PROGRESS_BAR(progressbar))+0.01;
if(new_val >= 1.0) new_val = 1.0;

gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progressbar), new_val);

if(new_val >= 1.0) {
/* 舊版是用 gtk_timeout_remove() */
g_source_remove(timer_id);  
return FALSE;
}

return TRUE; // 如此計時器才會一直 work
}

static void create_window(GtkWidget *widget, gpointer window)
{
// 產生並初始化視窗
...

GtkWidget *progressbar;
progressbar = gtk_progress_bar_new();
gtk_layout_put(GTK_LAYOUT(layout), progressbar, 100, 200);

...
 
// 100毫秒執行一次
/* 舊版是用 gtk_timeout_add() */
timeId = g_timeout_add(100 , flush_progressbar, progressbar);
}

int main(int argc, char *argv[])
{
// window - 主視窗
// window_create_after - 按下按鍵後產生的視窗
GtkWidget *window;
GtkWidget *window_create_after;
GtkWidget *button;

gtk_set_locale();
gtk_init(&argc, &argv);

// 產生並初始化 window
// 產生並出使化 button、加到 window 中
// 註冊 clicked 事件用來產生 window_create_after
// 以及產生一個 timer
 
gtk_main();
return(0);
}


這裡要注意的有兩點:
  1. 不要計時器中加入 delay 的機制(如:sleep),因為會導致前一次沒處理完,下一次又開始處理(re-entrant routine),導致 stack overflow。 
  2. 不要讓主程式(main process)去處理 progressbar 的值,因為只會看到最終完成的畫面,中間的遞增過程使用者看不到。會有這種結果其實也不難想像,由於主程式正在執行某個function call,即使你不斷地更新進度條的內容,這些 event handler 只能等到目前的程式執行完,控制權回到 caller (也許好幾層),才會去執行這些更新畫面的 handler。
 

最後再介紹兩支和進度條相關的函式:
gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(progressbar),0.1);
gtk_progress_bar_pulse(GTK_PROGRESS_BAR(progressbar));

  這兩支函式可以讓進度條內的東東左跑右跑,就是放入 Live CD,在 ubuntu 圖樣的那個畫面,進度條開始跑進度之前的行為。就如同以上的程式碼,每次以0.1的間隔在移動,移動完右邊,就移左邊;移動完左邊,就移動右邊。

2008年12月12日 星期五

Video For Linux Two

  這是最近一兩個禮拜 study 的主題:V4L2,是在 Linux 中用來擷取影像的 API,隨著你安裝作業系統,V4L2 就會一併裝上去,以下是自己製作的投影片,Enjoy it... : )





參考資料:http://v4l2spec.bytesex.org/spec-single/v4l2.html

2008年12月7日 星期日

HP Pavilion DV7 安裝 Ubuntu 8.10 沒有音效

  在公司的工作機是 HP Pavilion DV7 ,我在上面安裝 Vista Business 與 Ubuntu,想當然爾,Windows 的運作正常得很,不過進入 Ubuntu 時,登入畫面在正常情況下會發出「登登」兩聲,但我的卻是詭異的「登登登登......(聲音越來越小 囧)」,且登入後沒有聲音,這種奇妙的狀況當然要交給 Google 大神,以下修改 /etc/modprobe.d/alsa-base 這個檔案,重開機後即正常:
##ReadMore##
sudo vi /etc/modprobe.d/alsa-base
在檔案最後加上 options snd-hda-intel enable_msi=1
參考網頁:http://www.ubuntu-tw.org/modules/newbb/viewtopic.php?viewmode=thread&topic_id=11933&forum=3&post_id=61152

CodeBlocks 設定值無法儲存

  不知道為什麼,這次重灌 Ubuntu,竟然發生每次啟動 Codeblocks,都像"第一次"使用一樣,導致設定值都要重設,卻又儲存不了,Google了一下,發現原來它和 gcin 相衝(?),所以改了以下的設定檔,然後重開機就正常了:##ReadMore##
sudo gedit /etc/X11/xinit/xinput.d/gcin
將 GTK_IM_MODULE=gcin 改成 GTK_IM_MODULE=xim
  從來沒發生過的事情,這次竟然莫名其妙的發生了(攤)。
 
Blogger Templates