FIFO 기반 세마포어 설명

FIFO 기반 세마포어 설명

나는 많은 프로세스(수백 개의 노드에서 전송/실행되는 작업)에서 일부 병렬화를 수행하려고 합니다. 나는 이 해결책을 발견했습니다: https://unix.stackexchange.com/a/216475

    # initialize a semaphore with a given number of tokens
    open_sem(){
        mkfifo pipe-$$
        exec 3<>pipe-$$
        rm pipe-$$
        local i=$1
        for((;i>0;i--)); do
            printf %s 000 >&3
        done
    }
    
    # run the given command asynchronously and pop/push tokens
    run_with_lock(){
        local x
        # this read waits until there is something to read
        read -u 3 -n 3 x && ((0==x)) || exit $x
        (
         ( "$@"; )
        # push the return code of the command to the semaphore
        printf '%.3d' $? >&3
        )&
    }
    
    N=4
    open_sem $N
    for thing in {a..g}; do
        run_with_lock task $thing
    done 

여기에 설명이 필요합니다.

open_sem()

  1. 무엇을 합니까 exec 3<>pipe-$$?
  2. 나중에 삭제된 이유는 무엇인가요?

run_with_lock()

  1. 이 부분은 && ((0==x)) || exit $x무엇을 의미하나요 ?
  2. ( "$@"; )- 내가 아는 한 이것은 전달된 모든 인수의 목록입니다. 그런데 여기서는 무엇을 하고 있나요?

이것이 프로세스를 이해하는 데 있어 주요 장애물이지만, 전체 프로세스를 자유롭게 설명해주세요 :)

추신: 저는 단지 그 게시물 아래에 댓글을 달고 싶었지만 방금 가입했고 그렇게 할 만한 평판이 없습니다. 다른 사람들에게도 유용할 수 있습니다. 감사해요! 제이.

답변1

먼저 일반적인 아이디어에 대해 이야기합시다.

fifo에서 읽을 때 읽기 명령은 데이터가 fifo에 기록될 때까지 완료되지 않습니다. fifo에 대한 모든 읽기 명령은 다른 프로세스가 동일한 fifo에 쓸 때까지 스크립트를 "중단"시킵니다.

세마포어로 사용할 수 있습니다.

  1. 읽기 명령이 완료된 후에만 임의의 명령 X를 실행하고
  2. 명령이 완료되면 다른 프로세스가 세마포어를 사용하여 읽기 명령을 완료할 수 있도록 한 번 씁니다.

이제 N개의 초기 프로세스가 시작되도록 허용하기 위해 fifo에 쓰는 N 명령을 사용하여 세마포어를 설정할 수 있습니다.

따라서 N 명령을 fifo(이 경우 파일 설명자 3에 바인딩됨)에 기록하여 "세마포어"를 초기화하면 길이가 N인 대기열을 얻게 됩니다.

반복하다:임의의 명령 X를 가져와서 함수로 래핑하고, f()명령 X 앞의 줄을 읽고, 그 뒤에 쓰고, 래핑된 명령을 백그라운드 작업으로 보냅니다( f X &). 따라서 4개의 쓰기 명령이 설정된 경우 read해당 명령에 따라 4번째 백그라운드 작업이 실행되고 5번째 작업이 중지됩니다. 따라서 명령 후 래퍼에 쓰기 명령을 추가하면read X) 쓰기 명령을 실행합니다.

open_sem()

  1. exec프로세스의 입력 및 출력을 리디렉션하거나 현재 프로세스를 대체하는 데 사용할 수 있습니다. 이 경우 파일 설명자 3을 fifo로(쓰기) 및 fifo에서(읽기) 리디렉션합니다. <입력과 >출력을 위해. 명령 인수를 제공하면 exec(여기에서는 수행되지 않음) bash 쉘이 손상되고 해당 PID가 명령으로 대체됩니다. 이 단계를 건너뛰면 파일 설명자 3을 읽고 쓸 때 fifo에 있는 읽기 및 쓰기 잠금이 생성되지 않습니다.
  2. 제거할 필요는 없으며 단지 정리하는 방법일 뿐입니다.

run_with_lock()

  1. read명령은 FIFO에 기록된 내용을 변수로 읽어옵니다 x. 예제에서 fifo에 작성된 명령은 명령 X의 종료 상태 번호를 작성하므로 이전 X 명령이 성공적으로 종료되었는지(코드 0) 확인하는 데 사용할 수 있습니다. 변수가 0이 아니면 x종료되고 종료 상태는 변수 x에 저장됩니다.
  2. 첫 번째 인수 run_with_lock()는 명령이고 나머지 인수는 해당 명령에 대한 인수라고 가정합니다. 따라서 ("$@")&매개변수가 있는 명령은 백그라운드로 전송된 서브셸에서 실행됩니다.

답변2

이 작업을 수행하는 더 간단한 코드가 있다고 생각합니다. 나는 질문자가 제공한 예제와 여기에 제공된 코드를 부분적으로 기반으로 합니다.https://www.mlo.io/blog/2012/06/13/parallel-processes-in-bash/, 그리고 @methuselah-0의 설명 중 일부입니다.

# make a fifo pipe
mkfifo pipe
exec 3<>pipe
rm -f pipe

# fill with some number of values (will correspond to the number of spawned processes)
for i in `seq 7`; do
    { sleep $i; echo >&3; } &
done

# if a read from the queue succeeds, start a subprocess, otherwise wait
# for another to finish and restock the queue with a readable entry
for f in `ls /Users/pavelkomarov/data_cache`; do
    read <&3
    { python3 /Users/pavelkomarov/my_heavy_script.py -t $f; echo >&3; } &
done

관련 정보