GNU/Linux >> Linux の 問題 >  >> Linux

root アクセスなしで、参照 BLAS とリンクされている場合は、調整された BLAS で R を実行します

<ブロック引用>

なぜ私のやり方がうまくいかないのか

まず、UNIX の共有ライブラリは、アーカイブ ライブラリの動作を模倣するように設計されています (アーカイブ ライブラリは最初にありました)。特に、あなたが 03 を持っている場合 と 14 、両方ともシンボル 29 を定義 の場合、最初にロードされたライブラリが勝ちます:37 へのすべての参照 プログラム内のどこからでも (46 を含む) ) 55 にバインドされます 69 の定義 .

これは、プログラムを 78 に対してリンクした場合にどうなるかを模倣しています および 82 、両方のアーカイブ ライブラリが同じシンボル 97 を定義した場所 .アーカイブ リンクの詳細については、こちらをご覧ください。

上記から明らかなように、101 の場合 と 113 同じ記号のセットを定義する (行う )、および 122 の場合 最初にプロセスにロードされ、次に 134 からのルーチンがロードされます 決して

第二に、あなたは 145 以来、それを正しく判断しました 157 への直接リンク 、および 162 以降 172 への直接リンク 、 184 が保証されています

しかし、あなたは誤って 196 と決めました より良いですが、そうではありません:205 小さい バイナリ (私のシステムでは 11K。210 の 2.4MB と比較してください) )、そしてそれが行うことはほぼすべて 223 です 237 の .これは 241 で見るのは簡単です 出力:

strace -e trace=execve /usr/bin/Rscript --default-packages=base --vanilla /dev/null
execve("/usr/bin/Rscript", ["/usr/bin/Rscript", "--default-packages=base", "--vanilla", "/dev/null"], [/* 42 vars */]) = 0
execve("/usr/lib/R/bin/R", ["/usr/lib/R/bin/R", "--slave", "--no-restore", "--vanilla", "--file=/dev/null", "--args"], [/* 43 vars */]) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=89625, si_status=0, si_utime=0, si_stime=0} ---
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=89626, si_status=0, si_utime=0, si_stime=0} ---
execve("/usr/lib/R/bin/exec/R", ["/usr/lib/R/bin/exec/R", "--slave", "--no-restore", "--vanilla", "--file=/dev/null", "--args"], [/* 51 vars */]) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=89630, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++

つまり、スクリプトの実行が開始されるまでに 254 ロードされ、263 271 の依存関係として読み込まれます 実際にはしない

<ブロック引用>

それを機能させることはまったく可能ですか

おそらく。考えられる解決策は 2 つあります:

