Kapan harus menggunakan perpustakaan dinamis vs statis

Jawaban:

299

Perpustakaan statis menambah ukuran kode dalam biner Anda. Mereka selalu dimuat dan versi kode apa pun yang Anda kompilasi adalah versi kode yang akan dijalankan.

Perpustakaan dinamis disimpan dan diversi secara terpisah. Versi perpustakaan dinamis mungkin dimuat yang bukan yang asli yang dikirimkan dengan kode Anda jika pembaruan dianggap biner kompatibel dengan versi asli.

Selain itu perpustakaan dinamis tidak selalu dimuat - biasanya dimuat saat pertama kali dipanggil - dan dapat dibagi di antara komponen yang menggunakan perpustakaan yang sama (beberapa data dimuat, satu memuat kode).

Perpustakaan dinamis dianggap sebagai pendekatan yang lebih baik sebagian besar waktu, tetapi awalnya mereka memiliki kelemahan besar (google DLL hell), yang semuanya telah dieliminasi oleh OS Windows yang lebih baru (khususnya Windows XP).

Orion Adrian
sumber
71
Pada Windows / Mac (tidak ada manajer paket) benar-benar tidak ada alasan yang baik untuk menggunakan pustaka dinamis melalui statis. Karena Windows DLL tidak dapat dipindahkan, berbagi kode seringkali tidak berfungsi (dan biasanya setiap aplikasi mengirimkan dan menggunakan versi pustaka mereka sendiri). Satu-satunya manfaat nyata adalah lebih mudah memperbarui perpustakaan.
Zifre
5
di mac, saya menggunakan banyak perpustakaan dinamis. misalnya, mac os x memiliki sqlite3 embed. saya membuat program yang memiliki fitur database sqlite3 untuk penyimpanan kinerja. Namun, karena jarang digunakan penghubung dinamis menghemat waktu kompilasi, membuat pengujian lebih mudah / lebih cepat, tetapi, jika saya membuat versi rilis, saya pikir saya akan selalu menggunakan perpustakaan statis jika terjadi masalah kompatibilitas
ReachConnection
6
@Zifre: relocatable = dapat dimuat di alamat virtual yang berbeda. DLL tentu mendukung ini.
dma_k
20
@dma_k: DLL Windows dapat dimuat di alamat yang berbeda, tetapi hanya karena penghubung menyalin semua kode dan mengubah nomor alamat. Dengan objek yang dibagikan, semua referensi alamat bersifat relatif, sehingga beberapa proses dapat berbagi memori yang sama untuk objek yang dibagikan. Dengan kata lain, pada Windows, DLL 1 MB digunakan oleh 3 program = 3 MB. Di Linux, MB A SO digunakan oleh 3 program = 1 MB.
Zifre
7
Baik Windows dan Linux memiliki konsep relokasi load-timte dari pustaka bersama eli.thegreenplace.net/2011/08/08/25. Hal terbesar yang memungkinkan Position Independent Code bukan sesuatu yang istimewa untuk Linux, alih-alih pengalamatan relatif dengan RIP menambahkan dengan set instruksi x64; baik Windows dan Linux dapat menggunakan pengalamatan RIP relatif mengurangi jumlah perbaikan saat memindahkan pustaka.
clemahieu
194

