unlink 和 unlinkat 函数详解 Link to heading

1. 函数介绍 Link to heading

unlink函数unlinkat函数是Linux系统中用于删除文件的系统调用函数。可以把它们想象成"文件删除器",专门负责从文件系统中移除文件的目录项链接。

unlink函数是最基本的文件删除函数,它删除指定路径名的文件链接。而unlinkat函数是unlink的增强版本,提供了更灵活的相对路径操作能力,可以相对于指定的目录文件描述符进行操作。

这两个函数的工作原理是减少文件的链接计数,当链接计数变为0且没有进程打开该文件时,文件的数据块才会被真正释放。这就像一个"引用计数"机制,只有当所有引用都被删除时,文件才会真正消失。

使用场景:

  • 临时文件的清理
  • 程序运行时的文件管理
  • 系统维护和清理脚本
  • 安全删除敏感文件
  • 实现文件的原子操作

2. 函数原型 Link to heading

#include <unistd.h>

// 基本文件删除函数
int unlink(const char *pathname);

// 增强版文件删除函数(支持相对路径)
int unlinkat(int dirfd, const char *pathname, int flags);

3. 功能 Link to heading

  • unlink: 删除指定路径名的文件链接,减少文件的链接计数
  • unlinkat: 相对于指定目录文件描述符删除文件,支持更多选项

4. 参数 Link to heading

unlink参数: Link to heading

  • pathname: 文件路径名
    • 类型:const char*
    • 含义:要删除的文件的路径名

unlinkat参数: Link to heading

  • dirfd: 目录文件描述符

    • 类型:int
    • 含义:基准目录的文件描述符,或特殊值AT_FDCWD
  • pathname: 文件路径名

    • 类型:const char*
    • 含义:相对于dirfd的文件路径名
  • flags: 操作标志

    • 类型:int
    • 含义:控制删除行为的标志位
    • 常用值:
      • 0:删除普通文件
      • AT_REMOVEDIR:删除目录(相当于rmdir)

5. 返回值 Link to heading

  • 成功: 返回0
  • 失败: 返回-1,并设置errno错误码
    • EACCES:权限不足
    • EBUSY:文件正在被使用
    • EFAULT:路径名指向无效内存
    • EIO:I/O错误
    • EISDIR:pathname是目录(unlink)或不是目录(AT_REMOVEDIR)
    • ELOOP:符号链接循环
    • ENAMETOOLONG:路径名过长
    • ENOENT:文件或路径不存在
    • ENOTDIR:路径前缀不是目录
    • EPERM:操作不被允许
    • EROFS:文件系统只读

6. 相似函数或关联函数 Link to heading

  • remove(): 删除文件或目录的通用函数
  • rmdir(): 删除空目录
  • open(): 打开文件获取文件描述符
  • close(): 关闭文件描述符
  • stat(): 获取文件状态信息
  • access(): 检查文件访问权限
  • rename(): 重命名文件

7. 示例代码 Link to heading

示例1:基础unlink使用 - 简单文件删除 Link to heading

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

// 创建测试文件
int create_test_file(const char* filename, const char* content) {
    int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (fd == -1) {
        perror("创建文件失败");
        return -1;
    }
    
    if (write(fd, content, strlen(content)) == -1) {
        perror("写入文件失败");
        close(fd);
        return -1;
    }
    
    close(fd);
    printf("创建文件: %s\n", filename);
    return 0;
}

// 检查文件是否存在
int file_exists(const char* filename) {
    return access(filename, F_OK) == 0;
}

