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


22

Randal L. Schwartz 는 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
$

내가 이해하는 것은 다음과 같습니다.

  • 스크립트 <는 자체 내용의 사본 (에서 from $0)을 0서브 쉘 의 STDIN (예 : 파일 설명자 )으로 경로 재 지정합니다 ( ) .
  • 서브 쉘 내에서 스크립트는 flock -n -x파일 디스크립터 에서 비 블로킹 독점 잠금 ( ) 을 가져 오려고 시도합니다 0.
    • 해당 시도가 실패하면 서브 쉘이 종료됩니다 (다른 작업이 없기 때문에 기본 스크립트도 종료 됨).
    • 시도가 성공하면 서브 쉘이 원하는 태스크를 실행합니다.

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

  • 스크립트가 왜 다른 파일 의 내용이 아닌 자체 내용 의 사본을 서브 쉘에 의해 상속 된 파일 디스크립터로 경로 재지 정해야 합니까? (다른 파일에서 리디렉션을 시도하고 위와 같이 다시 실행했는데 실행 순서가 변경되었습니다. 백그라운드되지 않은 작업이 백그라운드 작업보다 먼저 잠금을 얻었습니다. 따라서 파일 자체 내용을 사용하면 경쟁 조건을 피할 수 있습니다. 그러나 어떻게?)
  • 어쨌든 스크립트가 서브 쉘이 상속 한 파일 디스크립터로 파일 내용의 사본을 경로 재지 정해야하는 이유는 무엇입니까?
  • 0한 쉘 에서 파일 디스크립터 에 독점 잠금을 유지하는 이유는 다른 쉘에서 실행되는 동일한 스크립트의 사본이 파일 디스크립터에 독점 잠금을 얻지 못하게하는 이유는 무엇 0입니까? 쉘은 표준 파일 기술자 (자신의 별도의 사본이없는 0, 1그리고 2, 즉, STDIN, STDOUT, 및 STDERR)를?

다른 파일에서 리디렉션하기 위해 실험을 시도했을 때 정확한 테스트 프로세스는 무엇입니까?
Freiheit

1
이 링크를 참조 할 수 있다고 생각합니다. stackoverflow.com/questions/185451/…
Deb Paikar

답변:


22

스크립트가 다른 파일의 내용이 아닌 자체 내용의 사본을 서브 쉘에 의해 상속 된 파일 디스크립터로 경로 재지 정해야하는 이유는 무엇입니까?

모든 스크립트 사본이 동일한 파일을 사용하는 한 모든 파일을 사용할 수 있습니다. 사용 $0하여 잠금을 스크립트 자체에 연결하기 : 스크립트를 복사하여 다른 용도로 수정하는 경우 잠금 파일의 새 이름을 지정할 필요가 없습니다. 편리합니다.

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

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

(다른 파일을 사용하여 위와 같이 다시 실행하려고 시도했으며 실행 순서가 변경되었습니다)

무작위 변형이 아니라 사용 된 파일 때문 이었습니까? 파이프 라인과 마찬가지로 명령이 어떤 순서로 실행되는지 확실하게 알 수있는 방법이 없습니다 cmd1 & cmd. 대부분 OS 스케줄러에 달려 있습니다. 시스템에서 임의의 변형이 발생합니다.

어쨌든 스크립트가 서브 쉘이 상속 한 파일 디스크립터로 파일 내용의 사본을 경로 재지 정해야하는 이유는 무엇입니까?

쉘 자체가 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

1
{ }대신에 사용 ( )하면 하위 쉘을 피할 수 있습니다.
R ..

G + 게시물에 대한 의견에서 더 나아가 누군가가을 사용하는 것과 거의 동일한 방법을 제안했습니다 exec.
David Z

@R .., 물론 이죠. 그러나 실제 스크립트 주위에 여분의 중괄호가 여전히 추악합니다.
ilkkachu

9

