Linux 入門 ---動的ライブラリと静的ライブラリのロード

動的ライブラリと静的ライブラリがあるのはなぜですか?

シナリオをシミュレーションしてみます。まず、2 つのヘッダー ファイルを作成します。
ここに画像の説明を挿入します
ファイル名から、add.h ヘッダー ファイルには加算関数の宣言が格納され、sub.h ヘッダー ファイルには減算関数の宣言が格納されていることがわかります。ヘッダファイルがあるので、対応するソースファイルがあるはずなので、ここにさらに2つのソースファイルを作成します:
ここに画像の説明を挿入します
次に、ヘッダファイルに関数の宣言を追加し、ソースファイルに関数の実装を追加します。以下のとおりであります:

//add.h
#pragma once                                                                                                                                             
#include<stdio.h>                                                                                                                                                                                                       
extern int add(int x ,int y); 
   
//add.c
#include"add.h"                                                                                                                                          
int add(int x ,int y)    
{
    
        
     printf("enter Add func, %d + %d = ?\n", x, y);    
     return x+y;    
}
  
//sub.h
#pragma once                                                                                                                                           
#include<stdio.h>                                  
extern int sub(int x ,int y);    

//sub.c
#include"sub.h"      
int sub(int x ,int y)    
{
    
        
    printf("enter Sub func, %d - %d = ?\n", x, y);    
    return x-y;                                                                                                                         
} 

これら 4 つのファイルを用意したら、別のファイルを作成し、これら 2 つのソース ファイルの内容を使用していくつかの関数を実装できます。そのため、ここでは main.c ファイルを作成します。 main.c のコードは次のとおりです
ここに画像の説明を挿入します

#include"add.h"    
#include"sub.h"    
int main()    
{
    
        
    int x =20;    
    int y=10;    
    printf("result: %d\n",add(x,y));    
    printf("result: %d\n",sub(x,y));                                                                                                    
    return 0;                                                       
}  

次に、gcc コマンドを使用して、下の図のような test という名前の実行可能プログラムを生成します。
ここに画像の説明を挿入します
この実行可能プログラムを実行すると、次のようなシーンが表示されます。
ここに画像の説明を挿入します
これは、コードの実装が正しいことを示しています。これまでの研究で、-c オプションを使用してバイナリ ファイルを生成できること、また、バイナリ ファイルを使用して次の操作などの実行可能プログラムを生成できることがわかっています。 そして、実行可能プログラムの実行結果はまったく同じです
ここに画像の説明を挿入します

ここに画像の説明を挿入します
バイナリ ファイルの内容は一般の人には理解できないため、この機能を使用して機密リソースを配布することができます。たとえば、使用してほしいものがあるが、基礎となる実装ロジックは知られたくない場合に使用できます。これなら使えるよ 実装するバイナリファイル:
ここに画像の説明を挿入します
違和感ないですか、機密関数として使えます
ここに画像の説明を挿入します
関数の作成者をフォルダ1、関数の利用者をフォルダ2とすると、フォルダ2この関数を使用したい場合は、この関数を知っている必要があります。実装方法は、add.o および sub.o ファイルをフォルダー 2 フォルダーにコピーし、main.c ファイルをフォルダー 2 フォルダーに割り当てる必要があります。例、次の図: 次に、
ここに画像の説明を挿入します
folder2 フォルダーにいます。 実行可能プログラムを再度生成したい場合、次のエラーがポップアップ表示されます。
ここに画像の説明を挿入します
ここで、add.h ファイルが見つからなかったことがわかります。これは、他の人にファイルを提供するときに、この方法では、他の人に .o ファイルだけでなく .h ファイルも提供する必要があります。
ここに画像の説明を挿入します
もう一度来てください。folder2 フォルダに移動して実行可能ファイルを生成すると、成功したことがわかります。
ここに画像の説明を挿入します
そして、それを実行すると、結果が前とまったく同じであることがわかります。
ここに画像の説明を挿入します
つまり、将来、他の人が私たちが実装した関数を使用したい場合は、.o ファイル (メソッドの実装) と .h ファイル (使用されるメソッド) を提供できますが、ここで問題が発生します。ここで転送して使用するのは非常に面倒なので、すべての .o ファイルをライブラリにパッケージ化して、関数やメソッドを転送するときに、相手にライブラリ ファイル、ライブラリを渡すだけで済むようにします。ファイル 複数の .o ファイルで構成されるファイルです。さまざまなツールやメソッドを使用して生成されたライブラリは、静的ライブラリと動的ライブラリと呼ばれます。ライブラリの本質は .o ファイルの集合です。次に、その方法を見てみましょう。静的ライブラリと動的ライブラリの生成、およびこれら 2 つのライブラリの違いについて説明します。

