이 스크립트는 자체 인스턴스가 하나만 실행되도록 어떻게 보장합니까?

이 스크립트는 자체 인스턴스가 하나만 실행되도록 어떻게 보장합니까?

2013년 8월 19일랜달 슈워츠풀어 주다이것Linux에서 "스크립트 인스턴스가 하나만 실행되고 경쟁 조건이나 잠금 파일을 정리할 필요가 없음"을 보장하도록 설계된 셸 스크립트:

#!/bin/sh
# randal_l_schwartz_001.sh
(
    if ! flock -n -x 0
    then
        echo "$$ cannot get flock"
        exit 0
    fi
    echo "$$ start"
    sleep 10 # for testing.  put the real task here
    echo "$$ end"
) < $0

광고된 대로 작동하는 것 같습니다.

$ ./randal_l_schwartz_001.sh & ./randal_l_schwartz_001.sh
[1] 11863
11863 start
11864 cannot get flock
$ 11863 end

[1]+  Done                    ./randal_l_schwartz_001.sh
$

이것이 나의 이해이다:

  • 스크립트는 <자체 내용(예: 에서)의 복사본을 서브셸의 $0STDIN(예: 파일 설명자)으로 리디렉션합니다( ).0
  • flock -n -x서브셸 내에서 스크립트는 파일 설명자에 대한 비차단 배타적 잠금( )을 획득하려고 시도합니다 0.
    • 이 시도가 실패하면 하위 쉘이 종료됩니다(다른 할 일이 없기 때문에 기본 스크립트도 마찬가지입니다).
    • 시도가 성공하면 서브셸이 필요한 작업을 실행합니다.

내 질문은 다음과 같습니다.

  • 스크립트가 다음 복사본을 서브셸의 상속된 파일 설명자로 리디렉션해야 하는 이유는 무엇입니까?그 자체의 콘텐츠다른 파일의 내용이 아닌? (위와 같이 다른 파일에서 리디렉션하고 다시 실행해 보았는데 실행 순서가 변경되었습니다. 비백그라운드 작업이 백그라운드 작업보다 먼저 잠금을 받습니다. 따라서 파일 자체 내용을 사용하면 경쟁 조건을 피할 수 있습니다. 하지만 어떻게 될까요? )
  • 어쨌든, 스크립트가 파일 내용의 복사본을 서브셸의 상속된 파일 설명자로 리디렉션해야 하는 이유는 무엇입니까?
  • 한 셸의 파일 설명자에 대한 배타적 잠금을 유지하면 0다른 셸에서 실행되는 동일한 스크립트의 복사본이 파일 설명자에 대한 배타적 잠금을 얻지 못하는 이유는 무엇입니까 0? 쉘에는 표준 파일 설명자( 0, 1, 및 2STDIN, STDOUT 및 STDERR)의 자체적이고 독립적인 복사본이 없습니까?

답변1

스크립트가 다른 파일의 내용이 아닌 자체 내용의 복사본을 하위 쉘이 상속한 파일 설명자로 리디렉션해야 하는 이유는 무엇입니까?

스크립트의 모든 복사본이 동일한 파일을 사용하는 한 모든 파일을 사용할 수 있습니다. just를 사용하면 $0잠금이 스크립트 자체에 바인딩됩니다. 스크립트를 복사하고 다른 용도로 수정하는 경우 잠금 파일의 새 이름을 만들 필요가 없습니다. 이것은 매우 편리합니다.

스크립트가 심볼릭 링크를 통해 호출되면 링크가 아닌 실제 파일에 잠금이 설정됩니다.

(물론 일부 프로세스가 스크립트를 실행하고 실제 경로 대신 0번째 인수로 구성된 값을 제공하면 중단됩니다. 그러나 그런 일은 거의 발생하지 않습니다.)

(위와 같이 다른 파일을 사용하여 다시 실행해 보니 실행 순서가 바뀌었습니다)

이것이 무작위 변경이 아니라 사용된 파일 때문이라고 확신합니까? 파이프와 마찬가지로 실제로 명령이 어디에 있는지 확인할 방법이 없습니다 cmd1 & cmd. 이는 주로 운영 체제 스케줄러에 따라 다릅니다. 내 시스템에 무작위 변경이 발생하고 있습니다.

