2013-02-27 29 views
8

tôi đã thực hiện một tập tin khóa cơ chế dọc theo dòng của những gợi ý từ các trang linux người đàn ông cho "mở", trong đó nêu: chương trìnhThực hiện một tập tin di động khóa cơ chế

xách tay mà muốn thực hiện khóa tập tin nguyên tử sử dụng một lockfile và cần phải tránh sự phụ thuộc vào hỗ trợ NFS cho O_EXCL, có thể tạo một tệp duy nhất trên cùng một hệ thống tệp (ví dụ: kết hợp tên máy chủ và PID) và sử dụng liên kết (2) để tạo liên kết tới tệp khóa . Nếu liên kết (2) trả về 0, khóa sẽ thành công. Nếu không, hãy sử dụng stat (2) trên tệp duy nhất để kiểm tra xem số liên kết của nó có tăng lên 2 hay không, trong trường hợp khóa cũng thành công.

Điều này dường như làm việc một cách hoàn hảo, tuy nhiên để có được mã số bảo hiểm 100% trong thử nghiệm của tôi, tôi cần phải bao gồm các trường hợp số lượng liên kết được tăng lên 2.

Tôi đã thử googling, nhưng tất cả Tôi dường như có thể tìm thấy là cùng một tài liệu tham khảo ở trên regurgitated là "cách nó được thực hiện".

Ai có thể giải thích cho tôi về những trường hợp nào sẽ khiến liên kết bị lỗi (trả về -1), nhưng số lượng liên kết được tăng lên 2?

+0

Rất tốt câu hỏi . Tôi không thể nghĩ ra bất kỳ hoàn cảnh nào mà điều này sẽ xảy ra, trừ khi hai quy trình cạnh tranh đều chọn cùng một tên tệp duy nhất cùng một lúc (điều này rõ ràng là xấu). Có thể là một giải pháp cho các lỗi NFS rất cũ? – Celada

+1

Bạn có cần tạo các tệp khóa trên NFS không?AFAIK bạn sẽ có thể sử dụng 'flock()' hoặc 'lockf()' trong hầu hết các trường hợp. – Hasturkun

Trả lời

2

Câu trả lời cho câu hỏi của bạn được cung cấp ở dưới cùng của liên kết (2) trang của Cẩm Nang Lập trình viên của Linux:

On NFS file systems, the return code may be wrong in case the NFS 
    server performs the link creation and dies before it can say so. Use 
    stat(2) to find out if the link got created. 
1

Tạo tệp khác có nhiều rắc rối hơn bất kỳ thứ gì. Tạo một thư mục thay thế và kiểm tra kết quả của quá trình tạo. Sổ tay Unix chỉ ra rằng chỉ có một nhiệm vụ có thể thành công trong việc tạo một thư mục, thư mục kia sẽ bị lỗi nếu thư mục đã tồn tại, bao gồm cả trường hợp 2 tác vụ đã thử nó cùng một lúc. Bản thân hệ điều hành xử lý vấn đề, do đó bạn không phải làm như vậy.

Nếu không phải vì khóa cũ, đó là tất cả những gì bạn phải làm. Tuy nhiên, mọi thứ xảy ra, các chương trình bị hủy bỏ và không phải lúc nào cũng loại bỏ khóa của họ. Vì vậy, việc triển khai có thể phức tạp hơn một chút.

Trong tập lệnh, tôi thường sử dụng mã bên dưới. Nó tự động xử lý khóa cũ. Bạn có thể thực hiện như nhau trong C. trang Kiểm tra người đàn ông:

man -s 2 mkdir 

EXECUTION_CONTROL_FILE: là một PATH tên và tên Dir, một cái gì đó giống như/usr/tmp/myAppName

second_of_now: trả lại thời gian hiện tại trong vài giây (bao gồm dưới đây)

LOCK_MAX_TIME: là bao lâu chỉ trong vài giây một khóa có thể tồn tại trước khi nó được coi là cũ

ngủ 5: Người ta luôn cho rằng một khóa sẽ làm điều gì đó ngắn và ngọt ngào. Nếu không, có thể chu trình giấc ngủ của bạn sẽ lâu hơn.

LockFile() { 
    L_DIR=${EXECUTION_CONTROL_FILE}.lock 
    L_DIR2=${EXECUTION_CONTROL_FILE}.lock2 
    (
    L_STATUS=1 
    L_FILE_COUNT=2 
    L_COUNT=10 
    while [ $L_STATUS != 0 ]; do 
    mkdir $L_DIR 2>/dev/null 
    L_STATUS=$? 
    if [ $L_STATUS = 0 ]; then 
     # Create the timetime stamp file 
     second_of_now >$L_DIR/timestamp 
    else 
     # The directory exists, check how long it has been there 
     L_NOW=`second_of_now` 
     L_THEN=`cat $L_DIR/timestamp 2>/dev/null` 
     # The file does not exist, how many times did this happen? 
     if [ "$L_THEN" = "" ]; then 
     if [ $L_FILE_COUNT != 0 ]; then 
      L_THEN=$L_NOW 
      L_FILE_COUNT=`expr $L_FILE_COUNT - 1` 
     else 
      L_THEN=0 
     fi 
     fi 
     if [ `expr $L_NOW - $L_THEN` -gt $LOCK_MAX_TIME ]; then 
     # We will try 10 times to unlock, but the 10th time 
     # we will force the unlock. 
     UnlockFile $L_COUNT 
     L_COUNT=`expr $L_COUNT - 1` 
     else 
     L_COUNT=10 # Reset this back in case it has gone down 
     sleep 5 
     fi 
    fi 
    done 
) 
    L_STATUS=$? 
    return $L_STATUS 
} 