Yang lain cukup menjelaskan apa itu perpustakaan statis, tetapi saya ingin menunjukkan beberapa peringatan menggunakan perpustakaan statis, setidaknya di Windows:

  • Lajang: Jika sesuatu harus bersifat global / statis dan unik, berhati-hatilah untuk meletakkannya di perpustakaan statis. Jika beberapa DLL ditautkan dengan perpustakaan statis itu, mereka masing-masing akan mendapatkan salinan singleton mereka sendiri. Namun, jika aplikasi Anda adalah EXE tunggal tanpa DLL khusus, ini mungkin tidak menjadi masalah.

  • Penghapusan kode yang tidak direferensikan : Ketika Anda menautkan ke perpustakaan statis, hanya bagian-bagian dari perpustakaan statis yang dirujuk oleh DLL / EXE Anda yang akan ditautkan ke DLL / EXE Anda.

    Misalnya, jika hanya mylib.libberisi a.objdan b.objdan DLL / EXE Anda hanya mereferensikan fungsi atau variabel a.obj, keseluruhannya b.objakan dibuang oleh tautan. Jika b.objberisi objek global / statis, konstruktor dan penghancurnya tidak akan dieksekusi. Jika konstruktor / destruktor tersebut memiliki efek samping, Anda mungkin kecewa dengan ketidakhadiran mereka.

    Demikian juga, jika pustaka statis berisi titik masuk khusus Anda mungkin perlu berhati-hati bahwa mereka benar-benar disertakan. Contoh dari ini dalam pemrograman tertanam (oke, bukan Windows) akan menjadi pengendali interupsi yang ditandai sebagai alamat tertentu. Anda juga harus menandai interrupt handler sebagai titik masuk untuk memastikannya tidak dibuang.

    Konsekuensi lain dari ini adalah bahwa pustaka statis dapat berisi file objek yang benar-benar tidak dapat digunakan karena referensi yang tidak terselesaikan, tetapi itu tidak akan menyebabkan kesalahan linker sampai Anda mereferensikan fungsi atau variabel dari file objek tersebut. Ini mungkin terjadi lama setelah perpustakaan ditulis.

  • Simbol debug: Anda mungkin ingin PDB terpisah untuk setiap pustaka statis, atau Anda mungkin ingin simbol debug ditempatkan di file objek sehingga mereka bisa digulirkan ke PDB untuk DLL / EXE. Dokumentasi Visual C ++ menjelaskan opsi yang diperlukan .

  • RTTI: Anda mungkin berakhir dengan banyak type_infoobjek untuk kelas yang sama jika Anda menautkan pustaka statis tunggal ke beberapa DLL. Jika program Anda menganggap bahwa itu type_infoadalah data "tunggal" dan penggunaan &typeid()atau type_info::before(), Anda mungkin mendapatkan hasil yang tidak diinginkan dan mengejutkan.

bk1e
sumber
23
Adapun poin tentang lajang, jangan lupa bahwa DLL mungkin dimuat beberapa kali (versi yang sama atau versi mulitple) dan masih belum ada jaminan tunggal.
Orion Adrian
Poin tambahan tentang penghapusan kode yang tidak direferensikan: Panggilan ke DLL juga memerlukan panggilan aktual untuk memaksa pemuatan DLL yang direferensikan. Menambahkannya sebagai referensi, tetapi tidak termasuk panggilan apa pun yang referensi itu masih akan memberi Anda hasil yang sama dengan memiliki perpustakaan statis yang tidak memanggil apa pun. Satu-satunya perbedaan adalah apa yang sebenarnya dikirimkan. Dalam kedua kasus, konstruktor statis dan destruktor tidak menyala.
Orion Adrian
@ bk1e Itu seharusnya tidak terjadi. .a akan selalu berisi semua simbol yang dibangunnya. Saat terhubung secara statis ke aplikasi Anda, ya hanya simbol-simbol yang digunakan yang akan ditautkan.
Miles Rout
62

Lib adalah unit kode yang dibundel dalam aplikasi Anda yang dapat dieksekusi.

Dll adalah unit mandiri dari kode yang dapat dieksekusi. Itu dimuat dalam proses hanya ketika panggilan dibuat ke dalam kode itu. Dll dapat digunakan oleh banyak aplikasi dan dimuat dalam banyak proses, sementara hanya memiliki satu salinan kode pada hard drive.

Kelebihan Dll : dapat digunakan untuk menggunakan kembali / berbagi kode antara beberapa produk; memuat dalam memori proses sesuai permintaan dan dapat dibongkar saat tidak diperlukan; dapat ditingkatkan secara terpisah dari sisa program.

