我注意到您询问的是 mknodeat,但这个函数在标准的 Linux 系统调用中并不存在。您可能想问的是以下几种情况之一:

  1. mknodat - 创建特殊文件的系统调用
  2. mknod - 传统的创建特殊文件函数
  3. mkdirat - 创建目录的系统调用(我在前面已经详细介绍过)

让我为您提供最可能相关的 mknodat 函数详解:

mknodat 函数详解 見出しへのリンク

1. 函数介绍 見出しへのリンク

mknodat 是 Linux 系统中用于创建特殊文件(设备文件、命名管道、符号链接等)的系统调用,它是 mknod 函数的增强版本,支持相对路径创建。可以把 mknodat 想象成"特殊文件创建工厂"——它能够根据指定的类型和参数创建各种系统特殊文件。

2. 函数原型 見出しへのリンク

#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sysmacros.h>

int mknodat(int dirfd, const char *pathname, mode_t mode, dev_t dev);

3. 功能 見出しへのリンク

mknodat 函数用于创建特殊文件,包括设备文件(字符设备、块设备)、命名管道(FIFO)等。如果 pathname 是相对路径,它相对于 dirfd 指定的目录来解释。

4. 参数 見出しへのリンク

  • dirfd: 目录文件描述符,作为路径解析的起始点
    • 如果是有效的文件描述符,则 pathname 相对于该目录解释
    • 如果是 AT_FDCWD,则 pathname 相对于当前工作目录解释
  • pathname: 要创建的特殊文件路径名
  • mode: 文件类型和权限模式
  • dev: 设备号(对于设备文件)

5. 文件类型和权限模式 見出しへのリンク

文件类型(mode 的高 4 位) 見出しへのリンク

类型 说明
S_IFREG 0100000 普通文件
S_IFDIR 0040000 目录
S_IFCHR 0020000 字符设备
S_IFBLK 0060000 块设备
S_IFIFO 0010000 命名管道
S_IFSOCK 0140000 套接字
S_IFLNK 0120000 符号链接

权限模式(mode 的低 12 位) 見出しへのリンク

权限 说明
S_IRWXU 0700 所有者读写执行
S_IRUSR 0400 所有者读
S_IWUSR 0200 所有者写
S_IXUSR 0100 所有者执行
S_IRWXG 0070 组读写执行
S_IRGRP 0040 组读
S_IWGRP 0020 组写
S_IXGRP 0010 组执行
S_IRWXO 0007 其他用户读写执行
S_IROTH 0004 其他用户读
S_IWOTH 0002 其他用户写
S_IXOTH 0001 其他用户执行

6. 设备号(dev 参数) 見出しへのリンク

对于字符设备和块设备,需要指定设备号:

  • 主设备号: 标识设备类型
  • 次设备号: 标识同一类型中的具体设备

使用 makedev() 宏创建设备号:

dev_t dev = makedev(major, minor);

7. 返回值 見出しへのリンク

  • 成功: 返回 0
  • 失败: 返回 -1,并设置相应的 errno 错误码

8. 常见错误码 見出しへのリンク

  • EACCES: 权限不足
  • EEXIST: 文件已存在
  • EFAULT: pathname 指针无效
  • EINVAL: 参数无效
  • ELOOP: 符号链接循环
  • ENAMETOOLONG: 路径名过长
  • ENOENT: 路径前缀不存在
  • ENOMEM: 内存不足
  • ENOSPC: 设备空间不足
  • ENOTDIR: dirfd 不是目录
  • EPERM: 权限不足(创建设备文件通常需要 root 权限)
  • EROFS: 文件系统只读

9. 相似函数或关联函数 見出しへのリンク

  • mknod: 传统的创建特殊文件函数
  • mkdirat: 创建目录
  • openat: 相对路径打开文件
  • mkfifo: 创建命名管道
  • creat: 创建普通文件

10. 示例代码 見出しへのリンク

示例1:基础用法 - 创建各种特殊文件 見出しへのリンク

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sysmacros.h>
#include <errno.h>
#include <string.h>