静的ライブラリの実装

ar コマンドを使用して静的ライブラリを作成します。 ar のプロセス全体はメソッドを使用してアーカイブされ、ar -rc 生成的库文件名 源文件名静的ライブラリを生成できます。たとえば、現在のパス内のファイルは次のとおりです
ここに画像の説明を挿入します
。 Makefile の内容と静的ライブラリの命名規則 lib の先頭は .a で終わるため、ライブラリの名前は libmymath.a になります。ライブラリ ファイルは add.o ファイルと sub.o ファイルに依存します。実装方法が のar -rc libmymath.a add.o sub.o場合、makefile の最初の命令の内容は次のようになります。

libmymath.a:add.o sub.o    
    ar -rc $@ $^   

ただし、現在のパスには add.o ファイルと sub.o ファイルがないため、これら 2 つのファイルの命令と対応する実装メソッドを追加する必要があるため、ここでのコードは次のようになります。

libmymath.a:add.o sub.o    
    ar -rc $@ $^
add.o:add.c    
    gcc -c  add.c    
sub.o:sub.c    
    gcc -c  sub.c

前の例から、関数を転送する場合は、.o ファイルだけでなく、対応する .h ファイルも転送する必要があることがわかっているため、ここでは転送の便宜のためにフォルダーを作成します。フォルダー内には、lib という名前のファイルがすべてのライブラリ ファイルを格納するために使用され、include という名前のファイルがすべてのヘッダー ファイルを格納するために使用されるため、ここで出力命令を追加します。この命令の実装方法は、一連のフォルダーを作成し、すべての .o ファイルを 1 つに入れる フォルダー内のすべての .h ファイルを別のフォルダーに入れると、ここでのコードは次のようになります。

libmymath.a:add.o sub.o    
    ar -rc $@ $^
add.o:add.c    
    gcc -c  add.c    
sub.o:sub.c    
    gcc -c  sub.c
.PHONY:output  
output:
    mkdir -p mylib/include
    mkdir -p mylib/lib 
    cp -f *.h mylib/include
    cp -f *.a mylib/lib  

最後のコマンドは clean コマンドです。このコマンドは、現在のパスにあるすべての .o ファイルとライブラリ ファイルを削除します。メイクファイルの完全なコードは次のとおりです。

libmymath.a:add.o sub.o    
    ar -rc $@ $^
add.o:add.c    
    gcc -c  add.c    
sub.o:sub.c    
    gcc -c  sub.c
.PHONY:output  
output:
    mkdir -p mylib/include
    mkdir -p mylib/lib 
    cp -f *.h mylib/include
    cp -f *.a mylib/lib 
.PHONY:clean
clean:
    rm -f *.o libmymath.a

make コマンドを入力すると、現在のパスにいくつかのファイルが表示されることがわかります。
ここに画像の説明を挿入します

libmymath.a ファイルをファイルすると、このファイルがアーカイブ ファイルであることがわかります。
ここに画像の説明を挿入します
ライブラリ ファイルとヘッダー ファイルの転送を容易にするために、make Output コマンドを入力してフォルダー セットを生成する必要もあります。

次に、tar コマンドを使用して、生成されたフォルダーをパッケージ化し、パッケージ化されたファイルを yum ソースに置くことで、他の人が yum を使用してダウンロードしたり、Web サイトにソフトウェアを置いて他の人がダウンロードできるようになります。
ここに画像の説明を挿入します

ダウンロードが完了すると、パッケージファイルは上位ディレクトリのfolder3ディレクトリに配置されます。
ここに画像の説明を挿入します

ダウンロード後に最初に行うことは、パッケージ化されたファイルを解凍して内部ファイルを取得することです。
ここに画像の説明を挿入します

