LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 4238|回复: 4

[转]/proc 文件系统

[复制链接]
发表于 2004-5-9 14:47:32 | 显示全部楼层 |阅读模式
在Linux 中有额外的机制可以为内核和内核模块将信息发送给进程 -- /proc文件系统。最初设计的目的是允许更方便的对进程信 息进行访问(因此得名),现在它被每一个有有趣的东西报告的内核使用,例如 /proc/modules 有模块的列表 /proc/meminfo 有内存使用的统计表。

使用proc 文件系统的方法和使用设备驱动程序非常相似--创建一个 /proc 文件需要的所有信息的结构, 包括任何处理函数的指针 (在我们的例子中只有一个,当某人试图从 /proc 文件读时调用的那一个)。然后,init_module 在内核中登记该结构而 cleanup_module 注销它。

我们使用 proc_register_dynamic3.1 的原因是我们不想预先决定我们的文件的节点数字,而是为防止冲突而由内核决定它。 通 常的文件系统存在于磁盘上而不是内存中(/proc 在内存中),在这中情况下,节点数是是指向文件 的索引节点所在的磁盘位置的 指针。节点包含文件的信息(例如文件的存取权限)和指向磁盘位置或文件数据可以被找到的几个位置的指针。

因为当文件被打开或关闭的时候不能得到调用,所以在这个模块中没有地方放置 MOD_INC_USE_COUNT 和 MOD_DEC_USE_COUNT,并且,如果文件被打开随后模块被移除,我们没有办法避免后果。在下一 章我们会看到一个艰难的但更灵 活的可以处理/proc文件的实现方式,它也可以让我们防止那个问题。


范例 procfs.c

/* procfs.c - create a "file" in /proc
* Copyright (C) 1998-1999 by Ori Pomerantz
*/

/* 必要的头文件 */

/* 内核模块标准头文件 */
#include <linux/kernel.h> /* 内核工作 */
#include <linux/module.h> /* 明确指定是模块 */

/* 处理CONFIG_MODVERSIONS */
#if CONFIG_MODVERSIONS==1
#define MODVERSIONS
#include <linux/modversions.h>
#endif

/* 使用proc 文件系统所必要的 */
#include <linux/proc_fs.h>

/* 在 2.2.3 版/usr/include/linux/version.h 中包含这个宏
* 但 2.0.35 版不包含 - 因此我在此加入这个以防需要。
*/
#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c))
#endif

/* 将数据放入 proc 文件*/

参数
====
1. 如果你决定使用缓冲区,数据应该被插入它。
2. 字符指针的指针。如果你不想使用由内核分配的缓冲区时这将有用。
3. 文件的当前位置。
4. 第一个参数中的缓冲区的大小。
5. Zero (为将来使用?)。


用法和返回值
============
如果你像我那样使用自己的缓冲区,将它放在第二个参数的位置并返回在那个缓冲区中使用的字节数。

返回值为0 意味着这次没有更多信息(文件尾)。负数返回值是错误条件。


更多信息
========
我不是通过读文档而发现用这个函数去做什么的,而是通过读使用它的代码。我只想看看什么使用了
proc_dir_entry的get_info成员(如果你有兴趣的话,我使用了一个查找和搜索的组合),我发现它被
<内核源文件目录>/fs/proc/array.c使用。

如果有什么关于内核的事情不清楚,这是一个通常的办法。在 Linux 中我们有自由的获取源代码的巨大优势--使用它。

int procfile_read(char *buffer,
char **buffer_location,
off_t offset,
int buffer_length,
int zero)
{
int len;/* The number of bytes actually used */

/* 这是静态的,因此当我们离开这个函数时它还会待在内存中 */
static char my_buffer[80];

static int count = 1;

/* 我们将所有的信息放在一个里面,所以如果用户问是否有更多的信息,答案总是“没有”。
*
* 这是很重要的,因为来自库的标准读函数将持续发布读系统调用直到内核答复没有更多信息
* 或它的缓冲区被填满。
*/
if (offset > 0)
return 0;

/* 填充缓冲区并得到长度 */
len = sprintf(my_buffer,
"For the %d%s time, go away!\n", count,
(count % 100 > 10 && count % 100 < 14) ? "th" :
(count % 10 == 1) ? "st" :
(count % 10 == 2) ? "nd" :
(count % 10 == 3) ? "rd" : "th" );
count++;

/* 告诉调用函数缓冲区在哪儿*/
*buffer_location = my_buffer;

/* 返回长度 */
return len;
}