<オール>
  • 287 のふりをする 実際は 299 です
  • 309 全体を再構築します 319 に対するパッケージ .
  • #1 については、320 する必要があります 、それから 337 のコピーを確認してください 346 を設定することにより、システムの前に検出されます

    これは私にとってはうまくいくようです:

    mkdir /tmp/libblas
    # pretend that libc.so.6 is really libblas.so.3
    cp /lib/x86_64-linux-gnu/libc.so.6 /tmp/libblas/libblas.so.3
    LD_LIBRARY_PATH=/tmp/libblas /usr/bin/Rscript /dev/null
    Error in dyn.load(file, DLLpath = DLLpath, ...) :
      unable to load shared object '/usr/lib/R/library/stats/libs/stats.so':
      /usr/lib/liblapack.so.3: undefined symbol: cgemv_
    During startup - Warning message:
    package ‘stats’ in options("defaultPackages") was not found
    

    エラーが発生したことに注意してください (私の「ふり」 357 実際には 363 のコピーであるため、期待されるシンボルを定義していません ).

    375 のバージョンを確認することもできます この方法でロードされています:

    LD_DEBUG=libs LD_LIBRARY_PATH=/tmp/libblas /usr/bin/Rscript /dev/null |& grep 'libblas\.so\.3'
         91533: find library=libblas.so.3 [0]; searching
         91533:   trying file=/usr/lib/R/lib/libblas.so.3
         91533:   trying file=/usr/lib/x86_64-linux-gnu/libblas.so.3
         91533:   trying file=/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/amd64/server/libblas.so.3
         91533:   trying file=/tmp/libblas/libblas.so.3
         91533: calling init: /tmp/libblas/libblas.so.3
    

    #2 について、あなたはこう言いました:

    <ブロック引用>

    テストしたいマシンにルート アクセス権がないため、OpenBLAS への実際のリンクは不可能です。

    385 をビルドできる場合 、確かに 398 の独自のバージョンを構築することもできます .

    更新:

    <ブロック引用>

    libblas.so.3 と libopenblas.so.0 が同じシンボルを定義していると最初に述べましたが、これはどういう意味ですか?それらは異なる SONAME を持っていますが、システムでそれらを区別するには不十分ですか?

    記号と 409 何も持っていない

    410 からの出力にシンボルが表示されます と 428 . 433 に関連する記号 446 など 、両方のライブラリに表示されます。

    454 についての混乱 たぶん Windows から来ています。 461 Windows 上の s は、まったく異なる設計になっています。特に 478 の場合 シンボル 488 をインポートします 492 から 、両方 シンボルの名前 (508 ) 519 そのシンボルのインポート元 (521 ) は 533 に記録されます s インポート テーブル。

    これにより、547 を簡単に取得できます import 550 567 から 、 575 の間 580 から同じシンボルをインポートします .

    ただし、これによりライブラリの挿入が難しくなり、アーカイブ ライブラリの動作とはまったく異なる動作をします (Windows でも)。

    どちらの設計が全体的に優れているかについては意見が分かれていますが、どちらのシステムもモデルを変更することはありません。

    UNIX が Windows スタイルのシンボル バインディングをエミュレートする方法があります。594 を参照してください。 dlopen の man ページにあります。注意:これらは危険に満ちており、UNIX の専門家を混乱させる可能性があり、広く使用されておらず、実装のバグがある可能性があります。

    アップデート 2:

    <ブロック引用>

    R をコンパイルしてホーム ディレクトリにインストールするということですか?

    はい。

    <ブロック引用>

    次に、それを呼び出したいときは、自分のバージョンの実行可能プログラムへのパスを明示的に指定する必要があります。そうしないと、代わりにシステム上のものが呼び出される可能性がありますか?または、このパスを環境変数 $PATH の最初の位置に配置して、システムをごまかすことはできますか?

    どちらの方法でも機能します。


    ********************

    解決策 1:

    ********************

    雇用されたロシア人のおかげで、私の問題はついに解決されました.調査には、Linux システムのデバッグとパッチ適用に関する重要なスキルが必要です 、そしてこれは私が学んだ大きな資産だと思います。ここに解決策を投稿し、元の投稿のいくつかの点を修正します。

    1 R の呼び出しについて

    最初の投稿で、R を起動するには 2 つの方法があると述べました。 または 619 .しかし、私はそれらの違いを誤って誇張しています。重要な Linux デバッグ機能 626 を使用して、起動プロセスを調査してみましょう。 (639 を参照) )。シェルでコマンドを入力した後、実際には多くの興味深いことが起こっています。

    strace -e trace=process [command]
    

    プロセス管理に関係するすべてのシステム コールを追跡します。その結果、プロセスの fork、wait、および実行のステップを監視できます。マニュアルページには記載されていませんが、@Employed Russian は 646 のサブクラスのみを指定できることを示しています。 、たとえば、657 実行ステップのために。

    662 の場合

    ~/Desktop/dgemm$ time strace -e trace=execve R --vanilla < /dev/null > /dev/null
    execve("/usr/bin/R", ["R", "--vanilla"], [/* 70 vars */]) = 0
    --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=5777, si_status=0, si_utime=0, si_stime=0} ---
    execve("/usr/lib/R/bin/exec/R", ["/usr/lib/R/bin/exec/R", "--vanilla"], [/* 79 vars */]) = 0
    --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=5778, si_status=0, si_utime=0, si_stime=0} ---
    +++ exited with 0 +++
    
    real    0m0.345s
    user    0m0.256s
    sys     0m0.068s
    

    677 の間

    ~/Desktop/dgemm$ time strace -e trace=execve Rscript --default-packages=base --vanilla /dev/null
    execve("/usr/bin/Rscript", ["Rscript", "--default-packages=base", "--vanilla", "/dev/null"], [/* 70 vars */]) = 0
    execve("/usr/lib/R/bin/R", ["/usr/lib/R/bin/R", "--slave", "--no-restore", "--vanilla", "--file=/dev/null"], [/* 71 vars */]) = 0
    --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=5822, si_status=0, si_utime=0, si_stime=0} ---
    --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=5823, si_status=0, si_utime=0, si_stime=0} ---
    execve("/usr/lib/R/bin/exec/R", ["/usr/lib/R/bin/exec/R", "--slave", "--no-restore", "--vanilla", "--file=/dev/null"], [/* 80 vars */]) = 0
    --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=5827, si_status=0, si_utime=0, si_stime=0} ---
    +++ exited with 0 +++
    
    real    0m0.063s
    user    0m0.020s
    sys     0m0.028s
    

    689 も使用しています 起動時間を測定します。注意してください

    <オール>
  • 698 702 よりも約 5.5 倍高速です .その理由の 1 つは、711 です。 726 の間、起動時に 6 つのデフォルト パッケージを読み込みます 1 つの 730 のみを読み込みます コントロールによるパッケージ:745 .ただし、この設定がなくてもはるかに高速です。
  • 最終的に、両方の起動プロセスが 753 に向けられます 、そして私の最初の投稿で、私はすでに 762 を悪用しました この実行可能ファイルが 775 をロードすることを示します 、 788 にリンクされています . @Employed Russian の説明によると、先にロードした BLAS ライブラリが勝つため、私の元の方法が機能するわけがありません。
  • 791 を正常に実行するには 、素晴らしいを使用しました ファイル 805 必要に応じて入力ファイルおよび出力ファイルとして。例:812 824 の間、入力ファイルを要求します 両方を要求します。コマンドをスムーズに実行し、出力をきれいにするために、null デバイスを両方に供給します。 null デバイスは物理的に存在するファイルですが、驚くほど機能します。そこから読み取る場合、何も含まれていません。書き込み中は、すべてを破棄します。
  • 2.チート R

    839 以来 とにかくロードされますが、私たちができる唯一のことは、このライブラリの独自のバージョンを提供することです。元の投稿で述べたように、root アクセス権があれば、 848 を使用することで、これは非常に簡単です。 、システムLinuxがこの切り替えを完了するのに役立つように.しかし、@Employed Russian は、root アクセスなしでシステムをごまかす素晴らしい方法を提供しています:R が起動時にどのように BLAS ライブラリを見つけているかを調べて、システムのデフォルトが見つかる前に私たちのバージョンをフィードするようにしましょう! 共有ライブラリがどのように検出されロードされるかを監視するには、環境変数 853 を使用します .

    プレフィックス 864 を持つ多くの Linux 環境変数があります。 、870 に記載されているとおり .これらの変数は実行可能ファイルの前に割り当てることができるため、プログラムの実行機能を変更できます。便利な変数には次のものがあります:

    • 884 ランタイム ライブラリの検索パスを設定します。
    • 895 共有ライブラリのルックアップとロードを追跡するため;
    • 907 プログラムによってロードされたすべてのライブラリを表示するため (914 と同様に動作します) );
    • 921 他のすべてのライブラリが検索される前に、最初にライブラリをプログラムに強制的に挿入するため;
    • 932942 1 のプロファイリング用 指定された共有ライブラリ。セクション 3.4.1.1 sprof を読んだ R ユーザー R 拡張機能の記述については、これが R 内からコンパイルされたコードのプロファイリングに使用されることを思い出してください。

    952 の使用 見ることができます:

    ~/Desktop/dgemm$ LD_DEBUG=help cat
    Valid options for the LD_DEBUG environment variable are:
    
      libs        display library search paths
      reloc       display relocation processing
      files       display progress for input file
      symbols     display symbol table processing
      bindings    display information about symbol binding
      versions    display version dependencies
      scopes      display scope information
      all         all previous options combined
      statistics  display relocation statistics
      unused      determined unused DSOs
      help        display this help message and exit
    
      To direct the debugging output into a file instead of standard output a filename can be specified using the LD_DEBUG_OUTPUT environment variable.
    

    ここで、963 の使用に特に関心があります。 .たとえば、

    ~/Desktop/dgemm$ LD_DEBUG=libs Rscript --default-packages=base --vanilla /dev/null |& grep blas
      5974: find library=libblas.so.3 [0]; searching
      5974:   trying file=/usr/lib/R/lib/libblas.so.3
      5974:   trying file=/usr/lib/i386-linux-gnu/i686/sse2/libblas.so.3
      5974:   trying file=/usr/lib/i386-linux-gnu/i686/cmov/libblas.so.3
      5974:   trying file=/usr/lib/i386-linux-gnu/i686/libblas.so.3
      5974:   trying file=/usr/lib/i386-linux-gnu/sse2/libblas.so.3
      5974:   trying file=/usr/lib/i386-linux-gnu/libblas.so.3
      5974:   trying file=/usr/lib/jvm/java-7-openjdk-i386/jre/lib/i386/client/libblas.so.3
      5974:   trying file=/usr/lib/libblas.so.3
      5974: calling init: /usr/lib/libblas.so.3
      5974: calling fini: /usr/lib/libblas.so.3 [0]
    

    R プログラムが 978 を見つけてロードしようとしたさまざまな試みを示しています . 989 の独自のバージョンを提供できれば 、そして R が最初にそれを見つけることを確認してください。それから問題は解決されます。

    まずはシンボリックリンク 990 を作ってみましょう OpenBLAS ライブラリ 1006 への作業パス 、次にデフォルトの 1019 を展開します 私たちの作業パスで(そしてエクスポート それ):

    ~/Desktop/dgemm$ ln -sf libopenblas.so libblas.so.3
    ~/Desktop/dgemm$ export LD_LIBRARY_PATH = $(pwd):$LD_LIBRARY_PATH  ## put our working path at top
    

    ライブラリの読み込みプロセスをもう一度確認してみましょう:

    ~/Desktop/dgemm$ LD_DEBUG=libs Rscript --default-packages=base --vanilla /dev/null |& grep blas
      6063: find library=libblas.so.3 [0]; searching
      6063:   trying file=/usr/lib/R/lib/libblas.so.3
      6063:   trying file=/usr/lib/i386-linux-gnu/i686/sse2/libblas.so.3
      6063:   trying file=/usr/lib/i386-linux-gnu/i686/cmov/libblas.so.3
      6063:   trying file=/usr/lib/i386-linux-gnu/i686/libblas.so.3
      6063:   trying file=/usr/lib/i386-linux-gnu/sse2/libblas.so.3
      6063:   trying file=/usr/lib/i386-linux-gnu/libblas.so.3
      6063:   trying file=/usr/lib/jvm/java-7-openjdk-i386/jre/lib/i386/client/libblas.so.3
      6063:   trying file=/home/zheyuan/Desktop/dgemm/libblas.so.3
      6063: calling init: /home/zheyuan/Desktop/dgemm/libblas.so.3
      6063: calling fini: /home/zheyuan/Desktop/dgemm/libblas.so.3 [0]
    

    すごい! R をだますことに成功しました。

    3. OpenBLAS を試す

    ~/Desktop/dgemm$ Rscript --default-packages=base --vanilla mmperf.R
    GFLOPs = 8.77
    

    これで、すべてが期待どおりに機能します!

    4. 1028 の設定を解除します (念のため)

    1032 の設定を解除することをお勧めします 使用後。

    ~/Desktop/dgemm$ unset LD_LIBRARY_PATH
    

    ********************

    解決策 2:

    ********************

    ここでは、環境変数 1042 を利用する別のソリューションを提供します。 ソリューション 1 に記載 . 1053 の使用 Cライブラリ1061の前であっても、他のプログラムの前に特定のライブラリをプログラムに強制的にロードするため、より「残忍」です。 !これは、Linux 開発における緊急のパッチ適用によく使用されます。

    元の投稿のパート 2 に示されているように 、共有 BLAS ライブラリ 1076 SONAME を持っています 1089 . SONAME は動的ライブラリ ローダーが実行時に検索する内部名であるため、1097 へのシンボリック リンクを作成する必要があります。 このSONAMEで :

    ~/Desktop/dgemm$ ln -sf libopenblas.so libopenblas.so.0
    

    次にエクスポートします:

    ~/Desktop/dgemm$ export LD_PRELOAD=$(pwd)/libopenblas.so.0
    

    フルパス 11041111 にフィードする必要があります 1121 の場合でも、ロードが成功した場合 1135 の下にあります .

    1141 を起動します 1152までに何が起こるかを確認してください :

    ~/Desktop/dgemm$ LD_DEBUG=libs Rscript --default-packages=base --vanilla /dev/null |& grep blas
      4860: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
      4860: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
      4865: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
      4868: calling fini: /home/zheyuan/Desktop/dgemm/libopenblas.so [0]
      4870: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
      4869: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
      4867: calling fini: /home/zheyuan/Desktop/dgemm/libopenblas.so [0]
      4860: find library=libblas.so.3 [0]; searching
      4860:   trying file=/usr/lib/R/lib/libblas.so.3
      4860:   trying file=/usr/lib/i386-linux-gnu/i686/sse2/libblas.so.3
      4860:   trying file=/usr/lib/i386-linux-gnu/i686/cmov/libblas.so.3
      4860:   trying file=/usr/lib/i386-linux-gnu/i686/libblas.so.3
      4860:   trying file=/usr/lib/i386-linux-gnu/sse2/libblas.so.3
      4860:   trying file=/usr/lib/i386-linux-gnu/libblas.so.3
      4860:   trying file=/usr/lib/jvm/java-7-openjdk-i386/jre/lib/i386/client/libblas.so.3
      4860:   trying file=/usr/lib/libblas.so.3
      4860: calling init: /usr/lib/libblas.so.3
      4860: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
      4874: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
      4876: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
      4860: calling fini: /home/zheyuan/Desktop/dgemm/libopenblas.so [0]
      4860: calling fini: /usr/lib/libblas.so.3 [0]
    

    ソリューション 1 で見たものとの比較 独自のバージョンの 1164 で R をごまかす 、私たちはそれを見ることができます

    • 1174 最初に読み込まれるため、1180 によって最初に検出されます;
    • 1193以降 1208 が見つかりました 1218 の検索と読み込みを続けます .ただし、これは「先着順」では効果がありません 元の回答で説明されているルール

    よし、すべて動作するので、1223 をテストします プログラム:

    ~/Desktop/dgemm$ Rscript --default-packages=base --vanilla mmperf.R
    GFLOPs = 9.62
    

    結果の 9.62 は、以前の解で見た 8.77 よりも大きいのは、単なる偶然です。 OpenBLAS を使用するためのテストとして、より正確な結果を得るために実験を何度も実行することはありません。

    そしていつものように、最後に環境変数を設定解除します:

    ~/Desktop/dgemm$ unset LD_PRELOAD
    

    Linux
    1. パスワードプロンプトなしでルートとして特定のプログラムを実行する方法は?

    2. ルートプロパティなしでコマンドを実行する方法は?

    3. sudo なしで ifconfig を実行する

    1. RSA キーを使用してパスワードなしの ssh をセットアップする方法

    2. root アクセス権を持つ Linux で指定されたユーザーのパーミッションを知るにはどうすればよいですか?

    3. root アクセスなしで 1024 未満のポートにバインドする

    1. パスワードなしで Linux root ユーザー mysql root アクセスを許可する

    2. root として実行すると Systemctl コマンドがタイムアウトする

    3. root アクセスで実行するように Systemd サービスを構成する