나는 많은 프로세스(수백 개의 노드에서 전송/실행되는 작업)에서 일부 병렬화를 수행하려고 합니다. 나는 이 해결책을 발견했습니다: 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()
- 무엇을 합니까
exec 3<>pipe-$$
? - 나중에 삭제된 이유는 무엇인가요?
run_with_lock()
- 이 부분은
&& ((0==x)) || exit $x
무엇을 의미하나요 ? ( "$@"; )
- 내가 아는 한 이것은 전달된 모든 인수의 목록입니다. 그런데 여기서는 무엇을 하고 있나요?
이것이 프로세스를 이해하는 데 있어 주요 장애물이지만, 전체 프로세스를 자유롭게 설명해주세요 :)
추신: 저는 단지 그 게시물 아래에 댓글을 달고 싶었지만 방금 가입했고 그렇게 할 만한 평판이 없습니다. 다른 사람들에게도 유용할 수 있습니다. 감사해요! 제이.
답변1
먼저 일반적인 아이디어에 대해 이야기합시다.
fifo에서 읽을 때 읽기 명령은 데이터가 fifo에 기록될 때까지 완료되지 않습니다. fifo에 대한 모든 읽기 명령은 다른 프로세스가 동일한 fifo에 쓸 때까지 스크립트를 "중단"시킵니다.
세마포어로 사용할 수 있습니다.
- 읽기 명령이 완료된 후에만 임의의 명령 X를 실행하고
- 명령이 완료되면 다른 프로세스가 세마포어를 사용하여 읽기 명령을 완료할 수 있도록 한 번 씁니다.
이제 N개의 초기 프로세스가 시작되도록 허용하기 위해 fifo에 쓰는 N 명령을 사용하여 세마포어를 설정할 수 있습니다.
따라서 N 명령을 fifo(이 경우 파일 설명자 3에 바인딩됨)에 기록하여 "세마포어"를 초기화하면 길이가 N인 대기열을 얻게 됩니다.
반복하다:임의의 명령 X를 가져와서 함수로 래핑하고, f()
명령 X 앞의 줄을 읽고, 그 뒤에 쓰고, 래핑된 명령을 백그라운드 작업으로 보냅니다( f X &
). 따라서 4개의 쓰기 명령이 설정된 경우 read
해당 명령에 따라 4번째 백그라운드 작업이 실행되고 5번째 작업이 중지됩니다. 따라서 명령 후 래퍼에 쓰기 명령을 추가하면read
X) 쓰기 명령을 실행합니다.
open_sem()
exec
프로세스의 입력 및 출력을 리디렉션하거나 현재 프로세스를 대체하는 데 사용할 수 있습니다. 이 경우 파일 설명자 3을 fifo로(쓰기) 및 fifo에서(읽기) 리디렉션합니다.<
입력과>
출력을 위해. 명령 인수를 제공하면exec
(여기에서는 수행되지 않음) bash 쉘이 손상되고 해당 PID가 명령으로 대체됩니다. 이 단계를 건너뛰면 파일 설명자 3을 읽고 쓸 때 fifo에 있는 읽기 및 쓰기 잠금이 생성되지 않습니다.- 제거할 필요는 없으며 단지 정리하는 방법일 뿐입니다.
run_with_lock()
- 이
read
명령은 FIFO에 기록된 내용을 변수로 읽어옵니다x
. 예제에서 fifo에 작성된 명령은 명령 X의 종료 상태 번호를 작성하므로 이전 X 명령이 성공적으로 종료되었는지(코드 0) 확인하는 데 사용할 수 있습니다. 변수가 0이 아니면x
종료되고 종료 상태는 변수 x에 저장됩니다. - 첫 번째 인수
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