聚會時間公告: 因應COSCUP 2011, Kalug 8月份休會一次

三月 14, 2011

小惡魔AppleBOY
AppleBOY
is about »

tag cloud

» [Linux Kernel] 讀取 /proc 底下資料最佳方法: seq_file interface

前言

最近在整合公司內部 Atheros(被高通買下) 晶片的 Router,從原本 2.6.15 升級到 2.6.34.7,升級過程遇到很多困難,其中一項升級 Wireless Driver 部份,發現在 Kernel Socket 與 User Space 溝通之間出了問題,利用 Ioctl 來取得目前在 AP 上面所有 Client 資料(包含 mac address, 處於 N or G mode…等),在 User Space 上會掉資料,後來利用 /proc 底下檔案來跟 User 之間溝通,才沒有發生任何問題,由於輸出的檔案比較多,就偏向用 2.6 Kernel 提供的 seq_file 介面( interface )建立虛擬檔案 (virtual file) 與 User Space 溝通(此方法為 Alexander Viro 所設計),此功能其實在 2.4.15 已經實做了,只是在 2.6 版本才被大量使用。

程式設計師可以透過引入 seq_file.h> 來實做 seq_file interface,seq_file 最大優勢就是讀取完全沒有4k boundry 的限制,也就是不用管會不會超出 output buffer。

The iterator interface

為了能夠讓 iterator 正常運作,我們必須實做 4 個 function (start, next, stop, show),跑得過程為 start -> show -> next -> show -> next -> stop,為了方便講解,參考 Linux Kernel(4)- seq_file 裡面範例如下:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h> /* Necessary because we use proc fs */
#include <linux/seq_file.h> /* for seq_file */
#include <linux/uaccess.h>

MODULE_LICENSE("GPL");

#define MAX_LINE 1000
static uint32_t *lines;

/**
 * seq_start() takes a position as an argument and returns an iterator which
 * will start reading at that position.
 */

static void* seq_start(struct seq_file *s, loff_t *pos)
{
    uint32_t *lines;

    if (*pos >= MAX_LINE) {
        return NULL; // no more data to read
    }

    lines = kzalloc(sizeof(uint32_t), GFP_KERNEL);
    if (!lines) {
        return NULL;
    }

    *lines = *pos + 1;

    return lines;
}

/**
 * move the iterator forward to the next position in the sequence
 */

static void* seq_next(struct seq_file *s, void *v, loff_t *pos)
{
    uint32_t *lines = v;
    *pos = ++(*lines);
    if (*pos >= MAX_LINE) {
        return NULL; // no more data to read
    }
    return lines;
}

/**
 * stop() is called when iteration is complete (clean up)
 */

static void seq_stop(struct seq_file *s, void *v)
{
    kfree(v);
}

/**
 * success return 0, otherwise return error code
 */

static int seq_show(struct seq_file *s, void *v)
{
    seq_printf(s, "Line #%d: This is Brook's demo\n", *((uint32_t*)v));
    return 0;
}

static struct seq_operations seq_ops = {
    .start = seq_start,
    .next  = seq_next,
    .stop  = seq_stop,
    .show  = seq_show
};

static int proc_open(struct inode *inode, struct file *file)
{
    return seq_open(file, &seq_ops);
}

static struct file_operations proc_ops = {
    .owner   = THIS_MODULE, // system
    .open    = proc_open,
    .read    = seq_read,    // system
    .llseek  = seq_lseek,   // system
    .release = seq_release  // system
};

static int __init init_modules(void)
{
    struct proc_dir_entry *ent;

    ent = create_proc_entry("brook", 0, NULL);
    if (ent) {
        ent->proc_fops = &proc_ops;
    }
    return 0;
}

static void __exit exit_modules(void)
{
    if (lines) {
        kfree(lines);
    }
    remove_proc_entry("brook", NULL);
}

module_init(init_modules);
module_exit(exit_modules);


裡面可以發現實做了 seq_start, seq_next, seq_stop, seq_show,首先,我們要實作 start() 函式,其主要功能就是回傳 iterator 要讀取的單一位置(position),position 從 0 開始,而我們打算從第一行開始讀取
void * (*start) (struct seq_file *m, loff_t *pos);

接下來實做 next() 功能,其主要是將 position 移動到下一個位置,所以移動到下一個,我們必須將 iterator 加一
void * (*next) (struct seq_file *m, void *v, loff_t *pos);

最後要實作 stop(),當 iterator 執行完畢,我們必須將 start() 所用到的記憶體釋放
void (*stop) (struct seq_file *m, void *v);

Formatted Output

顯示的 callback function 為 show,seq_file 提供了幾個 function 讓大家使用

int seq_printf(struct seq_file *m, const char *f, ...): 類似 printf 或 printk
int seq_puts(struct seq_file *m, const char *s): 印字串
int seq_putc(struct seq_file *m, char c): 印字元

最後說明一下 module 啟動流程,首先定義您要的 /proc/file,給接下來在 module 初始化的時候宣告

static int __init init_modules(void)
{
    struct proc_dir_entry *ent;

    ent = create_proc_entry("brook", 0, NULL);
    if (ent) {
        ent->proc_fops = &proc_ops;
    }
    return 0;
}