int main() {
    printf("=== 基础unlink使用示例 ===\n");
    
    // 创建测试文件
    const char* test_files[] = {
        "test_file1.txt",
        "test_file2.txt",
        "test_file3.txt"
    };
    
    const char* test_contents[] = {
        "这是测试文件1的内容\n",
        "这是测试文件2的内容\n",
        "这是测试文件3的内容\n"
    };
    
    int num_files = sizeof(test_files) / sizeof(test_files[0]);
    
    printf("1. 创建测试文件:\n");
    for (int i = 0; i < num_files; i++) {
        if (create_test_file(test_files[i], test_contents[i]) == -1) {
            exit(EXIT_FAILURE);
        }
    }
    
    // 验证文件创建成功
    printf("\n2. 验证文件存在:\n");
    for (int i = 0; i < num_files; i++) {
        if (file_exists(test_files[i])) {
            printf("✓ 文件 %s 存在\n", test_files[i]);
        } else {
            printf("✗ 文件 %s 不存在\n", test_files[i]);
        }
    }
    
    // 使用unlink删除文件
    printf("\n3. 使用unlink删除文件:\n");
    for (int i = 0; i < num_files; i++) {
        printf("删除文件: %s ... ", test_files[i]);
        
        if (unlink(test_files[i]) == 0) {
            printf("成功\n");
            
            // 验证文件已被删除
            if (!file_exists(test_files[i])) {
                printf("  ✓ 文件 %s 已被成功删除\n", test_files[i]);
            } else {
                printf("  ✗ 文件 %s 仍然存在\n", test_files[i]);
            }
        } else {
            printf("失败: %s\n", strerror(errno));
        }
    }
    
    // 演示错误处理
    printf("\n4. 错误处理演示:\n");
    
    // 尝试删除不存在的文件
    const char* nonexistent_file = "nonexistent.txt";
    printf("删除不存在的文件 %s: ", nonexistent_file);
    if (unlink(nonexistent_file) == -1) {
        printf("失败(预期行为): %s\n", strerror(errno));
    }
    
    // 尝试删除目录
    const char* test_dir = "test_directory";
    if (mkdir(test_dir, 0755) == 0) {
        printf("删除目录 %s: ", test_dir);
        if (unlink(test_dir) == -1) {
            printf("失败(预期行为): %s\n", strerror(errno));
        }
        // 清理目录
        rmdir(test_dir);
    }
    
    // 尝试删除只读文件
    const char* readonly_file = "readonly_file.txt";
    if (create_test_file(readonly_file, "只读文件") == 0) {
        // 设置只读权限
        if (chmod(readonly_file, 0444) == 0) {
            printf("删除只读文件 %s: ", readonly_file);
            if (unlink(readonly_file) == -1) {
                printf("失败: %s\n", strerror(errno));
            } else {
                printf("成功(权限允许)\n");
            }
        }
    }
    
    printf("\n=== 基础unlink演示完成 ===\n");
    
    return 0;
}

示例2:unlinkat使用 - 相对路径文件删除 Link to heading

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

// 创建目录和文件结构
int create_test_structure() {
    // 创建测试目录
    if (mkdir("unlinkat_test", 0755) == -1 && errno != EEXIST) {
        perror("创建测试目录失败");
        return -1;
    }
    
    // 在测试目录中创建子目录
    if (mkdir("unlinkat_test/subdir1", 0755) == -1 && errno != EEXIST) {
        perror("创建子目录失败");
        return -1;
    }
    
    if (mkdir("unlinkat_test/subdir2", 0755) == -1 && errno != EEXIST) {
        perror("创建子目录失败");
        return -1;
    }
    
    // 创建测试文件
    int fd;
    
    // 在根目录创建文件
    fd = open("unlinkat_test/file1.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (fd != -1) {
        write(fd, "文件1内容", 9);
        close(fd);
    }
    
    // 在subdir1中创建文件
    fd = open("unlinkat_test/subdir1/file2.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (fd != -1) {
        write(fd, "文件2内容", 9);
        close(fd);
    }
    
    // 在subdir2中创建文件
    fd = open("unlinkat_test/subdir2/file3.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (fd != -1) {
        write(fd, "文件3内容", 9);
        close(fd);
    }
    
    printf("测试目录结构创建完成\n");
    return 0;
}

// 显示目录内容
void show_directory_contents(const char* path) {
    DIR* dir = opendir(path);
    if (dir == NULL) {
        printf("无法打开目录 %s: %s\n", path, strerror(errno));
        return;
    }
    
    printf("目录 %s 的内容:\n", path);
    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");
}

