请教proc文件系统问题

发布于 2022-09-21 00:56:21 字数 308 浏览 14 评论 0

在卸载driver模块时,需要通过remove_proc_entry删除自己创建的proc文件。
但是,如果此时要删除的proc文件正被别的模块使用(不停的读数据),
remove_proc_entry仅仅在proc_dir_entry中置一个删除标志位。
driver模块卸载掉后,用ls是无法看到此proc文件了,但是如果试图去打开
这个已经不存在的proc文件时,会导致系统kernel panic。
发生panic的位置在proc_file_read中。好像是inode已经不存在了。

大家遇到过这个问题吗?如何解决?谢谢

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

眼眸 2022-09-28 00:56:21

建议LZ看一下LDD3中对proc文件的讲解。
“remove_proc_entry仅仅在proc_dir_entry中置一个删除标志位”这个说法是不正确的,因为并没有引用计数,所以应该是直接删除了,而后你再调用proc_file_read,操作的便成了一块未知的内存区域,所以难免会导致kernel panic。所以,LDD3上说,尽量不要在/proc下新增加proc文件,而推荐采用sysfs。

擦肩而过的背影 2022-09-28 00:56:21

“如果此时要删除的proc文件正被别的模块使用”所以引用计数大于0,
所以“remove_proc_entry仅仅在proc_dir_entry中置一个删除标志位”。

其实如果不执行后面的打开文件操作,在remove_proc_entry前打开的文件
还是可以继续读的,只是读到的内容为空。
而只要再试图打开此文件,就会导致前面的读出现问题。

猫卆 2022-09-28 00:56:21

是这样的,执行remove_proc_entry后,应该是inode都删除掉了,而执行remove_proc_entry之前,inode还存在,只是模块的read和write为NULL了吧,所以读出的内容为空。
而inode都不存在了,再去读,也许就是panic的原因了。

森末i 2022-09-28 00:56:21

看了一下源码,的确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?

刚开始学习驱动编程,对文件系统不熟悉,多谢指教。

骄兵必败 2022-09-28 00:56:21

首先,inode是有引用计数的“atomic_t i_count;”所以,当inode被引用的时候,如果执行删除操作,那只是减少其引用计数,直到引用它的进程释放inode,即i_count为0时才会执行真正的删除操作。因此,刚开始读的时候,这个inode是存在的,就不会导致pannic,而只是返回NULL。而到后面,inode引用计数为0被删除后,再调用读操作时,引用了空指针,直接导致了pannic。

小梨窩很甜 2022-09-28 00:56:21

晕了晕了!!!
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();
}

暖树树初阳… 2022-09-28 00:56:21

原帖由 dreamice 于 2008-8-20 10:57 发表
首先,inode是有引用计数的“atomic_t i_count;”所以,当inode被引用的时候,如果执行删除操作,那只是减少其引用计数,直到引用它的进程释放inode,即i_count为0时才会执行真正的删除操作。因此,刚开始读的时 ...

但是,我有一个程序一直在开着此文件,并不停的读数据,引用计数不可能为0。
后来又打开此proc文件时才出现的panic。

我用此方法卸载系统自己的模块看看,如果也出panic,那可能是内核的问题了。

月竹挽风 2022-09-28 00:56:21

这个地方已经很明显了啊。“我有一个程序一直在开着此文件,并不停的读数据,引用计数不可能为0”
但是当这个程序读完以后,引用计数就变为0,对inode执行真正的删除操作。所以,以后再打开时,就会导致pannic了。

失退 2022-09-28 00:56:21

原帖由 dreamice 于 2008-8-20 11:22 发表
这个地方已经很明显了啊。“我有一个程序一直在开着此文件,并不停的读数据,引用计数不可能为0”
但是当这个程序读完以后,引用计数就变为0,对inode执行真正的删除操作。所以,以后再打开时,就会导致pannic了。

这个程序一直在执行,并没有停止。
是在原来读取程序不退出的情况下,打开proc文件时出现的panic。

其实这种情况应该很常见呀,看系统自己的驱动也是直接用remove_proc_entry处理的。
但是可能导致panic出现,这就有些怪了。

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文