我們可以看到當建立 proc file 時綁定特定 proc_ops operation

static struct file_operations proc_ops = {
    .owner   = THIS_MODULE, // system
    .open    = proc_open,
    .read    = seq_read,    // system
    .llseek  = seq_lseek,   // system
    .release = seq_release  // system
};

接著打開檔案時呼叫 proc_open:

static int proc_open(struct inode *inode, struct file *file)
{
    return seq_open(file, &seq_ops);
}

proc_open 則是回傳 seq_file 四個 seq_ops operation

static struct seq_operations seq_ops = {
    .start = seq_start,
    .next  = seq_next,
    .stop  = seq_stop,
    .show  = seq_show
};

就是上面所列的四個最主要的 function。

Related View

十二月 27, 2010

小惡魔AppleBOY
AppleBOY
is about »

tag cloud

» [Linux] 釋放虛擬記憶體 (cache)

Linux Kernel 2.6.16 之後加入了 drop caches 的機制,可以讓系統清出多餘的記憶體,這對於搞嵌入式系統相當重要阿,Memory 不夠就不能 upgrade firmware,我們只要利用讀寫 proc 檔案就可以清除 cache 記憶體檔案,底下是操作步驟: 1. 釋放 pagecache:捨棄一般沒使用的 cache echo 1 > /proc/sys/vm/drop_caches 2. 釋放 dentries and inodes: echo 2 > /proc/sys/vm/drop_caches 3. 釋放 pagecache, dentries and inodes:(效果等同於1+2) echo 3 > /proc/sys/vm/drop_caches Reference: Drop Caches 觀察 Linux 的虛擬記憶體 Related View切換 Ubuntu apt 的 mirror site (0)[Linux] [...]

七月 27, 2010

小惡魔AppleBOY
AppleBOY
is about »

tag cloud

» [Kernel Driver] 撰寫簡易 Timer 機制

在底層 Linux Kernel 提供了時序(timing)機制,方便驅動程式設計者所使用,核心是依據硬體發出的『計時器中斷』來追蹤時間的流動狀況。我們可以依據 HZ 的值來設計 Delay 機制,讓驅動程式可以每隔固定一段時間啟動或者是發出訊號,也可以利用 Timer 來讓 LED 閃爍變化,在介紹 Timer API 之前,可以先參考 Linux Kernel: 簡介HZ, tick and jiffies 這篇文章,瞭解一些相關名詞,舉例:如果想知道一秒後的 jiffies 時間,可以寫成底下: ...詳全文(共1659字)

七月 25, 2010

小惡魔AppleBOY
AppleBOY
is about »

tag cloud

» [Linux Kernel] 簡單 hello world: License and Module 介紹(part 3)

在 Kernel 2.4 或以上版本,在編譯模組完成,要進行 load module 之前,你會發現底下訊息: # insmod hello-3.o Warning: loading hello-3.o will taint the kernel: no license ...詳全文(共2142字)

七月 20, 2010

小惡魔AppleBOY
AppleBOY
is about »

tag cloud

» [Linux Kernel] 撰寫 Hello, World module: The __init and __exit Macros (part 2).

再看此篇之前,可以先閱讀作者先前寫的:『[Linux Kernel Driver] 撰寫簡單 Hello, World module (part 1).』,今天要介紹 Driver 的 init module 區別,在 Kernel 2.4 版本,您可以自行定義 init 跟 cleanup 函式,他們不再被個別稱為 init_module()cleanup_module(),現在都使用 module_init()module_exit() 兩大巨集,這兩函式被定義在 linux/init.h 檔案裡面,所以在寫程式務必將其 include 喔,另外一個核心模組(MODULE_LICENSE),用於讓核心知道此模組遵守自由授權條款,若沒這項宣告,核心會跟您抱怨的喔,底下為範例:

#include <linux/kernel.h> /* pr_info所需 include 檔案*/
#include <linux/init.h>
#include <linux/module.h> /* 所有 module 需要檔案*/
#include <linux/version.h>

MODULE_DESCRIPTION("Hello World !!");
MODULE_AUTHOR("Bo-Yi Wu <appleboy.tw AT gmail.com>");
MODULE_LICENSE("GPL");

static int __init hello_init(void)
{
    pr_info("Hello, world appleboy\n");
    pr_info("The process is \"%s\" (pid %i)\n", current->comm, current->pid);
    return 0;
}

static void __exit hello_exit(void)
{
    printk(KERN_INFO "Goodbye\n");
}

module_init(hello_init);
module_exit(hello_exit);

編譯過程,可以自行修改 Makefile,可以打開 kernel/android-2.6.29/drivers/i2c/chips/Makefile 參考範例,您會發現很多類似底下寫法:

obj-$(CONFIG_TWL4030_POWEROFF)  += twl4030-poweroff.o
obj-$(CONFIG_TWL4030_MADC)  += twl4030-madc.o
obj-$(CONFIG_RTC_X1205_I2C) += x1205.o
obj-$(CONFIG_SENSORS_BOSCH_BMA150)  += bma150.o

