내부에 숫자가 있는 텍스트 파일이 있고 ksh에 script.sh가 있습니다. 스크립트는 파일을 읽고 숫자를 가져온 다음 숫자를 1씩 증가시키고 파일의 새 숫자를 덮어쓴 다음 잠시 동안 대기하고 숫자가 120이 될 때까지 프로세스를 반복합니다.
이 스크립트를 동시에 n번 실행하고 싶습니다. 어떻게 해야 합니까?
하지만 file.txt를 편집하려는 n개의 프로세스가 있을 것이고, 하나의 프로세스만 편집하기 위해 프로세스가 완료(휴면)되면 두 번째 프로세스가 편집할 수 있게 됩니다.
잠금 파일을 사용해야 한다고 말씀하실 수도 있겠지만, 아쉽게도 사용할 수 없어서 세마포어를 시뮬레이션할 수 있는 다른 방법을 찾아야 합니다.
어떤 아이디어가 있나요?
감사해요.
답변1
나는 외부에서 숫자의 작동을 제어하는 것을 선호합니다. 스크립트를 호출하고 현재 숫자를 매개변수로 전달하기만 하면 됩니다.
n=10
read nr < number.file
seq $((nr+1)) 120 | xargs -n 1 -P $n script.sh
스크립트 자체는 다음과 같이 단순화됩니다.
#!/bin/ksh
number=$1
echo "Do job with/for number $number"
echo $number > number.file
sleep 10
물론 작업 기간이 다를 수 있는 경우 더 큰 파일을 덮어쓰는 것을 방지하기 위해 쓰기 전에 디지털 파일의 현재 내용을 확인하는 것이 좋습니다. 그러나 이는 임무 연속성이 특정 유형의 복구를 지원해야 하는 경우에만 중요합니다.
답변2
파일을 잠그지 않고 이 작업을 수행하려면 뭔가 이상한 것이 필요한 것 같습니다. 파일을 잠그는 것(또는 디렉토리 생성이 원자적이어야 하기 때문에 디렉토리를 잠그는 것이 더 좋습니다)은 이 문제에 대한 표준적이고 실행 가능한 솔루션이기 때문입니다. 나는 당신이 원하는 것을 정확하게 수행하지는 않았지만 마음에 떠오른 몇 가지 아이디어는 다음과 같습니다.
SysV 세마포어의 상태를 확인하기 위해 작은 C 프로그램을 작성할 수 있습니다. man semget
또는 로 시작하십시오 man semop
. 지루하고 이상할 겁니다.
Oracle sqlplus
및 PL/SQL 블록을 사용하여 다음 작업을 수행할 수 있습니다.
lock table table_name in exclusive mode
그런 다음 다시 호출하여 sqlplus
잠금을 해제합니다. 이전에 비슷한 일을 한 적이 있는데 프로세스가 기다리거나 잠금이 해제되지 않도록 매우 주의해야 합니다. 사무직에만 관심이 있어서 전화 한 번만 하는 경우도 있을 수 있습니다 sqlplus
.
왼쪽 필드에서는 명명된 파이프를 뮤텍스로 사용할 수 있습니다. 당신은 정말로 그것을 시도해야합니다.
커널 모듈을 로드할 수 있다면 커널 모듈은 /proc
더미 파일을 사용하여 뮤텍스나 세마포어 역할을 할 수 있습니다. IBM DeveloperWorks는기사파일이 생성되는 로드 가능한 모듈에서 /proc
.
어쩌면 당신은 구현할 수 있습니다데커 알고리즘파일이나 명명된 파이프의 값을 사용합니다.
semop()
내가 작성한 내용을 검토한 후에 C 프로그램을 제외하고는 이것이 실제로 작동하는지 확신할 수 없습니다 . 모두 많은 실험이 필요합니다.
답변3
가정:
- 스크립트의 모든 인스턴스는 동일한 컴퓨터에서 실행됩니다.
- 귀하의 스크립트는 알려져 있지만 다른 프로그램에서는 사용되지 않는 디렉토리에 쓸 수 있습니다. 디렉토리는 "일반" 파일 시스템(특히 비NFS)에 있습니다.
set -C; (: >foo) 2>/dev/null
파일 생성( ), 이름 바꾸기( ) 및 삭제( ) mv
와 같은 원자성 작업을 사용하여 rm
잠금을 처리할 수 있습니다.
프로세스에 알리려면 신호를 보내야 합니다. 그러나 프로세스를 대상으로 지정하는 것은 문제가 있습니다. 프로세스 ID를 어딘가에 저장하면 ID가 여전히 유효한지, 관련 없는 프로세스에서 재사용했는지 확인할 수 없습니다. 두 프로세스를 동기화하는 한 가지 방법은 파이프에 바이트를 쓰는 것입니다. 판독기는 작성자가 나타날 때까지 차단되며 그 반대의 경우도 마찬가지입니다.
먼저 디렉터리를 설정합니다. 이름이 지정된 파일 lock
과 이름이 지정된 파이프를 만듭니다 pipe
.
if ! [ -d /script-locking-directory ]; then
# The directory doesn't exist, create and populate it
{
mkdir /script-locking-directory-$$ &&
mkfifo /script-locking-directory-$$/pipe &&
touch /script-locking-directory-$$/lock &&
mv /script-locking-directory-$$ /script-locking-directory
} || {
# An error happened, so clean up
err=$?
rm -r /script-locking-directory-$$
# Exit, unless another instance of the script created the directory
# at the same time as us
if ! [ -d /script-locking-directory ]; then exit $?; fi
}
fi
파일 이름을 변경하여 잠금을 구현하겠습니다 lock
. 또한 잠금에 있는 모든 웨이터에게 알리는 간단한 구성표를 사용할 것입니다. 즉, 파이프에 바이트를 에코하고 모든 웨이터가 파이프에서 읽어 대기하도록 합니다. 이것은 간단한 해결책입니다.
take_lock () {
while ! mv lock lock.held 2>/dev/null; do
read <pipe # wait for a write on the pipe
done
}
release_lock () {
mv lock.held lock
read <pipe & # make sure there is a reader on the pipe so we don't block
echo >pipe # notify all readers
}
이 방식은 모든 웨이터를 깨우므로 비효율적일 수 있지만 경합이 많이 발생하지 않는 한(즉, 동시에 많은 웨이터가 발생하는 경우) 문제가 되지 않습니다.
위 코드의 주요 문제점은 잠금 홀더가 죽으면 잠금이 해제되지 않는다는 것입니다. 이 상황을 어떻게 감지할 수 있습니까? PID 재사용으로 인해 잠금과 같은 프로세스만 찾을 수는 없습니다. 우리가 할 수 있는 일은 스크립트에서 잠금 파일을 열고 새 스크립트 인스턴스가 시작될 때 잠금 파일이 열려 있는지 확인하는 것입니다.
break_lock () {
if ! [ -e "lock.held" ]; then return 1; fi
if [ -n "$(fuser lock.held)" ]; then return 1; fi
# If we get this far, the lock holder died
if mv lock.held lock.breaking.$$ 2>/dev/null; then
# Check that someone else didn't break the lock and take it just now
if [ -n "$(fuser lock.breaking.$$)" ]; then
mv lock.breaking.$$ lock.held
return 0
fi
mv lock.breaking.$$ lock
fi
return 0 # whether we did break a lock or not, try taking it again
}
take_lock () {
while ! mv lock lock.taking.$$ 2>/dev/null; do
if break_lock; then continue; fi
read <pipe # wait for a write on the pipe
done
exec 9<lock.taking.$$
mv lock.taking.$$ lock.held
}
release_lock () {
# lock.held might not exist if someone else is trying to break our lock.
# So we try in a loop.
while ! mv lock.held lock.releasing.$$ 2>/dev/null; do :; done
exec 9<&-
mv lock.releasing.$$ lock
read <pipe & # make sure there is a reader on the pipe so we don't block
echo >pipe # notify all readers
}
이 구현은 다른 인스턴스가 내부에 있는 동안 잠금 보유자가 죽으면 여전히 교착 상태가 될 수 있습니다 take_lock
. 잠금은 세 번째 인스턴스가 시작될 때까지 유지됩니다. 또한 스크립트가 take_lock
or 내에서 사라지지 않는다고 가정합니다 release_lock
.
경고: 위의 코드는 제가 브라우저에서 직접 작성한 것입니다. 아직 테스트하지 않았습니다(정확하다는 입증은 훨씬 적습니다).