如何在proc中加入一个文件

简介

PROC是一个特殊的文件系统,核心藉由这个文件系统可以提供应用程序一个 安全的界
面来存取某些特别的资料。例如硬件/内存的使用状况,或者是某 个进程开启了那些
文件。

当我们在核心中加入某个新的功能或是驱动程序后,可能会想提供一个方法 让应用程
序可以取得驱动程序中某些状态,一般来说这个功能可能需要经过写出 一个处理ioctl
的调用来完成。这个界面对于一些比较“功能性”的信息可能是很 适合的,因为使用
者程序可能必须将这些信息读出后再做一定的处理。但对于一 些“实时性”的信息,
例如内存的使用状况,或者是驱动设备的统计资料等。 我们可能需要一个比较简单
易用的界面来取得它们。

PROC文件系统便提供了我们这样的一个界面,我们可以用cat,more或任何的 文本编辑
程序来查看这些信息,例如来查看一个系统内存的使用状况可以用

# cat /proc/meminfo
total: used: free: shared: buffers: cached:
Mem: 31698944 12648448 19050496 6770688 1581056 6815744
Swap: 0 0 0
MemTotal: 30956 kB
MemFree: 18604 kB
MemShared: 6612 kB
Buffers: 1544 kB
Cached: 6656 kB
SwapTotal: 0 kB
SwapFree: 0 kB

目前在Linux中几乎所有的信息都可以经过PROC文件系统取得一些统计性甚或功能 性的
信息。有些驱动程序甚至允许经过PROC文件系统改变某个功能变量的值,例 如JAVA执
行格式的驱动程式允许我们经由PROC文件系统设定JAVA直译程序的执 行文件名称。

加入一个文件

如果你的驱动程序不是很复杂,那直接在PROC的根目录底下加入一个文件是比较简 单
的做法。下面举一个例子告诉大家这是多麻简单的一件事,但如果你想要提供比 较复
杂的信息,后面会有更进一步的说明。

接下来我以一个很简单的例子做说明,在这个例子之中我们在PROC的根目录底 下加入
一个叫做test的文件,你可以用下面的命令读出其中的值。

# cat /proc/test
test status

要达到这个目的我们必须改变三个文件,第一个是include/linux/proc_fs.h这个文件。

enum root_directory_inos {
PROC_ROOT_INO = 1,
PROC_LOADAVG,
PROC_UPTIME,
PROC_MEMINFO,
PROC_KMSG,
PROC_VERSION,
PROC_CPUINFO,
PROC_PCI,
PROC_SELF, /* will change inode # */
PROC_NET,
PROC_SCSI,
PROC_MALLOC,
PROC_KCORE,
PROC_MODULES,
PROC_STAT,
PROC_DEVICES,
PROC_INTERRUPTS,
PROC_FILESYSTEMS,
PROC_KSYMS,
PROC_DMA,
PROC_IOPORTS,
#ifdef __SMP_PROF__
PROC_SMP_PROF,
#endif
PROC_PROFILE, /* whether enabled or not */
PROC_CMDLINE,
PROC_SYS,
PROC_MTAB,
PROC_MD,
PROC_RTC,
PROC_LOCKS
#ifdef CONFIG_TESTPROC
,PROC_TEST
#endif
};

在CONFIG_TESTPROC中的程序是我们新加入的部份,这个enumerate中申明的是在PROC
根目录下的一些文件代码。PROC是一个虚拟的文件系统,所以实际上的信息可能散见
于各驱动程序之中,而非在任何设备之上。那INODE中的位置栏位记载些什么呢? 对一
般的文件系统而言它们应该是设备的逻辑位置,而在PROC中记载的就是上列的数值了。

PROC提供了一些其本的支持给驱动程序可以很容易的在文件系统中加入一个文件, 而
不必各自去做一些处理INODE的动作。在我们的要求中只需在根目录中加入一个叫 test
的文件就可以了。所以我们可以直接修改root.c这个文件的proc_root_init这 个函
数,这个函数负责在根目录中加入各种不同驱动程序需要的文件。

#ifdef CONFIG_TESTPROC
proc_register(&proc_root, &(struct proc_dir_entry) {
PROC_TEST, 4, "test",
S_IFREG | S_IRUGO, 1, 0, 0,
});
#endif

请注意"test"和PROC_TEST这二个参数,前者是在目录中的文件名称,而后者是这个文
件所对映的代码。接下来我们会看到,这个代码被用来调用定义于驱动程序中的函
数。这个函数的功能是提供这个文件的内容。