int main() {
    printf("=== unlinkat使用示例 ===\n");
    
    // 创建测试结构
    if (create_test_structure() == -1) {
        exit(EXIT_FAILURE);
    }
    
    // 显示初始目录结构
    printf("1. 初始目录结构:\n");
    show_directory_contents("unlinkat_test");
    show_directory_contents("unlinkat_test/subdir1");
    show_directory_contents("unlinkat_test/subdir2");
    
    // 使用unlinkat删除文件
    printf("2. 使用unlinkat删除文件:\n");
    
    // 打开基准目录
    int base_dirfd = open("unlinkat_test", O_RDONLY);
    if (base_dirfd == -1) {
        perror("打开基准目录失败");
        exit(EXIT_FAILURE);
    }
    
    // 相对于基准目录删除文件
    printf("相对于基准目录删除 file1.txt: ");
    if (unlinkat(base_dirfd, "file1.txt", 0) == 0) {
        printf("成功\n");
    } else {
        printf("失败: %s\n", strerror(errno));
    }
    
    // 使用AT_FDCWD(当前工作目录)删除文件
    printf("使用AT_FDCWD删除 subdir1/file2.txt: ");
    if (unlinkat(AT_FDCWD, "unlinkat_test/subdir1/file2.txt", 0) == 0) {
        printf("成功\n");
    } else {
        printf("失败: %s\n", strerror(errno));
    }
    
    // 打开subdir2并删除其中的文件
    int subdir2_fd = open("unlinkat_test/subdir2", O_RDONLY);
    if (subdir2_fd != -1) {
        printf("相对于subdir2删除 file3.txt: ");
        if (unlinkat(subdir2_fd, "file3.txt", 0) == 0) {
            printf("成功\n");
        } else {
            printf("失败: %s\n", strerror(errno));
        }
        close(subdir2_fd);
    }
    
    // 显示删除后的目录结构
    printf("\n3. 删除后的目录结构:\n");
    show_directory_contents("unlinkat_test");
    show_directory_contents("unlinkat_test/subdir1");
    show_directory_contents("unlinkat_test/subdir2");
    
    // 演示AT_REMOVEDIR标志(删除目录)
    printf("4. 使用AT_REMOVEDIR删除空目录:\n");
    
    // 创建一个空目录用于测试
    if (mkdir("unlinkat_test/empty_dir", 0755) == 0) {
        printf("创建空目录 empty_dir\n");
        show_directory_contents("unlinkat_test");
        
        printf("使用AT_REMOVEDIR删除 empty_dir: ");
        if (unlinkat(base_dirfd, "empty_dir", AT_REMOVEDIR) == 0) {
            printf("成功\n");
        } else {
            printf("失败: %s\n", strerror(errno));
        }
        
        show_directory_contents("unlinkat_test");
    }
    
    // 错误处理演示
    printf("\n5. 错误处理演示:\n");
    
    // 尝试删除不存在的文件
    printf("删除不存在的文件 nonexistent.txt: ");
    if (unlinkat(base_dirfd, "nonexistent.txt", 0) == -1) {
        printf("失败(预期行为): %s\n", strerror(errno));
    }
    
    // 尝试删除非空目录(不使用AT_REMOVEDIR)
    printf("删除非空目录 subdir1(不使用AT_REMOVEDIR): ");
    if (unlinkat(base_dirfd, "subdir1", 0) == -1) {
        printf("失败(预期行为): %s\n", strerror(errno));
    }
    
    // 尝试删除文件但使用了AT_REMOVEDIR
    printf("删除文件但使用AT_REMOVEDIR: ");
    if (unlinkat(AT_FDCWD, "unlinkat_test/subdir1/file2.txt", AT_REMOVEDIR) == -1) {
        printf("失败(预期行为): %s\n", strerror(errno));
    }
    
    // 清理资源
    close(base_dirfd);
    
    // 最终清理
    rmdir("unlinkat_test/subdir1");
    rmdir("unlinkat_test");
    
    printf("\n=== unlinkat演示完成 ===\n");
    
    return 0;
}

示例3:高级应用 - 安全临时文件管理 Link to heading

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <time.h>
#include <sys/types.h>