파일 잠금은 파일 description을 통해 파일에 첨부 됩니다 . 높은 수준에서 스크립트의 한 인스턴스에서 작업 순서는 다음과 같습니다.

  1. 잠금이 첨부 된 파일 (“잠금 파일”)을 엽니 다.
  2. 잠금 파일을 잠그십시오.
  3. 물건을하십시오.
  4. 잠금 파일을 닫습니다. 파일을 열어 만든 파일 설명에 첨부 된 잠금이 해제됩니다.

잠금을 유지하면 잠금이 수행하는 동일한 스크립트의 다른 사본이 실행되지 않습니다. 파일의 독점 잠금이 시스템 어딘가에 존재하는 한 다른 파일 설명을 통해서도 동일한 잠금의 두 번째 인스턴스를 작성하는 것은 불가능합니다.

파일을 열면 파일 설명이 작성 됩니다 . 이것은 프로그래밍 인터페이스에서 많은 직접적인 가시성을 갖지 않는 커널 객체입니다. 파일 디스크립터를 통해 간접적으로 파일 설명에 액세스하지만 일반적으로 파일에 액세스 (콘텐츠 또는 메타 데이터 읽기 또는 쓰기)하는 것으로 생각합니다. 잠금은 파일이나 설명자가 아닌 파일 설명에 대한 속성 인 속성 중 하나입니다.

처음에 파일을 열면 파일 설명에 단일 파일 디스크립터가 있지만 다른 디스크립터 ( dup시스템 호출 패밀리)를 작성하거나 서브 프로세스 (이후 상위 및 자식은 동일한 파일 설명에 액세스 할 수 있습니다). 파일 디스크립터는 명시 적으로 또는 프로세스가 종료 될 때 닫힐 수 있습니다. 파일에 첨부 된 마지막 파일 디스크립터가 닫히면 파일 설명이 닫힙니다.

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

  1. 리디렉션 <$0은 서브 쉘에서 스크립트 파일을 열고 파일 설명을 작성합니다. 이 시점에서 서브 파일에 설명자 번호 0 인 단일 파일 설명자가 설명에 첨부되어 있습니다.
  2. 서브 쉘이 호출 flock되고 종료 될 때까지 기다립니다. flock이 실행되는 동안 서브 쉘에는 number 0과 flock 프로세스에는 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와 같은 일부 네트워크 파일 시스템은 버그가있을 수 있습니다) 버그가 아닌 파일 시스템에, 나는 다른 잠금 파일을 사용하는 경쟁 조건을 허용 할 수 표시되지 않습니다. 귀하의 조작 오류가 의심됩니다.


경쟁 조건이있다 : 당신이 통제 할 수 있는 잠금을 얻을 스크립트의 인스턴스입니다. 다행히도 거의 모든 목적을 위해 중요하지 않습니다.
마크

4
@Mark 잠금에 대한 경쟁이 있지만 경쟁 조건이 아닙니다. 경쟁 조건이 타이밍은 두 프로세스가 동시에 동일한 임계 영역에있는 것으로, 뭔가 나쁜 일이 일어날 수 있도록 할 수있을 때입니다. 어떤 프로세스가 중요 섹션에 들어갈 지 알지 못하는 것은 비결정론 적이며, 경쟁 조건이 아닙니다.
Gilles 'SO- 악마 중지'

1
참고로, "파일 설명"의 링크는 개념에 대한 구체적인 설명이 아니라 Open Group 사양 색인 페이지를 가리키는 것으로 생각합니다. 또는 unix.stackexchange.com/a/195164/85039
Sergiy Kolodyazhnyy

5

잠금에 사용되는 파일은 중요하지 않습니다. 스크립트는 $0존재하는 것으로 알려진 파일이기 때문에 사용 합니다.

잠금이 획득되는 순서는 기계가 두 작업을 얼마나 빨리 시작할 수 있는지에 따라 다소 임의적입니다.

반드시 0 일 필요는없는 파일 디스크립터를 사용할 수 있습니다. 잠금은 디스크립터 자체가 아니라 파일 디스크립터에 열린 파일에 보유 됩니다.

( flock -x 9 || exit 1
  echo 'Locking for 5 secs'; sleep 5; echo 'Done' ) 9>/tmp/lock &
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.