Apa yang dimaksud dengan 'terhubung secara statis' dan 'terhubung secara dinamis'?

229

Saya sering mendengar istilah 'terhubung secara statis' dan 'terhubung secara dinamis', sering kali mengacu pada kode yang ditulis dalam C , C ++ atau C # . Apa yang mereka, apa yang sebenarnya mereka bicarakan, dan apa yang mereka tautkan?

UnkwnTech
sumber

Jawaban:

445

Ada (dalam kebanyakan kasus, diskon kode ditafsirkan) dua tahap dalam mendapatkan dari kode sumber (apa yang Anda tulis) ke kode yang dapat dieksekusi (apa yang Anda jalankan).

Yang pertama adalah kompilasi yang mengubah kode sumber menjadi modul objek.

Yang kedua, menghubungkan, adalah apa yang menggabungkan modul objek bersama untuk membentuk suatu executable.

Perbedaan dibuat untuk, antara lain, memungkinkan perpustakaan pihak ketiga untuk dimasukkan dalam executable Anda tanpa Anda melihat kode sumbernya (seperti perpustakaan untuk akses database, komunikasi jaringan dan antarmuka pengguna grafis), atau untuk menyusun kode dalam bahasa yang berbeda ( C dan kode assembly misalnya) lalu menghubungkan semuanya.

Saat Anda menautkan file secara statis ke file yang dapat dieksekusi, konten file tersebut disertakan pada waktu tautan. Dengan kata lain, isi file secara fisik dimasukkan ke dalam executable yang akan Anda jalankan.

Ketika Anda menautkan secara dinamis , penunjuk ke file yang ditautkan (misalnya, nama file dari file tersebut) dimasukkan dalam file yang dapat dieksekusi dan isi dari file tersebut tidak termasuk pada waktu tautan. Hanya ketika Anda nanti menjalankan executable file-file yang terhubung secara dinamis ini dibeli dan mereka hanya membeli ke dalam salinan executable yang ada dalam memori, bukan yang ada di disk.

Ini pada dasarnya metode penautan yang ditangguhkan. Ada metode yang bahkan lebih ditangguhkan (disebut keterlambatan mengikat pada beberapa sistem) yang tidak akan membawa file yang terhubung secara dinamis sampai Anda benar-benar mencoba memanggil fungsi di dalamnya.

File yang terhubung secara statis 'dikunci' ke executable pada waktu tautan sehingga tidak pernah berubah. File yang terhubung secara dinamis yang dirujuk oleh yang dapat dieksekusi dapat berubah hanya dengan mengganti file pada disk.

Ini memungkinkan pembaruan fungsionalitas tanpa harus menautkan kembali kode; loader menghubungkan ulang setiap kali Anda menjalankannya.

Ini baik dan buruk - di satu sisi, memungkinkan pembaruan dan perbaikan bug yang lebih mudah, di sisi lain dapat menyebabkan program berhenti bekerja jika pembaruan tidak kompatibel - ini kadang-kadang bertanggung jawab atas "DLL neraka" yang ditakuti sebagian orang sebutkan bahwa aplikasi dapat rusak jika Anda mengganti pustaka yang terhubung secara dinamis dengan pustaka yang tidak kompatibel (pengembang yang seharusnya berharap akan diburu dan dihukum berat, omong-omong).


Sebagai contoh , mari kita lihat kasus pengguna yang mengkompilasi main.cfile mereka untuk tautan statis dan dinamis.

Phase     Static                    Dynamic
--------  ----------------------    ------------------------
          +---------+               +---------+
          | main.c  |               | main.c  |
          +---------+               +---------+
Compile........|.........................|...................
          +---------+ +---------+   +---------+ +--------+
          | main.o  | | crtlib  |   | main.o  | | crtimp |
          +---------+ +---------+   +---------+ +--------+
Link...........|..........|..............|...........|.......
               |          |              +-----------+
               |          |              |
          +---------+     |         +---------+ +--------+
          |  main   |-----+         |  main   | | crtdll |
          +---------+               +---------+ +--------+
Load/Run.......|.........................|..........|........
          +---------+               +---------+     |
          | main in |               | main in |-----+
          | memory  |               | memory  |
          +---------+               +---------+

Anda dapat melihat dalam kasus statis bahwa program utama dan pustaka runtime C dihubungkan bersama pada waktu tautan (oleh pengembang). Karena pengguna biasanya tidak dapat menautkan kembali yang dapat dieksekusi, mereka terjebak dengan perilaku perpustakaan.

Dalam kasus dinamis, program utama ditautkan dengan pustaka impor runtime C (sesuatu yang menyatakan apa yang ada di pustaka dinamis tetapi tidak benar-benar mendefinisikannya ). Ini memungkinkan tautan untuk menautkan meskipun kode sebenarnya tidak ada.

Kemudian, pada saat runtime, loader sistem operasi melakukan penautan yang terlambat dari program utama dengan DLL runtime C (pustaka tautan dinamis atau pustaka bersama atau nomenklatur lainnya).

