機能の異なる3種類がfoo_wrapper
にあります
source_func_wrapper
のpython-機能とPythonのランタイムが呼び出しを処理していますこの機能の
header_func
は、コンパイル時に使用されるインライン関数なので、後でその定義/マシンコードは必要ありません。
source_func
は静的(これはfoo_wrapper
の場合)または動的(私はこれがtest_lib
のあなたの希望であると仮定します)リンカーによって処理されなければなりません。私はセットアップが箱から出して動作しないしない理由を、説明しようが、拳私は(少なくとも私の意見では)2を紹介したいと思いますダウン
さらに最良の選択肢:
A:この問題は完全に回避してください。 foo_wrapper
は、foo.h
のc関数をラップします。つまり、他のすべてのモジュールはでこれらのラッパー関数を使用する必要があります。誰もが機能に直接アクセスできる場合は、ラッパーの種類全体が廃止されます。あなた `PYXファイルにfoo.h
インターフェースを隠す:
#foo_wrapper.pdx
cdef source_func_wrapper()
cdef header_func_wrapper()
#foo_wrapper.pyx
cdef extern from "foo.h":
int source_func()
int header_func()
cdef source_func_wrapper():
return source_func()
cdef header_func_wrapper():
B: C関数経由で直接FOO-機能を使用するために有効かもしれません。この場合、stdc++
-library:foo.cpp
のcythonと同じ戦略を共有ライブラリにする必要があります。必要な場合はcimport
経由でインポートできるfoo.pdx
-file(pyxはありません)のみが必要です。さらに、libfoo.so
は、foo_wrapper
とtest_lib
の両方に依存して追加する必要があります。
しかし、アプローチBは、より多くの喧騒を意味します - あなたは、ダイナミックローダがそれを見つけることができるどこかlibfoo.so
を配置する必要があり...
他の代替:
我々が見るように、 foo_wrapper
+ test_lib
を動作させる方法はたくさんあります。まず、動的ライブラリの読み込みがPythonでどのように機能するかを詳しく見てみましょう。
我々は手でtest_lib.so
見てみることから始め:
>>> nm test_lib.so --undefined
....
U PyXXXXX
U source_func
Py
で開始し、実行時にPythonの実行によって提供されるほとんどが、未定義のシンボルが多いです。しかし、また、私たちの悪党 - source_func
があります。
は今、私たちは
LD_DEBUG=libs,files,symbols python
経由でのpythonを起動して、import test_lib
を経由して私たちの拡張モジュールを読み込みます。 dlopen
経由
>>>>: file=./test_lib.so [0]; dynamically loaded by python [0]
Pythonの負荷test_lib.so
とルックアップ/ test_lib.so
から未定義シンボルを解決するために開始します:トリガーデバッグ-traceでは、以下を参照してくださいすることができます
>>>>: symbol=PyExc_RuntimeError; lookup in file=python [0]
>>>>: symbol=PyExc_TypeError; lookup in file=python [0]
これらのpythonシンボルが検出されました非常に迅速に - それらはすべてPython実行可能ファイルで定義されています - 動的リンカーが最初に参照する場所です(この実行ファイルが-Wl,-export-dynamic
とリンクされている場合)。しかし、それはsource_func
と異なっている:
>>>>: symbol=source_func; lookup in file=python [0]
>>>>: symbol=source_func; lookup in file=/lib/x86_64-linux-gnu/libpthread.so.0 [0]
...
>>>>: symbol=source_func; lookup in file=/lib64/ld-linux-x86-64.so.2 [0]
>>>>: ./test_lib.so: error: symbol lookup error: undefined symbol: source_func (fatal)
だから、ロードされたすべての共有ライブラリを検索した後、シンボルが見つからない場合、我々は中止しなければなりません。楽しい事実は、foo_wrapper
がまだロードされていないので、source_func
はそこで参照できません(次のステップでは、test_lib
の依存関係としてPythonでロードされます)。
プリロードされたfoo_wrapper.so
でPythonを起動するとどうなりますか?foo_wrapper
をプリロードするので、成功import test_lib
を呼び出す
LD_DEBUG=libs,files,symbols LD_PRELOAD=$(pwd)/foo_wrapper.so python
この時間は、ダイナミックローダーは、シンボルを検索し、最初の場所である(後のpython-実行可能):
>>>>: symbol=source_func; lookup in file=python [0]
>>>>: symbol=source_func; lookup in file=/home/ed/python_stuff/cython/two/foo_wrapper.so [0]
しかし、それは、ときに動作しない方法foo_wrapper.so
はプリロードされていませんか?
ext_modules = cythonize([
Extension("test_lib", ["test_lib.pyx"],
libraries=[':foo_wrapper.so'],
library_dirs=['.'],
)])
これは、以下のリンカコマンドにつながる:まずはtest_lib
の私達の設定にライブラリとしてfoo_wrapper.so
を追加してみましょう
gcc ... test_lib.o -L. -l:foo_wrapper.so -o test_lib.so
を私たちは今の記号を見ると、私たちは何の違いを見ていない:
を
>>> nm test_lib.so --undefined
....
U PyXXXXX
U source_func
source_func
は未定義です。共有ライブラリとリンクすることの利点は何ですか?違いはtest_lib.so
ためで、必要に応じて、今foo_wrapper.so
が表示されていること、である:
>>>> readelf -d test_lib.so| grep NEEDED
0x0000000000000001 (NEEDED) Shared library: [foo_wrapper.so]
0x0000000000000001 (NEEDED) Shared library: [libpthread.so.0]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
ld
、リンクこれは動的リンカの仕事ですが、それは予行演習を行い、注目することによって、ダイナミックリンカを助ける、ということはありませんシンボルを解決するにはfoo_wrapper.so
が必要です。したがって、シンボルの検索が開始される前にロードする必要があります。しかし、それはシンボルsource_func
がfoo_wrapper.so
で調べられなければならないと明示的には言わない - 我々は実際にそれを見つけ出してどこでも使うことができる。
が再びのpythonを起動することができます、プリロードせずにこの時間:
>>>> LD_DEBUG=libs,files,symbols python
>>>> import test_lib
....
>>>> file=./test_lib.so [0]; dynamically loaded by python [0]....
>>>> file=foo_wrapper.so [0]; needed by ./test_lib.so [0]
>>>> find library=foo_wrapper.so [0]; searching
>>>> search cache=/etc/ld.so.cache
.....
>>>> `foo_wrapper.so: cannot open shared object file: No such file or directory.
OK]をクリックして、今、ダイナミックリンカが知っている、それはfoo_wrapper.so
を見つけるために持っているが、それはパスにどこにもありませんので、我々は、エラーメッセージが表示されます。
ダイナミックリンカーに共有ライブラリを探す場所を伝えなければなりません。多くの方法があり、そのうちの一つは、設定することですLD_DYNAMIC_LIBRARY
:
LD_DEBUG=libs,symbols,files LD_LIBRARY_PATH=. python
>>>> import test_lib
....
>>>> find library=foo_wrapper.so [0]; searching
>>>> search path=./tls/x86_64:./tls:./x86_64:. (LD_LIBRARY_PATH)
>>>> ...
>>>> trying file=./foo_wrapper.so
>>>> file=foo_wrapper.so [0]; generating link map
今回
foo_wrapper.so
が発見された
(ダイナミックローダがLD_LIBRARY_PATH
でほのめかした場所を見て)、ロードされた後、test_lib.so
に未定義シンボルを解決するために使用。
runtime_library_dirs
-setup引数を使用した場合の違いは何ですか?
ext_modules = cythonize([
Extension("test_lib", ["test_lib.pyx"],
libraries=[':foo_wrapper.so'],
library_dirs=['.'],
runtime_library_dirs=['.']
)
])
となりましLD_DYNAMIC_LIBRARY
経由で設定されていない場合でもそうRPATH
呼ば上で発見された
LD_DEBUG=libs,symbols,files python
>>>> import test_lib
....
>>>> file=foo_wrapper.so [0]; needed by ./test_lib.so [0]
>>>> find library=foo_wrapper.so [0]; searching
>>>> search path=./tls/x86_64:./tls:./x86_64:. (RPATH from file ./test_lib.so)
>>>> trying file=./foo_wrapper.so
>>>> file=foo_wrapper.so [0]; generating link map
foo_wrapper.so
を呼び出します。しかし、これが欲しかっれるものではないほとんどの時間である現在の作業ディレクトリへの相対パスである
>>>> readelf -d test_lib.so | grep RPATH
0x000000000000000f (RPATH) Library rpath: [.]
:私たちは、このRPATH
は、静的なリンカによって挿入されて見ることができます。一つは、今示す絶対パスを渡すか
ext_modules = cythonize([
Extension("test_lib", ["test_lib.pyx"],
libraries=[':foo_wrapper.so'],
library_dirs=['.'],
extra_link_args=["-Wl,-rpath=$ORIGIN/."] #rather than runtime_library_dirs
)
])
が得shared library. readelf
の(移動/コピーを通して、例えば変えることができる)は、現在位置からの相対パスを作るために使用する必要があります
>>>> readelf -d test_lib.so | grep RPATH
0x000000000000000f (RPATH) Library rpath: [$ORIGIN/.]
を意味します必要な共有ライブラリは、ロードされた共有ライブラリのパス、つまりtest_lib.so
に対して相対的に検索されます。
私が主張していないfoo_wrapper.so
のシンボルを再利用したい場合は、これも設定方法です。
ただし、既に作成したライブラリを使用する可能性があります。
元の設定に戻りましょう。最初にfoo_wrapper
(プリロードの一種として)をインポートしてからtest_lib
をインポートするとどうなりますか?私:
>>>> import foo_wrapper
>>>>> import test_lib
これはデフォルトでは機能しません。しかし、なぜ?明らかに、foo_wrapper
のロードされたシンボルは他のライブラリには見えません。 Pythonはdlopen
を共有ライブラリの動的ロードに使用し、this good articleで説明されているように、いくつかの異なる戦略が可能です。どのフラグが設定されているかを確認するには、
>>>> import sys
>>>> sys.getdlopenflags()
>>>> 2
を使用できます。 2
はRTLD_NOW
を意味します。つまり、シンボルは共有ライブラリのロード時に直接解決されます。動的にロードされたライブラリの外部/外部にシンボルを表示するには、RTLD_GLOBAL=256
にORフラグを付ける必要があります。
>>> import sys; import ctypes;
>>> sys.setdlopenflags(sys.getdlopenflags()| ctypes.RTLD_GLOBAL)
>>> import foo_wrapper
>>> import test_lib
、それは動作しますが、私たちのデバッグトレースを示しています
>>> symbol=source_func; lookup in file=./foo_wrapper.so [0]
>>> file=./foo_wrapper.so [0]; needed by ./test_lib.so [0] (relocation dependency)
もう一つの興味深い詳細:Pythonは輸入foo_wrapper
を経由して二回モジュールをロードしないのでfoo_wrapper.so
は、一度ロードされます。しかし、たとえそれが2回開かれても、メモリ内に1回だけ存在します(2回目の読取りでは共有ライブラリの参照カウントが増加します)。
しかし、今勝った洞察力で、我々はさらに行くことができる:
>>>> import sys;
>>>> sys.setdlopenflags(1|256)#RTLD_LAZY+RTLD_GLOBAL
>>>> import test_lib
>>>> test_lib.do_it()
>>>> ... it works! ....
なぜこれを? RTLD_LAZY
は、シンボルがロード時に直接ではなく、最初に使用されたときに解決されることを意味します。しかし、最初の使用(test_lib.do_it()
)の前にfoo_wrapper
がロードされ(test_lib
モジュール内にインポートされます)、RTLD_GLOBAL
のために、後で解決するためにシンボルを使用することができます。
RTLD_GLOBAL
を使用しない場合、この場合、foo_wrapper
の必要なシンボルがグローバルに表示されないため、test_lib.do_it()
を呼び出すとエラーが発生します。thisを参照して、シングルトン:ちょうど両方のモジュールfoo_wrapper
とfoo.cpp
に対するtest_lib
をリンクするためにこのような素晴らしいアイデアではない理由を疑問に
。
RE:「Everyone Everyoneだけが機能に直接アクセスできれば、これでラッパー全体が廃止されます」:これは私がやろうとしていることです。 pyとcythonの一般的なケースのcdefedクラスですが、必要に応じてcythonのコアlib関数を使用することができます。 IMHOは有効なユースケースです。 – SleepProgger
私はこのようにtbhをしなければならないと少し苛立ちます。 Cythonは私の 'foo.c'コードを組み込みますが、なぜ私はcdefed関数(pxdで宣言されています - > .hに変換され、pyxで定義された - > .cファイルに変換される) 。基本的にまったく同じシナリオ、または何が私はここで行方不明ですか? – SleepProgger
@SleepProggerはおそらく「廃止」であり、「muddy design」はあまりにも強い言語であるため、ラッパーを介して関数を呼び出す際にオーバーヘッドが発生し、直接Cコールが必要な場合があります。 – ead