// 显示文件信息
void show_file_info(const char *path) {
    struct stat file_stat;
    
    if (stat(path, &file_stat) == -1) {
        printf("  无法获取文件信息: %s\n", strerror(errno));
        return;
    }
    
    printf("  路径: %s\n", path);
    printf("  Inode: %ld\n", (long)file_stat.st_ino);
    printf("  权限: %o", file_stat.st_mode & 07777);
    
    // 显示文件类型
    printf(" (");
    switch (file_stat.st_mode & S_IFMT) {
        case S_IFREG:  printf("普通文件"); break;
        case S_IFDIR:  printf("目录"); break;
        case S_IFCHR:  printf("字符设备"); break;
        case S_IFBLK:  printf("块设备"); break;
        case S_IFIFO:  printf("命名管道"); break;
        case S_IFSOCK: printf("套接字"); break;
        case S_IFLNK:  printf("符号链接"); break;
        default:       printf("未知类型"); break;
    }
    printf(")\n");
    
    // 如果是设备文件,显示设备号
    if (S_ISCHR(file_stat.st_mode) || S_ISBLK(file_stat.st_mode)) {
        printf("  主设备号: %d\n", major(file_stat.st_rdev));
        printf("  次设备号: %d\n", minor(file_stat.st_rdev));
    }
    
    printf("  所有者: UID=%d, GID=%d\n", 
           file_stat.st_uid, file_stat.st_gid);
    printf("\n");
}

int main() {
    printf("=== mknodat 基础示例 ===\n\n");
    
    // 1. 创建命名管道 (FIFO)
    printf("1. 创建命名管道:\n");
    if (mknodat(AT_FDCWD, "test_fifo", S_IFIFO | 0666, 0) == 0) {
        printf("  ✓ 成功创建命名管道 test_fifo\n");
        show_file_info("test_fifo");
    } else {
        if (errno == EACCES) {
            printf("  ⚠ 权限不足,需要适当权限才能创建 FIFO\n");
        } else {
            printf("  ✗ 创建命名管道失败: %s\n", strerror(errno));
        }
    }
    
    // 2. 创建普通文件(不推荐,应使用 open())
    printf("2. 创建普通文件:\n");
    if (mknodat(AT_FDCWD, "test_file", S_IFREG | 0644, 0) == 0) {
        printf("  ✓ 成功创建普通文件 test_file\n");
        show_file_info("test_file");
    } else {
        printf("  ✗ 创建普通文件失败: %s\n", strerror(errno));
    }
    
    // 3. 尝试创建字符设备(需要 root 权限)
    printf("3. 创建字符设备 (需要 root 权限):\n");
    dev_t null_dev = makedev(1, 3);  // /dev/null 的设备号
    if (mknodat(AT_FDCWD, "test_char_dev", S_IFCHR | 0666, null_dev) == 0) {
        printf("  ✓ 成功创建字符设备 test_char_dev\n");
        show_file_info("test_char_dev");
    } else {
        if (errno == EPERM) {
            printf("  ⚠ 权限不足: 创建设备文件需要 root 权限\n");
        } else {
            printf("  ✗ 创建字符设备失败: %s\n", strerror(errno));
        }
    }
    
    // 4. 尝试创建已存在的文件
    printf("4. 尝试创建已存在的文件:\n");
    if (mknodat(AT_FDCWD, "test_fifo", S_IFIFO | 0666, 0) == -1) {
        if (errno == EEXIST) {
            printf("  ✓ 正确: 文件已存在 (EEXIST)\n");
        } else {
            printf("  ✗ 意外错误: %s\n", strerror(errno));
        }
    }
    
    // 清理创建的文件
    printf("\n清理测试文件:\n");
    if (unlink("test_fifo") == 0) {
        printf("  ✓ 删除 test_fifo\n");
    }
    if (unlink("test_file") == 0) {
        printf("  ✓ 删除 test_file\n");
    }
    if (access("test_char_dev", F_OK) == 0 && unlink("test_char_dev") == 0) {
        printf("  ✓ 删除 test_char_dev\n");
    }
    
    return 0;
}

示例2:使用目录文件描述符 見出しへのリンク

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sysmacros.h>
#include <errno.h>
#include <string.h>
#include <dirent.h>