如果要編譯成 module 可以設定成 obj-m += bma150.o,編譯到 Kernel image,則會寫成 obj-y += bma150.o,然而 $(CONFIG_SENSORS_BOSCH_BMA150) 是從 make menuconfig 設定,當然為什麼 menuconfig 會出現此設定,那就要從 kernel/android-2.6.29/drivers/i2c/chips/Kconfig 裡面加入 CONFIG_SENSORS_BOSCH_BMA150 設定, 選好之後,觀看 Kernel 資料夾底下的 .config 內容,看到 CONFIG_SENSORS_BOSCH_BMA150=y,這樣就正確了。

What is different of __init and __exit?

在寫 G-Senser Driver 時候,您會發現 static int __init BMA150_init(void) 跟 static void __exit BMA150_exit(void),跟平常寫 C 語言宣告函式不一樣吧,這兩個巨集分別有不同意義喔,當然也可以將 span style=”color:red”>__init 跟 __exit 拿掉,這不會影響 Driver 的編譯,但是會影響記憶體的優化,Driver 啟動時會呼叫 BMA150_init 函式,如果有加上 __init,當此 init function 執行完,會將記憶體 Release 給系統,這是針對 built-in 的方式才適用,如果是編譯成模組方式,則不會有此功能,然而 __exit 是 Driver 結束後會呼叫的 function,但是跟 __init 剛好功能相反,在 built-in 的 Kernel 映像檔並不會執行到 __exit,編譯成模組才會有釋放記憶體效果,這兩巨集可以參考 linux/init.h 檔案。

Related View

六月 21, 2010

小惡魔AppleBOY
AppleBOY
is about »

tag cloud

» [Linux Kernel] 撰寫簡單 Hello, World module (part 1).

來筆記如何在 Kernel 撰寫 hello world! module,在 Ubuntu Kernel 2.6.31-14 環境下撰寫,其實不難啦,首先先進入 Kernel 目錄,請在 /usr/src 底下看自己的系統版本,或者是利用 uname -r 來知道 Kernel 版本,底下是在 Ubuntu Kernel 2.6.31-14 Kernel 實做:

進入 Kernel 目錄

#
# cd Kernel directory
#
cd /usr/src/linux-headers-2.6.31-14-generic-pae

建立 hello 目錄

#
# mkdir directory
#
mkdir hello

建立 Makfile 以及 hello.c

hello.c:

#include <linux/kernel.h> /* pr_info 所需 include 檔案*/
#include <linux/init.h>
#include <linux/module.h> /* 所有 module 需要檔案*/
#include <linux/version.h>

MODULE_DESCRIPTION("Hello World !!");
MODULE_AUTHOR("Bo-Yi Wu <appleboy.tw AT gmail.com>");
MODULE_LICENSE("GPL");

static int __init hello_init(void)
{
    pr_info("Hello, world\n");
    pr_info("The process is \"%s\" (pid %i)\n", current->comm, current->pid);
    return 0;
}

static void __exit hello_exit(void)
{
    printk(KERN_INFO "Goodbye\n");
}

module_init(hello_init);
module_exit(hello_exit);

Makefile:

#
# Makefile by appleboy <appleboy.tw AT gmail.com>
#
obj-m       += hello.o
KVERSION := $(shell uname -r)

all:
    $(MAKE) -C /lib/modules/$(KVERSION)/build M=$(PWD) modules

clean:
    $(MAKE) -C /lib/modules/$(KVERSION)/build M=$(PWD) clean

之後只要切換到 hello 目錄,直接打 make 就可以產生出 hello.ko 檔案,直接載入 hello.ko 方式:

insmod ./hello.ko

移除 hello.ko

rmmod ./hello.ko

之後到 /var/log/message 底下就可以看到訊息:
Kernel Hello World

Related View

五月 28, 2010

小惡魔AppleBOY
AppleBOY
is about »

tag cloud

» [Linux Kernel] built-in vs. module

在編譯 Android Linux Kernel 2.6.29 Driver,常常遇到該把 Driver 用 built-in 或者是編譯成 module 呢?這其實看人習慣,就跟問你編輯器是用 Vim 或者是 emacs 是同樣意思,這兩者是有很大的差異,built-in 用在開機自動讀取載入,所以直接編譯成 uImage 檔案給嵌入式系統,像是 SCSI 或者是 SATA Driver 都建議編譯成 built-in 的方式,反而是一些音效驅動程式,可以編譯成 module,NTFS 就是可以編譯成 module,等您需要的時候在動態載入就可以,這樣可以減少 Kernel Image 的使用空間。

如果不想用 built-in 編譯,開機又需要驅動程式,那就需要透過 initrd 方式來啟動。底下整理兩者差異:

built-in:

開機自動載入,不可移除
Linux Kernel Image 大
需要重新 Compile

module:

可動態載入
Linux Kernel Image 小
不需要重新 Compile

reference:
[gentoo-user] kernel: built-in vs. module

Related View

  • Page 1 of 1 ( 7 posts )
  • linux kernel
support:

biggo.com.tw

A Django site.