一、flock 基础命令格式

flock [选项] <文件|文件描述符> <命令>
# 或
flock [选项] <文件|文件描述符> -c <命令字符串>

二、核心选项及示例说明

1. -s (共享锁 Shared Lock)

  • 作用 获取共享锁(读锁),允许多进程同时读取,但阻止排他锁。

  • 场景 需要多个进程同时读取某个文件,但要求写入操作互斥。

  • 示例

    # 两个终端同时运行此命令不会阻塞
    flock -s data.lock -c "cat data.txt; sleep 5"
    

2. -x (排他锁 Exclusive Lock )

  • 作用 获取排他锁(写锁),禁止其他进程获取任何类型的锁。

  • 场景 需要对文件进行写入操作时,保证数据一致性。

  • 示例

    # 第二个进程会阻塞直到第一个完成
    flock -x log.lock -c "echo 'New log entry' >> app.log; sleep 3"
    

3. -n (非阻塞 Non-blocking)

  • 作用 非阻塞模式。若无法立即获取锁,立即失败并退出,不会等待。

  • 场景 快速判断资源是否可用,无需等待。

  • 示例

    if flock -n 200; then
        echo "Got lock, doing work..."
        sleep 2
    else
        echo "Resource busy, exiting."
    fi 200>data.lock
    

4. -w <秒数> (超时等待 Timeout)

  • 作用 指定等待锁的超时时间。超过设定时间仍无法获取锁则放弃。

  • 场景 希望在一定时间内尝试获取锁,避免无限等待。

  • 示例

    # 最多等待5秒
    if flock -w 5 200; then
        echo "Lock acquired within 5s"
    else
        echo "Timeout! Lock unavailable"
    fi 200>config.lock
    

5. -u (手动解锁 Unlock)

  • 作用 手动释放文件描述符关联的锁(通常配合 exec 使用)。

  • 场景 需要更精细控制锁的生命周期。

  • 示例

    exec 200>db.lock
    flock -x 200  # 加排他锁
    echo "Writing to DB..."
    flock -u 200  # 手动释放锁
    

6. -E <错误码> (自定义错误码)

  • 作用 定义在超时或非阻塞模式下无法获取锁时返回的退出码。

  • 场景 通过不同错误码区分失败原因,便于脚本处理。

  • 示例

    flock -E 66 -w 10 200 || handle_error $?
    

7. -o (关闭自动关闭描述符)

  • 作用 保持文件描述符打开,不传递给子进程。

  • 场景 需要在父进程保持锁,而子进程不使用锁时。

  • 示例

    (
      flock -o 200  # 文件描述符200保持打开
      echo "Parent keeps lock..."
      ./child_script.sh  # 子进程不继承锁
    ) 200>file.lock
    

三、综合应用案例

案例1:保证单实例运行

# 通过锁文件保证同一时刻只有一个脚本实例运行
LOCK_FILE="/tmp/script.lock"
exec 200>"${LOCK_FILE}"

if ! flock -n 200; then
    echo "Another instance is running. Exiting."
    exit 1
fi

# 主业务逻辑...
echo "Working..."
sleep 10

案例2:数据库文件读写保护

DB_FILE="app.db"
LOCK_FILE="${DB_FILE}.lock"

query_database() {
    local sql="$1"
    (
        flock -x 200
        sqlite3 "${DB_FILE}" "${sql}"
    ) 200>"${LOCK_FILE}"
}

# 并发安全查询
query_database "SELECT * FROM users;" &
query_database "UPDATE stats SET views=views+1;" &

案例3:带超时的日志追加

LOG_FILE="debug.log"
LOG_LOCK="${LOG_FILE}.lock"

log_message() {
    local msg="$1"
    (
        if flock -w 5 200; then
            echo "$(date) - ${msg}" >> "${LOG_FILE}"
        else
            echo "Logging timeout!" >&2
        fi
    ) 200>"${LOG_LOCK}"
}

# 并行写入测试
for i in {1..5}; do
    log_message "Test $i" &
done

四、注意事项

  1. 文件描述符管理

    exec 200>file.lock  # 明确管理文件描述符
    flock -x 200        # 使用200而非文件名
    
  2. 锁粒度控制 加锁粒度要尽可能小。例如只锁需要保护的临界区代码。

  3. NFS 文件系统 flock 在 NFS 上的行为可能不可靠,需查阅具体实现文档。


可以通过 man flock 查看完整的官方文档。