Предположения:
Можно использовать атомарные операции, такие как создание файла (set -C; (: >foo) 2>/dev/null
), переименование (mv
) и удаление (rm
) обработать блокировку.
Для уведомления процесса можно отправить ему сигнал; однако это проблематично для определения местоположения целевых процессов: при хранении идентификаторов процесса где-нибудь Вы не можете быть уверены, что идентификаторы все еще действительны, они, возможно, были снова использованы несвязанным процессом. Один способ синхронизировать два процесса состоит в том, чтобы записать байт на канале; читатель заблокируется, пока устройство записи не подойдет и наоборот.
Во-первых, настройте каталог. Создайте названный файл 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
или release_lock
.
Предупреждение: Я написал код выше непосредственно в браузере, я не протестировал его (уже не говоря о доказанном, который он исправляет).
Не использовать -S 10G
, это слишком много (и вероятно не выполнение, что Вы думаете). Запуск OOMkiller действительно означает, что Ваша система использует весь, его - память.
Согласно алгоритму, используемому sort
, это будет использовать память согласно тому, что доступно: половина самого большого количества между TotalMem/8 и AvailableMem.
Так, например, если у Вас есть 4 ГБ доступной мадам (из 8 ГБ), sort
будет использовать 2 ГБ RAM. Это должно также создать много файлов на 2 ГБ в/bigdisk и наконец сортировать их с объединением.
Установка Try vm.overcommit_memory = 1
: «…
Когда этот флаг равняется 1, ядро притворяется, что всегда существует достаточно памяти, пока это на самом деле не заканчивается.
…»
И действительно используйте подкачку.
sort
а не от ядра.
– Totor
16.03.2013, 20:12
sort
использование/потребности слишком много памяти. С overcommit_memory=2
, sort
просто не malloc()
и очень, вероятно, не отсортирует, затем. It
– Totor
06.05.2015, 02:19
Создание ответа от моего комментария:
У меня была такая же проблема, когда я использовал / Run / Shm
, как мой / BigDisk
для хранения Сортировка
TEMP файлов. / RUN / SHM
- это RAM-диск, поэтому, когда сортировка необходима для кэширования частичных результатов на диске (который он делает, когда память почти заполнена), память выбежала. Убил ядро сортировка
, так как это был процесс, используя наибольшую память.
Использование местоположения, хранящегося на физическом диске вместо RAM-диска, решила его.
sort
? Сверитьсяfile $(which sort)
. – Totor 19.03.2013, 02:07