到此PROC文件系统中的修改已经完成了,当核心被重新建立后已经可以在文件系统中
看见test这个文件了。但这个文件的内容是空的,如何为它加入内容呢? 这 件工作应
该由驱动程序来做,但如何将二者连起来呢? 这就要靠刚才定义的PROC_TEST 这个代码
来完成,在array.c中的get_root_array是用来将代码转换为函式的位置, 它的程序为

static int get_root_array(char * page, int type, char **start, off_t offset, int length)
{
switch (type) {
case PROC_LOADAVG:
return get_loadavg(page);

case PROC_UPTIME:
return get_uptime(page);

case PROC_MEMINFO:
return get_meminfo(page);

#ifdef CONFIG_PCI
case PROC_PCI:
return get_pci_list(page);
#endif

case PROC_CPUINFO:
return get_cpuinfo(page);

case PROC_VERSION:
return get_version(page);

#ifdef CONFIG_DEBUG_MALLOC
case PROC_MALLOC:
return get_malloc(page);
#endif

#ifdef CONFIG_MODULES
case PROC_MODULES:
return get_module_list(page);

case PROC_KSYMS:
return get_ksyms_list(page, start, offset, length);
#endif

case PROC_STAT:
return get_kstat(page);

case PROC_DEVICES:
return get_device_list(page);

case PROC_INTERRUPTS:
return get_irq_list(page);

case PROC_FILESYSTEMS:
return get_filesystem_list(page);

case PROC_DMA:
return get_dma_list(page);

case PROC_IOPORTS:
return get_ioport_list(page);
#ifdef CONFIG_BLK_DEV_MD
case PROC_MD:
return get_md_status(page);
#endif
#ifdef __SMP_PROF__
case PROC_SMP_PROF:
return get_smp_prof_list(page);
#endif
case PROC_CMDLINE:
return get_cmdline(page);

case PROC_MTAB:
return get_filesystem_info( page );
#ifdef CONFIG_RTC
case PROC_RTC:
return get_rtc_status(page);
#endif
#ifdef CONFIG_TESTPROC
case PROC_TEST:
return get_test_status(page);
#endif
case PROC_LOCKS:
return get_locks_status(page);
}
return -EBADF;
}

这段程序我们不需要解释了吧! 如果各位有兴趣看看核心是如何由文件系统 连结到这
个函数的过程,请看下一节的说明。接下来我们就看一下,我们所 加入的
get_test_status这个函数的内容。通常这个函数应该位于驱动程序或 核心之中,所以
我把它放在drivers/char之中,这个位置只是一个任意的选 择,没有其它的意义,你
可以因自已的需要做不同的选择。

我将文件的名称取做testproc.c,内容是

#include

int get_test_status(char *buf)
{
char *p;

p = buf;

p += sprintf(p,"test status\n");
return p - buf;
}

这个程序好像....好像....太简单了一点吧,不过如果你去找一下其它的函数,结构
几乎是一样的,我们只要将字串或任何信息填入buf之中,并且将实际的长度传 回即
可。因为buf是一个传入的缓冲区,那一定有长度的限制吧! 当然有,它的长 度是一个
内存页的长度,在X86中就是4096个字节。超过了可能会毁了核心, 一定要小心。

至此整个工作已经完成,但如果你此时就去做编译核心的工作,你会得到一个
'get_test_status undefined'的错误,这是因为我们并没有告诉核心多了testproc 这
个文件。我们必须修改在drivers/char中的Config.in和Makefile二个文件,在
Config.in中加入

bool 'Test proc' CONFIG_TESTPROC

这会让我们在做make menuconfig或make config时会多出一个选项,让我们可以设定
是否要加入这个功能。还记得前面的代码吗? 所有的代码都被放在

#ifdef CONFIG_TESTPROC

#endif

之间。但testproc.c这个文件如何加入呢? 这个要靠在Makefile中加入

ifeq ($(CONFIG_TESTPROC),y)
L_OBJS += testproc.o
endif

你可以看到只有在CONFIG_TESTPROC被选择的情况下testproc.c才会被编译且 连结至核
心之中。至此你便可以重新建立一个核心了,记得在编译前先执行 make menuconfig并
将CONFIG_TESTPROC的选项打开。

遥远的行程

[未完成.....]

加入一个子目录

[未完成....]

.
注:这是一位台湾同胞写的,我见到时已经由BIG5码转位GB码了,而且没有作者名字。
    我在收录时将"程式"改为"程序","引数"改为"参数"等等,不一而足。
 

本文转自中文Linux论坛