Pemilik runtime C dapat menjatuhkan DLL baru kapan saja untuk memberikan pembaruan atau perbaikan bug. Seperti yang dinyatakan sebelumnya, ini memiliki kelebihan dan kekurangan.

paxdiablo
sumber
11
Tolong perbaiki saya jika saya salah, tetapi pada Windows, perangkat lunak cenderung menyertakan pustaka sendiri dengan instalasi, bahkan jika mereka terhubung secara dinamis. Pada banyak sistem Linux dengan manajer paket, banyak pustaka yang terhubung secara dinamis ("objek bersama") sebenarnya dibagi antara perangkat lunak.
Paul Fisher
6
@ PaulF: hal-hal seperti kontrol umum Windows, DirectX, .NET, dan lain-lain, banyak menggunakan aplikasi sedangkan di Linux, Anda cenderung menggunakan apt atau yum atau sesuatu seperti itu untuk mengelola dependensi - jadi Anda benar dalam pengertian itu . Menangkan Aplikasi yang mengirimkan kode mereka sendiri karena DLL cenderung tidak membagikannya.
paxdiablo
31
Ada tempat khusus yang disediakan di lingkaran kesembilan neraka bagi mereka yang memperbarui DLL mereka dan merusak kompatibilitas. Ya, jika antarmuka hilang atau dimodifikasi, maka tautan dinamis akan jatuh dalam tumpukan. Itu sebabnya tidak boleh dilakukan. Dengan segala cara, tambahkan function2 () ke DLL Anda, tetapi jangan ubah function () jika orang menggunakannya. Cara terbaik untuk menanganinya adalah dengan mengkode ulang function () sedemikian rupa sehingga ia memanggil function2 (), tetapi jangan mengubah tanda tangan function ().
paxdiablo
1
@ Paul Fisher, saya tahu ini sudah terlambat tapi ... perpustakaan yang dikirimkan dengan Windows DLL bukan perpustakaan lengkap, itu hanya sekelompok bertopik yang memberi tahu penghubung apa isi DLL. Tautan kemudian dapat secara otomatis memasukkan informasi ke .exe untuk memuat DLL, dan simbol tidak muncul sebagai tidak terdefinisi.
Mark Ransom
1
@ Antropedro, Anda benar pada semua hal kembali arti dari lib, impor dan nama DLL. Sufiks hanya merupakan konvensi jadi jangan membaca terlalu banyak tentang hal itu (misalnya, DLL mungkin memiliki .dllatau .soekstensi) - anggap jawabannya menjelaskan konsep daripada menjadi deskripsi yang tepat. Dan, sesuai dengan teks, ini adalah contoh yang menunjukkan penghubung statis dan dinamis untuk hanya file runtime C jadi, ya, itulah yang ditunjukkan oleh `crt pada semuanya.
paxdiablo
221

Saya pikir jawaban yang baik untuk pertanyaan ini harus menjelaskan apa linking adalah .

Ketika Anda mengkompilasi beberapa kode C (misalnya), itu diterjemahkan ke bahasa mesin. Hanya urutan byte yang, ketika dijalankan, menyebabkan prosesor untuk menambah, mengurangi, membandingkan, "goto", membaca memori, menulis memori, hal semacam itu. Barang ini disimpan dalam file objek (.o).

Sekarang, dahulu kala, para ilmuwan komputer menemukan benda "subrutin" ini. Jalankan-ini-potongan-kode-dan-kembali-sini. Tidak terlalu lama sebelum mereka menyadari bahwa subrutin yang paling berguna dapat disimpan di tempat khusus dan digunakan oleh program apa pun yang membutuhkannya.

Sekarang di hari-hari awal programmer harus memasukkan alamat memori tempat subrutin ini berada. Sesuatu seperti CALL 0x5A62. Ini membosankan dan bermasalah jika alamat memori tersebut perlu diubah.

Jadi, prosesnya otomatis. Anda menulis sebuah program yang memanggil printf(), dan kompiler tidak tahu alamat memori printf. Jadi kompiler hanya menulis CALL 0x0000, dan menambahkan catatan ke file objek yang mengatakan "harus mengganti 0x0000 ini dengan lokasi memori printf ".

Hubungan statis berarti bahwa program tautan (yang GNU disebut ld ) menambahkan printfkode mesin langsung ke file yang dapat dieksekusi, dan mengubah 0x0000 ke alamat printf. Ini terjadi ketika executable Anda dibuat.

Tautan dinamis berarti bahwa langkah di atas tidak terjadi. File yang dapat dieksekusi masih memiliki catatan yang mengatakan "harus mengganti 0x000 dengan lokasi memori printf". Pemuat sistem operasi perlu menemukan kode printf, memuatnya ke dalam memori, dan memperbaiki alamat CALL, setiap kali program dijalankan .

Adalah umum untuk program memanggil beberapa fungsi yang akan dihubungkan secara statis (fungsi perpustakaan standar seperti printfbiasanya terhubung secara statis) dan fungsi lain yang terhubung secara dinamis. Yang statis "menjadi bagian" dari yang dapat dieksekusi dan yang dinamis "bergabung" ketika executable dijalankan.

Ada kelebihan dan kekurangan untuk kedua metode, dan ada perbedaan antara sistem operasi. Tetapi karena Anda tidak bertanya, saya akan mengakhiri ini di sini.

Artelius
sumber
4
Saya juga melakukannya, namun saya hanya bisa memilih 1 jawaban.
UnkwnTech
1
Artelius, saya mencari penjelasan mendalam tentang cara kerja level rendah yang gila ini. tolong balas dengan buku apa yang harus kita baca untuk mendapatkan pengetahuan mendalam tentang hal-hal di atas. Terima kasih.
mahesh
1
Maaf, saya tidak dapat menyarankan buku apa pun. Anda harus belajar bahasa assembly terlebih dahulu. Maka Wikipedia dapat memberikan tinjauan yang layak tentang topik-topik tersebut. Anda mungkin ingin melihat lddokumentasi GNU .
Artelius
31

Pustaka yang terhubung secara statis terhubung pada saat kompilasi. Pustaka yang terhubung secara dinamis dimuat pada saat dijalankan. Menghubungkan statis membuat bit perpustakaan menjadi executable Anda. Tautan dinamis yang hanya membuat roti dalam referensi ke perpustakaan; bit untuk perpustakaan dinamis ada di tempat lain dan bisa ditukar nanti.

John D. Cook
sumber
16

Karena tidak ada tulisan di atas yang benar-benar menunjukkan cara menautkan sesuatu secara statis dan melihat bahwa Anda melakukannya dengan benar, maka saya akan mengatasi masalah ini:

Program C sederhana

#include <stdio.h>

int main(void)
{
    printf("This is a string\n");
    return 0;
}

Tautan secara dinamis program C.

gcc simpleprog.c -o simpleprog

Dan jalankan filedi biner:

file simpleprog 

Dan itu akan menunjukkan itu terkait secara dinamis sesuatu di sepanjang baris:

"simpleprog: ELF 64-bit LSB dapat dieksekusi, x86-64, versi 1 (SYSV), ditautkan secara dinamis (menggunakan lib bersama), untuk GNU / Linux 2.6.26, BuildID [sha1] = 0xf715572611118b04f686809d90d1c0d75c6028f0f, tidak dilucuti"

Alih-alih, mari kita tautkan secara statis program kali ini:

gcc simpleprog.c -static -o simpleprog

Menjalankan file pada biner yang terhubung secara statis ini akan menunjukkan:

file simpleprog 

"simpleprog: ELF 64-bit LSB yang dapat dieksekusi, x86-64, versi 1 (GNU / Linux), terhubung secara statis, untuk GNU / Linux 2.6.26, BuildID [sha1] = 0x8c0b12250801c5a7c7434647b7dc65a644d6132b, tidak dilucuti"

Dan Anda dapat melihatnya terhubung secara statis. Sayangnya, tidak semua perpustakaan mudah untuk secara statis menghubungkan cara ini dan mungkin memerlukan upaya lebih lama menggunakan libtoolatau menghubungkan kode objek dan perpustakaan C dengan tangan.

Untungnya banyak pustaka C yang disematkan suka muslmenawarkan opsi penautan statis untuk hampir semua pustaka jika tidak semua pustaka mereka.

Sekarang stracebiner yang Anda buat dan Anda dapat melihat bahwa tidak ada perpustakaan yang diakses sebelum program dimulai:

strace ./simpleprog

Sekarang bandingkan dengan output stracepada program yang terhubung secara dinamis dan Anda akan melihat bahwa strace versi yang terhubung secara statis jauh lebih pendek!


sumber
2

(Saya tidak tahu C # tetapi menarik untuk memiliki konsep tautan statis untuk bahasa VM)

Tautan dinamis melibatkan mengetahui cara menemukan fungsionalitas yang diperlukan yang hanya Anda miliki referensi dari program Anda. Anda bahasa runtime atau OS mencari sepotong kode pada sistem file, jaringan atau cache kode yang dikompilasi, mencocokkan referensi, dan kemudian mengambil beberapa langkah untuk mengintegrasikannya ke gambar program Anda di memori, seperti relokasi. Semuanya dilakukan saat runtime. Itu bisa dilakukan secara manual atau oleh kompiler. Ada kemampuan untuk memperbarui dengan risiko mengacaukan (yaitu, DLL neraka).

Menghubungkan statis dilakukan pada waktu kompilasi itu, Anda memberi tahu kompiler di mana semua bagian fungsional berada dan memerintahkannya untuk mengintegrasikannya. Tidak ada pencarian, tidak ada ambiguitas, tidak ada kemampuan untuk memperbarui tanpa kompilasi ulang. Semua dependensi Anda secara fisik menyatu dengan gambar program Anda.

artificialidiot
sumber