Kontra Dll : dampak kinerja pemuatan dll dan rebasing kode; masalah versi ("dll neraka")

Pro lib : tidak ada dampak kinerja karena kode selalu dimuat dalam proses dan tidak diubah; tidak ada masalah versi.

Lib cons : executable / process "bloat" - semua kode ada di executable Anda dan dimuat saat proses dimulai; no reuse / sharing - setiap produk memiliki salinan kodenya sendiri.

Franci Penov
sumber
Rebasing juga dapat dilakukan saat build menggunakan rebase.exe atau dengan melewatkan opsi / BASE ke link.exe. Apakah ini efektif tergantung pada apakah ada konflik ruang alamat yang tidak terduga saat runtime.
bk1e
24

Selain implikasi teknis dari pustaka statis vs dinamis (file statis membundel semuanya dalam satu pustaka biner vs dinamis yang memungkinkan berbagi kode di antara beberapa executable yang berbeda), ada implikasi hukumnya .

Misalnya, jika Anda menggunakan kode berlisensi LGPL dan Anda menautkan secara statis terhadap perpustakaan LGPL (dan dengan demikian membuat satu biner besar), kode Anda secara otomatis menjadi Open Source (kode bebas kebebasan) kode LGPL. Jika Anda menautkan ke objek yang dibagikan, maka Anda hanya perlu LGPL perbaikan / perbaikan bug yang Anda buat ke perpustakaan LGPL itu sendiri.

Ini menjadi masalah yang jauh lebih penting jika Anda memutuskan bagaimana mengompilasi aplikasi seluler Anda misalnya (di Android Anda memiliki pilihan statis vs dinamis, di iOS Anda tidak - selalu statis).

rburhum
sumber
23

Program C ++ dibangun dalam dua fase

  1. Kompilasi - menghasilkan kode objek (.obj)
  2. Menautkan - menghasilkan kode yang dapat dieksekusi (.exe atau .dll)

Pustaka statis (.lib) hanyalah kumpulan file .obj dan karenanya bukan program yang lengkap. Itu belum mengalami tahap kedua (menghubungkan) membangun program. DLL, di sisi lain, seperti exe dan karenanya adalah program yang lengkap.

Jika Anda membangun pustaka statis, itu belum ditautkan dan oleh karena itu konsumen pustaka statis Anda harus menggunakan kompiler yang sama dengan yang Anda gunakan (jika Anda menggunakan g ++, mereka harus menggunakan g ++).

Jika Anda membangun dll (dan membuatnya dengan benar ), Anda telah membangun program lengkap yang dapat digunakan oleh semua konsumen, tidak peduli kompiler mana yang mereka gunakan. Ada beberapa batasan meskipun, pada ekspor dari dll, jika kompatibilitas kompiler silang diinginkan.

tcb
sumber
1
Ini berita baru buat saya. Pembatasan apa yang ada dengan kompiler silang saat menggunakan DLL? Memiliki programmer yang membangun tanpa membutuhkan toolchain yang sama sepertinya merupakan nilai tambah yang besar untuk DLL
Dan
1
Jawaban ini informatif. Menambahkan peringatan kecil: consumers of your static library will have to use the same compiler that you usedjika perpustakaan statis menggunakan perpustakaan C ++, seperti #include <iostream>.
truthadjustr
seseorang tidak dapat mengkonsumsi c ++ dll kecuali compiler yang sama digunakan (karena tidak ada standar c ++ abi, simbol-simbol hancur dalam cara yang berbeda). Modul dll dan klien harus menggunakan kompiler yang sama dan pengaturan build yang sama
kcris
19

Membuat perpustakaan statis

$$:~/static [32]> cat foo.c
#include<stdio.h>
void foo()
{
printf("\nhello world\n");
}
$$:~/static [33]> cat foo.h
#ifndef _H_FOO_H
#define _H_FOO_H