// 创建特殊文件并显示信息
int create_and_show_special_file(int dirfd, const char *pathname, 
                                mode_t mode, dev_t dev, const char *description) {
    printf("=== %s ===\n", description);
    
    if (mknodat(dirfd, pathname, mode, dev) == 0) {
        printf("  ✓ 成功创建: %s\n", pathname);
        
        // 显示文件信息
        struct stat file_stat;
        if (fstatat(dirfd, pathname, &file_stat, 0) == 0) {
            printf("  类型: ");
            switch (file_stat.st_mode & S_IFMT) {
                case S_IFIFO: printf("命名管道"); break;
                case S_IFCHR: printf("字符设备"); break;
                case S_IFBLK: printf("块设备"); break;
                case S_IFREG: printf("普通文件"); break;
                default: printf("其他类型"); break;
            }
            printf("\n");
            printf("  权限: %o\n", file_stat.st_mode & 0777);
            printf("  Inode: %ld\n", (long)file_stat.st_ino);
            
            if (S_ISCHR(file_stat.st_mode) || S_ISBLK(file_stat.st_mode)) {
                printf("  设备号: %d,%d\n", 
                       major(file_stat.st_rdev), minor(file_stat.st_rdev));
            }
        }
        
        return 0;
    } else {
        printf("  ✗ 创建失败: %s\n", strerror(errno));
        return -1;
    }
}

int main() {
    printf("=== mknodat 目录文件描述符示例 ===\n\n");
    
    // 创建基础目录结构
    printf("创建测试环境:\n");
    if (mkdir("base_dir", 0755) == -1 && errno != EEXIST) {
        perror("创建 base_dir 失败");
        return 1;
    }
    printf("  ✓ 创建 base_dir\n");
    
    // 打开目录获取文件描述符
    int base_dirfd = open("base_dir", O_RDONLY);
    if (base_dirfd == -1) {
        perror("打开 base_dir 失败");
        rmdir("base_dir");
        return 1;
    }
    printf("  ✓ 打开 base_dir,fd=%d\n\n", base_dirfd);
    
    // 1. 使用目录文件描述符创建相对路径文件
    printf("1. 使用目录文件描述符创建相对路径文件:\n");
    if (create_and_show_special_file(base_dirfd, "relative_fifo", 
                                   S_IFIFO | 0666, 0,
                                   "在 base_dir 中创建 relative_fifo") == 0) {
        // 显示目录内容
        DIR *dir = opendir("base_dir");
        if (dir) {
            printf("  base_dir 内容:\n");
            struct dirent *entry;
            while ((entry = readdir(dir)) != NULL) {
                if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
                    printf("    %s\n", entry->d_name);
                }
            }
            closedir(dir);
        }
        printf("\n");
    }
    
    // 2. 创建多层嵌套目录中的特殊文件
    printf("2. 创建嵌套目录中的特殊文件:\n");
    if (mkdirat(base_dirfd, "subdir", 0755) == 0) {
        printf("  ✓ 创建子目录 subdir\n");
        
        // 在子目录中创建 FIFO
        int subdir_fd = openat(base_dirfd, "subdir", O_RDONLY);
        if (subdir_fd != -1) {
            if (create_and_show_special_file(subdir_fd, "nested_fifo",
                                           S_IFIFO | 0644, 0,
                                           "在 subdir 中创建 nested_fifo") == 0) {
                // 显示嵌套目录内容
                DIR *subdir = opendir("base_dir/subdir");
                if (subdir) {
                    printf("  subdir 内容:\n");
                    struct dirent *entry;
                    while ((entry = readdir(subdir)) != NULL) {
                        if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
                            printf("    %s\n", entry->d_name);
                        }
                    }
                    closedir(subdir);
                }
            }
            close(subdir_fd);
        }
    }
    printf("\n");
    
    // 3. 使用 AT_FDCWD 创建文件
    printf("3. 使用 AT_FDCWD 创建文件:\n");
    if (create_and_show_special_file(AT_FDCWD, "at_fdcwd_fifo",
                                   S_IFIFO | 0666, 0,
                                   "使用 AT_FDCWD 创建文件") == 0) {
        show_file_info("at_fdcwd_fifo");
    }
    
    // 4. 使用绝对路径(忽略 dirfd)
    printf("4. 使用绝对路径创建文件:\n");
    char abs_path[512];
    char *cwd = getcwd(NULL, 0);
    if (cwd) {
        snprintf(abs_path, sizeof(abs_path), "%s/absolute_fifo", cwd);
        free(cwd);
        
        if (create_and_show_special_file(base_dirfd, abs_path,
                                       S_IFIFO | 0666, 0,
                                       "使用绝对路径创建文件") == 0) {
            show_file_info("absolute_fifo");
        }
    }
    
    // 清理资源
    printf("清理测试文件:\n");
    close(base_dirfd);
    
    // 递归删除目录
    unlink("base_dir/subdir/nested_fifo");
    rmdir("base_dir/subdir");
    rmdir("base_dir");
    unlink("at_fdcwd_fifo");
    unlink("absolute_fifo");
    
    printf("  ✓ 清理完成\n");
    
    return 0;
}

