flock系统调用及示例

flock – 文件锁

函数介绍

flock系统调用用于对文件进行加锁操作,实现进程间的文件访问同步。文件锁可以防止多个进程同时修改同一文件,保证数据的一致性。
(https://www.calcguide.tech/2025/08/17/flock%e7%b3%bb%e7%bb%9f%e8%b0%83%e7%94%a8%e5%8f%8a%e7%a4%ba%e4%be%8b/)

函数原型

#include <sys/file.h>

int flock(int fd, int operation);

功能

对指定文件描述符对应的文件进行加锁或解锁操作。

参数

  • int fd: 文件描述符
  • int operation: 锁操作类型
    • LOCK_SH: 共享锁(读锁),多个进程可以同时持有
    • LOCK_EX: 排他锁(写锁),只能有一个进程持有
    • LOCK_UN: 解锁
    • LOCK_NB: 非阻塞模式(与上述操作组合使用)

返回值

  • 成功时返回0
  • 失败时返回-1,并设置errno:
    • EAGAIN/EWOULDBLOCK: 非阻塞模式下无法获取锁
    • EBADF: 文件描述符无效
    • EINTR: 系统调用被信号中断
    • EINVAL: 参数无效
    • EOPNOTSUPP: 文件系统不支持锁

相似函数

  • fcntl(): 更灵活的文件锁操作
  • lockf(): POSIX文件锁接口

示例代码

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

int main() {
    int fd;
    pid_t pid;
    
    printf("=== Flock函数示例 ===\n");
    
    // 创建测试文件
    fd = open("test_flock.txt", O_CREAT | O_RDWR | O_TRUNC, 0644);
    if (fd == -1) {
        perror("创建测试文件失败");
        exit(EXIT_FAILURE);
    }
    printf("成功创建测试文件,文件描述符: %d\n", fd);
    
    // 写入初始数据
    const char *initial_data = "Initial data for flock test\n";
    if (write(fd, initial_data, strlen(initial_data)) == -1) {
        perror("写入初始数据失败");
        close(fd);
        exit(EXIT_FAILURE);
    }
    
    // 示例1: 基本的排他锁操作
    printf("\n示例1: 基本的排他锁操作\n");
    
    // 获取排他锁
    if (flock(fd, LOCK_EX) == -1) {
        perror("获取排他锁失败");
    } else {
        printf("  成功获取排他锁\n");
        
        // 在锁保护下写入数据
        const char *exclusive_data = "Data written with exclusive lock\n";
        if (write(fd, exclusive_data, strlen(exclusive_data)) == -1) {
            perror("  写入数据失败");
        } else {
            printf("  成功写入数据到文件\n");
        }
        
        // 释放锁
        if (flock(fd, LOCK_UN) == -1) {
            perror("  释放锁失败");
        } else {
            printf("  成功释放排他锁\n");
        }
    }
    
    // 示例2: 共享锁操作
    printf("\n示例2: 共享锁操作\n");
    
    // 获取共享锁
    if (flock(fd, LOCK_SH) == -1) {
        perror("获取共享锁失败");
    } else {
        printf("  成功获取共享锁\n");
        
        // 在锁保护下读取数据
        char buffer[200];
        lseek(fd, 0, SEEK_SET);
        ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
        if (bytes_read > 0) {
            buffer[bytes_read] = '\0';
            printf("  读取文件内容:\n%s", buffer);
        }
        
        // 释放锁
        if (flock(fd, LOCK_UN) == -1) {
            perror("  释放共享锁失败");
        } else {
            printf("  成功释放共享锁\n");
        }
    }
    
    // 示例3: 非阻塞锁操作
    printf("\n示例3: 非阻塞锁操作\n");
    
    // 先获取一个排他锁
    if (flock(fd, LOCK_EX) == -1) {
        perror("获取排他锁失败");
    } else {
        printf("  进程已持有排他锁\n");
        
        // 尝试非阻塞获取另一个排他锁(应该失败)
        if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                printf("  非阻塞获取排他锁: 立即返回EAGAIN(锁被占用)\n");
            } else {
                perror("  非阻塞获取锁失败");
            }
        } else {
            printf("  非阻塞获取排他锁成功(不应该发生)\n");
            flock(fd, LOCK_UN);  // 释放意外获取的锁
        }
        
        // 尝试非阻塞获取共享锁(也应该失败,因为有排他锁)
        if (flock(fd, LOCK_SH | LOCK_NB) == -1) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                printf("  非阻塞获取共享锁: 立即返回EAGAIN(有排他锁)\n");
            } else {
                perror("  非阻塞获取共享锁失败");
            }
        } else {
            printf("  非阻塞获取共享锁成功(不应该发生)\n");
            flock(fd, LOCK_UN);  // 释放意外获取的锁
        }
        
        // 释放锁
        flock(fd, LOCK_UN);
        printf("  释放排他锁\n");
    }
    
    // 示例4: 多进程锁演示
    printf("\n示例4: 多进程锁演示\n");
    
    pid = fork();
    if (pid == -1) {
        perror("fork失败");
        close(fd);
        exit(EXIT_FAILURE);
    } else if (pid == 0) {
        // 子进程
        printf("  子进程 %d 开始执行\n", getpid());
        
        // 子进程打开同一个文件
        int child_fd = open("test_flock.txt", O_RDWR);
        if (child_fd == -1) {
            perror("  子进程打开文件失败");
            exit(EXIT_FAILURE);
        }
        
        printf("  子进程尝试获取排他锁...\n");
        if (flock(child_fd, LOCK_EX) == -1) {
            perror("  子进程获取排他锁失败");
        } else {
            printf("  子进程成功获取排他锁\n");
            
            // 写入子进程数据
            char child_data[100];
            sprintf(child_data, "Data from child process %d\n", getpid());
            if (write(child_fd, child_data, strlen(child_data)) == -1) {
                perror("  子进程写入数据失败");
            } else {
                printf("  子进程写入数据成功\n");
            }
            
            sleep(3);  // 持有锁3秒
            
            // 释放锁
            if (flock(child_fd, LOCK_UN) == -1) {
                perror("  子进程释放锁失败");
            } else {
                printf("  子进程释放排他锁\n");
            }
        }
        
        close(child_fd);
        exit(EXIT_SUCCESS);
    } else {
        // 父进程
        sleep(1);  // 让子进程先运行
        
        printf("  父进程 %d 尝试获取排他锁...\n", getpid());
        
        // 父进程尝试获取排他锁(会被阻塞直到子进程释放)
        printf("  父进程获取排他锁(会被阻塞)...\n");
        if (flock(fd, LOCK_EX) == -1) {
            perror("  父进程获取排他锁失败");
        } else {
            printf("  父进程成功获取排他锁(子进程已释放)\n");
            
            // 写入父进程数据
            char parent_data[100];
            sprintf(parent_data, "Data from parent process %d\n", getpid());
            if (write(fd, parent_data, strlen(parent_data)) == -1) {
                perror("  父进程写入数据失败");
            } else {
                printf("  父进程写入数据成功\n");
            }
            
            // 释放锁
            if (flock(fd, LOCK_UN) == -1) {
                perror("  父进程释放锁失败");
            } else {
                printf("  父进程释放排他锁\n");
            }
        }
        
        // 等待子进程结束
        wait(NULL);
    }
    
    // 示例5: 共享锁并发演示
    printf("\n示例5: 共享锁并发演示\n");
    
    // 创建多个子进程同时获取共享锁
    for (int i = 0; i < 3; i++) {
        pid = fork();
        if (pid == 0) {
            // 子进程
            int child_fd = open("test_flock.txt", O_RDONLY);
            if (child_fd != -1) {
                printf("  子进程 %d 尝试获取共享锁...\n", getpid());
                
                if (flock(child_fd, LOCK_SH) == 0) {
                    printf("  子进程 %d 成功获取共享锁\n", getpid());
                    
                    // 读取数据
                    char read_buffer[200];
                    lseek(child_fd, 0, SEEK_SET);
                    ssize_t bytes_read = read(child_fd, read_buffer, sizeof(read_buffer) - 1);
                    if (bytes_read > 0) {
                        read_buffer[bytes_read] = '\0';
                        printf("  子进程 %d 读取数据成功\n", getpid());
                    }
                    
                    sleep(2);  // 持有共享锁2秒
                    
                    flock(child_fd, LOCK_UN);
                    printf("  子进程 %d 释放共享锁\n", getpid());
                }
                
                close(child_fd);
            }
            exit(EXIT_SUCCESS);
        }
    }
    
    // 等待所有子进程结束
    for (int i = 0; i < 3; i++) {
        wait(NULL);
    }
    
    // 示例6: 锁的继承和关闭行为
    printf("\n示例6: 锁的继承和关闭行为\n");
    
    if (flock(fd, LOCK_EX) == 0) {
        printf("  进程持有排他锁\n");
        
        // 复制文件描述符
        int dup_fd = dup(fd);
        if (dup_fd != -1) {
            printf("  复制文件描述符: %d -> %d\n", fd, dup_fd);
            
            // 使用复制的fd释放锁
            if (flock(dup_fd, LOCK_UN) == 0) {
                printf("  使用复制的fd释放锁成功\n");
            }
            
            close(dup_fd);
        }
        
        // 重新获取锁
        if (flock(fd, LOCK_EX) == 0) {
            printf("  重新获取锁成功\n");
            flock(fd, LOCK_UN);
        }
    }
    
    // 示例7: 错误处理演示
    printf("\n示例7: 错误处理演示\n");
    
    // 尝试对无效文件描述符加锁
    if (flock(999, LOCK_EX) == -1) {
        printf("  对无效文件描述符加锁: %s\n", strerror(errno));
    }
    
    // 清理资源
    printf("\n清理资源...\n");
    if (close(fd) == -1) {
        perror("关闭文件失败");
    } else {
        printf("成功关闭文件描述符 %d\n", fd);
    }
    
    // 删除测试文件
    if (unlink("test_flock.txt") == -1) {
        perror("删除测试文件失败");
    } else {
        printf("成功删除测试文件\n");
    }
    
    return 0;
}


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

