Вы сталкиваетесь с довольно распространенной проблемой в мире Cygwin . Многие URL упоминают (имеют дело) об этом, но я собираюсь перечислить те, с которыми я столкнулся:
- [SO]: ошибка Cygwin: «-bash: fork: retry: ресурс временно недоступен»
- [SO]: проблема Cygwin - невозможно переназначить; тот же адрес, что и у родителя
- [SuperUser]: фатальная ошибка Cygwin не может быть переназначена .. Что это значит?
- [WordPress]: Cygwin и Rails - невозможно переназначить на тот же адрес, что и родительский; умер в ожидании загрузки dll, errno 11
- [SO]: ошибка Cygwin: «child_info_fork :: abort: загружен по другому адресу:»
«Магия за кулисами» очень хорошо объяснена в [Cygwin]: проблемы с созданием процесса ( выделения мои):
Семантика fork
требует, чтобы разветвленный дочерний процесс имел точно такую же структуру адресного пространства, что и его родительский процесс. Однако Windows не обеспечивает встроенной поддержки клонирования адресного пространства между процессами , и некоторые функции активно подрывают надежную реализацию fork
. Три проблемы особенно распространены:
- Коллизии базовых адресов DLL. В отличие от общих библиотек * nix, которые используют «независимый от позиции код», общие библиотеки Windows принимают фиксированный базовый адрес. Всякий раз, когда сталкиваются жесткие диапазоны адресов двух библиотек DLL (что происходит довольно часто), загрузчик Windows должен «перебазировать» один из них на другой адрес. Однако он может не разрешать конфликты последовательно и может перебазировать другую DLL и / или перемещать ее по разному адресу каждый раз. Cygwin обычно может компенсировать этот эффект, когда он включает в себя динамически открываемые библиотеки, но коллизии между статически связанными dll (зависимостями, известными во время компиляции) разрешаются до инициализации
cygwin1.dll
и не могут быть впоследствии исправлены. Эту проблему можно решить только путем удаления конфликтов базовых адресов, которые вызывают проблему, обычно с помощью инструмента rebaseall
.
- Рандомизация размещения адресного пространства (ASLR). Начиная с Vista, Windows реализует ASLR, что означает, что стеки потоков, кучи, файлы с отображением в памяти и статически связанные библиотеки размещаются в разных (случайных) местах в каждом процессе. Такое поведение мешает правильному
fork
, и если неподвижный объект (куча процесса или системная DLL) оказывается в неправильном месте, Cygwin не может ничего сделать, чтобы компенсировать (хотя он будет повторяться несколько раз автоматически ).
Устранение неполадок выполняется в [Cygwin.FAQ]: 4.45. Как исправить ошибки fork ()? ( подчеркивает все еще мое). С риском спамить ответ, я собираюсь вставить его здесь:
К сожалению, Windows не использует модель создания процесса fork / exec, которая есть в UNIX-подобных ОС, поэтому Cygwin трудно реализовать надежный и правильный fork()
, который может привести к сообщениям об ошибках, таких как:
- невозможно переназначить somedll на тот же адрес, что и родительский
- не удалось выделить кучу
- умер в ожидании загрузки DLL
- child -1 - умер в ожидании longjmp перед инициализацией
- STATUS_ACCESS_VIOLATION
- ресурс временно недоступен
Возможные решения для вышеуказанных ошибок:
- Перезапустите любой процесс, который пытается (и не может) использовать
fork()
. Иногда Windows устанавливает рабочую среду, которая даже враждебнее fork()
, чем обычно.
- Убедитесь, что вы удалили (не просто отключили) все программное обеспечение на BLODA .
- Переключитесь с 32-разрядного Cygwin на 64-разрядный Cygwin, если ваша ОС и процессор поддерживают это. С большим адресным пространством fork () с меньшей вероятностью потерпит неудачу.
Попробуйте установить для переменной среды CYGWIN значение «detect_bloda», что позволит выполнить дополнительную отладку, которая может указывать, какое другое программное обеспечение вызывает проблему.
См. в этом письме для получения дополнительной информации.
Принудительная полная перебазировка: запустите триггер перебазирования полная перебазировка , закройте все программы Cygwin и запустите установку Cygwin .
По умолчанию программа установки Cygwin автоматически выполняет пошаговую перебазку вновь установленных файлов.Принудительная полная перебазировка приводит к очистке карты перебазирования перед выполнением перебазировки.
См. / usr / share / doc / rebase / README и / usr / share/doc/Cygwin/_autorebase.README для получения более подробной информации .
Обратите внимание, что установка новых пакетов или обновление существующих отменяет эффекты rebase и часто вызывает повторное появление fork()
.
См. Раздел Создание процесса Руководства пользователя по техническим причинам, из-за которых трудно fork()
работать надежно.
Чтобы воспроизвести проблему, я использовал:
- Cygwin 32 :
- Вероятность столкнуться с проблемой очень великавыше
- Это не мой главный Cygwin env
- Python 3.6.4 + VEnv
- Расположен по адресу / home / cfati / Work / Dev / VEnvs / py_032_03.06.04_test0
Iпытался воспроизвести васточное поведение (с _lbfgsb * .dll ), но pip -v install scipy
не удалось его построить.
Начиная с [SciPy]: установка SciPy в Windows описывает довольно сложный процесс, и у меня не было никакой гарантии, что в конце я смогу воспроизвести проблему, я попробовал с numpy 's .dll s ( numpy был успешно установлен как scipy зависимость), но я не смог (как сторонаВ результате import numpy
загружает кучу .dll с), но вызов fork ( через subprocess.Popen
) не завершился неудачей.
Затем я решил взять дело в свои руки и создать небольшую программу, которая загружает несколько .dll s, а затем разветвляется (снова через subprocess.Popen
), чтобы сделать проблему воспроизводимой.насколько это возможно.
dll.c :
#include <stdio.h>
#if defined(_WIN32)
# define DLL_EXPORT __declspec(dllexport)
#else
# define DLL_EXPORT
#endif
DLL_EXPORT int test() {
printf("[%s] (%d) - [%s]\n", __FILE__, __LINE__, __FUNCTION__);
}
code.py :
#!/usr/bin/env python3
import sys
import os
import subprocess
import time
import select
import random
import ctypes
DLLS = [os.path.join(os.path.dirname(__file__), "dll{:d}.dll".format(item)) for item in range(2)]
def main():
random.seed(os.getpid())
random.shuffle(DLLS)
if len(sys.argv) == 1:
print("Python {:s} on {:s}\n".format(sys.version.replace("\n", ""), sys.platform))
print("Process 0x{:08X}".format(os.getpid()))
for dll in DLLS:
ctypes.cdll.LoadLibrary(dll)
idx = 0
while sys.stdin not in select.select([sys.stdin], [], [], 1)[0]:
p = subprocess.Popen([sys.executable] + sys.argv + [str(idx)])
#p.communicate()
time.sleep(1)
idx += 1
else:
sleep_time = 3
print("Process 0x{:08X} (inner) will end in {:d} seconds".format(os.getpid(), sleep_time))
time.sleep(sleep_time)
if __name__ == "__main__":
main()
Примечания :
- Сценарий немного другой, вместо модуля расширения Python , у меня есть обычный (фиктивный) .dll ,который я пытаюсь загрузить через [Python 3.Docs]: ctypes - библиотека сторонних функций для Python .Это не должно иметь никакого значения, так как Python не видит его (как модуль или как внешний .dll ) , ему все равно придется его загружать в процесс (так же)
- Сценарий:
- Я загружаю 2 таких .dll с (на самом деле это то же самое .dll скопированы под другим именем, поэтому они оба имеют одинаковый предпочитаемый базовый адрес ), поэтому 1 st .dll , вероятно, будетзагружается по этому адресу, в то время как следующий .dll будет загружен с другим
- Затем я fork процесса, а в дочернем, на основе случайногофактор, я переключаю .dll порядок загрузки
- Когда 2 nd .dll будет загружен на предпочтительной базе, там будетбыть несовместимым с родительским процессом, приводящим к ошибке
- Сначала все было в моем cwd , затем (чтобы быть ближе к вашей проблеме) я создал Python пакет с файлами.Обратите внимание, что я сделал это неправильно (установка через файл setup.py ), но копировал вручную все
[cfati@cfati-5510-0:/cygdrive/e/Work/Dev/StackOverflow/q054370263]> ~/sopr.sh
*** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***
[prompt]>
[prompt]> uname -a
CYGWIN_NT-10.0-WOW cfati-5510-0 2.11.2(0.329/5/3) 2018-11-08 14:30 i686 Cygwin
[prompt]> ls
code.py dll.c scipy.txt
[prompt]> # List the currently installed packages in the !!! VEENV !!! Python
[prompt]> ls -l ~/Work/Dev/VEnvs/py_032_03.06.04_test0/lib/python3.6/site-packages
total 33
drwxr-xr-x+ 1 cfati None 0 Jan 30 01:40 __pycache__
-rw-r--r-- 1 cfati None 126 Jan 30 01:40 easy_install.py
drwxr-xr-x+ 1 cfati None 0 Feb 2 21:41 numpy
drwxr-xr-x+ 1 cfati None 0 Feb 2 21:41 numpy-1.16.1.dist-info
drwxr-xr-x+ 1 cfati None 0 Jan 30 01:40 pip
drwxr-xr-x+ 1 cfati None 0 Jan 30 01:40 pip-19.0.1.dist-info
drwxr-xr-x+ 1 cfati None 0 Jan 30 01:40 pkg_resources
drwxr-xr-x+ 1 cfati None 0 Jan 30 01:40 setuptools
drwxr-xr-x+ 1 cfati None 0 Jan 30 01:40 setuptools-40.7.1.dist-info
drwxr-xr-x+ 1 cfati None 0 Jan 30 01:40 wheel
drwxr-xr-x+ 1 cfati None 0 Jan 30 01:40 wheel-0.32.3.dist-info
[prompt]>
[prompt]> ~/Work/Dev/VEnvs/py_032_03.06.04_test0/bin/python -m q054370263
/home/cfati/Work/Dev/VEnvs/py_032_03.06.04_test0/bin/python: No module named q054370263
[prompt]> # Create the package in site-packages dir
[prompt]> mkdir ~/Work/Dev/VEnvs/py_032_03.06.04_test0/lib/python3.6/site-packages/q054370263
[prompt]> cp code.py ~/Work/Dev/VEnvs/py_032_03.06.04_test0/lib/python3.6/site-packages/q054370263/__main__.py
[prompt]> gcc -fPIC -shared -o ~/Work/Dev/VEnvs/py_032_03.06.04_test0/lib/python3.6/site-packages/q054370263/dll0.dll dll.c
[prompt]> cp ~/Work/Dev/VEnvs/py_032_03.06.04_test0/lib/python3.6/site-packages/q054370263/dll0.dll ~/Work/Dev/VEnvs/py_032_03.06.04_test0/lib/python3.6/site-packages/q054370263/dll1.dll
[prompt]> ls
code.py dll.c scipy.txt
[prompt]> ls -l ~/Work/Dev/VEnvs/py_032_03.06.04_test0/lib/python3.6/site-packages/q054370263
total 260
-rwxr-x--- 1 cfati None 1012 Feb 3 12:39 __main__.py
-rwxr-xr-x 1 cfati None 129844 Feb 3 12:22 dll0.dll
-rwxr-xr-x 1 cfati None 129844 Feb 3 12:22 dll1.dll
[prompt]>
[prompt]> # Attempt to reproduce the problem by simply running the package
[prompt]> ~/Work/Dev/VEnvs/py_032_03.06.04_test0/bin/python -m q054370263
Python 3.6.4 (default, Jan 7 2018, 17:45:56) [GCC 6.4.0] on cygwin
Process 0x00001B38
18 [main] python3 21616 child_info_fork::abort: address space needed by 'dll0.dll' (0xD90000) is already occupied
Traceback (most recent call last):
File "/usr/lib/python3.6/runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "/usr/lib/python3.6/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/home/cfati/Work/Dev/VEnvs/py_032_03.06.04_test0/lib/python3.6/site-packages/q054370263/__main__.py", line 37, in <module>
main()
File "/home/cfati/Work/Dev/VEnvs/py_032_03.06.04_test0/lib/python3.6/site-packages/q054370263/__main__.py", line 25, in main
p = subprocess.Popen([sys.executable] + sys.argv + [str(idx)])
File "/usr/lib/python3.6/subprocess.py", line 709, in __init__
restore_signals, start_new_session)
File "/usr/lib/python3.6/subprocess.py", line 1275, in _execute_child
restore_signals, start_new_session, preexec_fn)
BlockingIOError: [Errno 11] Resource temporarily unavailable
[prompt]>
[prompt]> ~/Work/Dev/VEnvs/py_032_03.06.04_test0/bin/python -m q054370263
Python 3.6.4 (default, Jan 7 2018, 17:45:56) [GCC 6.4.0] on cygwin
Process 0x000055E8
Process 0x00005764 (inner) will end in 3 seconds
1 [main] python3 21224 child_info_fork::abort: address space needed by 'dll1.dll' (0x6D0000) is already occupied
Traceback (most recent call last):
File "/usr/lib/python3.6/runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "/usr/lib/python3.6/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/home/cfati/Work/Dev/VEnvs/py_032_03.06.04_test0/lib/python3.6/site-packages/q054370263/__main__.py", line 37, in <module>
main()
File "/home/cfati/Work/Dev/VEnvs/py_032_03.06.04_test0/lib/python3.6/site-packages/q054370263/__main__.py", line 25, in main
p = subprocess.Popen([sys.executable] + sys.argv + [str(idx)])
File "/usr/lib/python3.6/subprocess.py", line 709, in __init__
restore_signals, start_new_session)
File "/usr/lib/python3.6/subprocess.py", line 1275, in _execute_child
restore_signals, start_new_session, preexec_fn)
BlockingIOError: [Errno 11] Resource temporarily unavailable
Такошибка вполне воспроизводимая.Я также хотел бы добавить сюда детали .dll ( Зависимость Уокер ):
Почему обычная rebase ( rebaseall ) не решает проблему?
- Пакеты Cygwin имеют скрипт постинсталляции, который вызывает rebase для их .dll s
rebase поиск в стандартных путях к библиотекам ( / lib , / usr / lib , ...).Это можно отрегулировать в соответствии с / usr / share / doc / Cygwin / _autorebase.README :
Пакеты могут указывать потенциальные местоположения таких динамическихобъекты, известные путем удаления файла (названного по имени пакета) в / var / lib / rebase / dynpath.d / .Если какие-либо динамические объекты устанавливаются пользователями, эти местоположения должны быть объявлены в / var / lib / rebase / user.d / (имя файла должно совпадать с именем пользователя, если на нем несколько пользователейsystem)
Python требует такой настройки, поскольку пакеты могут содержать .dll s
- pip как у пакетов нет есть сценарий постустановки (который будет перебазировать их .dll s)
- VEnv находится в домашний путь пользователя , который не в стандартных путях к библиотекам (поэтому даже rebaseall будет игнорировать их)
Обратите внимание, что все перебазированные .dll s хранятся в DB : / etc / rebase.db (. $ {ARCH}) .
[prompt]> ls /var/lib/rebase/dynpath.d/
perl python2 python3
[prompt]> cat /var/lib/rebase/dynpath.d/python3
/usr/lib/python3.6/site-packages
[prompt]> ls /var/lib/rebase/user.d/
[prompt]>
[prompt]> grep -r "dll0.dll" /etc/rebase.db.i386
[prompt]>
Чтобы инструменты .dll были выбраны инструментами rebase , им нужныбыть рекламируемым.Это можно сделать двумя способами:
Укажите их в одном из пользовательских мест, поэтому при следующей полной перезагрузке они больше не будут игнорироваться (просто добавьте VEnv dir и другие, если есть):
[prompt]> echo /home/cfati/Work/Dev/VEnvs/py_032_03.06.04_test0 >/var/lib/rebase/user.d/${USER}
[prompt]> cat /var/lib/rebase/user.d/cfati
/home/cfati/Work/Dev/VEnvs/py_032_03.06.04_test0
Вручную перебазировать .dll s
Они оба работали для меня, но я собираюсь привести пример только для варианта 2 nd (как это проще).Процесс состоит из 2 шагов:
Обновленное окно Dependency Walker (отметьте Предпочитаемую базу и сравните его с изображением на предыдущем изображении):
А также rebase DB «запрос» (теперь вернулся из Mintty ):
[prompt]> grep -r "dll0.dll" /etc/rebase.db.i386
Binary file /etc/rebase.db.i386 matches
Что важнее, запуск кода :
[prompt]> ~/Work/Dev/VEnvs/py_032_03.06.04_test0/bin/python -m q054370263
Python 3.6.4 (default, Jan 7 2018, 17:45:56) [GCC 6.4.0] on cygwin
Process 0x000052D0
Process 0x00004634 (inner) will end in 3 seconds
Process 0x00004864 (inner) will end in 3 seconds
Process 0x00005CFC (inner) will end in 3 seconds
Process 0x00005A5C (inner) will end in 3 seconds
Process 0x00005098 (inner) will end in 3 seconds
Process 0x00005840 (inner) will end in 3 seconds
Process 0x000058C4 (inner) will end in 3 seconds
Process 0x000051DC (inner) will end in 3 seconds
Process 0x00001A5C (inner) will end in 3 seconds
Process 0x00003D2C (inner) will end in 3 seconds
Process 0x00000DA0 (inner) will end in 3 seconds
Как видноможет разветвляться несколько раз (остановился только потому, что я нажал Enter ).