void foo();

#endif
$$:~/static [34]> cat foo2.c
#include<stdio.h>
void foo2()
{
printf("\nworld\n");
}
$$:~/static [35]> cat foo2.h
#ifndef _H_FOO2_H
#define _H_FOO2_H

void foo2();

#endif
$$:~/static [36]> cat hello.c
#include<foo.h>
#include<foo2.h>
void main()
{
foo();
foo2();
}
$$:~/static [37]> cat makefile
hello: hello.o libtest.a
        cc -o hello hello.o -L. -ltest
hello.o: hello.c
        cc -c hello.c -I`pwd`
libtest.a:foo.o foo2.o
        ar cr libtest.a foo.o foo2.o
foo.o:foo.c
        cc -c foo.c
foo2.o:foo.c
        cc -c foo2.c
clean:
        rm -f foo.o foo2.o libtest.a hello.o

$$:~/static [38]>

membuat perpustakaan yang dinamis

$$:~/dynamic [44]> cat foo.c
#include<stdio.h>
void foo()
{
printf("\nhello world\n");
}
$$:~/dynamic [45]> cat foo.h
#ifndef _H_FOO_H
#define _H_FOO_H

void foo();

#endif
$$:~/dynamic [46]> cat foo2.c
#include<stdio.h>
void foo2()
{
printf("\nworld\n");
}
$$:~/dynamic [47]> cat foo2.h
#ifndef _H_FOO2_H
#define _H_FOO2_H

void foo2();

#endif
$$:~/dynamic [48]> cat hello.c
#include<foo.h>
#include<foo2.h>
void main()
{
foo();
foo2();
}
$$:~/dynamic [49]> cat makefile
hello:hello.o libtest.sl
        cc -o hello hello.o -L`pwd` -ltest
hello.o:
        cc -c -b hello.c -I`pwd`
libtest.sl:foo.o foo2.o
        cc -G -b -o libtest.sl foo.o foo2.o
foo.o:foo.c
        cc -c -b foo.c
foo2.o:foo.c
        cc -c -b foo2.c
clean:
        rm -f libtest.sl foo.o foo

2.o hello.o
$$:~/dynamic [50]>
Vijay
sumber
13

Pustaka statis dikompilasi ke dalam klien. .Lib digunakan pada waktu kompilasi dan isi pustaka menjadi bagian dari executable yang dikonsumsi.

Pustaka dinamis dimuat saat runtime dan tidak dikompilasi ke dalam klien yang dapat dieksekusi. Pustaka dinamis lebih fleksibel karena banyak klien yang dapat dieksekusi dapat memuat DLL dan memanfaatkan fungsinya. Ini juga menjaga ukuran keseluruhan dan rawatan kode klien Anda ke minimum.

Jordan Parmer
sumber
13

Anda harus memikirkan dengan hati-hati tentang perubahan dari waktu ke waktu, versi, stabilitas, kompatibilitas, dll.

Jika ada dua aplikasi yang menggunakan kode bersama, apakah Anda ingin memaksa aplikasi tersebut untuk berubah bersama, jika mereka harus kompatibel satu sama lain? Kemudian gunakan dll. Semua exe akan menggunakan kode yang sama.

Atau apakah Anda ingin mengisolasi mereka dari satu sama lain, sehingga Anda dapat mengubah satu dan yakin Anda tidak melanggar yang lain. Kemudian gunakan lib statis.

DLL neraka adalah ketika Anda mungkin HARUS menggunakan lib statis, tetapi Anda menggunakan dll sebagai gantinya, dan tidak semua ongkos comaptible dengan itu.

Corey Trager
sumber
9

Perpustakaan statis harus ditautkan ke executable akhir; itu menjadi bagian dari executable dan mengikutinya ke mana pun ia pergi. Pustaka dinamis dimuat setiap kali executable dieksekusi dan tetap terpisah dari executable sebagai file DLL.