示例3:完整的特殊文件管理工具 見出しへのリンク

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sysmacros.h>
#include <errno.h>
#include <string.h>
#include <getopt.h>
#include <libgen.h>

// 全局配置结构
struct mknod_config {
    int file_type;      // 文件类型
    mode_t file_mode;   // 文件权限模式
    int major;          // 主设备号
    int minor;          // 次设备号
    int verbose;        // 详细输出模式
    int ignore_exist;   // 忽略已存在错误
    char *base_dir;     // 基础目录路径
};

// 创建特殊文件
int create_special_file(int base_dirfd, const char *pathname, 
                       const struct mknod_config *config) {
    mode_t mode = config->file_mode;
    dev_t dev = 0;
    
    // 设置文件类型
    switch (config->file_type) {
        case 'p':  // FIFO
            mode |= S_IFIFO;
            break;
        case 'c':  // 字符设备
            mode |= S_IFCHR;
            dev = makedev(config->major, config->minor);
            break;
        case 'b':  // 块设备
            mode |= S_IFBLK;
            dev = makedev(config->major, config->minor);
            break;
        case 'f':  // 普通文件
            mode |= S_IFREG;
            break;
        default:
            fprintf(stderr, "未知文件类型: %c\n", config->file_type);
            return -1;
    }
    
    // 创建特殊文件
    int result = mknodat(base_dirfd, pathname, mode, dev);
    
    if (result == 0) {
        if (config->verbose) {
            printf("创建文件: %s", pathname);
            switch (config->file_type) {
                case 'p': printf(" (FIFO)"); break;
                case 'c': printf(" (字符设备 %d,%d)", config->major, config->minor); break;
                case 'b': printf(" (块设备 %d,%d)", config->major, config->minor); break;
                case 'f': printf(" (普通文件)"); break;
            }
            printf(" 权限 %o\n", config->file_mode);
        }
    } else {
        if (errno == EEXIST) {
            if (config->ignore_exist) {
                if (config->verbose) {
                    printf("文件已存在: %s\n", pathname);
                }
                return 0;  // 视为成功
            } else {
                fprintf(stderr, "文件已存在: %s\n", pathname);
                return -1;
            }
        } else if (errno == EPERM) {
            fprintf(stderr, "权限不足: 创建设备文件需要 root 权限\n");
            return -1;
        } else {
            fprintf(stderr, "创建文件失败 '%s': %s\n", pathname, strerror(errno));
            return -1;
        }
    }
    
    return result;
}

// 显示文件信息
void show_file_details(int base_dirfd, const char *pathname) {
    struct stat st;
    if (fstatat(base_dirfd, pathname, &st, 0) == 0) {
        printf("文件详细信息:\n");
        printf("  路径: %s\n", pathname);
        printf("  类型: ");
        switch (st.st_mode & S_IFMT) {
            case S_IFIFO: printf("命名管道 (FIFO)\n"); break;
            case S_IFCHR: printf("字符设备\n"); break;
            case S_IFBLK: printf("块设备\n"); break;
            case S_IFREG: printf("普通文件\n"); break;
            case S_IFDIR: printf("目录\n"); break;
            case S_IFLNK: printf("符号链接\n"); break;
            case S_IFSOCK: printf("套接字\n"); break;
            default: printf("未知类型\n"); break;
        }
        printf("  权限: %o\n", st.st_mode & 0777);
        printf("  Inode: %ld\n", (long)st.st_ino);
        printf("  所有者: UID=%d, GID=%d\n", st.st_uid, st.st_gid);
        
        if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
            printf("  设备号: %d,%d (主:%d, 次:%d)\n", 
                   major(st.st_rdev), minor(st.st_rdev),
                   major(st.st_rdev), minor(st.st_rdev));
        }
    }
}

// 显示帮助信息
void show_help(const char *program_name) {
    printf("用法: %s [选项] 文件...\n", program_name);
    printf("\n选项:\n");
    printf("  -t, --type=TYPE        文件类型 (p=FIFO, c=字符设备, b=块设备, f=普通文件)\n");
    printf("  -m, --mode=MODE        设置文件权限模式\n");
    printf("  --major=MAJOR          主设备号 (字符/块设备)\n");
    printf("  --minor=MINOR          次设备号 (字符/块设备)\n");
    printf("  -v, --verbose          显示详细信息\n");
    printf("  -i, --ignore-exist     忽略已存在的文件\n");
    printf("  -b, --base-dir=DIR     指定基础目录\n");
    printf("  -h, --help             显示此帮助信息\n");
    printf("\n示例:\n");
    printf("  %s -t p pipe1 pipe2              # 创建命名管道\n", program_name);
    printf("  %s -t c --major=1 --minor=3 null # 创建字符设备\n", program_name);
    printf("  %s -t b --major=8 --minor=0 sda  # 创建块设备\n", program_name);
    printf("  %s -t f -m 644 regular_file      # 创建普通文件\n", program_name);
    printf("  %s -b /tmp -t p relative_pipe    # 在指定目录创建\n", program_name);
}

