请教proc文件系统问题
在卸载driver模块时,需要通过remove_proc_entry删除自己创建的proc文件。
但是,如果此时要删除的proc文件正被别的模块使用(不停的读数据),
remove_proc_entry仅仅在proc_dir_entry中置一个删除标志位。
driver模块卸载掉后,用ls是无法看到此proc文件了,但是如果试图去打开
这个已经不存在的proc文件时,会导致系统kernel panic。
发生panic的位置在proc_file_read中。好像是inode已经不存在了。
大家遇到过这个问题吗?如何解决?谢谢
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
建议LZ看一下LDD3中对proc文件的讲解。
“remove_proc_entry仅仅在proc_dir_entry中置一个删除标志位”这个说法是不正确的,因为并没有引用计数,所以应该是直接删除了,而后你再调用proc_file_read,操作的便成了一块未知的内存区域,所以难免会导致kernel panic。所以,LDD3上说,尽量不要在/proc下新增加proc文件,而推荐采用sysfs。
“如果此时要删除的proc文件正被别的模块使用”所以引用计数大于0,
所以“remove_proc_entry仅仅在proc_dir_entry中置一个删除标志位”。
其实如果不执行后面的打开文件操作,在remove_proc_entry前打开的文件
还是可以继续读的,只是读到的内容为空。
而只要再试图打开此文件,就会导致前面的读出现问题。
是这样的,执行remove_proc_entry后,应该是inode都删除掉了,而执行remove_proc_entry之前,inode还存在,只是模块的read和write为NULL了吧,所以读出的内容为空。
而inode都不存在了,再去读,也许就是panic的原因了。
看了一下源码,的确inode被删除了,只是proc_dir_entry结构没有删除而置了标志位。
这就怪了:(
为什么原来的那个读程序没有出问题,而是等到再次打开此不存在的文件时才会出问题。
/*
* Remove a /proc entry and free it if it's not currently in use.
* If it is in use, we set the 'deleted' flag.
*/
void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
{
struct proc_dir_entry **p;
struct proc_dir_entry *de;
const char *fn = name;
int len;
if (!parent && xlate_proc_name(name, &parent, &fn) != 0)
goto out;
len = strlen(fn);
for (p = &parent->subdir; *p; p=&(*p)->next ) {
if (!proc_match(len, fn, *p))
continue;
de = *p;
*p = de->next;
de->next = NULL;
if (S_ISDIR(de->mode))
parent->nlink--;
proc_kill_inodes(de);
de->nlink = 0;
WARN_ON(de->subdir);
if (!atomic_read(&de->count))
free_proc_entry(de);
else {
de->deleted = 1;
printk("remove_proc_entry: %s/%s busy, count=%d\n",
parent->name, de->name, atomic_read(&de->count));
}
break;
}
out:
return;
}
inode在remove_proc_entry之后已经删除了,但是为什么read不会出问题呢?
static ssize_t
proc_file_read(struct file *file, char __user *buf, size_t nbytes,
loff_t *ppos)
{
struct inode * inode = file->f_dentry->d_inode;
char *page;
ssize_t retval=0;
int eof=0;
ssize_t n, count;
char *start;
struct proc_dir_entry * dp;
dp = PDE(inode);
if (!(page = (char*) __get_free_page(GFP_KERNEL)))
return -ENOMEM;
while ((nbytes > 0) && !eof) {
count = min_t(size_t, PROC_BLOCK_SIZE, nbytes);
start = NULL;
if (dp->get_info) {
/* Handle old net routines */
n = dp->get_info(page, &start, *ppos, count);
if (n < count)
eof = 1;
} else if (dp->read_proc) {
/*
* How to be a proc read function
* ------------------------------
* Prototype:
* int f(char *buffer, char **start, off_t offset,
* int count, int *peof, void *dat)
*
* Assume that the buffer is "count" bytes in size.
*
* If you know you have supplied all the data you
* have, set *peof.
*
* You have three ways to return data:
* 0) Leave *start = NULL. (This is the default.)
* Put the data of the requested offset at that
* offset within the buffer. Return the number (n)
* of bytes there are from the beginning of the
* buffer up to the last byte of data. If the
* number of supplied bytes (= n - offset) is
* greater than zero and you didn't signal eof
* and the reader is prepared to take more data
* you will be called again with the requested
* offset advanced by the number of bytes
* absorbed. This interface is useful for files
* no larger than the buffer.
* 1) Set *start = an unsigned long value less than
* the buffer address but greater than zero.
* Put the data of the requested offset at the
* beginning of the buffer. Return the number of
* bytes of data placed there. If this number is
* greater than zero and you didn't signal eof
* and the reader is prepared to take more data
* you will be called again with the requested
* offset advanced by *start. This interface is
* useful when you have a large file consisting
* of a series of blocks which you want to count
* and return as wholes.
* (Hack by Paul.Russell@rustcorp.com.au)
* 2) Set *start = an address within the buffer.
* Put the data of the requested offset at *start.
* Return the number of bytes of data placed there.
* If this number is greater than zero and you
* didn't signal eof and the reader is prepared to
* take more data you will be called again with the
* requested offset advanced by the number of bytes
* absorbed.
*/
n = dp->read_proc(page, &start, *ppos,
count, &eof, dp->data);
} else
break;
if (n == 0) /* end of file */
break;
if (n < 0) { /* error */
if (retval == 0)
retval = n;
break;
}
if (start == NULL) {
if (n > PAGE_SIZE) {
printk(KERN_ERR
"proc_file_read: Apparent buffer overflow!\n");
n = PAGE_SIZE;
}
n -= *ppos;
if (n <= 0)
break;
if (n > count)
n = count;
start = page + *ppos;
} else if (start < page) {
if (n > PAGE_SIZE) {
printk(KERN_ERR
"proc_file_read: Apparent buffer overflow!\n");
n = PAGE_SIZE;
}
if (n > count) {
/*
* Don't reduce n because doing so might
* cut off part of a data block.
*/
printk(KERN_WARNING
"proc_file_read: Read count exceeded\n");
}
} else /* start >= page */ {
unsigned long startoff = (unsigned long)(start - page);
if (n > (PAGE_SIZE - startoff)) {
printk(KERN_ERR
"proc_file_read: Apparent buffer overflow!\n");
n = PAGE_SIZE - startoff;
}
if (n > count)
n = count;
}
n -= copy_to_user(buf, start < page ? page : start, n);
if (n == 0) {
if (retval == 0)
retval = -EFAULT;
break;
}
*ppos += start < page ? (unsigned long)start : n;
nbytes -= n;
buf += n;
retval += n;
}
free_page((unsigned long) page);
return retval;
}
按理说,在proc_file_read的第一行执行代码就应该出问题呀
dp = PDE(inode);
此时inode已经不存在了而PDE定义如下
#define PDE(inode) ((inode)->u.generic_ip)
应该是,即使不再次打开此文件,原来的读文件也应该出问题。
kernel的bug?
刚开始学习驱动编程,对文件系统不熟悉,多谢指教。
首先,inode是有引用计数的“atomic_t i_count;”所以,当inode被引用的时候,如果执行删除操作,那只是减少其引用计数,直到引用它的进程释放inode,即i_count为0时才会执行真正的删除操作。因此,刚开始读的时候,这个inode是存在的,就不会导致pannic,而只是返回NULL。而到后面,inode引用计数为0被删除后,再调用读操作时,引用了空指针,直接导致了pannic。
晕了晕了!!!
remove_proc_entry好像并没有删除inode。
只是把文件操作置为NULL,这样可以解释为什么一直读不会出问题。
但是,inode到底什么时候删的呀?
/*
* Kill an inode that got unregistered..
*/
static void proc_kill_inodes(struct proc_dir_entry *de)
{
struct list_head *p;
struct super_block *sb = proc_mnt->mnt_sb;
/*
* Actually it's a partial revoke().
*/
file_list_lock();
list_for_each(p, &sb->s_files) {
struct file * filp = list_entry(p, struct file, f_list);
struct dentry * dentry = filp->f_dentry;
struct inode * inode;
struct file_operations *fops;
if (dentry->d_op != &proc_dentry_operations)
continue;
inode = dentry->d_inode;
if (PDE(inode) != de)
continue;
fops = filp->f_op;
filp->f_op = NULL;
fops_put(fops);
}
file_list_unlock();
}
但是,我有一个程序一直在开着此文件,并不停的读数据,引用计数不可能为0。
后来又打开此proc文件时才出现的panic。
我用此方法卸载系统自己的模块看看,如果也出panic,那可能是内核的问题了。
这个地方已经很明显了啊。“我有一个程序一直在开着此文件,并不停的读数据,引用计数不可能为0”
但是当这个程序读完以后,引用计数就变为0,对inode执行真正的删除操作。所以,以后再打开时,就会导致pannic了。
这个程序一直在执行,并没有停止。
是在原来读取程序不退出的情况下,打开proc文件时出现的panic。
其实这种情况应该很常见呀,看系统自己的驱动也是直接用remove_proc_entry处理的。
但是可能导致panic出现,这就有些怪了。