'ลิงก์แบบสแตติก' และ 'ลิงก์แบบไดนามิก' หมายถึงอะไร


229

ผมมักจะได้ยินคำว่า 'เชื่อมโยงแบบคง' และ 'เชื่อมโยงแบบไดนามิก' มักจะอยู่ในการอ้างอิงถึงรหัสที่เขียนในC , C ++หรือC # พวกเขาคืออะไรพวกเขากำลังพูดถึงอะไรกันแน่และพวกเขากำลังเชื่อมโยงอะไรกัน?

คำตอบ:


445

มี (ในกรณีส่วนใหญ่รหัสส่วนลดที่แปลความหมาย) มีสองขั้นตอนในการรับจากซอร์สโค้ด (สิ่งที่คุณเขียน) ไปยังโค้ดที่เรียกใช้งานได้ (สิ่งที่คุณเรียกใช้)

สิ่งแรกคือการคอมไพล์ซึ่งเปลี่ยนซอร์สโค้ดเป็นโมดูลวัตถุ

การเชื่อมโยงที่สองคือสิ่งที่รวมโมดูลวัตถุเข้าด้วยกันเพื่อสร้างปฏิบัติการ

ความแตกต่างนั้นมีไว้เพื่อให้ห้องสมุดของบุคคลที่สามถูกรวมอยู่ในปฏิบัติการของคุณโดยที่คุณไม่ต้องเห็นซอร์สโค้ดของพวกเขา (เช่นไลบรารีสำหรับการเข้าถึงฐานข้อมูลการสื่อสารเครือข่ายและอินเทอร์เฟซผู้ใช้แบบกราฟิก) หรือสำหรับการคอมไพล์ ตัวอย่าง C และรหัสแอสเซมบลี) จากนั้นเชื่อมโยงทั้งหมดเข้าด้วยกัน

เมื่อคุณลิงก์ไฟล์ไปยังไฟล์ที่สามารถเรียกทำงานได้แบบสแตติกเนื้อหาของไฟล์นั้นจะถูกรวมในเวลาลิงก์ กล่าวอีกนัยหนึ่งเนื้อหาของไฟล์จะถูกแทรกลงในไฟล์สั่งการที่คุณจะเรียกใช้

เมื่อคุณลิงก์แบบไดนามิกตัวชี้ไปยังไฟล์ที่กำลังลิงก์อยู่ (เช่นชื่อไฟล์ของไฟล์) จะรวมอยู่ในแฟ้มที่ปฏิบัติการได้และเนื้อหาของไฟล์ดังกล่าวจะไม่รวมอยู่ในเวลาลิงก์ เฉพาะเมื่อคุณเรียกใช้ไฟล์ปฏิบัติการในภายหลังว่ามีการซื้อไฟล์ที่ลิงก์แบบไดนามิกเหล่านี้และพวกเขาจะซื้อเฉพาะไฟล์สำเนาที่อยู่ในหน่วยความจำของไฟล์ปฏิบัติการเท่านั้นไม่ใช่ไฟล์ในดิสก์

มันเป็นวิธีการเชื่อมโยงที่เลื่อนออกไป มีวิธีการที่เลื่อนออกไปมากขึ้น (เรียกว่าการรวมล่าช้าในบางระบบ) ซึ่งจะไม่นำไฟล์ที่เชื่อมโยงแบบไดนามิกจนกว่าคุณจะพยายามเรียกใช้ฟังก์ชันภายใน

ไฟล์ที่ลิงก์แบบสแตติกจะถูก 'ล็อค' เพื่อเรียกใช้งาน ณ เวลาลิงก์ดังนั้นจึงไม่มีการเปลี่ยนแปลง ไฟล์ที่ลิงก์แบบไดนามิกที่อ้างอิงโดยโปรแกรมเรียกทำงานสามารถเปลี่ยนแปลงได้เพียงแค่แทนที่ไฟล์บนดิสก์

สิ่งนี้อนุญาตให้อัปเดตการทำงานโดยไม่ต้องเชื่อมโยงรหัสอีกครั้ง ตัวโหลดจะทำการลิงก์ซ้ำทุกครั้งที่คุณเรียกใช้

