4. 创建proc目录
在 proc 文件系统中,目录(directory)是组织虚拟文件节点的核心容器。与普通文件系统不同,proc 目录并不对应物理存储介质上的目录项,而是通过内核函数 proc_mkdir 在内存中动态创建的虚拟目录节点。本章将深入解析 proc_mkdir 的函数原型、参数含义、多级目录创建策略以及目录权限的精细控制方法。
4.1 proc_mkdir 函数原型与参数详解
proc_mkdir 是 Linux 内核提供用于在 proc 文件系统中创建目录的核心 API。其函数原型定义在 <linux/proc_fs.h> 头文件中:
struct proc_dir_entry *proc_mkdir(const char *name,
struct proc_dir_entry *parent);
| 参数 | 类型 | 说明 |
|---|---|---|
name |
const char * |
目录名称字符串,仅支持单级目录名(如 "my_driver"),不能包含路径分隔符 /。 |
parent |
struct proc_dir_entry * |
父目录的 proc_dir_entry 指针。若为 NULL,则目录创建在 proc 根目录 /proc 下。 |
| 返回值 | struct proc_dir_entry * |
成功时返回新创建目录的 proc_dir_entry 指针;失败返回 NULL。 |
关键提示:
proc_mkdir 创建的目录默认权限为 0555(读+执行,无写权限),且目录的所有者与组均为 root。如果需要自定义权限,必须使用 proc_mkdir_mode 函数。
4.2 单级目录创建实战
以下代码演示如何在 /proc 根目录下创建一个名为 my_driver 的目录:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
static struct proc_dir_entry *my_dir;
static int __init my_init(void)
{
// 在 /proc 根目录下创建 "my_driver" 目录
my_dir = proc_mkdir("my_driver", NULL);
if (!my_dir) {
pr_err("Failed to create /proc/my_driver\n");
return -ENOMEM;
}
pr_info("Directory /proc/my_driver created successfully\n");
return 0;
}
static void __exit my_exit(void)
{
if (my_dir)
proc_remove(my_dir);
pr_info("Directory /proc/my_driver removed\n");
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
验证方法:加载模块后执行
ls -ld /proc/my_driver,应看到 dr-xr-xr-x 2 root root 0 ... 的目录项。
4.3 多级目录创建策略
proc 文件系统不支持类似 mkdir -p 的递归创建方式。要创建多级目录(如 /proc/my_driver/sub_dir),必须逐级调用 proc_mkdir,并保存每一级的 proc_dir_entry 指针作为下一级的父目录参数。
常见陷阱:不能直接使用
proc_mkdir("my_driver/sub_dir", NULL),因为 proc_mkdir 不解析路径分隔符,该调用会尝试创建名为 "my_driver/sub_dir" 的单级目录,导致失败。
正确的多级目录创建流程如下:
static struct proc_dir_entry *parent_dir;
static struct proc_dir_entry *child_dir;
static int __init my_init(void)
{
// 第一级:创建 /proc/my_driver
parent_dir = proc_mkdir("my_driver", NULL);
if (!parent_dir)
return -ENOMEM;
// 第二级:在 /proc/my_driver 下创建 sub_dir
child_dir = proc_mkdir("sub_dir", parent_dir);
if (!child_dir) {
proc_remove(parent_dir);
return -ENOMEM;
}
pr_info("Created /proc/my_driver/sub_dir\n");
return 0;
}
static void __exit my_exit(void)
{
// 只需移除顶层目录,内核会递归清理子目录
if (parent_dir)
proc_remove(parent_dir);
}
最佳实践:移除目录时只需调用
proc_remove 移除顶层目录,内核会自动递归释放所有子目录和文件节点。切勿单独移除子目录后再移除父目录,可能导致双重释放。
4.4 目录权限设置:proc_mkdir_mode 函数
当需要自定义目录的访问权限时,应使用 proc_mkdir_mode 函数。其原型如下:
struct proc_dir_entry *proc_mkdir_mode(const char *name,
umode_t mode,
struct proc_dir_entry *parent);
与 proc_mkdir 相比,多了一个 mode 参数,用于指定目录的权限位(使用标准的 Unix 权限掩码)。
| 权限宏 | 数值 | 含义 |
|---|---|---|
S_IRUGO |
0444 |
所有用户只读 |
S_IRWXUGO |
0777 |
所有用户读、写、执行 |
S_IRUSR | S_IXUSR |
0500 |
所有者读+执行,其他无权限 |
S_IRWXU | S_IRGRP | S_IXGRP |
0750 |
所有者完全控制,组用户读+执行 |
示例:创建一个仅所有者可读写执行、其他用户只读执行的目录:
static struct proc_dir_entry *secure_dir;
static int __init my_init(void)
{
// 权限:所有者 rwx (0700),组用户 r-x (050),其他用户 r-x (050)
secure_dir = proc_mkdir_mode("secure", S_IRWXU | S_IRGRP | S_IXGRP |
S_IROTH | S_IXOTH, NULL);
if (!secure_dir)
return -ENOMEM;
pr_info("Created /proc/secure with mode 0755\n");
return 0;
}
4.5 目录创建与权限设置流程总览
下图展示了 proc 目录从创建到权限设置的完整流程:
4.6 目录权限与安全注意事项
- 最小权限原则:proc 目录默认权限
0555通常已足够。除非确实需要用户写入目录(如动态创建子目录),否则不应开放写权限。 - 权限继承:在 proc 目录下创建的文件或子目录,其权限由各自的创建函数独立指定,不会自动继承父目录权限。
- root 用户限制:即使目录权限设置为
0777,非 root 用户也无法通过mkdir或rmdir系统调用修改 proc 目录结构,因为 proc 文件系统在内核层面限制了目录修改操作。 - 调试建议:开发阶段可使用
proc_mkdir_mode设置0755权限,方便调试时查看目录内容;发布前应收紧权限。
4.7 常见错误与排查
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
proc_mkdir 返回 NULL |
内核内存不足,或目录名已存在 | 检查 dmesg 输出;使用 ls /proc 确认目录是否已存在 |
| 目录创建成功但无法访问 | 权限设置过严(如 0000) |
使用 proc_mkdir_mode 设置至少 0555 权限 |
| 多级目录创建失败 | 父目录指针传递错误或父目录已被移除 | 确保每级目录创建后检查返回值,并正确传递指针 |
| 模块卸载时内核崩溃 | 重复移除目录或移除顺序错误 | 只调用一次 proc_remove 移除顶层目录 |
调试技巧:在
dmesg 中启用 proc 调试信息(echo 1 > /sys/kernel/debug/proc/level),可以查看目录创建和移除的详细内核日志。