次に、このフォルダー内のすべてのヘッダー ファイルをシステムのヘッダー ファイル ディレクトリにインストールします (つまり、/usr/include/すべてのライブラリ ファイルをシステムのライブラリ ファイルにコピーします)。その後/lib64/、上記のコピー プロセスがインストールになります。いわゆるインストールは、ターゲットへのインストールです。ファイルは、システムによって指定されたパスにコピーされます。システムは、このパスを通じてこれらのファイルを見つけることができます。次に、ライブラリ ファイルとヘッダー ファイルがあります。論理的に言えば、folder3 フォルダー内のこれらの関数を使用して、いくつかの関数を実行できます。 main.c ファイルをfolder3 ファイルにコピーし、このファイルを使用して実行可能プログラムを生成します。たとえば、次の操作:
ここに画像の説明を挿入します

しかし、実行可能プログラムを生成するとき、問題が発生します: コンパイラーがヘッダー ファイルを見つけられないということです。コンパイラーがヘッダー ファイルを検索するとき、デフォルトでは 2 つの場所で検索することがわかっています: 1 つは現在のパスにあり、もう 1 つはヘッダー ファイルです。はシステムで指定されたパスにあります。検索の際、ヘッダーファイルとライブラリファイルをシステムパスにダウンロードしていないため、現在の検索方法は現在のパス下で検索します。ヘッダーファイルはフォルダー3フォルダーにありますが、ヘッダー ファイルは深すぎて main とは関係ありません。c ファイルは同じレベルにあるため、ターゲット ファイルが見つかりません。ここでの問題を解決するには、ヘッダー ファイルを検索する場所をコンパイラーに指示する必要があります。 -I オプションを追加して、検索パスを指定する必要があります。たとえば、次の操作です。
ここに画像の説明を挿入します
エラーはここでも報告されますが、エラーの理由はリンク エラーです。つまり、ヘッダー ファイルは見つかったが、ライブラリは見つかりませんでした。ファイルが見つかりませんが、前にコードを入力したときに、ライブラリ ファイルの場所をコンパイラに伝えませんでした。では、なぜコンパイルして渡すことができるのでしょうか? C および C++ ライブラリはシステムのデフォルト パスにも保存されるため、つまり、ライブラリ ファイルは lib64 および /usr/lib に保存されるため、-L オプションとライブラリ ファイルへのパスを追加する必要があります。ただし、
ここに画像の説明を挿入します
実行後もエラーが報告されます。これは、サードパーティのライブラリをリンクするときに、システムがサードパーティのライブラリの名前を認識していないため、ライブラリの名前を指定する必要があるためです。しかし、前にコードを書いたとき、ライブラリの名前を指定しなかったのに、なぜ正常に実行できるのでしょうか? 理由は非常に単純で、指定しなくても普通に渡せるのではなく、コンパイラが自動的に入力してくれるからです。なぜ C++ のコンパイラは g++ と呼ばれ、C 言語のコンパイラは gcc と呼ばれるのでしょうか? これらのコンパイラは次のことを知っているからです。あなたが作成したプログラムにはどのライブラリがありませんが、サードパーティのライブラリの場合はわかりません。そのため、 -l に加えてライブラリの名前を追加します。ここでの名前はプレフィックスとサフィックスを削除する必要があります。たとえば、次の操作:
ここに画像の説明を挿入します
実行可能プログラムを生成するときに、ここでの接続がダイナミック リンクであることもわかります。リンク ライブラリを表示すると、作成したライブラリは接続されていません。これは、gcc のデフォルトがダイナミック リンクであり、実行可能ファイルの生成時に複数のライブラリがリンクされるためです。したがって、ここで問題が発生します。Gcc のデフォルトは動的リンクですが、これは推奨プロセスです。特定のライブラリが動的か静的かは、動的ライブラリと静的リンクのどちらを提供するかによって決まります。動的ライブラリと静的ライブラリの両方がここで選択する権利は gcc にあります。ライブラリが 100 個あり、そのうち 70 個が動的ライブラリ、30 個が静的ライブラリである場合、リンク時にライブラリは 1 つずつリンクされますが、ライブラリが 1 つある限り、ライブラリは 1 つずつリンクされます。つまり動的にリンクされます 実行可能プログラムは動的にリンクされます。ここで実行プログラムを生成するのはやはり面倒なので、命令数を減らしたい場合は、ヘッダファイルをすべて/usr/include/にコピーし、ライブラリファイルを/lib64/にコピーする方法がとられます。この動作では、インストールプロセス中にターゲットファイルが指定されたパスにコピーされますが、どのライブラリを使用するかわからないため、実行時にエラーが報告されます。そのため、コピーが完了したとしても、どのライブラリを使用しているかをプログラムに伝えます。