นี่คือทั้งดีและไม่ดี - ในมือข้างหนึ่งก็ช่วยให้การปรับปรุงง่ายขึ้นและการแก้ไขข้อผิดพลาดในขณะที่มันสามารถนำไปสู่โปรแกรมที่หยุดทำงานหากการปรับปรุงจะเข้ากันไม่ได้ - บางครั้งก็รับผิดชอบใน "นรกนรก" พูดถึงในแอปพลิเคชั่นที่สามารถใช้งานไม่ได้หากคุณเปลี่ยนไลบรารี่ที่เชื่อมโยงแบบไดนามิกด้วยแอพพลิเคชั่นที่ไม่รองรับ (นักพัฒนาที่ทำสิ่งนี้


ในฐานะที่เป็น ตัวอย่างให้ดูขอให้กรณีของผู้ใช้ของพวกเขารวบรวมmain.cไฟล์แบบคงที่และเชื่อมโยงแบบไดนามิก

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  |
          +---------+               +---------+

คุณสามารถเห็นได้ในกรณีสแตติกที่โปรแกรมหลักและไลบรารีรันไทม์ C ถูกลิงก์พร้อมกัน ณ เวลาลิงก์ (โดยผู้พัฒนา) เนื่องจากโดยทั่วไปผู้ใช้จะไม่สามารถเชื่อมโยงไฟล์ที่เรียกใช้ซ้ำได้พวกเขาจะติดอยู่กับพฤติกรรมของไลบรารี

ในกรณีแบบไดนามิกโปรแกรมหลักจะเชื่อมโยงกับไลบรารีการนำเข้า C runtime (สิ่งที่ประกาศสิ่งที่อยู่ในไลบรารีแบบไดนามิก แต่ไม่ได้กำหนดจริง) วิธีนี้ช่วยให้ตัวเชื่อมโยงเชื่อมโยงแม้ว่ารหัสจริงจะหายไป

จากนั้นเมื่อรันไทม์ตัวโหลดระบบปฏิบัติการจะทำการเชื่อมโยงล่าช้าของโปรแกรมหลักด้วย C runtime DLL (ไดนามิกลิงก์ไลบรารีหรือไลบรารีที่แบ่งใช้หรือชื่ออื่น ๆ )

เจ้าของ C runtime สามารถปล่อย DLL ใหม่ได้ตลอดเวลาเพื่อให้การปรับปรุงหรือแก้ไขข้อบกพร่อง ตามที่ระบุไว้ก่อนหน้านี้มีทั้งข้อดีและข้อเสีย


11
โปรดแก้ไขให้ฉันถ้าฉันผิด แต่ใน Windows ซอฟต์แวร์มีแนวโน้มที่จะรวมไลบรารีของตัวเองเข้ากับการติดตั้งแม้ว่าพวกเขาจะเชื่อมโยงแบบไดนามิก ในระบบ Linux หลายระบบที่มีตัวจัดการแพคเกจไลบรารีที่เชื่อมโยงแบบไดนามิกจำนวนมาก ("วัตถุที่ใช้ร่วมกัน") จะถูกแชร์ระหว่างซอฟต์แวร์
พอลฟิชเชอร์

6
@PaulF: สิ่งต่าง ๆ เช่นตัวควบคุมทั่วไปของ Windows DirectX,. NET และอื่น ๆ พร้อมกับแอปพลิเคชั่นมากมายในขณะที่บน Linux คุณมักจะใช้ apt หรือ yum หรืออะไรทำนองนั้นในการจัดการการพึ่งพา - ดังนั้นคุณจึงถูกต้อง . รับรางวัลแอพที่จัดส่งรหัสของตัวเองในฐานะ DLLs มักจะไม่แบ่งปันให้
paxdiablo

31
มีสถานที่พิเศษที่สงวนไว้ในวงกลมที่เก้าของนรกสำหรับผู้ที่อัปเดต DLLs และแบ่งความเข้ากันได้แบบย้อนหลัง ใช่หากอินเตอร์เฟสหายไปหรือถูกแก้ไขการเชื่อมโยงแบบไดนามิกจะตกอยู่ในกอง นั่นเป็นสาเหตุที่ไม่ควรทำ โดยทั้งหมดหมายความว่าเพิ่ม function2 () ลงใน DLL ของคุณ แต่อย่าเปลี่ยน function () ถ้ามีคนใช้งาน วิธีที่ดีที่สุดในการจัดการนั่นคือ recode function () ในลักษณะที่เรียก function2 () แต่อย่าเปลี่ยนลายเซ็นของ function ()
paxdiablo

1
@ พอลฟิชเชอร์ฉันรู้ว่ามันสายไปแล้ว แต่ ... ห้องสมุดที่มาพร้อมกับ Windows DLL ไม่ใช่ไลบรารี่แบบเต็มมันเป็นเพียงแค่สตับที่บอก linker ว่า DLL นั้นมีอะไรบ้าง ตัวเชื่อมโยงสามารถใส่ข้อมูลลงใน. exe เพื่อโหลด DLL โดยอัตโนมัติและสัญลักษณ์จะไม่ปรากฏขึ้นเป็นไม่ได้กำหนด
Mark Ransom

1
@Santropedro คุณถูกต้องในการนับทั้งหมดตามความหมายของชื่อ lib, import และ DLL คำต่อท้ายเป็นแบบแผนเท่านั้นดังนั้นอย่าอ่านมากเกินไป (ตัวอย่างเช่น DLL อาจมี.dllหรือเป็น.soส่วนขยาย) - คิดว่าคำตอบเป็นการอธิบายแนวคิดแทนที่จะเป็นคำอธิบายที่ถูกต้อง และตามข้อความนี้เป็นตัวอย่างที่แสดงการเชื่อมโยงแบบสแตติกและแบบไดนามิกสำหรับไฟล์ C runtime ดังนั้นใช่นั่นคือสิ่งที่ `crt บ่งชี้ในพวกเขาทั้งหมด
paxdiablo

221

ฉันคิดว่าคำตอบที่ดีสำหรับคำถามนี้ควรอธิบายว่าการเชื่อมโยงคืออะไร

เมื่อคุณรวบรวมรหัส C (ตัวอย่าง) มันจะถูกแปลเป็นภาษาเครื่อง เพียงลำดับของไบต์ที่เมื่อรันจะทำให้โปรเซสเซอร์เพิ่มลบเปรียบเทียบ "goto" อ่านหน่วยความจำเขียนหน่วยความจำเรียงลำดับของสิ่งนั้น สิ่งนี้ถูกเก็บไว้ในไฟล์ object (.o)

เมื่อนานมาแล้วนักวิทยาศาสตร์คอมพิวเตอร์ได้ประดิษฐ์สิ่งนี้ "รูทีนย่อย" ดำเนินการนี้-ก้อนของรหัสและผลตอบแทนที่นี่ ไม่นานก่อนที่พวกเขาจะรู้ว่ารูทีนย่อยที่มีประโยชน์ที่สุดสามารถเก็บไว้ในสถานที่พิเศษและใช้งานโดยโปรแกรมใด ๆ ที่ต้องการ

ในตอนแรกโปรแกรมเมอร์จะต้องเจาะที่อยู่หน่วยความจำที่รูทีนย่อยเหล่านี้ตั้งอยู่ที่ CALL 0x5A62สิ่งที่ชอบ นี่เป็นที่น่าเบื่อและมีปัญหาหากที่อยู่หน่วยความจำเหล่านั้นจำเป็นต้องเปลี่ยน

ดังนั้นกระบวนการเป็นไปโดยอัตโนมัติ คุณเขียนโปรแกรมที่โทรและคอมไพเลอร์ไม่ทราบที่อยู่ของหน่วยความจำprintf() printfคอมไพเลอร์เพิ่งเขียนCALL 0x0000และเพิ่มหมายเหตุลงในไฟล์อ็อบเจ็กต์ที่บอกว่า "ต้องแทนที่ 0x0000 นี้ด้วยตำแหน่งหน่วยความจำของprintf "

คงที่เชื่อมโยงหมายความว่าโปรแกรมลิงเกอร์ (แอฟริกาหนึ่งเรียกว่าLD ) เพิ่มprintf's รหัสเครื่องโดยตรงไปยังแฟ้มที่ปฏิบัติการของคุณและเปลี่ยนแปลง 0x0000 printfไปยังที่อยู่ของ สิ่งนี้จะเกิดขึ้นเมื่อสร้างไฟล์ปฏิบัติการได้

การเชื่อมโยงแบบไดนามิกหมายความว่าขั้นตอนข้างต้นไม่เกิดขึ้น ไฟล์เรียกทำงานยังคงมีข้อความระบุว่า "ต้องแทนที่ 0x000 ด้วยตำแหน่งหน่วยความจำของ printf" โหลดเดอร์ของระบบปฏิบัติการต้องการค้นหารหัส printf โหลดลงในหน่วยความจำและแก้ไขที่อยู่ CALL ทุกครั้งที่มีการเรียกใช้โปรแกรมแต่ละครั้งที่มีการรันโปรแกรม

เป็นเรื่องปกติสำหรับโปรแกรมที่จะเรียกใช้ฟังก์ชั่นบางอย่างซึ่งจะถูกเชื่อมโยงแบบคงที่ (ฟังก์ชั่นไลบรารีมาตรฐานเช่น printfมักจะเชื่อมโยงแบบคงที่) และฟังก์ชั่นอื่น ๆ ที่เชื่อมโยงแบบไดนามิก ส่วนที่คงที่ "กลายเป็นส่วนหนึ่ง" ของความสามารถในการปฏิบัติการและตัวแบบไดนามิก "เข้าร่วม" เมื่อเรียกใช้งาน

มีข้อดีและข้อเสียของทั้งสองวิธีและมีความแตกต่างระหว่างระบบปฏิบัติการ แต่เมื่อคุณไม่ได้ถามฉันจะจบที่นี่


4
ฉันก็ทำเช่นกัน แต่ฉันจะเลือก 1 คำตอบเท่านั้น
UnkwnTech

1
อาร์เทลิอุสฉันกำลังมองลึกเข้าไปเกี่ยวกับคำอธิบายของคุณเกี่ยวกับการทำงานของสิ่งระดับต่ำเหล่านี้ที่บ้าคลั่ง โปรดตอบด้วยหนังสือที่เราต้องอ่านเพื่อรับความรู้เชิงลึกเกี่ยวกับสิ่งต่าง ๆ ข้างต้น ขอบคุณ.
mahesh

1
ขออภัยฉันไม่สามารถแนะนำหนังสือใด ๆ คุณควรเรียนรู้ภาษาประกอบก่อน จากนั้น Wikipedia สามารถให้ภาพรวมที่ดีของหัวข้อดังกล่าว คุณอาจต้องการดูldเอกสารGNU
Artelius

31

ไลบรารีที่ลิงก์แบบสแตติกถูกลิงก์ในเวลาคอมไพล์ ไลบรารีที่ลิงก์แบบไดนามิกถูกโหลด ณ รันไทม์ การเชื่อมโยงแบบคงที่จะทำให้บิตไลบรารีลงในไฟล์เรียกทำงานของคุณ การลิงก์แบบไดนามิกจะทำเฉพาะการอ้างอิงกับไลบรารีเท่านั้น บิตสำหรับไลบรารีแบบไดนามิกมีอยู่ที่อื่นและสามารถสลับออกได้ในภายหลัง


16

เนื่องจากไม่มีการโพสต์ข้างต้นแสดงวิธีเชื่อมโยงบางสิ่งบางอย่างแบบคงที่และเห็นว่าคุณทำถูกต้องแล้วดังนั้นฉันจะแก้ไขปัญหานี้:

โปรแกรม C อย่างง่าย

#include <stdio.h>

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

ลิงก์โปรแกรม C แบบไดนามิก

gcc simpleprog.c -o simpleprog

และทำงานfileบนไบนารี:

file simpleprog 

และนั่นจะแสดงให้เห็นว่ามีการเชื่อมโยงบางสิ่งบางอย่างตามแนวของ:

"simpleprog: ELF 64- บิต LSB ที่ปฏิบัติการได้, x86-64, รุ่น 1 (SYSV), ลิงก์แบบไดนามิก (ใช้ libs ที่ใช้ร่วมกัน), สำหรับ GNU / Linux 2.6.26, BuildID [sha1] = 0xf715572611a8b04f686809d90d1c0d75c6028f0

ให้เราเชื่อมโยงโปรแกรมในเวลานี้:

gcc simpleprog.c -static -o simpleprog

การเรียกใช้ไฟล์ในไบนารีที่เชื่อมโยงแบบคงที่นี้จะแสดง:

file simpleprog 

"simpleprog: ELF 64- บิต LSB ที่ปฏิบัติการได้, x86-64, รุ่น 1 (GNU / Linux), การเชื่อมโยงแบบคงที่, สำหรับ GNU / Linux 2.6.26, BuildID [sha1] = 0x8c0b12250801c5a7c7434647b7dc65a644d6132b, ไม่ได้ถูกสแตติก"

และคุณสามารถเห็นมันเชื่อมโยงอย่างมีความสุขแบบคงที่ น่าเสียดายที่ไม่ใช่ว่าทุกไลบรารีจะง่ายต่อการเชื่อมโยงแบบคงที่และอาจต้องใช้ความพยายามเพิ่มเติมlibtoolหรือใช้การเชื่อมโยงรหัสวัตถุและไลบรารี C ด้วยมือ

โชคดีที่ไลบรารี C แบบฝังจำนวนมากเช่นmuslเสนอตัวเลือกการเชื่อมโยงแบบคงที่สำหรับเกือบทั้งหมดหากไม่ใช่ไลบรารีทั้งหมด

ตอนนี้straceไบนารีที่คุณสร้างขึ้นและคุณสามารถเห็นว่าไม่มีไลบรารีใด ๆ ที่เข้าถึงได้ก่อนที่โปรแกรมจะเริ่มต้น:

strace ./simpleprog

ตอนนี้เปรียบเทียบกับผลลัพธ์ของstraceในโปรแกรมที่เชื่อมโยงแบบไดนามิกและคุณจะเห็นว่า strace ของเวอร์ชันที่เชื่อมโยงแบบคงที่นั้นสั้นกว่ามาก!


2

(ฉันไม่ทราบ C # แต่เป็นที่น่าสนใจที่จะมีแนวคิดการเชื่อมโยงแบบคงที่สำหรับภาษา VM)

การเชื่อมโยงแบบไดนามิกเกี่ยวข้องกับการรู้วิธีการค้นหาฟังก์ชั่นที่จำเป็นซึ่งคุณมีเพียงการอ้างอิงจากโปรแกรมของคุณ ภาษาของคุณรันไทม์หรือ OS ค้นหารหัสบนระบบแฟ้มเครือข่ายหรือแคชรหัสที่คอมไพล์จับคู่ข้อมูลอ้างอิงจากนั้นใช้มาตรการหลายอย่างเพื่อรวมเข้ากับภาพโปรแกรมของคุณในหน่วยความจำเช่นการย้าย พวกเขาทั้งหมดจะทำที่รันไทม์ มันสามารถทำได้ด้วยตนเองหรือโดยคอมไพเลอร์ มีความสามารถในการอัปเดตโดยมีความเสี่ยงที่จะเกิดความสับสน (เช่น DLL hell)

การเชื่อมโยงสแตติกทำในเวลาคอมไพล์ซึ่งคุณบอกคอมไพเลอร์ว่าส่วนการทำงานทั้งหมดอยู่ที่ไหนและสั่งให้รวมเข้าด้วยกัน ไม่มีการค้นหาไม่มีความกำกวมไม่มีความสามารถในการอัปเดตโดยไม่ต้องคอมไพล์ใหม่ การอ้างอิงทั้งหมดของคุณนั้นเป็นรูปแบบเดียวกับอิมเมจโปรแกรม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.