Anda akan menggunakan DLL ketika Anda ingin dapat mengubah fungsi yang disediakan oleh perpustakaan tanpa harus menautkan kembali yang dapat dieksekusi (cukup ganti file DLL, tanpa harus mengganti file yang dapat dieksekusi).

Anda akan menggunakan perpustakaan statis setiap kali Anda tidak memiliki alasan untuk menggunakan perpustakaan dinamis.

spotcatbug
sumber
Anda mungkin juga menggunakan DLL ketika beberapa aplikasi lain menggunakan fungsi yang sama - ini dapat mengurangi jejak kaki.
Tim
Juga, memperluas konsep awal Anda, arsitektur "plug-in" di mana Anda ingin memperbolehkan fungsionalitas yang ditambahkan / tidak diketahui nanti tanpa harus membangun kembali atau merilis kembali hanya dapat dilakukan dengan perpustakaan dinamis.
Tim
8

Makalah Ulrich Drepper tentang " Bagaimana Menulis Perpustakaan yang Dibagi " juga merupakan sumber yang bagus yang merinci cara terbaik untuk memanfaatkan perpustakaan bersama, atau apa yang ia sebut sebagai "Objek Bersama Dinamis" (DSO). Ini lebih berfokus pada perpustakaan bersama dalam format biner ELF , tetapi beberapa diskusi juga cocok untuk Windows DLL.

Kosong
sumber
5

Untuk diskusi yang sangat baik tentang topik ini, baca artikel ini dari Sun.

Ini masuk ke semua manfaat termasuk bisa memasukkan perpustakaan interposing. Rincian lebih lanjut tentang penempatan dapat ditemukan di artikel ini di sini .

Rob Wells
sumber
4

Benar-benar trade off yang Anda buat (dalam proyek besar) adalah pada waktu pemuatan awal, perpustakaan akan dihubungkan pada satu waktu atau yang lain, keputusan yang harus dibuat adalah apakah tautan tersebut akan memakan waktu cukup lama yang dibutuhkan oleh kompilator untuk menggigit peluru dan melakukannya di depan, atau dapatkah penghubung dinamis melakukannya pada waktu pengambilan.

pfranza
sumber
3

Jika pustaka Anda akan dibagikan di antara beberapa file yang dapat dieksekusi, seringkali masuk akal untuk membuatnya dinamis untuk mengurangi ukuran file yang dapat dieksekusi. Kalau tidak, pasti membuatnya statis.

Ada beberapa kelemahan menggunakan dll. Ada overhead tambahan untuk memuat dan menurunkannya. Ada juga ketergantungan tambahan. Jika Anda mengubah dll untuk membuatnya tidak kompatibel dengan eksekutif Anda, mereka akan berhenti bekerja. Di sisi lain, jika Anda mengubah pustaka statis, file executable Anda yang dikompilasi menggunakan versi lama tidak akan terpengaruh.

Dima
sumber
3

Jika pustaka statis, maka pada saat tautan kode terhubung dengan executable Anda. Ini membuat executable Anda lebih besar (daripada jika Anda pergi rute dinamis).

Jika pustaka itu dinamis maka pada tautan waktu referensi ke metode yang diperlukan dibangun untuk dieksekusi Anda. Ini berarti Anda harus mengirimkan perpustakaan Anda yang dapat dieksekusi dan dinamis. Anda juga harus mempertimbangkan apakah akses bersama ke kode di perpustakaan aman, lebih disukai memuat alamat di antara hal-hal lain.

Jika Anda bisa hidup dengan perpustakaan statis, pergi dengan perpustakaan statis.

Seb Rose
sumber
3