struct proc_dir_entry Our_Proc_File =
{
0, /* 节点数 - 忽略,它将被 proc_register[_dynamic] 填充*/
4, /* 文件名长度 */
"test", /* 文件名*/
S_IFREG | S_IRUGO, /* 文件模式 - 这是一个可以被拥有者,用户组和其他所有的用户读取的普通文件 */
1, /* 连接数 (文件被引用的目录数) */
0, 0, /* 文件的UID和GID - 我们将它赋予 root */
80, /* 用ls报告的文件大小。 */
NULL, /* 使用节点的函数(连接,删除,等等)--我们不支持 */
procfile_read, /* 对这个文件的读函数,当某人试图葱它读入什么时被调用。 */
NULL /* 我们能够在这儿有一个填充文件节点的函数,允许我们修改权限和拥有权,等等。 */
};



/* 初始化模块--登记 proc 文件 */
int init_module()
{
/* proc_register[_dynamic] 成功则成功,否则失败。 */
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,0)
/* 在 2.2版中,如果在这个结构中它为0则 proc_register 自动的分配一个动态的节点数
* 因此不再需要proc_register_dynamic
*/
return proc_register(&proc_root, &Our_Proc_File);
#else
return proc_register_dynamic(&proc_root, &Our_Proc_File);
#endif

/* proc_root 是 proc文件系统的根目录。这是我们想让我们的文件所处的位置 */
}


/* 清除 - 从 /proc中注销我们的文件 */
void cleanup_module()
{
proc_unregister(&proc_root, Our_Proc_File.low_ino);
}
将 /proc 作为输入

迄今为止,我们有两中办法从内核模块中产生输出:我们可以登记一个设备驱动程序并 mknod 一个设备文件,或者我们可以创建一个 /proc文件。这可以让内核模块告诉我们任何它可能告诉我们的事情。唯一的问题是这没有办法让我们告诉它。我们将输入发送给内核模块的第一个办法将是通过写回 /proc 文件。

因为 proc 文件系统主要是为了让内核报告它对进程的状态,所以对输入没有专门的预备。proc_dir_entry结构 没有包含一个指向 输入函数的指针而包含输出函数的指针。为了向/proc 文件中写,我们需要使用标准的文件系统机制。

在 Linux 中对文件系统登记有标准的机制。既然每个文件系统必须有它自己的处理节点和文件操作的函数, 所以有一个特殊的结构 保存所有这些函数的指针, inode_operations 结构, 包含一个指向 file_operations结构的指针。在 /proc 中,任何  时候登记一个新文件我们都允许特别指定哪个 inode_operations 结构将用于访问它。这就是我们使用的机制,inode_operations 结构包含指向 file_operations 结构的指针,而它又包含指向我们的module_input 和 module_output 函数的指针。

注意在内核中标准的读写的任务是颠倒的。读函数用作输出而写函数用于输入。造成这个局面的原因是读写是依据用户的观点--如果一个进程从内核中读什么,那么内核就需要输出它,而如果进程向内核中写什么,那么内核就需要将它作为输入接收。

这儿另一个引起注意的地方是 module_permission 函数。这个函数在进程试图用 /proc文件做什么的时候被调用,并且它决定是 否允许访问。现在它仅仅基于操作和当前用户的UID(就像 current 中的那样,一个指向包含当前运行进程的信息的结 构的指 针),但它也可以基于任何我们喜欢的东西,例如其他进程在用该文件做什么,时间,或者我们上次的接收的输入。

使用 put_user 和 get_user 的原因是在 Linux 中内存 (在Intel 架构下,在其他处理器下可能不同)是分段的。这意味 着指针不能单独由它自己指向一个唯一的内存位置,只是在内存的段中的位置,你需要知道它可以使用哪个内存段。对内核只有一个内存段,其他进程也各有一个。

