(III)將和大家介紹procfs,雖然後來procfs已經失控,也不再建議大家使用,但是還是有一定的用途,所以,有興趣的人可以一起探討一下。
/proc是一個特殊的檔案系統,當讀取/proc底下的檔案時,其內容由kernel動態產生,很多應用程式也都是存取/proc底下的檔案,我們藉由兩個範例來說明。第一個用於當/proc只提供read-only的時候,第二個用於/proc提供具有write功能的時候。
Read-Only
#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
MODULE_LICENSE("GPL");
#define MAX_LINE 1000
/* read file operations */
static int read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
uint32_t curline;
char *p, *np;
*start = p = np = page;
curline = *(uint32_t*) data;
printk("count=%d, off=%ld\n", count, off);
for (;curline < MAX_LINE && (p - page) < count; curline++) {
np += sprintf(p, "Line #%d: This is Brook's demo\n", curline);
if ((count - (np - page)) < (np - p)) {
break;
}
p = np;
}
if (curline < MAX_LINE) {
*eof = 1;
}
*(uint32_t*)data = curline;
return (p - page);
}
/* private data */
static uint32_t *lines;
static int __init init_modules(void)
{
struct proc_dir_entry *ent;
lines = kzalloc(sizeof(uint32_t), GFP_KERNEL);
if (!lines) {
printk("no mem\n");
return -ENOMEM;
}
*lines = 0;
/* create a procfs entry for read-only */
ent = create_proc_read_entry ("brook", S_IRUGO, NULL, read_proc, lines);
if (!ent) {
printk("create proc failed\n");
kfree(lines);
}
return 0;
}
static void __exit exit_modules(void)
{
if (lines) {
kfree(lines);
}
/* remove procfs entry */
remove_proc_entry("brook", NULL);
}
module_init(init_modules);
module_exit(exit_modules);
在這個例子中我們使用一個globa variable "lines",用於存放上次列印到第幾個數值,MAX_LINE是上限。一開始載入module就先分配記憶體給lines,接著呼叫create_proc_read_entry()建立一個唯讀的procfs檔案。
static inline struct proc_dir_entry *
create_proc_read_entry(const char *name,
mode_t mode, struct proc_dir_entry *base,
read_proc_t *read_proc, void * data);
name是proc底下的檔案名稱。mode是檔案的mode,可以直接用0666的表示法表示,如果是建立目錄則要or S_IFDIR。base如果為null,則會把/proc當根目錄,可以是其他的proc_dir_entry pointer,那麼就會以proc_dir_entry當根目錄開始往下長。read_proc則是當user讀取該檔案時的call-back function。data則用於存放額外資訊的地方,可以先alloc,然後每次執行read時,都可以拿出來使用。
typedef int (read_proc_t)(char *page, char **start, off_t off,
int count, int *eof, void *data);
page是系統給的buffer,也是我們要寫入的地方,寫到buffer裡面的資料,會被當成proc的內容。start如果只要一次就可以read完,可以忽略start,否則只要將page設定給start就可以了。count則是user-space想要讀取的資料量。offset指示目前檔案位置。eof指示是否已經讀到結尾。data用於存放額外的資訊。
Writable
#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
MODULE_LICENSE("GPL");
#define MAX_LINE 1000
/* write file operations */
static int write_proc(struct file *file, const char __user *buf,
unsigned long count, void *data)
{
char num[10], *p;
int nr, len;
/* no data be written */
if (!count) {
printk("count is 0\n");
return 0;
}
/* Input size is too large to write our buffer(num) */
if (count > (sizeof(num) - 1)) {
printk("input is too large\n");
return -EINVAL;
}
if (copy_from_user(num, buf, count)) {
printk("copy from user failed\n");
return -EFAULT;
}
/* atoi() */
p = num;
len = count;
nr = 0;
do {
unsigned int c = *p - '0';
if (*p == '\n') {
break;
}
if (c > 9) {
printk("%c is not digital\n", *p);
return -EINVAL;
}
nr = nr * 10 + c;
p++;
} while (--len);
*(uint32_t*) data = nr;
return count;
}
static int read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
uint32_t curline;
char *p, *np;
*start = p = np = page;
curline = *(uint32_t*) data;
printk("count=%d, off=%ld\n", count, off);
for (;curline < MAX_LINE && (p - page) < count; curline++) {
np += sprintf(p, "Line #%d: This is Brook's demo\n", curline);
if ((count - (np - page)) < (np - p)) {
break;
}
p = np;
}
if (curline < MAX_LINE) {
*eof = 1;
}
*(uint32_t*)data = curline;
return (p - page);
}
static uint32_t *lines;
static int __init init_modules(void)
{
struct proc_dir_entry *ent;
lines = kzalloc(sizeof(uint32_t), GFP_KERNEL);
if (!lines) {
printk("no mem\n");
return -ENOMEM;
}
*lines = 0;
ent = create_proc_entry("brook", S_IFREG | S_IRWXU, NULL);
if (!ent) {
printk("create proc failed\n");
kfree(lines);
} else {
ent->write_proc = write_proc;
ent->read_proc = read_proc;
ent->data = lines;
}
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);
第二個範例則提供了write的功能,所以不能直接使用create_proc_read_entry()建立procfs檔案,而是要使用create_proc_entry()建立procfs檔案,並且設定read/write file operations,以及"data"。該範例中的write是設定line。
typedef int (write_proc_t)(struct file *file, const char __user *buffer,
unsigned long count, void *data);

沒有留言:
張貼留言