The Kernel Newbie Corner: Kernel Debugging Using proc "Sequence" Files--Part 1
转载:https://www.linux.com/learn/linux-career-center/37985-the-kernel-newbie-corner-kernel-debugging-using-proc-qsequenceq-files-part-1
Over this column and the next one (and possibly the one after that, depending on how detailed we get), we're going to discuss kernel and module debugging using proc files. Specifically, we're going to discuss the seq_file implementation of proc files, which represents the newest and most powerful variation of the proc files we're interested in.
This first column will introduce the simpler variation of sequence files, while Part 2 (and beyond) will cover the more complete and formal usage of those files. And, not surprisingly, we're once again going to steal shamelessly from, and build on, what you can read in the classic book LDD3, found online here. In particular, we'll be working out of Chapter 4 of that book, so it would be in your best interest to have that portion of the chapter handy as you read what follows, since I plan on referring to parts of it as we go along.
Oh, and fair warning: a tiny part of this discussion is speculation, so feel free to use the comments section to correct any misinformation.
(The archive of all previous "Kernel Newbie Corner" articles can be found here.)
This is ongoing content from the Linux Foundation training program. If you want more content, please consider signing up for one of these classes.
What is the /proc Directory, Anyway?
If you've worked with Linux for even a short time, you probably already know the answer to that question. The /proc directory is an example of what is known as a "pseudo" filesystem in Linux; that is, something that isn't really a filesystem in that it doesn't take up any space on disk.
Rather, files under the /proc directory act as interfaces to internal kernel data structures, such that accessing entries under /proc magically accesses the underlying contents in kernel space. Put another way, the "files" you see under /proc aren't really "files"; instead, their contents are typically generated dynamically whenever you access them. And it's that dynamic content generation that's the theme of this and next week's columns because that's how you're going to debug the kernel and your loadable modules--by writing loadable modules that create simple proc files that allow you to list the contents of specific kernel data whenever you want.
Examples. We Need Examples.
Of course you do, so let's examine some of the proc files that already typically exist as a standard part of the Linux kernel. Consider listing the "version" of the running kernel via /proc/version:
$ cat /proc/version
Linux version 2.6.31-rc5 (rpjday@localhost.localdomain)
(gcc version 4.4.0 20090506 (Red Hat 4.4.0-4) (GCC) ) #3
SMP Mon Aug 3 11:24:19 EDT 2009
$
Where did all that information come from? It's certainly not stored in that file, since asking for the long listing of that file produces:
$ ls -l /proc/version
-r--r--r--. 1 root root 0 2009-08-07 11:22 /proc/version
It's a file with zero size. But that's because, again, it's not a real file, it's a pseudo file, whose "contents" are generated by some underlying code that implements that file whenever it's accessed. Put another way, the "contents" of numerous proc files is whatever the kernel programmer decided to generate as "output" for those proc files, using whatever combination of appropriate "print" statments that seemed appropriate at the time.
Let's list some other proc files, all of which contain potentially useful information, all of which are zero size, and all of which generate their content based on some underlying code we'll examine a bit later:
$ cat /proc/cpuinfo
$ cat /proc/cmdline
$ cat /proc/modules
$ cat /proc/meminfo
$ cat /proc/interrupts
... and so on and so on, check it out ...
And note well that the contents of various proc files are generated new each time, which is why you'll probably see slightly different output every time you list the contents of, say, /proc/meminfo. Our mission in this week's column is to show you how to design and create your own proc files, so you can, whenever you want, list them to examine whatever kernel data you choose to associate with them.
(Note that, while the contents of some proc files should change constantly as the system runs, others should remain static. For instance, you don't expect the contents of /proc/version or /proc/cpuinfo to change no matter how many times you list them. You get the idea, right?)
As an aside, you can also create writable proc files, so that writing data to a proc file can be used to modify kernel data structures. But since this is a column on simple debugging, we'll restrict ourselves to just readable proc files. Anyone wanting to get more ambitious is welcome to read the appropriate docs.
Exercise for the reader: Take some time and examine some of the other files under /proc that look like they might contain useful information. Don't be scared to go into some of those subdirectories. If you have the time, read the kernel documentation file Documentation/filesystems/proc.txt.
So What's a "Sequence" File, Then?
And here's where things get a bit tricky. Technically, a "proc file" is nothing more than the file you can see under the /proc directory--it has a name, an owner and group, a size of (typically) zero and some permissions that dictate who is allowed to perform what operations on it. And that's all.
A proc file by itself does absolutely nothing. What's necessary is to then implement some read and/or write code behind it that defines what it means to read from (or write to) that file. And a sequence file is simply one of the possible implementations you can use to define those operations. So what's so special about a sequence file?
At this point, for the sake of brevity, I'm going to refer you to the proper sections of LDD3 that discuss the rationale for sequence files, but I'll at least summarize it here. The "older" and current implementations of proc files had an awkward limitation of not being able to "print" more than a single page of output (a page being defined by the definition of the kernel PAGE_SIZE macro). Sequence files solve this problem by generating the "output" of a proc file as a sequence of writes, each of which can be up to a page in size, with no limit on the number of writes, effectively solving the problem and allowing unlimited output from a single proc file.
In fact, it's probably safe to say that, while you'll still find a lot of the old implementation in the current kernel tree, sequence files are easily the preferred way to implement output-only proc files, even when the output is very brief.
To emphasize the difference between proc files and sequence files, two important points:
- You can create proc files that don't use the underlying seq_file implementation, and
- you can create files based on the seq_file implementation in places other than under /proc.
An Example, Please
At this point, we definitely need a live example, so let's whip up a trivial proc file that will display the current value of jiffies (the tick counter) whenever we list it. Consider the loadable module jif.c (in this case, for a 64-bit system):
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h> #include <linux/fs.h> // for basic filesystem
#include <linux/proc_fs.h> // for the proc filesystem
#include <linux/seq_file.h> // for sequence files
#include <linux/jiffies.h> // for jiffies static struct proc_dir_entry* jif_file; static int
jif_show(struct seq_file *m, void *v)
{
seq_printf(m, "%llu\n",
(unsigned long long) get_jiffies_64());
return 0;
} static int
jif_open(struct inode *inode, struct file *file)
{
return single_open(file, jif_show, NULL);
} static const struct file_operations jif_fops = {
.owner = THIS_MODULE,
.open = jif_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
}; static int __init
jif_init(void)
{
jif_file = proc_create("jif", 0, NULL, &jif_fops); if (!jif_file) {
return -ENOMEM;
} return 0;
} static void __exit
jif_exit(void)
{
remove_proc_entry("jif", NULL);
} module_init(jif_init);
module_exit(jif_exit); MODULE_LICENSE("GPL");
At this point (using what you know from previous columns in this series), create the corresponding Makefile, and compile the module and load it. As soon as you load that module, verify that the following proc file now exists:
# ls -l /proc/jif
-r--r--r--. 1 root root 0 2009-08-10 19:48 /proc/jif
#
at which point, you should (as a regular user given the read permissions), be able to list that file as much as you want to display the current value of the appropriate kernel jiffies variable:
$ cat /proc/jif
4329225958
$ cat /proc/jif
4329226854
$ cat /proc/jif
4329227174
$ cat /proc/jif
4329227486
$ cat /proc/jif
4329227798
$ cat /proc/jif
4329228078
$
after which you can (as root) unload the module, at which point the proc file is removed. Yes, it really is that simple. And now, to work.
So What Just Happened There?
Let's summarize just the critical features of the above example, so you can start implementing your own (simple) debugging proc files; we'll leave the more complicated features for Part 2. So, about the above:
- The necessary header files should be self-explanatory--just use that list.
- The pointer of type proc_dir_entry (defined in the kernel header file include/linux/proc_fs.h, if you're interested) is used to keep track of the proc file that is created but, in fact, if your proc file code is simple enough, you might not even need to keep track of that. (You'll see why shortly.)
- Your "show" routine (in our case, jif_show()) is what defines what is displayed when someone lists your proc file. That routine always takes the same two argument types but, in simple cases like ours, you can ignore the second argument--it's only relevant when you're actually "sequencing" through the output, as you'll see in Part 2.)
In short, you can define whatever output you want to print based on any kernel routines or data structures you can think of, but keep in mind that, if this is a loadable module, you will have access to only that kernel data that's been "exported."
- The next two portions of our example--the jif_open routine and the jif_fops structure--should be self-explanatory as well. Just substitute your own "open" routine name in there, and leave the rest as is. The fact that this is a trivial sequence file that doesn't even need sequencing is represented by the use of the "single_" variation of the operations.
- The entry and exit routines should also be reasonably self-explanatory. The module entry routine creates the proc file with a name of "jif", the "0" argument represents the default file permission of 0444, the "NULL" means that the file should be created directly under the /proc directory, and the final argument identifies the file operations structure to associate with that file. In other words, the creation of the proc file and its association with its open and I/O routines is all done in a single call.
The exit routine is much simpler--it simply deletes the file by name.
Simple, no? But there is one more point worth making.
Can We Make This Any Simpler?
In fact, we can if we want to cut some corners. As you can see, our entry routine does the proper error checking on whether or not we could even create our proc file and, if that failed, we return a negative error code which, as always, causes the module to fail to load:
static int __init
jif_init(void)
{
jif_file = proc_create("jif", 0, NULL, &jif_fops); if (!jif_file) {
return -ENOMEM;
} return 0;
}
However, if you tighten up the code, you don't even need to save the pointer to the proc_dir_entry structure:
static int __init
jif_init(void)
{
if (!proc_create("jif", 0, NULL, &jif_fops)) {
return -ENOMEM;
} return 0;
}
You can do that since, if you look carefully, you don't really need that pointer anywhere else in the module. In an example this trivial, the exit routine simply has to delete the proc file by its name--it has no need for that pointer so, really, there's no need to hang onto it. At least for now.
And if you really wanted to tighten things up, you could bypass the error-checking altogether:
static int __init
jif_init(void)
{
proc_create("jif", 0, NULL, &jif_fops); return 0;
}
The above simply assumes that there's no possible way for that file creation step to fail. That's probably not wise for production-level code, but the chance of that step failing is typically small so it's probably acceptable for informal testing.
What About Some Kernel Examples?
And since you've seen the very basics of creating a short, output-only sequence file, it's worth seeing the code that's responsible for printing a number of those short files you saw under /proc earlier, such as /proc/version. A number of those files can be found in the kernel source tree, in the fs/proc directory, so let's examine version.c
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/utsname.h> static int version_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, linux_proc_banner,
utsname()->sysname,
utsname()->release,
utsname()->version);
return 0;
} static int version_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, version_proc_show, NULL);
} static const struct file_operations version_proc_fops = {
.open = version_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
}; static int __init proc_version_init(void)
{
proc_create("version", 0, NULL, &version_proc_fops);
return 0;
}
module_init(proc_version_init);
All of the above should look reasonably similar to your loadable module code, but keep two distinctions in mind:
- Notice that many of the kernel proc files in that directory have no module exit routine. That's because they're built into the kernel proper, and have no option to be built as loadable modules. Hence, they have no need for an exit routine. (For the same reason, they don't need to set a file operations "owner" field because they will never have a module owner.)
- As we've already noted, the proc files that are built into the kernel have access to all kernel symbols with external linkage, while your modules can only access what is exported.
Exercises for the reader: Take a look at some of the other simple sequence files in the kernel fs/proc directory, to see how they match up with their corresponding /proc files that we listed earlier. Some of them should be simple enough that you can see how they work.
In addition, if you have the time, write a loadable module that, when loaded, creates an output-only proc file (say, /proc/hz) that, when read, displays the kernel "HZ" value--that is, the configured kernel tick rate. It's up to you to figure out where to get that value and how to print it out.
Next week: More sequence files, of course.
The Kernel Newbie Corner: Kernel Debugging Using proc "Sequence" Files--Part 1的更多相关文章
- The Kernel Newbie Corner: Kernel Debugging with proc "Sequence" Files--Part 3
转载:https://www.linux.com/learn/linux-career-center/44184-the-kernel-newbie-corner-kernel-debugging-w ...
- The Kernel Newbie Corner: Kernel Debugging with proc "Sequence" Files--Part 2
转载:https://www.linux.com/learn/linux-career-center/39972-kernel-debugging-with-proc-qsequenceq-files ...
- Kernel Methods (5) Kernel PCA
先看一眼PCA与KPCA的可视化区别: 在PCA算法是怎么跟协方差矩阵/特征值/特征向量勾搭起来的?里已经推导过PCA算法的小半部分原理. 本文假设你已经知道了PCA算法的基本原理和步骤. 从原始输入 ...
- Kernel Methods (2) Kernel function
几个重要的问题 现在已经知道了kernel function的定义, 以及使用kernel后可以将非线性问题转换成一个线性问题. 在使用kernel 方法时, 如果稍微思考一下的话, 就会遇到以下几个 ...
- Debugging Information in Separate Files
[Debugging Information in Separate Files] gdb allows you to put a program's debugging information in ...
- Kernel Methods (4) Kernel SVM
(本文假设你已经知道了hard margin SVM的基本知识.) 如果要为Kernel methods找一个最好搭档, 那肯定是SVM. SVM从90年代开始流行, 直至2012年被deep lea ...
- Kernel Methods (3) Kernel Linear Regression
Linear Regression 线性回归应该算得上是最简单的一种机器学习算法了吧. 它的问题定义为: 给定训练数据集\(D\), 由\(m\)个二元组\(x_i, y_i\)组成, 其中: \(x ...
- Linux Kernel sys_call_table、Kernel Symbols Export Table Generation Principle、Difference Between System Calls Entrance In 32bit、64bit Linux
目录 . sys_call_table:系统调用表 . 内核符号导出表:Kernel-Symbol-Table . Linux 32bit.64bit环境下系统调用入口的异同 . Linux 32bi ...
- Jordan Lecture Note-12: Kernel典型相关分析(Kernel Canonical Correlation Analysis, KCCA).
Kernel典型相关分析 (一)KCCA 同样,我们可以引入Kernel函数,通过非线性的坐标变换达到之前CCA所寻求的目标.首先,假设映射$\Phi_X: x\rightarrow \Phi_X(x ...
随机推荐
- 检查 CPU 是否支持二级地址转换 - 摘自网络
Windows 8 Consumer Preview 于2月正式发布,随后 Windows Server 8 Beta 也公布了下载.整体对比,Windows 8 在硬件方面的要求并不高,其最低硬件需 ...
- Cocos2d-JS v3.0 alpha不支持cocos2d-x的Physics integration
cocos2d-x 3.0新的Physics integration,把chipmunk和Box2D封装到引擎内部 auto scene = Scene::createWithPhysics(); s ...
- QueryInterface
QueryInterface IUnknown *p2; hr = pInnerUnknown->QueryInterface(vGUID2, (void**)&p2); IUnknow ...
- 使用IIS6.0遇到问题后,常用的几种解决方法
1.检查 .Net Framework,是否安装完全,不确定的情况下使用:aspnet_regiis.exe -i 或者 aspnet_regiis.exe -r 2.检查 IIS 6.0 其它相关配 ...
- HDU 5861 Road (线段树)
Road 题目链接: http://acm.split.hdu.edu.cn/showproblem.php?pid=5861 Description There are n villages alo ...
- Android SDK Manager更新不了的解决办法
android SDK Manager更新不了,出现错误提示:"Failed to fetch URL..."! 可以用以下办法解决: 使用SDK Manager更新时出现问题 F ...
- (高精度运算4.7.21)UVA 10106 Product(大数乘法)
package com.njupt.acm; import java.math.BigInteger; import java.util.Scanner; public class UVA_10106 ...
- 那些不被关注但很重要的html标签
1.meta标签: <meta> 元素可提供有关页面的元信息(meta-information),比如针对搜索引擎和更新频度的描述和关键词. <meta> 标签位于文档的头部, ...
- MySQL事务处理和锁机制
事务处理和并发性 1.1 基础知识和相关概念 1 )全部的表类型都可以使用锁,但是只有 InnoDB 和 BDB 才有内置的事务功能. 2 )使用 begin 开始事务,使用 commit 结束事务, ...
- WPF的DataGrid绑定ItemsSource后第一次加载数据有个别列移位的解决办法
最近用WPF的DataGrid的时候,发现一个很弱智的问题,DataGrid的ItemsSource是绑定了一个属性: 然后取数给这个集合赋值的时候,第一次赋值,就会出现列移位 起初还以为是显卡的问题 ...