进程只能访问自己的内存段,因此当写普通的作为进程运行的程序时我们不必为段操心。当你写内核模块时,通常你想访问内核的内存段,它由系统自动的处理。然而,当内存缓冲区中的内容需要在当前进程和内核中传递时,内核的函数收到的是指向位于进程的内存段中的内存缓冲区的指针。 put_user 和 get_user 宏可以让你访问那些内存。

范例 procfs.c

/* procfs.c - 创建一个可以输入输出的在 /proc 中的“文件”。 */

/* Copyright (C) 1998-1999 by Ori Pomerantz */

/* 必要头文件 */

/* 标准头文件 */
#include <linux/kernel.h> /* 内核工作 */
#include <linux/module.h> /* 明确指定是模块 */

/* 处理 CONFIG_MODVERSIONS */
#if CONFIG_MODVERSIONS==1
#define MODVERSIONS
#include <linux/modversions.h>
#endif

/* 使用 proc 文件系统所必要的 */
#include <linux/proc_fs.h>

/* 在 2.2.3 版/usr/include/linux/version.h 包含这个宏,但是
* 在 2.0.35 版中不包含--因此加入这个以备需要。 */
#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c))
#endif



#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
#include <asm/uaccess.h> /* 得到 get_user 和 put_user */
#endif

/* 模块的文件函数 ********************** */


/* 在这儿保存上次收到的信息以证明我们可以处理我们的输入。 */
#define MESSAGE_LENGTH 80
static char Message[MESSAGE_LENGTH];


/* 既然我们使用了文件操作结构我们就不能使用预备的那个特殊的proc输出函数
* 我们不得不使用标准的读函数,就是这个函数。*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
static ssize_t module_output(
struct file *file, /* 要读的文件 */
char *buf, /* 要将数据放入的缓冲区(在用户内存段中) */
size_t len, /* 缓冲区长度 */
loff_t *offset) /* 文件偏移量--忽略 */
#else
static int module_output(
struct inode *inode, /* 要读的节点 */
struct file *file, /* 要读的文件 */
char *buf, /* 要将数据放入的缓冲区(在用户内存段中) */
int len) /* 缓冲区长度 */
#endif
{
static int finished = 0;
int i;
char message[MESSAGE_LENGTH+30];

/* 在没有更多信息的时候返回0以指明文件尾。否则进程会从我们的无穷循环中不断的读。 */
if (finished) {
finished = 0;
return 0;
}

/* 我们使用 put_user 将字符串从内核的内存段中拷贝到调用我们的文件的进程的内存段中。
* 顺便说一下, get_user的用法相反。*/
sprintf(message, "Last input:%s", Message);
for(i=0; i<len && message; i++)
put_user(message, buf+i);


/* 注意,我们假设消息的长度小于 len,或者被截短。在真实的情况下,如果消息的长度小于
* len 那么我们会返回 len 而在下次调用时将用消息的第 len+1 个字节开始填充。 */
finished = 1;

return i; /* 返回“读”到的字节 */
}


/* 当用户向/proc文件中写时这个函数接收从用户来的输入。 */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
static ssize_t module_input(
struct file *file, /* 文件自己 */
const char *buf, /* 存有输入的缓冲区 */
size_t length, /* 缓冲区长度 */
loff_t *offset) /* 文件偏移量--忽略 */
#else
static int module_input(
struct inode *inode, /* 文件节点 */
struct file *file, /* 文件自己 */
const char *buf, /* 存有输入的缓冲区 */
int length) /* 缓冲区长度 */
#endif
{
int i;

/* 将输入存入 Message,module_output 将在以后能使用它。 */
for(i=0; i<MESSAGE_LENGTH-1 && i<length; i++)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
get_user(Message, buf+i);
/* 在 2.2 版中 get_user 的语义改变了,它不再返回一个字符而是期待将一个变量作为它的第一参数
* 和一个用户内存段的指针作为第二参数。
*
* 这个改变的原因是在 2.2 版中, get_user 也可以读短整型数或整数。它是通过使用sizeof来知道它将
* 读到何种变量的,为此,它需要那个变量自身。
*/
#else
Message = get_user(buf+i);
#endif
Message = '\0'; /* 标准的以0作终止符的字符串 */

/* 我们需要返回使用的输入字节数。 */
return i;
}