ダイナミックライブラリの実現

ダイナミック ライブラリも、以前と同様に gcc を使用して .o で終わるバイナリ ファイルを生成しますが、ここでバイナリ ファイルを生成するときは、-fPIC オプションを追加する必要があります。このオプションは、.o ファイルの生成時に位置に依存しないコードを生成します。たとえば、次のようにします。 操作:
ここに画像の説明を挿入します
次に、すべての .o ファイルをアーカイブします。ただし、ここでのアーカイブ手順は、静的ライブラリの手順とは異なります。動的ライブラリは、gcc 命令と -shared オプションを使用してアーカイブされます。たとえば、次の操作:これにより
ここに画像の説明を挿入します
、「ダイナミック ライブラリの場合は、同じ理由で別のフォルダーを作成します。フォルダー内には 2 つの小さなフォルダーがあり、1 つはヘッダー ファイルの保存用で、もう 1 つはライブラリ ファイルの保存用です。次に、この大きなファイルをフォルダー 3 フォルダーにコピーします。たとえば、次の操作:
ここに画像の説明を挿入します
ここに画像の説明を挿入します

同じ原則が適用され、実行可能プログラムを生成するときに、ヘッダー ファイルが見つからないことが通知されるため、
ここに画像の説明を挿入します
-I オプションを追加して特定のパスを指定する必要があり、
ここに画像の説明を挿入します
その後、ライブラリの場所をコンパイラに通知する必要があります。ファイルが存在するため、 -L オプションを追加する必要があります。 :
ここに画像の説明を挿入します
ライブラリの場所を見つけるだけでは十分ではなく、ライブラリの名前をコンパイラに伝える必要があるため、 -l オプションを追加する必要がありますライブラリの名前を指定します:
ここに画像の説明を挿入します
ここで実行可能プログラムが実際に生成されていることがわかりますが、プログラムを実行するとわかります。ここにはまだ問題があり、ldd コマンドを使用してライブラリの接続ライブラリを表示すると、このファイルを見ると、libmymath.so ダイナミック ライブラリがまったく接続されていないことがわかります。
ここに画像の説明を挿入します

ここでのエラーは、ライブラリがリンクされておらず、ライブラリが見つからないことを意味しますが、ライブラリの名前、ライブラリの場所、ヘッダファイルを伝えたのに、なぜ見つからないのでしょうか?理由は非常に簡単です。 . ここで言ったことは gcc に伝えるということです プログラムはコンパイルされてリンクされていますが、やはり gcc と関係があるのでしょうか?それは問題ではありません。プログラムは実行時にオペレーティング システムとシェルに依存する必要があるため、オペレーティング システムとシェルもライブラリの場所を知る必要があります。オペレーティング システムとシェルはシステム パスの下でのみ検索します。先ほど書き込んだファイルがそれらのパスの下にありません。そのため、ファイルが見つからないため、上記の問題が発生します。ここでの問題を解決するには、オペレーティング システムとシェルにダイナミック ライブラリを見つけさせる必要があります。その後、解決する方法は複数あります。ここで問題が発生します。 方法 1:
環境変数を変更する
環境変数 LD_LIBRARY_PATH には、オペレーティング システムのデフォルトの検索パスが記録されます。
ここに画像の説明を挿入します

ライブラリのパスを環境変数にインポートする限り、オペレーティング システムはパス内のライブラリを検索します。たとえば、次の操作: このようにして、ライブラリのパスが環境変数にインポートされ
ここに画像の説明を挿入します
ます, そして、上記のプログラムを実行します。問題がないことがわかります。
ここに画像の説明を挿入します
これが環境変数を変更する方法 1 です。この方法の問題点の 1 つは、ログを記録するたびに環境変数が更新されるため、永続的ではないことです。システムに組み込まれるため、このメソッドは永続的ではありません。