어쨌든, 스크립트가 파일 내용의 복사본을 서브셸의 상속된 파일 설명자로 리디렉션해야 하는 이유는 무엇입니까?

flock잠금을 유지하는 유틸리티 뿐만 아니라 잠금을 유지하는 파일 설명의 복사본을 쉘 자체에 저장하는 것처럼 보입니다 . flock(2)잠금을 사용하여 생성된 잠금은 잠금을 보유하는 파일 설명자가 닫힐 때 해제됩니다.

flock두 가지 모드가 있습니다. 파일 이름을 기반으로 잠금을 획득한 다음 외부 명령을 실행하거나(이 경우 flock필요한 열린 파일 설명자가 저장됨) 파일 설명자를 외부에서 가져오므로 외부 프로세스가 저장을 담당합니다. .

이 문서의 내용은 여기와 관련이 없으며 사본이 만들어지지 않았습니다. 서브셸 자체로의 리디렉션은 데이터를 복사하지 않고 파일에 대한 핸들만 엽니다.

한 셸에서 파일 설명자 0에 대한 배타적 잠금을 유지하면 다른 셸에서 실행되는 동일한 스크립트의 복사본이 파일 설명자 0에 대한 배타적 잠금을 얻지 못하는 이유는 무엇입니까? 쉘에는 표준 파일 설명자(0, 1, 2, STDIN, STDOUT 및 STDERR)의 자체적이고 독립적인 복사본이 없습니까?

예, 하지만 잠겨 있습니다문서, 파일 설명자가 아닌. 한 번에 하나의 열린 파일 인스턴스만 잠금을 보유할 수 있습니다.


잠긴 파일에 대한 핸들을 열어 서브쉘 없이 exec동일한 작업을 수행 할 수 있어야 한다고 생각합니다.

$ cat lock.sh
#!/bin/sh

exec 9< "$0"

if ! flock -n -x 9; then
    echo "$$/$1 cannot get flock" 
    exit 0
fi

echo "$$/$1 got the lock"
sleep 2
echo "$$/$1 exit"

$ ./lock.sh bg & ./lock.sh fg ; wait; echo
[1] 11362
11363/fg got the lock
11362/bg cannot get flock
11363/fg exit
[1]+  Done                    ./lock.sh bg

답변2

파일 잠금 기능 제공도착하다파일을 통해파일 설명. 요약하면 스크립트 인스턴스의 작업 순서는 다음과 같습니다.

  1. 잠금이 연결된 파일("잠금 파일")을 엽니다.
  2. 잠금 파일을 잠급니다.
  3. 일하는 것.
  4. 잠금 파일을 닫습니다. 그러면 파일을 열어 생성된 파일 설명에 연결된 잠금이 해제됩니다.

잠금을 유지하면 동일한 스크립트의 다른 복사본이 실행되는 것을 방지할 수 있습니다. 잠금이 수행하는 작업이기 때문입니다. 시스템의 파일에 배타적 잠금이 존재하는 한, 다른 파일 설명을 통해서라도 동일한 잠금의 두 번째 인스턴스를 생성하는 것은 불가능합니다.

파일을 열면 다음이 생성됩니다.파일 설명. 이는 커널 개체이며 프로그래밍 인터페이스에서 직접적인 가시성을 많이 갖지 않습니다. 파일 설명자를 통해 간접적으로 파일 설명에 액세스하지만 일반적으로 파일에 액세스하는 것(해당 내용이나 메타데이터를 읽거나 쓰는 것)으로 생각합니다. 잠금은 파일이나 설명자가 아닌 파일 설명의 속성 중 하나입니다.

처음에 파일이 열리면 파일 설명에는 단일 파일 설명자가 있지만 dup다른 설명자(일련의 시스템 호출)를 만들거나 하위 프로세스(그런 다음 상위 및 하위 프로세스)를 분기하여 더 많은 설명자를 만들 수 있습니다. 어린이도 동일한 파일 설명에 액세스할 수 있습니다. 파일 설명자는 명시적으로 닫히거나 해당 파일 설명자가 있는 프로세스가 종료될 때 닫힐 수 있습니다. 파일에 첨부된 마지막 파일 설명자가 닫히면 파일 설명도 닫힙니다.