#### 
#### Remove access lock 
#### 
UnlockFile() { 
    U_DIR=${EXECUTION_CONTROL_FILE}.lock 
    U_DIR2=${EXECUTION_CONTROL_FILE}.lock2 
    (
    # This 'cd' fixes an issue with UNIX which sometimes report this error: 
    # rm: cannot determine if this is an ancestor of the current working directory 
    cd `dirname "${EXECUTION_CONTROL_FILE}"` 

    mkdir $U_DIR2 2>/dev/null 
    U_STATUS=$? 
    if [ $U_STATUS != 0 ]; then 
    if [ "$1" != "0" ]; then 
     return 
    fi 
    fi 

    trap "rm -rf $U_DIR2" 0 

    # The directory exists, check how long it has been there 
    # in case it has just been added again 
    U_NOW=`second_of_now` 
    U_THEN=`cat $U_DIR/timestamp 2>/dev/null` 
    # The file does not exist then we assume it is obsolete 
    if [ "$U_THEN" = "" ]; then 
    U_THEN=0 
    fi 
    if [ `expr $U_NOW - $U_THEN` -gt $LOCK_MAX_TIME -o "$1" = "mine" ]; then 
    # Remove lock directory as it is still too old 
    rm -rf $U_DIR 
    fi 

    # Remove this short lock directory 
    rm -rf $U_DIR2 
) 
    U_STATUS=$? 
    return $U_STATUS 
} 

#### 
second_of_now() { 
    second_of_day `date "+%y%m%d%H%M%S"` 
} 

#### 
#### Return which second of the date/time this is. The parameters must 
#### be in the form "yymmddHHMMSS", no centuries for the year and 
#### years before 2000 are not supported. 
second_of_day() { 
    year=`printf "$1\n"|cut -c1-2` 
    year=`expr $year + 0` 
    month=`printf "$1\n"|cut -c3-4` 
    day=`printf "$1\n"|cut -c5-6` 
    day=`expr $day - 1` 
    hour=`printf "$1\n"|cut -c7-8` 
    min=`printf "$1\n"|cut -c9-10` 
    sec=`printf "$1\n"|cut -c11-12` 
    sec=`expr $min \* 60 + $sec` 
    sec=`expr $hour \* 3600 + $sec` 
    sec=`expr $day \* 86400 + $sec` 
    if [ `expr 20$year % 4` = 0 ]; then 
    bisex=29 
    else 
    bisex=28 
    fi 
    mm=1 
    while [ $mm -lt $month ]; do 
    case $mm in 
     4|6|9|11) days=30 ;; 
     2) days=$bisex ;; 
     *) days=31 ;; 
    esac 
    sec=`expr $days \* 86400 + $sec` 
    mm=`expr $mm + 1` 
    done 
    year=`expr $year + 2000` 
    while [ $year -gt 2000 ]; do 
    year=`expr $year - 1` 
    if [ `expr $year % 4` = 0 ]; then 
     sec=`expr 31622400 + $sec` 
    else 
     sec=`expr 31536000 + $sec` 
    fi 
    done 
    printf "$sec\n" 
} 

Sử dụng như thế này:

# Make sure that 2 operations don't happen at the same time 
    LockFile 
    # Make sure we get rid of our lock if we exit unexpectedly 
    trap "UnlockFile mine" 0 
. 
. Do what you have to do 
. 
    # We need to remove the lock 
    UnlockFile mine 
+0

Điều đó có vẻ racy, IMHO. Bất kể nguyên tử nào bạn có thể nhận được từ 'mkdir()' (trong đó, btw, bạn cũng có thể tạo ra một tập tin với 'O_EXCL') bạn sẽ mất sau khi truy cập tập tin dấu thời gian. Nếu bạn đang ở trong một kịch bản shell, có lẽ bạn nên sử dụng lệnh 'flock' từ util-linux. – Hasturkun

+0

Mục đích ở đây là mô tả một kỹ thuật sử dụng ngôn ngữ kịch bản riêng và vẫn có thể tương thích với mã C muốn truy cập cùng một tài nguyên. Sử dụng "mkdir" và kiểm tra mã trả về sẽ là đủ, nhưng điều đó sẽ không xử lý khóa cũ. Nếu cơ chế khóa của bạn được sử dụng độc quyền bởi mã C, thì bạn có lúc xử lý tốt hơn nhiều chức năng của mình và tôi đồng ý, điều này sẽ gây đau nhiều hơn. Tôi tự hỏi tại sao, với tất cả các công cụ có sẵn trong Unix, không có công cụ nào được viết (hoặc bắt được) để thao tác khóa trong các tập lệnh. – cpu

+0

Một công cụ không tồn tại để thao tác khóa trong các tập lệnh, như tôi đã đề cập trước đó, tiện ích [flock] (http://man7.org/linux/man-pages/man1/flock.1.html) từ [util-linux ] (https://www.kernel.org/pub/linux/utils/util-linux/) gói thực hiện điều đó. xem thêm http://stackoverflow.com/a/1985512 – Hasturkun

Các vấn đề liên quan