// 临时文件信息结构
typedef struct {
    char filepath[256];
    int fd;
    time_t create_time;
    size_t size;
} temp_file_info_t;

// 创建安全的临时文件
temp_file_info_t* create_secure_temp_file(const char* prefix) {
    temp_file_info_t* temp_info = malloc(sizeof(temp_file_info_t));
    if (!temp_info) {
        perror("内存分配失败");
        return NULL;
    }
    
    // 生成唯一的临时文件名
    snprintf(temp_info->filepath, sizeof(temp_info->filepath), 
             "%s_%ld_%d.tmp", prefix, time(NULL), getpid());
    
    // 创建临时文件(安全模式:创建后立即删除目录项)
    temp_info->fd = open(temp_info->filepath, 
                        O_CREAT | O_EXCL | O_RDWR | O_TRUNC, 
                        0600);
    
    if (temp_info->fd == -1) {
        perror("创建临时文件失败");
        free(temp_info);
        return NULL;
    }
    
    temp_info->create_time = time(NULL);
    temp_info->size = 0;
    
    // 立即删除目录项,这样即使程序异常退出,文件也会自动清理
    // 但文件描述符仍然有效,可以继续读写
    if (unlink(temp_info->filepath) == -1) {
        perror("删除临时文件目录项失败");
        close(temp_info->fd);
        free(temp_info);
        return NULL;
    }
    
    printf("创建安全临时文件: fd=%d (路径已被删除)\n", temp_info->fd);
    return temp_info;
}

// 向临时文件写入数据
int write_to_temp_file(temp_file_info_t* temp_info, const char* data) {
    ssize_t bytes_written = write(temp_info->fd, data, strlen(data));
    if (bytes_written == -1) {
        perror("写入临时文件失败");
        return -1;
    }
    
    temp_info->size += bytes_written;
    printf("向临时文件写入 %zd 字节\n", bytes_written);
    return 0;
}

// 从临时文件读取数据
int read_from_temp_file(temp_file_info_t* temp_info, char* buffer, size_t buffer_size) {
    // 重置文件指针到开始
    if (lseek(temp_info->fd, 0, SEEK_SET) == -1) {
        perror("重置文件指针失败");
        return -1;
    }
    
    ssize_t bytes_read = read(temp_info->fd, buffer, buffer_size - 1);
    if (bytes_read == -1) {
        perror("读取临时文件失败");
        return -1;
    }
    
    buffer[bytes_read] = '\0';
    printf("从临时文件读取 %zd 字节\n", bytes_read);
    return bytes_read;
}

// 关闭并清理临时文件
void close_temp_file(temp_file_info_t* temp_info) {
    if (temp_info) {
        if (temp_info->fd != -1) {
            close(temp_info->fd);
        }
        printf("临时文件已关闭和清理\n");
        free(temp_info);
    }
}

