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的选项打开。