int main() {
    int fd;
    pid_t pid;
    
    printf("=== Flock函数示例 ===\n");
    
    // 创建测试文件
    fd = open("test_flock.txt", O_CREAT | O_RDWR | O_TRUNC, 0644);
    if (fd == -1) {
        perror("创建测试文件失败");
        exit(EXIT_FAILURE);
    }
    printf("成功创建测试文件,文件描述符: %d\n", fd);
    
    // 写入初始数据
    const char *initial_data = "Initial data for flock test\n";
    if (write(fd, initial_data, strlen(initial_data)) == -1) {
        perror("写入初始数据失败");
        close(fd);
        exit(EXIT_FAILURE);
    }
    
    // 示例1: 基本的排他锁操作
    printf("\n示例1: 基本的排他锁操作\n");
    
    // 获取排他锁
    if (flock(fd, LOCK_EX) == -1) {
        perror("获取排他锁失败");
    } else {
        printf("  成功获取排他锁\n");
        
        // 在锁保护下写入数据
        const char *exclusive_data = "Data written with exclusive lock\n";
        if (write(fd, exclusive_data, strlen(exclusive_data)) == -1) {
            perror("  写入数据失败");
        } else {
            printf("  成功写入数据到文件\n");
        }
        
        // 释放锁
        if (flock(fd, LOCK_UN) == -1) {
            perror("  释放锁失败");
        } else {
            printf("  成功释放排他锁\n");
        }
    }
    
    // 示例2: 共享锁操作
    printf("\n示例2: 共享锁操作\n");
    
    // 获取共享锁
    if (flock(fd, LOCK_SH) == -1) {
        perror("获取共享锁失败");
    } else {
        printf("  成功获取共享锁\n");
        
        // 在锁保护下读取数据
        char buffer[200];
        lseek(fd, 0, SEEK_SET);
        ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
        if (bytes_read > 0) {
            buffer[bytes_read] = '\0';
            printf("  读取文件内容:\n%s", buffer);
        }
        
        // 释放锁
        if (flock(fd, LOCK_UN) == -1) {
            perror("  释放共享锁失败");
        } else {
            printf("  成功释放共享锁\n");
        }
    }
    
    // 示例3: 非阻塞锁操作
    printf("\n示例3: 非阻塞锁操作\n");
    
    // 先获取一个排他锁
    if (flock(fd, LOCK_EX) == -1) {
        perror("获取排他锁失败");
    } else {
        printf("  进程已持有排他锁\n");
        
        // 尝试非阻塞获取另一个排他锁(应该失败)
        if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                printf("  非阻塞获取排他锁: 立即返回EAGAIN(锁被占用)\n");
            } else {
                perror("  非阻塞获取锁失败");
            }
        } else {
            printf("  非阻塞获取排他锁成功(不应该发生)\n");
            flock(fd, LOCK_UN);  // 释放意外获取的锁
        }
        
        // 尝试非阻塞获取共享锁(也应该失败,因为有排他锁)
        if (flock(fd, LOCK_SH | LOCK_NB) == -1) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                printf("  非阻塞获取共享锁: 立即返回EAGAIN(有排他锁)\n");
            } else {
                perror("  非阻塞获取共享锁失败");
            }
        } else {
            printf("  非阻塞获取共享锁成功(不应该发生)\n");
            flock(fd, LOCK_UN);  // 释放意外获取的锁
        }
        
        // 释放锁
        flock(fd, LOCK_UN);
        printf("  释放排他锁\n");
    }
    
    // 示例4: 多进程锁演示
    printf("\n示例4: 多进程锁演示\n");
    
    pid = fork();
    if (pid == -1) {
        perror("fork失败");
        close(fd);
        exit(EXIT_FAILURE);
    } else if (pid == 0) {
        // 子进程
        printf("  子进程 %d 开始执行\n", getpid());
        
        // 子进程打开同一个文件
        int child_fd = open("test_flock.txt", O_RDWR);
        if (child_fd == -1) {
            perror("  子进程打开文件失败");
            exit(EXIT_FAILURE);
        }
        
        printf("  子进程尝试获取排他锁...\n");
        if (flock(child_fd, LOCK_EX) == -1) {
            perror("  子进程获取排他锁失败");
        } else {
            printf("  子进程成功获取排他锁\n");
            
            // 写入子进程数据
            char child_data[100];
            sprintf(child_data, "Data from child process %d\n", getpid());
            if (write(child_fd, child_data, strlen(child_data)) == -1) {
                perror("  子进程写入数据失败");
            } else {
                printf("  子进程写入数据成功\n");
            }
            
            sleep(3);  // 持有锁3秒
            
            // 释放锁
            if (flock(child_fd, LOCK_UN) == -1) {
                perror("  子进程释放锁失败");
            } else {
                printf("  子进程释放排他锁\n");
            }
        }
        
        close(child_fd);
        exit(EXIT_SUCCESS);
    } else {
        // 父进程
        sleep(1);  // 让子进程先运行
        
        printf("  父进程 %d 尝试获取排他锁...\n", getpid());
        
        // 父进程尝试获取排他锁(会被阻塞直到子进程释放)
        printf("  父进程获取排他锁(会被阻塞)...\n");
        if (flock(fd, LOCK_EX) == -1) {
            perror("  父进程获取排他锁失败");
        } else {
            printf("  父进程成功获取排他锁(子进程已释放)\n");
            
            // 写入父进程数据
            char parent_data[100];
            sprintf(parent_data, "Data from parent process %d\n", getpid());
            if (write(fd, parent_data, strlen(parent_data)) == -1) {
                perror("  父进程写入数据失败");
            } else {
                printf("  父进程写入数据成功\n");
            }
            
            // 释放锁
            if (flock(fd, LOCK_UN) == -1) {
                perror("  父进程释放锁失败");
            } else {
                printf("  父进程释放排他锁\n");
            }
        }
        
        // 等待子进程结束
        wait(NULL);
    }
    
    // 示例5: 共享锁并发演示
    printf("\n示例5: 共享锁并发演示\n");
    
    // 创建多个子进程同时获取共享锁
    for (int i = 0; i < 3; i++) {
        pid = fork();
        if (pid == 0) {
            // 子进程
            int child_fd = open("test_flock.txt", O_RDONLY);
            if (child_fd != -1) {
                printf("  子进程 %d 尝试获取共享锁...\n", getpid());
                
                if (flock(child_fd, LOCK_SH) == 0) {
                    printf("  子进程 %d 成功获取共享锁\n", getpid());
                    
                    // 读取数据
                    char read_buffer[200];
                    lseek(child_fd, 0, SEEK_SET);
                    ssize_t bytes_read = read(child_fd, read_buffer, sizeof(read_buffer) - 1);
                    if (bytes_read > 0) {
                        read_buffer[bytes_read] = '\0';
                        printf("  子进程 %d 读取数据成功\n", getpid());
                    }
                    
                    sleep(2);  // 持有共享锁2秒
                    
                    flock(child_fd, LOCK_UN);
                    printf("  子进程 %d 释放共享锁\n", getpid());
                }
                
                close(child_fd);
            }
            exit(EXIT_SUCCESS);
        }
    }
    
    // 等待所有子进程结束
    for (int i = 0; i < 3; i++) {
        wait(NULL);
    }
    
    // 示例6: 锁的继承和关闭行为
    printf("\n示例6: 锁的继承和关闭行为\n");
    
    if (flock(fd, LOCK_EX) == 0) {
        printf("  进程持有排他锁\n");
        
        // 复制文件描述符
        int dup_fd = dup(fd);
        if (dup_fd != -1) {
            printf("  复制文件描述符: %d -> %d\n", fd, dup_fd);
            
            // 使用复制的fd释放锁
            if (flock(dup_fd, LOCK_UN) == 0) {
                printf("  使用复制的fd释放锁成功\n");
            }
            
            close(dup_fd);
        }
        
        // 重新获取锁
        if (flock(fd, LOCK_EX) == 0) {
            printf("  重新获取锁成功\n");
            flock(fd, LOCK_UN);
        }
    }
    
    // 示例7: 错误处理演示
    printf("\n示例7: 错误处理演示\n");
    
    // 尝试对无效文件描述符加锁
    if (flock(999, LOCK_EX) == -1) {
        printf("  对无效文件描述符加锁: %s\n", strerror(errno));
    }
    
    // 清理资源
    printf("\n清理资源...\n");
    if (close(fd) == -1) {
        perror("关闭文件失败");
    } else {
        printf("成功关闭文件描述符 %d\n", fd);
    }
    
    // 删除测试文件
    if (unlink("test_flock.txt") == -1) {
        perror("删除测试文件失败");
    } else {
        printf("成功删除测试文件\n");
    }
    
    return 0;
}
此条目发表在linux文章分类目录,贴了标签。将固定链接加入收藏夹。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注