/* 这个函数决定是否允许一个操作(返回0允许,非0不允许同时指明为什么不允许)。
*
* 操作可以是下面的值:
* 0 - 执行 (运行“文件”在我们的情况中是没有意义的)。
* 2 - 写(向内核模块输入)。
* 4 - 读(从内核模块输出)。
*
* 这是一个真实的检查文件权限的函数。由 ls -l 返回的权限只做参考并可以被忽略。
*/
static int module_permission(struct inode *inode, int op)
{
/* 我们可以允许任何人从我们的模块读但只有 root (UID为 0) 可以向它写 */
if (op == 4 || (op == 2 && current->euid == 0))
return 0;

/* 如果是其他值,访问被禁止 */
return -EACCES;
}

/* 文件被打开--我们并不真正关心这个而是这意味着我们需要增加模块的引用计数。 */
int module_open(struct inode *inode, struct file *file)
{
MOD_INC_USE_COUNT;

return 0;
}

/* 文件被关闭--同样只关心引用计数 */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
int module_close(struct inode *inode, struct file *file)
#else
void module_close(struct inode *inode, struct file *file)
#endif
{
MOD_DEC_USE_COUNT;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
return 0; /* success */
#endif
}

/* 作为 /proc 文件登记的结构,含有所有相关函数的指针。 ********** */

/* 对我们的 proc 文件的操作。这是放当某人试图对我们的文件做什么的时候需要调用的函数指针的地方。
* NULL意味着我们不想处理什么事情。
*/
static struct file_operations File_Ops_4_Our_Proc_File =
{
NULL, /* lseek */
module_output, /* 从文件“读” */
module_input, /* 向文件“写” */
NULL, /* readdir */
NULL, /* select */
NULL, /* ioctl */
NULL, /* mmap */
module_open, /* 某人打开了文件 */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
NULL, /* 刷新,在 2.2 版中加到这儿*/
#endif
module_close, /* 某人关闭了文件 */
/* 等等,等等。(在/usr/include/linux/fs.h中它们都被给出)。因为我们在这儿没有放任何东西,系
* 统将保持缺省的数据,在Unix 中为0 (当作为指针时为NULLs )。
*/
};



/* 对我们的proc文件的节点操作。我们需要它,因此我们将在某些地方指定我们想使用的文件操作的结构
* 和用于权限的函数。当然指定任何其他对节点做什么时被调用的函数也是可能的。
* (尽管我们不操心还是放置了 NULL)。
*/
static struct inode_operations Inode_Ops_4_Our_Proc_File =
{
&File_Ops_4_Our_Proc_File,
NULL, /* 创建 */
NULL, /* 查找 */
NULL, /* 连接 */
NULL, /* 删除连接 */
NULL, /* 符号连接 */
NULL, /* 创建目录 */
NULL, /* 删除目录 */
NULL, /* 创建设备 */
NULL, /* 更名 */
NULL, /* 读连接 */
NULL, /* 连接跟随 */
NULL, /* 读页 */
NULL, /* 写页 */
NULL, /* bmap */
NULL, /* 截短 */
module_permission /* 权限检查 */
};


/* 目录项 */
static struct proc_dir_entry Our_Proc_File =
{
0, /* 节点数--忽略,将被 proc_register[_dynamic] 代替 */
7, /* 文件名长度 */
"rw_test", /* 文件名 */
S_IFREG | S_IRUGO | S_IWUSR,
/* 文件模式--这是一个可以被拥有者,用户组以及其他人读的普通文件。
* 当然,它的拥有者可以写。
*
* 实际上这个成员仅仅用于引用,它的 module_permission 做实际的检查。
* 它可以使用这个成员,但我们的实现为了简单而没有用
*/
1, /* 连接数(文件被引用的目录) */
0, 0, /* 文件的UID和GID -我们将它赋予 root */
80, /* 由ls报告的文件大小。 */
&Inode_Ops_4_Our_Proc_File,
/* 指向文件的节点结构的指针,如果我们需要。在我们的情况中我们需要因为我们需要写函数。*/
NULL
/* 文件的读函数。不相关,因为我们将它放入上面的节点结构。 */
};


/* 初始化模块和清除模块 ******************* */