Kami menggunakan banyak DLL (> 100) dalam proyek kami. DLL ini memiliki ketergantungan satu sama lain dan oleh karena itu kami memilih pengaturan tautan dinamis. Namun memiliki kelemahan sebagai berikut:

  • startup lambat (> 10 detik)
  • DLL harus diversi, karena windows memuat modul pada keunikan nama. Komponen tertulis sendiri jika tidak akan mendapatkan versi yang salah dari DLL (yaitu yang sudah dimuat bukannya set didistribusikan sendiri)
  • Pengoptimal hanya dapat mengoptimalkan dalam batas DLL. Misalnya pengoptimal mencoba untuk menempatkan data dan kode yang sering digunakan di samping satu sama lain, tetapi ini tidak akan berfungsi melintasi batas-batas DLL

Mungkin setup yang lebih baik adalah menjadikan semuanya perpustakaan statis (dan karena itu Anda hanya memiliki satu executable). Ini hanya berfungsi jika tidak ada duplikasi kode yang terjadi. Sebuah tes tampaknya mendukung asumsi ini, tetapi saya tidak dapat menemukan kutipan MSDN resmi. Jadi misalnya membuat 1 exe dengan:

  • exe menggunakan shared_lib1, shared_lib2
  • shared_lib1 gunakan shared_lib2
  • shared_lib2

Kode dan variabel shared_lib2 harus ada di final yang dapat dieksekusi hanya sekali. Adakah yang bisa mendukung pertanyaan ini?

gast128
sumber
Bukankah Anda seharusnya menggunakan arahan pra-kompiler dengan cara tertentu untuk menghindari duplikasi kode?
Paceman
Afaiac precompiling hanya bekerja pada basis per modul (exe / dll / lib). Pra-kompilasi terutama dimaksudkan untuk mempercepat kompilasi meskipun juga mencegah beberapa inklusi dalam unit kompilasi. Namun termasuk penjaga adalah cara yang lebih baik untuk mencapai efek ini.
gast128
2

Pustaka statis adalah arsip yang berisi kode objek untuk pustaka, ketika ditautkan ke aplikasi yang dikompilasi ke dalam kode yang dapat dieksekusi. Pustaka bersama berbeda karena tidak dikompilasi ke dalam executable. Sebagai gantinya linker dinamis mencari beberapa direktori untuk mencari perpustakaan yang dibutuhkan, lalu memuatnya ke dalam memori. Lebih dari satu yang dapat dieksekusi dapat menggunakan pustaka bersama yang sama pada saat yang sama, sehingga mengurangi penggunaan memori dan ukuran yang dapat dieksekusi. Namun, ada lebih banyak file untuk didistribusikan dengan executable. Anda perlu memastikan bahwa perpustakaan diinstal ke sistem penggunaan di suatu tempat di mana linker dapat menemukannya, tautan statis menghilangkan masalah ini tetapi menghasilkan file yang dapat dieksekusi lebih besar.

Terence Simpson
sumber
2

Jika pekerjaan Anda pada proyek yang disematkan atau platform khusus perpustakaan statis adalah satu-satunya cara untuk pergi, sering kali mereka tidak terlalu repot untuk dikompilasi ke dalam aplikasi Anda. Juga memiliki proyek dan makefile yang mencakup semuanya membuat hidup lebih bahagia.

Robert Gould
sumber
2

Saya akan memberikan aturan umum bahwa jika Anda memiliki basis kode yang besar, semua dibangun di atas perpustakaan tingkat rendah (misalnya kerangka kerja Utils atau Gui), yang ingin Anda partisi menjadi perpustakaan yang lebih mudah dikelola kemudian menjadikannya perpustakaan statis. Pustaka dinamis tidak benar-benar membelikan Anda apa-apa dan ada lebih sedikit kejutan - hanya akan ada satu contoh lajang misalnya.

Jika Anda memiliki pustaka yang sepenuhnya terpisah dengan basis kode lainnya (mis. Pustaka pihak ketiga) maka pertimbangkan untuk menjadikannya sebagai dll. Jika pustaka adalah LGPL, Anda mungkin harus menggunakan dll karena kondisi lisensi.

the_mandrill
sumber