int main(int argc, char *argv[]) {
    struct mknod_config config = {
        .file_type = 'p',    // 默认创建 FIFO
        .file_mode = 0666,   // 默认权限
        .major = 0,
        .minor = 0,
        .verbose = 0,
        .ignore_exist = 0,
        .base_dir = NULL
    };
    
    printf("=== 特殊文件创建工具 ===\n\n");
    
    // 解析命令行参数
    static struct option long_options[] = {
        {"type",        required_argument, 0, 't'},
        {"mode",        required_argument, 0, 'm'},
        {"major",       required_argument, 0, 1000},
        {"minor",       required_argument, 0, 1001},
        {"verbose",     no_argument,       0, 'v'},
        {"ignore-exist", no_argument,      0, 'i'},
        {"base-dir",    required_argument, 0, 'b'},
        {"help",        no_argument,       0, 'h'},
        {0, 0, 0, 0}
    };
    
    int opt;
    while ((opt = getopt_long(argc, argv, "t:m:vi:b:h", long_options, NULL)) != -1) {
        switch (opt) {
            case 't':
                if (strlen(optarg) == 1) {
                    config.file_type = optarg[0];
                } else {
                    fprintf(stderr, "文件类型应为单个字符\n");
                    return 1;
                }
                break;
            case 'm': {
                char *endptr;
                config.file_mode = strtol(optarg, &endptr, 8);
                if (*endptr != '\0') {
                    fprintf(stderr, "无效的权限模式: %s\n", optarg);
                    return 1;
                }
                break;
            }
            case 1000:  // --major
                config.major = atoi(optarg);
                break;
            case 1001:  // --minor
                config.minor = atoi(optarg);
                break;
            case 'v':
                config.verbose = 1;
                break;
            case 'i':
                config.ignore_exist = 1;
                break;
            case 'b':
                config.base_dir = optarg;
                break;
            case 'h':
                show_help(argv[0]);
                return 0;
            default:
                fprintf(stderr, "使用 '%s --help' 查看帮助信息\n", argv[0]);
                return 1;
        }
    }
    
    // 检查文件参数
    if (optind >= argc) {
        fprintf(stderr, "错误: 请指定至少一个文件名\n");
        fprintf(stderr, "使用 '%s --help' 查看帮助信息\n", argv[0]);
        return 1;
    }
    
    // 验证参数
    if ((config.file_type == 'c' || config.file_type == 'b') && 
        (config.major == 0 && config.minor == 0)) {
        fprintf(stderr, "创建设备文件需要指定主设备号和次设备号\n");
        return 1;
    }
    
    // 打开基础目录
    int base_dirfd = AT_FDCWD;
    if (config.base_dir) {
        base_dirfd = open(config.base_dir, O_RDONLY);
        if (base_dirfd == -1) {
            fprintf(stderr, "无法打开基础目录 '%s': %s\n", 
                    config.base_dir, strerror(errno));
            return 1;
        }
        if (config.verbose) {
            printf("使用基础目录: %s (fd=%d)\n", config.base_dir, base_dirfd);
        }
    }
    
    // 创建所有指定的文件
    int success_count = 0;
    int error_count = 0;
    
    if (config.verbose) {
        printf("配置信息:\n");
        printf("  文件类型: ");
        switch (config.file_type) {
            case 'p': printf("命名管道 (FIFO)\n"); break;
            case 'c': printf("字符设备\n"); break;
            case 'b': printf("块设备\n"); break;
            case 'f': printf("普通文件\n"); break;
            default: printf("未知\n"); break;
        }
        printf("  权限模式: %o\n", config.file_mode);
        if (config.file_type == 'c' || config.file_type == 'b') {
            printf("  设备号: %d,%d\n", config.major, config.minor);
        }
        printf("  忽略已存在: %s\n", config.ignore_exist ? "是" : "否");
        printf("\n");
    }
    
    for (int i = optind; i < argc; i++) {
        const char *filename = argv[i];
        
        if (create_special_file(base_dirfd, filename, &config) == 0) {
            success_count++;
            if (config.verbose) {
                show_file_details(base_dirfd, filename);
                printf("\n");
            }
        } else {
            error_count++;
        }
    }
    
    // 显示统计信息
    printf("\n=== 操作统计 ===\n");
    printf("成功: %d 个文件\n", success_count);
    printf("失败: %d 个文件\n", error_count);
    
    // 清理资源
    if (config.base_dir && base_dirfd != AT_FDCWD) {
        close(base_dirfd);
    }
    
    return (error_count > 0) ? 1 : 0;
}