위의 작업 순서가 파일 설명에 어떤 영향을 미치는지는 다음과 같습니다.

  1. 리디렉션은 <$0서브셸에서 스크립트 파일을 열어 파일 설명을 만듭니다. 이 시점에서 파일 설명자가 설명에 추가됩니다: 서브쉘의 설명자 번호 0.
  2. 서브쉘을 호출 flock하고 종료될 때까지 기다립니다. 무리가 실행되면 설명에 두 개의 설명자가 추가됩니다. 즉, 하위 쉘의 숫자 0과 무리 프로세스의 숫자 0입니다. Flock이 잠금을 획득하면 파일 설명의 속성을 설정합니다. 다른 파일 설명이 이미 해당 파일에 대한 잠금을 보유하고 있는 경우, 이는 배타적 잠금이므로 Flock은 잠금을 획득할 수 없습니다.
  3. 서브쉘은 몇 가지 일을 할 수 있습니다. 잠금이 있는 설명에 여전히 열린 파일 설명자가 있으므로 설명은 그대로 유지되며 아무도 잠금을 제거하지 않았기 때문에 잠금을 유지합니다.
  4. 서브쉘은 닫는 괄호에서 종료됩니다. 그러면 잠금이 설정된 마지막 파일 설명자가 닫히므로 이 시점에서 잠금이 사라집니다.

스크립트가 리디렉션을 사용하는 이유 $0는 리디렉션이 셸에서 파일을 여는 유일한 방법이고 리디렉션을 활성 상태로 유지하는 것이 파일 설명자를 열어두는 유일한 방법이기 때문입니다. 서브쉘은 표준 입력을 읽지 않으며 단지 열어두기만 하면 됩니다. 개시 및 종료 통화에 직접 액세스할 수 있는 언어에서는 다음을 사용할 수 있습니다.

fd = open($0)
flock(fd, LOCK_EX)
do stuff
close(fd)

리디렉션에 내장된 명령을 사용하면 실제로 셸에서 동일한 작업 순서를 얻을 수 있습니다 exec.

exec <$0
flock -n -x 0
# do stuff
exec <&-

스크립트가 원래 표준 입력에 계속 액세스하려는 경우 다른 파일 설명자를 사용할 수 있습니다.

exec 3<$0
flock -n -x 0
# do stuff
exec 3<&-

또는 서브쉘을 사용하십시오:

(
  flock -n -x 3
  # do stuff
) 3<$0

잠금은 스크립트 파일에 있을 필요는 없습니다. 읽기 위해 열 수 있는 모든 파일에 있을 수 있습니다. 따라서 존재해야 하며 일반 파일이나 명명된 파이프와 같이 읽을 수 있는 파일 형식이어야 하지만 디렉터리는 아니어야 하며 스크립트 프로세스에는 다음이 있어야 합니다. 읽을 수 있는 권한). 스크립트 파일의 장점은 존재하고 읽을 수 있다는 것이 보장된다는 것입니다(스크립트가 호출되는 시간과 스크립트가 리디렉션에 도달하는 시간 사이에 외부적으로 삭제하는 극단적인 경우는 제외 <$0).

성공하고 스크립트가 잠금에 버그가 없는 파일 시스템에 있는 한 flock(NFS와 같은 일부 네트워크 파일 시스템에는 버그가 있을 수 있음) 다른 잠금 파일을 사용하면 경쟁 조건이 어떻게 허용되는지 알 수 없습니다. 나는 당신이 실수를 한 것 같아요.

답변3

잠금에 사용되는 파일은 중요하지 않습니다. 스크립트는 해당 파일이 $0존재하는 것으로 알려져 있으므로 해당 파일을 사용합니다.

잠금이 획득되는 순서는 컴퓨터가 두 작업을 얼마나 빨리 시작하는지에 따라 다소 무작위입니다.

반드시 0일 필요는 없지만 모든 파일 설명자를 사용할 수 있습니다. 자물쇠 보유문서설명자 자체가 아닌 파일 설명자를 엽니다.

( flock -x 9 || exit 1
  echo 'Locking for 5 secs'; sleep 5; echo 'Done' ) 9>/tmp/lock &

관련 정보