/* 初始化模块-登记 proc 文件 */
int init_module()
{
/* 如果 proc_register[_dynamic] 成功则成功,否则失败。 */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
/* 在 2.2版中,如果它在结构中的值为0, proc_register自动分配一个动态的节点数。因此这儿不再需要
* proc_register_dynamic
*/
return proc_register(&proc_root, &Our_Proc_File);
#else
return proc_register_dynamic(&proc_root, &Our_Proc_File);
#endif
}


/* 清除 - 从 /proc 中注销我们的文件 */
void cleanup_module()
{
proc_unregister(&proc_root, Our_Proc_File.low_ino);
}



浅析2.4下增加proc文件的实现

小数点(tinydot@alian.org) 2002.09.05

一、
最近偶然情况下看到有关proc文件的东东,于是乎去网站上搜索了一把,发现还是有很多高人写的文章(有关程序设计的仅有2篇,而且是翻译的)。细细研读,顿觉简单乎,于是丢弃一边等用时再说啦。不觉间吃过了几次午饭(老板管吃午饭),一日工作又翻到有关proc部分,欣欣然想一试身手,实践一把。结果遭到了巨大的打击,还好苦尽甘来,终于大功告成。(建议不了解linux下模块编程的先去补课啦,要不然没有看明白可别说我文章差啊!呵呵...)
首先从《将/proc作为输入》中拷贝粘贴出相关的代码部分,然后拿到2.4核心linux下编译,结果很多编译错误,还好linux开发源代码(开源就是好啊!),翻阅2.2和2.4的源代码发现2.4中的proc接口和2.2有很多差别,首先注册和注销proc文件的接口就不同啦,在2.2中是proc_register和roc_unregister,其中参数structproc_dir_entry需要自己逐项去赋值,而在2.4下这两个函数没有被引出(见/proc/ksyms)都又被封装了一层,你只需要调用create_proc_entry和remove_proc_entry就行啊,通过调用create_proc_entry,系统分配初始化并返回一structproc_dir_entry,用户根据需要进行相应的修改就行啦,一般是定制自己的读写函数,这是用来和用户空间进行交互的。其实这两个封装函数在2.2中也引出了,但是好像2.2下大家都不用。下面是我的实现代码:


/* tproc.c - create a "file" in /proc
*
* Copyright (C) 2002 by tinydot
*/

#ifndef __KERNEL__
# define __KERNEL__
#endif
#ifndef MODULE
# define MODULE
#endif
#ifndef __KERNEL_SYSCALLS__
#define __KERNEL_SYSCALLS__
#endif

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <string.h>
#include <asm/uaccess.h>

char msg[512];
struct proc_dir_entry *Our_Proc_File = NULL;

/*定制自己的读proc文件的函数,也就是由核心读取传递给用户空间*/
ssize_t our_read(struct file *file, char *buf, size_t size, loff_t *ppos)
{
int i;
for (i = 0; i < size && i < 512 && msg; i++)
put_user(msg, buf + i); /*由核心空间到用户空间*/

return i; /*返回读到的实际长度,表现在用户空间就是read的返回值,这是几则用户空间read的返回值就是几*/
}

/*定制自己的写proc文件的函数,也就是由用户空间得到值并写到proc文件中*/
static ssize_t our_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
int i = 0;
memset(msg, 0, sizeof(msg));
for (i = 0; i < count && i < 512 - 1; i++)
get_user(msg, buf+i); /*由用户空间到核心空间*/

msg = '\0'; /*这一句必不可少,如果不信,试试就知道后果啦,如果真的试,出了事情与我无关啊*/

return i; /*返回写的实际长度,情况同上啦*/
}

struct file_operations fos = {
read: our_read, /*指定用我们的读函数*/
write: our_write, /*指定用我们的写函数, 还有其他例如open lseek close...*/
};

int init_module(void)
{

/*在proc下增加test文件*/
Our_Proc_File = create_proc_entry("test", S_IRUSR | S_IWUSR, NULL);
Our_Proc_File->proc_fops = &fos; /*指定用我们定制的操作*/

return 0;
}

void cleanup_module(void)
{
remove_proc_entry("test", Our_Proc_File);
}