编译和运行说明 見出しへのリンク

# 编译示例程序
gcc -o mknodat_example1 example1.c
gcc -o mknodat_example2 example2.c
gcc -o mknodat_example3 example3.c

# 运行示例
./mknodat_example1
./mknodat_example2
./mknodat_example3 --help
./mknodat_example3 -t p test_pipe
./mknodat_example3 -t f -m 644 test_file
sudo ./mknodat_example3 -t c --major=1 --minor=3 test_char_dev

系统要求检查 見出しへのリンク

# 检查系统调用支持
grep -w mknodat /usr/include/asm/unistd_64.h

# 检查设备文件权限
ls -l /dev/null /dev/zero

# 检查当前用户权限
id

重要注意事项 見出しへのリンク

  1. 权限要求: 创建设备文件通常需要 root 权限
  2. 路径解析: 相对路径相对于 dirfd 解析
  3. 权限掩码: 实际权限受 umask 影响
  4. 设备号: 字符设备和块设备需要正确的设备号
  5. 错误处理: 始终检查返回值和 errno

实际应用场景 見出しへのリンク

  1. 系统管理: 创建设备文件和特殊文件
  2. 进程间通信: 创建命名管道
  3. 设备驱动: 开发和测试设备文件
  4. 容器技术: 在容器中创建特殊文件
  5. 系统初始化: 系统启动时创建必要的特殊文件

与相关函数的区别 見出しへのリンク

// mknod - 传统的特殊文件创建
mknod("path", mode, dev);                    // 相对于当前目录

// mknodat - 增强版特殊文件创建
mknodat(AT_FDCWD, "path", mode, dev);        // 相对于当前目录
mknodat(dirfd, "relative_path", mode, dev);  // 相对于指定目录
mknodat(dirfd, "/absolute_path", mode, dev); // 绝对路径,忽略 dirfd

// 其他相关函数
mkfifo("path", mode);                        // 创建命名管道
open("path", O_CREAT, mode);                 // 创建普通文件

最佳实践 見出しへのリンク

// 安全的特殊文件创建函数
int safe_mknodat(int dirfd, const char *pathname, mode_t mode, dev_t dev) {
    // 验证参数
    if (!pathname) {
        errno = EINVAL;
        return -1;
    }
    
    // 检查文件类型
    mode_t file_type = mode & S_IFMT;
    if (file_type != S_IFIFO && file_type != S_IFCHR && 
        file_type != S_IFBLK && file_type != S_IFREG) {
        errno = EINVAL;
        return -1;
    }
    
    // 检查设备文件权限
    if ((file_type == S_IFCHR || file_type == S_IFBLK) && geteuid() != 0) {
        errno = EPERM;
        return -1;
    }
    
    // 创建特殊文件
    int result = mknodat(dirfd, pathname, mode, dev);
    
    // 处理常见情况
    if (result == -1) {
        switch (errno) {
            case EEXIST:
                // 检查文件类型是否匹配
                struct stat st;
                if (fstatat(dirfd, pathname, &st, 0) == 0) {
                    if ((st.st_mode & S_IFMT) == file_type) {
                        return 0;  // 文件已存在且类型匹配,视为成功
                    }
                }
                break;
        }
    }
    
    return result;
}

// 创建设备文件的辅助函数
int create_device_file(int dirfd, const char *pathname, 
                      int is_char_device, int major, int minor, mode_t mode) {
    mode_t file_type = is_char_device ? S_IFCHR : S_IFBLK;
    dev_t dev = makedev(major, minor);
    
    return mknodat(dirfd, pathname, file_type | mode, dev);
}

这些示例展示了 mknodat 函数的各种使用方法,从基础的特殊文件创建到完整的管理工具,帮助你全面掌握 Linux 系统中的特殊文件创建机制。