2 番目の方法: この方法をインストールするには、
ダイナミック ライブラリを /lib64 にコピーします。これにより、オペレーティング システムはシステム ライブラリから作成したライブラリを見つけることができますが、作成したライブラリは必ずしも安全ではないため、表示されません。ここではこの方法が実装されていますので、皆さんはこの方法を試さないでください。

3 番目の方法: 構成ファイルによる方法。
システム内に次のようなパスがあります: /etc/ld.so.conf.d/、このパスの下に多くの構成ファイルがあります:
ここに画像の説明を挿入します
オペレーティング システムはライブラリを検索するときに構成ファイル (.conf で終わるファイル) を照会するためです。したがって、「構成ファイルの作成」を渡して、オペレーティング システムに対応するダイナミック ライブラリを見つけさせることができます。そのため、ここではまず .conf で終わるファイルを作成し、次に新しく作成したファイルにライブラリのパスを入力します。その後、強制終了します。ファイルを保存し
ここに画像の説明を挿入します
、ldconfig コマンドを使用してすべての conf ファイルを更新します。これにより、作成したばかりのプログラムを永続的かつ通常通りに実行できるようになります。
ここに画像の説明を挿入します
これが方法 3 です。

4 番目の方法: ソフト リンク
プログラムがライブラリ ファイルを検索するとき、デフォルトでは現在のパス内で検索するため、プログラムが配置されているパスにソフト リンクを作成して、ダイナミック ライブラリ ファイルを指すようにすることができます。次の操作:
ここに画像の説明を挿入します
次に、プログラムを再度実行すると、正常に実行できることがわかります。
ここに画像の説明を挿入します

動的ライブラリと静的ライブラリのロード

静的ライブラリはロード処理を考慮せず、実行可能プログラムを生成する際に、該当するコードを直接プログラムにコピーし(printfを使用するコードは、ライブラリ内のprintfに関連するコードをコピーします)、その後プログラムを実行します。メモリにロードされます 複数のプログラムを静的にリンクすると冗長性が生じます ここで問題が発生します ライブラリのコードをプログラムにコピーします プログラムのどこにコピーされますか? ディスク内のプログラムには仮想地質空間が含まれており、ライブラリ内のコードはディスク上のコード領域にあるため、実行可能プログラムを生成する際にはライブラリ内のコードがプログラムのコード領域にコピーされ、したがって、プログラムは関数検索領域を検索するときにもコードを削除します。動的リンクとは、ダイナミック ライブラリ内の指定された関数のアドレスを実行可能プログラムに書き込むことです。printf 関数にもファイル内に対応するアドレスがあるため、そのアドレスを実行可能プログラムに書き込むと、プログラムはaddress 対応メソッド、このアドレスは何ですか? 以前、位置に依存しないコードと呼ばれるものがあると述べましたが、これは何を意味するのでしょうか? 人生の例に基づいて理解しましょう。ある道路に信号が 1 つだけあり、その信号から 100 メートル東にレストランがあるとします。このレストランを見つけたい場合は、信号を見つけるだけで済みます。この道です 信号さえ見つかれば 東に 100 メートル歩くとレストランが見つかります このようなアドレスを相対アドレスと呼びます プログラムにロードされる動的ライブラリのアドレスは相対アドレスですダイナミック ライブラリのアドレスは、開始アドレスを形成する 2 つのアドレスで構成されます。1 つは、ライブラリの開始アドレスを基準としたライブラリ内の関数のオフセット アドレスです。プログラムの開始がどのくらいであるかはわかりません。オペレーティング システムがプログラムを実行し、プログラムがライブラリ関数を使用する必要があることを検出すると、この関数に対応するダイナミック ライブラリがメモリにロードされ、そのライブラリがライブラリの共有領域にマッピングされます。ページテーブルを介して仮想メモリにアクセスし、最後にアドレス空間内のライブラリの開始アドレスが先頭に埋められるため、プログラムは共有領域上のライブラリ内の各関数の内容を開始アドレスに加えたものに従って見つけることができます。各関数の一意のオフセット、これがダイナミック ライブラリのロードです。一方、スタティック ライブラリは、ライブラリがロードされるときに比較的確実です。コードがメモリにロードされるとき、ライブラリ内のコードは決定されており、次のことが可能です。アドレスに従って直接検索される場合、これがダイナミック ライブラリのロードです。

おすすめ

転載: blog.csdn.net/qq_68695298/article/details/132638599