在create_proc_entry中第一个参数是指定添加proc文件的名称, 第二个参数是属性,例如读写
权限,是目录还是文件,以及连接文件等等,在include/linux/stat.h中定义了属性的值,如下

#ifndef _LINUX_STAT_H
#define _LINUX_STAT_H

#ifdef __KERNEL__

#include <asm/stat.h>

#endif

#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)

#define S_IFMT 00170000
#define S_IFSOCK 0140000
#define S_IFLNK 0120000
#define S_IFREG 0100000
#define S_IFBLK 0060000
#define S_IFDIR 0040000
#define S_IFCHR 0020000
#define S_IFIFO 0010000
#define S_ISUID 0004000
#define S_ISGID 0002000
#define S_ISVTX 0001000

#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)

#define S_IRWXU 00700
#define S_IRUSR 00400
#define S_IWUSR 00200
#define S_IXUSR 00100

#define S_IRWXG 00070
#define S_IRGRP 00040
#define S_IWGRP 00020
#define S_IXGRP 00010

#define S_IRWXO 00007
#define S_IROTH 00004
#define S_IWOTH 00002
#define S_IXOTH 00001

#endif

#ifdef __KERNEL__
#define S_IRWXUGO (S_IRWXU|S_IRWXG|S_IRWXO)
#define S_IALLUGO (S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO)
#define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH)
#define S_IWUGO (S_IWUSR|S_IWGRP|S_IWOTH)
#define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH)
#endif

#endif

如果是文件则第三个参数为NULL, 否则指定其父节点啦。

下面是测试程序:
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

int main(void)
{
int fd, i;
char buf[12] = "test proc";
fd = open("/proc/test", O_RDWR);
if (fd < 0)
{
printf("open proc file err!\n");
return -1;
}
i = write(fd, buf, strlen(buf)); /*由write到核心调用我们前面our_write写proc文件*/
printf("write the buf : %s, len: %d\n", buf, i);

memset(buf, 0, sizeof(buf));
i = read(fd, buf, sizeof(buf)); /*由read到核心调用我们前面our_read读proc文件*/
printf("read the buf : %s, len: %d\n", buf, i);

close(fd);
printf("the buf : %s, i = %d\n", buf, i);

return 0;
}

/*
* 简单的makefile
*/
# Makefile for a multifile kernel module

CC=gcc
MODCFLAGS := -Wall -DMODULE -D__KERNEL__ -DLINUX -I /usr/include/linux/

all: tproc.o tstproc

tproc.o:
$(CC) -O2 $(MODCFLAGS) -c tproc.c -o tproc.o
insmod tproc.o

tstproc:
$(CC) -O2 $(MODCFLAGS) -c test.c -o tstproc

clean:
rm -f *.o
rmmod tproc

运行make后,lsmod确定我们的tproc模块已经安装,这时你到/proc下面就发现已经多了一个文件“test”,然后运行刚才编译出的tstproc运行后两次输出应该相等才对.如果你想确认是否在运行tstproc时系统调用了我们定制的read和write可以在两个函数中分别加入printk打印测试信息到message中以便查看。

如果你要做复杂的读写定制可以参考linux源代码,简单的搜索就会发现在linux源代码中有N多的proc_read和proc_write,以及proc_open,proc_close, proc_lseek, proc_lock等等,你可以参照这些函数定制自己的函数。

二、参考文献
1.《将 /proc 作为输入 》作者和翻译不祥。
2.《Linux内核2.4版源代码分析大全》 李善平等编著 机械工业出版社
3. linux源代码

三、
感谢rayson(xh_joke@263.net)的帮助和指正才使此文得以奉献给各位朋友。
如果有朋友发现本文有错误或者就有关知识想进行交流请email to tinydot@alian.org
发表于 2004-5-9 21:31:37 | 显示全部楼层
感谢sunheart兄弟啦,我帮你顶下先:)
发表于 2004-5-14 21:30:14 | 显示全部楼层
好文!
ps:&nbsp是网页空格,希望斑竹把它们换成真正的空格。
发表于 2004-5-16 01:11:02 | 显示全部楼层
谢过兄弟!已修正
发表于 2010-3-3 17:48:12 | 显示全部楼层
太强了吧!!!!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表