使用procfs访问Linux内核Word文档下载推荐.docx
- 文档编号:6881514
- 上传时间:2023-05-07
- 格式:DOCX
- 页数:22
- 大小:34.45KB
使用procfs访问Linux内核Word文档下载推荐.docx
《使用procfs访问Linux内核Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《使用procfs访问Linux内核Word文档下载推荐.docx(22页珍藏版)》请在冰点文库上搜索。
每个文件都提供了有关这个特殊进程的详细信息。
例如,要查看init的command-line项的内容,只需对cmdline文件执行cat命令。
/proc中另外一些有趣的文件有:
cpuinfo,它标识了处理器的类型和速度;
pci,显示在PCI总线上找到的设备;
modules,标识了当前加载到内核中的模块。
清单1.对/proc的交互过程
[root@plato]#ls/proc
1204023472874474fbmdstatsys
1042061235629309filesystemsmeminfosysrq-trigger
113207323752933acpifsmiscsysvipc
13752124092934buddyinfoidemodulestty
1395218924452935businterruptsmountsuptime
1706220125142938cmdlineiomemmtrrversion
179221125152947cpuinfoioportsnetvmstat
180222326073cryptoirqpartitions
181227826083004deviceskallsymspci
182229126093008diskstatskcoreself
223012633056dmakmsgslabinfo
201523112805394driverloadavgstat
2019233728214execdomainslocksswaps
[root@plato1]#ls/proc/1
auxvcwdexeloginuidmemoom_adjrootstatmtask
cmdlineenvironfdmapsmountsoom_scorestatstatuswchan
[root@plato]#cat/proc/1/cmdline
init[5]
[root@plato]#
清单2展示了对/proc中的一个虚拟文件进行读写的过程。
这个例子首先检查内核的TCP/IP栈中的IP转发的目前设置,然后再启用这种功能。
清单2.对/proc进行读写(配置内核)
[root@plato]#cat/proc/sys/net/ipv4/ip_forward
[root@plato]#echo"
1"
>
/proc/sys/net/ipv4/ip_forward
1
另外,我们还可以使用sysctl来配置这些内核条目。
有关这个问题的更多信息,请参阅参考资料一节的内容。
顺便说一下,/proc文件系统并不是GNU/Linux系统中的惟一一个虚拟文件系统。
在这种系统上,sysfs是一个与/proc类似的文件系统,但是它的组织更好(从/proc中学习了很多教训)。
不过/proc已经确立了自己的地位,因此即使sysfs与/proc相比有一些优点,/proc也依然会存在。
还有一个debugfs文件系统,不过(顾名思义)它提供的更多是调试接口。
debugfs的一个优点是它将一个值导出给用户空间非常简单(实际上这不过是一个调用而已)。
内核模块简介
可加载内核模块(LKM)是用来展示/proc文件系统的一种简单方法,这是因为这是一种用来动态地向Linux内核添加或删除代码的新方法。
LKM也是Linux内核中为设备驱动程序和文件系统使用的一种流行机制。
如果您曾经重新编译过Linux内核,就可能会发现在内核的配置过程中,有很多设备驱动程序和其他内核元素都被编译成了模块。
如果一个驱动程序被直接编译到了内核中,那么即使这个驱动程序没有运行,它的代码和静态数据也会占据一部分空间。
但是如果这个驱动程序被编译成一个模块,就只有在需要内存并将其加载到内核时才会真正占用内存空间。
有趣的是,对于LKM来说,我们不会注意到有什么性能方面的差异,因此这对于创建一个适应于自己环境的内核来说是一种功能强大的手段,这样可以根据可用硬件和连接的设备来加载对应的模块。
下面是一个简单的LKM,可以帮助您理解它与在Linux内核中看到的标准(非动态可加载的)代码之间的区别。
清单3给出了一个最简单的LKM。
(可以从本文的下载一节中下载这个代码)。
清单3包括了必须的模块头(它定义了模块的API、类型和宏)。
然后使用MODULE_LICENSE定义了这个模块使用的许可证。
此处,我们定义的是GPL,从而防止会污染到内核。
清单3然后又定义了这个模块的init和cleanup函数。
my_module_init函数是在加载这个模块时被调用的,它用来进行一些初始化方面的工作。
my_module_cleanup函数是在卸载这个模块时被调用的,它用来释放内存并清除这个模块的踪迹。
注意此处printk的用法:
这是内核的printf函数。
KERN_INFO符号是一个字符串,可以用来对进入内核回环缓冲区的信息进行过滤(非常类似于syslog)。
最后,清单3使用module_init和module_exit宏声明了入口函数和出口函数。
这样我们就可以按照自己的意愿来对这个模块的init和cleanup函数进行命名了,不过我们最终要告诉内核维护函数就是这些函数。
清单3.一个简单的但可以正常工作的LKM(simple-lkm.c)
#include<
linux/module.h>
/*DefinesthelicenseforthisLKM*/
MODULE_LICENSE("
GPL"
);
/*Initfunctioncalledonmoduleentry*/
intmy_module_init(void)
{
printk(KERN_INFO"
my_module_initcalled.Moduleisnowloaded.\n"
return0;
}
/*Cleanupfunctioncalledonmoduleexit*/
voidmy_module_cleanup(void)
my_module_cleanupcalled.Moduleisnowunloaded.\n"
return;
/*Declareentryandexitfunctions*/
module_init(my_module_init);
module_exit(my_module_cleanup);
清单3尽管非常简单,但它却是一个真正的LKM。
现在让我们对其进行编译并在一个2.6版本的内核上进行测试。
2.6版本的内核为内核模块的编译引入了一种新方法,我发现这种方法比原来的方法简单了很多。
对于文件simple-lkm.c,我们可以创建一个makefile,其惟一内容如下:
obj-m+=simple-lkm.o
要编译LKM,请使用make命令,如清单4所示。
清单4.编译LKM
[root@plato]#make-C/usr/src/linux-`uname-r`SUBDIRS=$PWDmodules
make:
Enteringdirectory`/usr/src/linux-2.6.11'
CC[M]/root/projects/misc/module2.6/simple/simple-lkm.o
Buildingmodules,stage2.
MODPOST
CC/root/projects/misc/module2.6/simple/simple-lkm.mod.o
LD[M]/root/projects/misc/module2.6/simple/simple-lkm.ko
Leavingdirectory`/usr/src/linux-2.6.11'
结果会生成一个simple-lkm.ko文件。
这个新的命名约定可以帮助将这些内核对象(LKM)与标准对象区分开来。
现在可以加载或卸载这个模块了,然后可以查看它的输出。
要加载这个模块,请使用insmod命令;
反之,要卸载这个模块,请使用rmmod命令。
lsmod可以显示当前加载的LKM(参见清单5)。
清单5.插入、检查和删除LKM
[root@plato]#insmodsimple-lkm.ko
[root@plato]#lsmod
ModuleSizeUsedby
simple_lkm15360
autofs4262440
video139560
button52640
battery76840
ac37160
yenta_socket189523
rsrc_nonstatic94721yenta_socket
uhci_hcd321440
i2c_piix478240
dm_mod564683
[root@plato]#rmmodsimple-lkm
注意,内核的输出进到了内核回环缓冲区中,而不是打印到stdout上,这是因为stdout是进程特有的环境。
要查看内核回环缓冲区中的消息,可以使用dmesg工具(或者通过/proc本身使用cat/proc/kmsg命令)。
清单6给出了dmesg显示的最后几条消息。
清单6.查看来自LKM的内核输出
[root@plato]#dmesg|tail-5
cs:
IOportprobe0xa00-0xaff:
clean.
eth0:
Linkisdown
Linkisup,runningat100Mbithalf-duplex
my_module_initcalled.Moduleisnowloaded.
my_module_cleanupcalled.Moduleisnowunloaded.
可以在内核输出中看到这个模块的消息。
现在让我们暂时离开这个简单的例子,来看几个可以用来开发有用LKM的内核API。
回页首
集成到/proc文件系统中
内核程序员可以使用的标准API,LKM程序员也可以使用。
LKM甚至可以导出内核使用的新变量和函数。
有关API的完整介绍已经超出了本文的范围,因此我们在这里只是简单地介绍后面在展示一个更有用的LKM时所使用的几个元素。
创建并删除/proc项
要在/proc文件系统中创建一个虚拟文件,请使用create_proc_entry函数。
这个函数可以接收一个文件名、一组权限和这个文件在/proc文件系统中出现的位置。
create_proc_entry的返回值是一个proc_dir_entry指针(或者为NULL,说明在create时发生了错误)。
然后就可以使用这个返回的指针来配置这个虚拟文件的其他参数,例如在对该文件执行读操作时应该调用的函数。
create_proc_entry的原型和proc_dir_entry结构中的一部分如清单7所示。
清单7.用来管理/proc文件系统项的元素
structproc_dir_entry*create_proc_entry(constchar*name,mode_tmode,
structproc_dir_entry*parent);
structproc_dir_entry{
constchar*name;
//virtualfilename
mode_tmode;
//modepermissions
uid_tuid;
//File'
suserid
gid_tgid;
sgroupid
structinode_operations*proc_iops;
//Inodeoperationsfunctions
structfile_operations*proc_fops;
//Fileoperationsfunctions
structproc_dir_entry*parent;
//Parentdirectory
...
read_proc_t*read_proc;
///procreadfunction
write_proc_t*write_proc;
///procwritefunction
void*data;
//Pointertoprivatedata
atomic_tcount;
//usecount
};
voidremove_proc_entry(constchar*name,structproc_dir_entry*parent);
稍后我们就可以看到如何使用read_proc和write_proc命令来插入对这个虚拟文件进行读写的函数。
要从/proc中删除一个文件,可以使用remove_proc_entry函数。
要使用这个函数,我们需要提供文件名字符串,以及这个文件在/proc文件系统中的位置(parent)。
这个函数原型如清单7所示。
parent参数可以为NULL(表示/proc根目录),也可以是很多其他值,这取决于我们希望将这个文件放到什么地方。
表1列出了可以使用的其他一些父proc_dir_entry,以及它们在这个文件系统中的位置。
表1.proc_dir_entry快捷变量
proc_dir_entry
在文件系统中的位置
proc_root_fs
/proc
proc_net
/proc/net
proc_bus
/proc/bus
proc_root_driver
/proc/driver
回调函数
我们可以使用write_proc函数向/proc中写入一项。
这个函数的原型如下:
intmod_write(structfile*filp,constchar__user*buff,
unsignedlonglen,void*data);
filp参数实际上是一个打开文件结构(我们可以忽略这个参数)。
buff参数是传递给您的字符串数据。
缓冲区地址实际上是一个用户空间的缓冲区,因此我们不能直接读取它。
len参数定义了在buff中有多少数据要被写入。
data参数是一个指向私有数据的指针(参见清单7)。
在这个模块中,我们声明了一个这种类型的函数来处理到达的数据。
Linux提供了一组API来在用户空间和内核空间之间移动数据。
对于write_proc的情况来说,我们使用了copy_from_user函数来维护用户空间的数据。
读回调函数
我们可以使用read_proc函数从一个/proc项中读取数据(从内核空间到用户空间)。
intmod_read(char*page,char**start,off_toff,
intcount,int*eof,void*data);
page参数是这些数据写入到的位置,其中count定义了可以写入的最大字符数。
在返回多页数据(通常一页是4KB)时,我们需要使用start和off参数。
当所有数据全部写入之后,就需要设置eof(文件结束参数)。
与write类似,data表示的也是私有数据。
此处提供的page缓冲区在内核空间中。
因此,我们可以直接写入,而不用调用copy_to_user。
其他有用的函数
我们还可以使用proc_mkdir、symlinks以及proc_symlink在/proc文件系统中创建目录。
对于只需要一个read函数的简单/proc项来说,可以使用create_proc_read_entry,这会创建一个/proc项,并在一个调用中对read_proc函数进行初始化。
这些函数的原型如清单8所示。
清单8.其他有用的/proc函数
/*Createadirectoryintheprocfilesystem*/
structproc_dir_entry*proc_mkdir(constchar*name,
/*Createasymlinkintheprocfilesystem*/
structproc_dir_entry*proc_symlink(constchar*name,
structproc_dir_entry*parent,
constchar*dest);
/*Createaproc_dir_entrywitharead_proc_tinonecall*/
structproc_dir_entry*create_proc_read_entry(constchar*name,
mode_tmode,
structproc_dir_entry*base,
read_proc_t*read_proc,
void*data);
/*Copybuffertouser-spacefromkernel-space*/
unsignedlongcopy_to_user(void__user*to,
constvoid*from,
unsignedlongn);
/*Copybuffertokernel-spacefromuser-space*/
unsignedlongcopy_from_user(void*to,
constvoid__user*from,
/*Allocatea'
virtually'
contiguousblockofmemory*/
void*vmalloc(unsignedlongsize);
/*Freeavmalloc'
dblockofmemory*/
voidvfree(void*addr);
/*Exportasymboltothekernel(makeitvisibletothekernel)*/
EXPORT_SYMBOL(symbol);
/*Exportallsymbolsinafiletothekernel(declarebeforemodule.h)*/
EXPORT_SYMTAB
通过/proc文件系统实现财富分发
下面是一个可以支持读写的LKM。
这个简单的程序提供了一个财富甜点分发。
在加载这个模块之后,用户就可以使用echo命令向其中导入文本财富,然后再使用cat命令逐一读出。
清单9给出了基本的模块函数和变量。
init函数(init_fortune_module)负责使用vmalloc来为这个点心罐分配空间,然后使用memset将其全部清零。
使用所分配并已经清空的cookie_pot内存,我们在/proc中创建了一个proc_dir_entry项,并将其称为fortune。
当proc_entry成功创建之后,对自己的本地变量和proc_entry结构进行了初始化。
我们加载了/procread和write函数(如清单9和清单10所示),并确定这个模块的所有者。
cleanup函数简单地从/proc文件系统中删除这一项,然后释放cookie_pot所占据的内存。
cookie_pot是一个固定大小(4KB)的页,它使用两个索引进行管理。
第一个是cookie_index,标识了要将下一个cookie写到哪里去。
变量next_fortune标识了下一个cookie应该从哪里读取以便进行输出。
在所有的fortune项都读取之后,我们简单地回到了next_fortune。
清单9.模块的init/cleanu
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 使用 procfs 访问 Linux 内核
![提示](https://static.bingdoc.com/images/bang_tan.gif)