// 演示unlink的安全删除特性
void demonstrate_secure_delete() {
    printf("\n=== 安全删除特性演示 ===\n");
    
    // 创建普通文件
    const char* normal_file = "normal_temp.txt";
    int fd = open(normal_file, O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (fd != -1) {
        write(fd, "普通临时文件内容", 15);
        printf("创建普通临时文件: %s\n", normal_file);
        close(fd);
    }
    
    // 创建安全临时文件
    temp_file_info_t* secure_temp = create_secure_temp_file("secure");
    if (secure_temp) {
        write_to_temp_file(secure_temp, "安全临时文件内容");
        
        // 验证文件是否真的被删除了(但仍然可以访问)
        if (access(secure_temp->filepath, F_OK) == -1) {
            printf("✓ 安全临时文件的路径已被删除\n");
        } else {
            printf("✗ 安全临时文件路径仍然存在\n");
        }
        
        if (access(normal_file, F_OK) == 0) {
            printf("✓ 普通临时文件仍然存在\n");
        }
        
        // 读取安全临时文件内容
        char buffer[256];
        if (read_from_temp_file(secure_temp, buffer, sizeof(buffer)) > 0) {
            printf("安全临时文件内容: %s\n", buffer);
        }
        
        // 关闭安全临时文件
        close_temp_file(secure_temp);
    }
    
    // 清理普通文件
    unlink(normal_file);
    printf("普通临时文件已手动清理\n");
}

// 演示unlinkat的相对路径优势
void demonstrate_relative_path_advantage() {
    printf("\n=== 相对路径优势演示 ===\n");
    
    // 创建测试目录结构
    mkdir("demo_root", 0755);
    mkdir("demo_root/dir1", 0755);
    mkdir("demo_root/dir2", 0755);
    
    // 在各个目录创建文件
    int fd1 = open("demo_root/file_root.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
    int fd2 = open("demo_root/dir1/file_dir1.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
    int fd3 = open("demo_root/dir2/file_dir2.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
    
    if (fd1 != -1) {
        write(fd1, "根目录文件", 10);
        close(fd1);
    }
    if (fd2 != -1) {
        write(fd2, "目录1文件", 9);
        close(fd2);
    }
    if (fd3 != -1) {
        write(fd3, "目录2文件", 9);
        close(fd3);
    }
    
    printf("创建测试文件完成\n");
    
    // 使用传统方式删除(需要完整路径)
    printf("传统方式删除:\n");
    if (unlink("demo_root/file_root.txt") == 0) {
        printf("  删除 demo_root/file_root.txt 成功\n");
    }
    
    // 使用unlinkat相对路径删除
    printf("unlinkat相对路径删除:\n");
    int root_fd = open("demo_root", O_RDONLY);
    if (root_fd != -1) {
        if (unlinkat(root_fd, "dir1/file_dir1.txt", 0) == 0) {
            printf("  相对于root_fd删除 dir1/file_dir1.txt 成功\n");
        }
        
        if (unlinkat(root_fd, "dir2/file_dir2.txt", 0) == 0) {
            printf("  相对于root_fd删除 dir2/file_dir2.txt 成功\n");
        }
        close(root_fd);
    }
    
    // 清理目录
    rmdir("demo_root/dir1");
    rmdir("demo_root/dir2");
    rmdir("demo_root");
    
    printf("相对路径删除演示完成\n");
}

int main() {
    printf("=== unlink和unlinkat高级应用示例 ===\n");
    
    // 演示安全临时文件管理
    printf("1. 安全临时文件管理:\n");
    
    temp_file_info_t* temp1 = create_secure_temp_file("temp1");
    temp_file_info_t* temp2 = create_secure_temp_file("temp2");
    
    if (temp1) {
        write_to_temp_file(temp1, "第一个安全临时文件的内容\n");
        write_to_temp_file(temp1, "追加的内容\n");
        
        char buffer[256];
        read_from_temp_file(temp1, buffer, sizeof(buffer));
        printf("读取内容: %s", buffer);
        
        close_temp_file(temp1);
    }
    
    if (temp2) {
        write_to_temp_file(temp2, "第二个安全临时文件的内容\n");
        close_temp_file(temp2);
    }
    
    // 演示安全删除特性
    demonstrate_secure_delete();
    
    // 演示相对路径优势
    demonstrate_relative_path_advantage();
    
    // 性能对比测试
    printf("\n=== 性能对比测试 ===\n");
    
    // 创建大量测试文件
    const int file_count = 1000;
    printf("创建 %d 个测试文件...\n", file_count);
    
    for (int i = 0; i < file_count; i++) {
        char filename[64];
        snprintf(filename, sizeof(filename), "perf_test_%d.txt", i);
        int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
        if (fd != -1) {
            char content[32];
            snprintf(content, sizeof(content), "文件%d", i);
            write(fd, content, strlen(content));
            close(fd);
        }
    }
    
    // 测试unlink性能
    clock_t start = clock();
    for (int i = 0; i < file_count; i++) {
        char filename[64];
        snprintf(filename, sizeof(filename), "perf_test_%d.txt", i);
        unlink(filename);
    }
    clock_t end = clock();
    
    double unlink_time = ((double)(end - start)) / CLOCKS_PER_SEC;
    printf("删除 %d 个文件耗时: %.4f 秒\n", file_count, unlink_time);
    
    printf("\n=== 高级应用演示完成 ===\n");
    
    return 0;
}

示例4:错误处理和最佳实践 Link to heading

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <limits.h>

// 安全的unlink包装函数
int safe_unlink(const char* pathname) {
    if (pathname == NULL) {
        errno = EINVAL;
        return -1;
    }
    
    // 检查路径长度
    if (strlen(pathname) >= PATH_MAX) {
        errno = ENAMETOOLONG;
        return -1;
    }
    
    // 检查文件是否存在
    if (access(pathname, F_OK) != 0) {
        if (errno == ENOENT) {
            printf("文件 %s 不存在,无需删除\n", pathname);
            return 0;  // 不算错误
        } else {
            return -1;  // 其他访问错误
        }
    }
    
    // 执行删除操作
    int result = unlink(pathname);
    if (result == 0) {
        printf("成功删除文件: %s\n", pathname);
    } else {
        switch (errno) {
            case EACCES:
                fprintf(stderr, "权限不足,无法删除文件: %s\n", pathname);
                break;
            case EBUSY:
                fprintf(stderr, "文件正在被使用: %s\n", pathname);
                break;
            case EISDIR:
                fprintf(stderr, "不能使用unlink删除目录: %s\n", pathname);
                break;
            case EPERM:
                fprintf(stderr, "操作不被允许: %s\n", pathname);
                break;
            case EROFS:
                fprintf(stderr, "文件系统只读: %s\n", pathname);
                break;
            default:
                fprintf(stderr, "删除文件失败 %s: %s\n", pathname, strerror(errno));
                break;
        }
    }
    
    return result;
}

// 安全的unlinkat包装函数
int safe_unlinkat(int dirfd, const char* pathname, int flags) {
    if (pathname == NULL) {
        errno = EINVAL;
        return -1;
    }
    
    // 检查路径长度
    if (strlen(pathname) >= PATH_MAX) {
        errno = ENAMETOOLONG;
        return -1;
    }
    
    // 执行删除操作
    int result = unlinkat(dirfd, pathname, flags);
    if (result == 0) {
        if (flags & AT_REMOVEDIR) {
            printf("成功删除目录: %s (相对于fd %d)\n", pathname, dirfd);
        } else {
            printf("成功删除文件: %s (相对于fd %d)\n", pathname, dirfd);
        }
    } else {
        const char* type = (flags & AT_REMOVEDIR) ? "目录" : "文件";
        switch (errno) {
            case EACCES:
                fprintf(stderr, "权限不足,无法删除%s: %s\n", type, pathname);
                break;
            case EBUSY:
                fprintf(stderr, "%s正在被使用: %s\n", type, pathname);
                break;
            case EISDIR:
                if (flags & AT_REMOVEDIR) {
                    fprintf(stderr, "目标不是目录: %s\n", pathname);
                } else {
                    fprintf(stderr, "不能使用unlink删除目录: %s\n", pathname);
                }
                break;
            case ENOTDIR:
                fprintf(stderr, "路径前缀不是目录: %s\n", pathname);
                break;
            case ENOENT:
                fprintf(stderr, "%s不存在: %s\n", type, pathname);
                break;
            default:
                fprintf(stderr, "删除%s失败 %s: %s\n", type, pathname, strerror(errno));
                break;
        }
    }
    
    return result;
}

// 递归删除目录(使用unlinkat)
int recursive_remove_directory(const char* path) {
    DIR* dir = opendir(path);
    if (dir == NULL) {
        if (errno == ENOENT) {
            return 0;  // 目录不存在,视为成功
        }
        perror("打开目录失败");
        return -1;
    }
    
    // 打开目录文件描述符用于unlinkat
    int dirfd = open(path, O_RDONLY);
    if (dirfd == -1) {
        perror("打开目录文件描述符失败");
        closedir(dir);
        return -1;
    }
    
    struct dirent* entry;
    int result = 0;
    
    // 遍历目录中的所有项
    while ((entry = readdir(dir)) != NULL && result == 0) {
        // 跳过.和..
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }
        
        if (entry->d_type == DT_DIR) {
            // 递归删除子目录
            char subpath[PATH_MAX];
            snprintf(subpath, sizeof(subpath), "%s/%s", path, entry->d_name);
            result = recursive_remove_directory(subpath);
        } else {
            // 删除文件
            result = safe_unlinkat(dirfd, entry->d_name, 0);
        }
    }
    
    closedir(dir);
    close(dirfd);
    
    // 删除空目录本身
    if (result == 0) {
        result = rmdir(path);
        if (result == 0) {
            printf("成功删除目录: %s\n", path);
        } else {
            fprintf(stderr, "删除目录失败 %s: %s\n", path, strerror(errno));
        }
    }
    
    return result;
}

// 创建测试环境
int create_test_environment() {
    printf("创建测试环境...\n");
    
    // 创建目录结构
    mkdir("test_env", 0755);
    mkdir("test_env/dir1", 0755);
    mkdir("test_env/dir1/subdir", 0755);
    mkdir("test_env/dir2", 0755);
    
    // 创建各种测试文件
    int fd;
    
    // 普通文件
    fd = open("test_env/file1.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (fd != -1) {
        write(fd, "普通文件内容", 12);
        close(fd);
    }
    
    // 只读文件
    fd = open("test_env/readonly.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (fd != -1) {
        write(fd, "只读文件内容", 12);
        close(fd);
        chmod("test_env/readonly.txt", 0444);
    }
    
    // 在子目录中创建文件
    fd = open("test_env/dir1/subfile.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (fd != -1) {
        write(fd, "子目录文件", 10);
        close(fd);
    }
    
    // 符号链接
    symlink("file1.txt", "test_env/link_to_file1");
    
    printf("测试环境创建完成\n");
    return 0;
}

// 显示测试环境状态
void show_test_environment() {
    printf("\n当前测试环境状态:\n");
    
    DIR* dir = opendir("test_env");
    if (dir) {
        printf("test_env/ 目录内容:\n");
        struct dirent* entry;
        while ((entry = readdir(dir)) != NULL) {
            if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
                printf("  %s (%s)\n", entry->d_name, 
                       entry->d_type == DT_DIR ? "目录" : 
                       entry->d_type == DT_LNK ? "链接" : "文件");
            }
        }
        closedir(dir);
    }
}

int main() {
    printf("=== unlink和unlinkat错误处理与最佳实践 ===\n");
    
    // 创建测试环境
    if (create_test_environment() == -1) {
        exit(EXIT_FAILURE);
    }
    
    show_test_environment();
    
    // 演示安全删除函数
    printf("\n=== 安全删除函数演示 ===\n");
    
    // 正常删除
    safe_unlink("test_env/file1.txt");
    
    // 删除不存在的文件
    safe_unlink("test_env/nonexistent.txt");
    
    // 删除只读文件
    safe_unlink("test_env/readonly.txt");
    
    // 尝试删除目录
    safe_unlink("test_env/dir1");
    
    show_test_environment();
    
    // 演示unlinkat的高级用法
    printf("\n=== unlinkat高级用法演示 ===\n");
    
    // 相对于目录文件描述符删除
    int dirfd = open("test_env", O_RDONLY);
    if (dirfd != -1) {
        printf("使用unlinkat删除符号链接:\n");
        safe_unlinkat(dirfd, "link_to_file1", 0);
        
        printf("使用unlinkat删除子目录中的文件:\n");
        safe_unlinkat(dirfd, "dir1/subfile.txt", 0);
        
        close(dirfd);
    }
    
    show_test_environment();
    
    // 演示AT_REMOVEDIR用法
    printf("\n=== AT_REMOVEDIR用法演示 ===\n");
    
    // 创建空目录用于测试
    mkdir("test_env/empty_dir", 0755);
    show_test_environment();
    
    dirfd = open("test_env", O_RDONLY);
    if (dirfd != -1) {
        printf("使用AT_REMOVEDIR删除空目录:\n");
        safe_unlinkat(dirfd, "empty_dir", AT_REMOVEDIR);
        close(dirfd);
    }
    
    show_test_environment();
    
    // 演示递归删除(使用unlinkat)
    printf("\n=== 递归删除演示 ===\n");
    printf("递归删除整个test_env目录:\n");
    recursive_remove_directory("test_env");
    
    // 验证目录已被删除
    if (access("test_env", F_OK) == -1) {
        printf("✓ test_env目录已被成功删除\n");
    } else {
        printf("✗ test_env目录仍然存在\n");
    }
    
    // 演示最佳实践
    printf("\n=== 最佳实践演示 ===\n");
    
    // 1. 创建临时文件并安全删除
    printf("1. 安全临时文件管理:\n");
    int temp_fd = open("safe_temp.txt", O_CREAT | O_EXCL | O_RDWR, 0600);
    if (temp_fd != -1) {
        write(temp_fd, "临时数据", 8);
        printf("  创建临时文件\n");
        
        // 立即删除目录项
        if (unlink("safe_temp.txt") == 0) {
            printf("  ✓ 立即删除目录项,文件更安全\n");
            // 可以继续使用文件描述符
            lseek(temp_fd, 0, SEEK_SET);
            char buffer[32];
            read(temp_fd, buffer, sizeof(buffer));
            printf("  可以继续读写文件内容\n");
        }
        close(temp_fd);
    }
    
    // 2. 错误处理最佳实践
    printf("\n2. 错误处理最佳实践:\n");
    const char* test_paths[] = {
        NULL,                    // NULL指针
        "",                      // 空字符串
        "/very/long/path/that/exceeds/the/maximum/path/length/allowed/by/the/system/and/should/cause/an/error",  // 路径过长
        "nonexistent_file.txt"   // 不存在的文件
    };
    
    for (int i = 0; i < 4; i++) {
        printf("  测试路径 %d: ", i + 1);
        if (safe_unlink(test_paths[i]) == -1) {
            if (errno == EINVAL) {
                printf("参数无效(预期)\n");
            } else if (errno == ENAMETOOLONG) {
                printf("路径名过长(预期)\n");
            } else {
                printf("其他错误: %s\n", strerror(errno));
            }
        } else {
            printf("成功\n");
        }
    }
    
    // 清理可能残留的文件
    unlink("safe_temp.txt");
    
    printf("\n=== 错误处理与最佳实践演示完成 ===\n");
    
    return 0;
}

编译和运行 Link to heading

# 编译示例1
gcc -o unlink_example1 unlink_example1.c
./unlink_example1

# 编译示例2
gcc -o unlink_example2 unlink_example2.c
./unlink_example2

# 编译示例3
gcc -o unlink_example3 unlink_example3.c
./unlink_example3

# 编译示例4
gcc -o unlink_example4 unlink_example4.c
./unlink_example4

重要注意事项 Link to heading

  1. 链接计数: unlink减少文件链接计数,不一定会立即释放磁盘空间
  2. 打开文件: 已打开的文件即使被unlink也不会立即删除
  3. 权限要求: 需要对包含文件的目录有写权限
  4. 目录删除: unlink不能删除目录,需要使用rmdir或unlinkat+AT_REMOVEDIR
  5. 符号链接: unlink删除符号链接本身,不删除目标文件
  6. 原子性: unlink操作是原子的
  7. 错误处理: 必须检查返回值并适当处理错误
  8. 安全性: 可用于安全删除临时文件

最佳实践 Link to heading

  1. 使用安全包装函数: 对unlink和unlinkat进行封装以提供更好的错误处理
  2. 检查返回值: 始终检查函数返回值
  3. 权限检查: 在删除前检查必要的权限
  4. 安全临时文件: 创建后立即unlink以提高安全性
  5. 相对路径: 在批量操作时使用unlinkat提高效率
  6. 错误日志: 记录删除操作的详细信息
  7. 资源清理: 确保及时清理不需要的文件

通过这些示例,你可以理解unlink和unlinkat在文件管理方面的强大功能,它们为Linux系统提供了灵活、安全的文件删除机制。