Câu trả lời:
Có (trong hầu hết các trường hợp, chiết khấu mã được diễn giải) hai giai đoạn trong việc chuyển từ mã nguồn (những gì bạn viết) sang mã thực thi (những gì bạn chạy).
Đầu tiên là biên dịch biến mã nguồn thành các mô đun đối tượng.
Thứ hai, liên kết, là những gì kết hợp các mô-đun đối tượng với nhau để tạo thành một thực thi.
Sự khác biệt được tạo ra, trong số những thứ khác, cho phép các thư viện của bên thứ ba được bao gồm trong tệp thực thi của bạn mà không cần bạn nhìn thấy mã nguồn của chúng (chẳng hạn như thư viện để truy cập cơ sở dữ liệu, giao tiếp mạng và giao diện người dùng đồ họa) hoặc để biên dịch mã bằng các ngôn ngữ khác nhau ( C và mã lắp ráp chẳng hạn) và sau đó liên kết tất cả chúng lại với nhau.
Khi bạn liên kết tĩnh một tệp thành tệp thực thi, nội dung của tệp đó được bao gồm tại thời điểm liên kết. Nói cách khác, nội dung của tệp được chèn vật lý vào tệp thực thi mà bạn sẽ chạy.
Khi bạn liên kết động , một con trỏ tới tệp được liên kết trong (ví dụ tên tệp của tệp) được bao gồm trong tệp thực thi và nội dung của tệp đã nói không được đưa vào tại thời điểm liên kết. Chỉ khi bạn chạy tệp thực thi này, các tệp được liên kết động này mới được mua và chúng chỉ được mua vào bản sao trong bộ nhớ của tệp thực thi, chứ không phải tệp trên đĩa.
Về cơ bản, đây là một phương pháp liên kết hoãn lại. Có một phương thức thậm chí còn bị trì hoãn hơn (được gọi là ràng buộc muộn trên một số hệ thống) sẽ không mang lại tệp được liên kết động cho đến khi bạn thực sự cố gắng gọi một hàm trong nó.
Các tệp được liên kết tĩnh được 'khóa' để thực thi tại thời điểm liên kết để chúng không bao giờ thay đổi. Một tệp được liên kết động được tham chiếu bởi một tệp thực thi có thể thay đổi chỉ bằng cách thay thế tệp trên đĩa.
Điều này cho phép cập nhật chức năng mà không phải liên kết lại mã; trình tải lại liên kết lại mỗi khi bạn chạy nó.
Điều này vừa tốt vừa xấu - một mặt, nó cho phép cập nhật và sửa lỗi dễ dàng hơn, mặt khác nó có thể dẫn đến các chương trình ngừng hoạt động nếu các bản cập nhật không tương thích - đôi khi điều này chịu trách nhiệm cho "địa ngục DLL" đáng sợ mà một số người đề cập đến việc các ứng dụng có thể bị phá vỡ nếu bạn thay thế một thư viện được liên kết động bằng một thư viện không tương thích (nhân viên phát triển làm điều này sẽ bị săn lùng và trừng phạt nghiêm khắc).
Như một ví dụ , chúng ta hãy nhìn vào trường hợp của một người sử dụng biên dịch của họ main.c
tập tin cho tĩnh và liên kết động.
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 |
+---------+ +---------+
Bạn có thể thấy trong trường hợp tĩnh rằng chương trình chính và thư viện thời gian chạy C được liên kết với nhau tại thời điểm liên kết (bởi các nhà phát triển). Vì người dùng thường không thể liên kết lại tệp thực thi, nên họ bị mắc kẹt với hành vi của thư viện.
Trong trường hợp động, chương trình chính được liên kết với thư viện nhập thời gian chạy C (một cái gì đó khai báo những gì trong thư viện động nhưng không thực sự xác định nó). Điều này cho phép trình liên kết liên kết mặc dù mã thực tế bị thiếu.
Sau đó, tại thời gian chạy, trình tải hệ điều hành thực hiện liên kết muộn chương trình chính với DLL thời gian chạy C (thư viện liên kết động hoặc thư viện chia sẻ hoặc danh pháp khác).
Chủ sở hữu của thời gian chạy C có thể thả một DLL mới bất cứ lúc nào để cung cấp các bản cập nhật hoặc sửa lỗi. Như đã nêu trước đó, điều này có cả ưu điểm và nhược điểm.
.dll
hoặc .so
mở rộng) - hãy nghĩ câu trả lời là giải thích các khái niệm thay vì mô tả chính xác. Và, theo văn bản, đây là một ví dụ hiển thị liên kết tĩnh và động cho chỉ các tệp thời gian chạy C, vì vậy, đó là những gì `crt chỉ ra trong tất cả chúng.
Tôi nghĩ rằng một câu trả lời tốt cho câu hỏi này nên giải thích liên kết là gì .
Khi bạn biên dịch một số mã C (ví dụ), nó được dịch sang ngôn ngữ máy. Chỉ là một chuỗi byte, khi chạy, làm cho bộ xử lý thêm, trừ, so sánh, "goto", đọc bộ nhớ, ghi bộ nhớ, loại đó. Công cụ này được lưu trữ trong các tệp đối tượng (.o).
Bây giờ, một thời gian dài trước đây, các nhà khoa học máy tính đã phát minh ra thứ "chương trình con" này. Thực thi-this-chunk-of-code-and-return-here. Không quá lâu trước khi họ nhận ra rằng các chương trình con hữu ích nhất có thể được lưu trữ ở một nơi đặc biệt và được sử dụng bởi bất kỳ chương trình nào cần chúng.
Bây giờ trong những ngày đầu, các lập trình viên sẽ phải bấm vào địa chỉ bộ nhớ mà các chương trình con này được đặt tại. Một cái gì đó như CALL 0x5A62
. Điều này thật tẻ nhạt và có vấn đề nếu những địa chỉ bộ nhớ đó cần phải được thay đổi.
Vì vậy, quá trình đã được tự động. Bạn viết một chương trình gọi printf()
và trình biên dịch không biết địa chỉ bộ nhớ của printf
. Vì vậy, trình biên dịch chỉ cần ghi CALL 0x0000
và thêm một ghi chú vào tệp đối tượng có nội dung "phải thay thế 0x0000 này bằng vị trí bộ nhớ của printf ".
Liên kết tĩnh có nghĩa là chương trình liên kết (GNU được gọi là ld ) thêm printf
mã máy trực tiếp vào tệp thực thi của bạn và thay đổi 0x0000 thành địa chỉ của printf
. Điều này xảy ra khi thực thi của bạn được tạo ra.
Liên kết động có nghĩa là bước trên không xảy ra. Tệp thực thi vẫn có một ghi chú cho biết "phải thay thế 0x000 bằng vị trí bộ nhớ của printf". Trình tải của hệ điều hành cần tìm mã printf, tải nó vào bộ nhớ và sửa địa chỉ CALL, mỗi khi chương trình được chạy .
Các chương trình thường gọi một số chức năng sẽ được liên kết tĩnh (các chức năng thư viện tiêu chuẩn như printf
thường được liên kết tĩnh) và các chức năng khác được liên kết động. Những cái tĩnh "trở thành một phần" của thực thi và những cái động "tham gia" khi thực thi được chạy.
Có những ưu điểm và nhược điểm của cả hai phương pháp và có sự khác biệt giữa các hệ điều hành. Nhưng vì bạn không hỏi, tôi sẽ kết thúc chuyện này ở đây.
ld
tài liệu GNU .
Các thư viện liên kết tĩnh được liên kết tại thời gian biên dịch. Các thư viện liên kết động được tải vào thời gian chạy. Liên kết tĩnh mang bit thư viện vào tệp thực thi của bạn. Liên kết động chỉ bakes trong một tham chiếu đến thư viện; các bit cho thư viện động tồn tại ở nơi khác và có thể được hoán đổi sau đó.
Bởi vì không có bài viết nào ở trên thực sự chỉ ra cách liên kết tĩnh một cái gì đó và thấy rằng bạn đã làm đúng, vì vậy tôi sẽ giải quyết vấn đề này:
Một chương trình C đơn giản
#include <stdio.h>
int main(void)
{
printf("This is a string\n");
return 0;
}
Tự động liên kết chương trình C
gcc simpleprog.c -o simpleprog
Và chạy file
trên nhị phân:
file simpleprog
Và điều đó sẽ cho thấy nó được liên kết động một cái gì đó dọc theo dòng:
"Simpleprog: ELF 64-bit LSB thực thi, x86-64, phiên bản 1 (SYSV), được liên kết động (sử dụng libs được chia sẻ), cho GNU / Linux 2.6.26, BuildID [sha1] = 0xf715572611a8b04f686809d90d1c75
Thay vào đó, hãy để chúng tôi liên kết tĩnh chương trình lần này:
gcc simpleprog.c -static -o simpleprog
Chạy tệp trên nhị phân được liên kết tĩnh này sẽ hiển thị:
file simpleprog
"Simpleprog: ELF 64-bit LSB thực thi, x86-64, phiên bản 1 (GNU / Linux), được liên kết tĩnh, đối với GNU / Linux 2.6.26, BuildID [sha1] = 0x8c0b12250801c5a7c7434647b7dc65a644"
Và bạn có thể thấy nó được liên kết tĩnh hạnh phúc. Tuy nhiên, thật đáng buồn là không phải tất cả các thư viện đều đơn giản để liên kết tĩnh theo cách này và có thể yêu cầu nỗ lực mở rộng bằng cách sử dụng libtool
hoặc liên kết mã đối tượng và thư viện C bằng tay.
May mắn thay, nhiều thư viện C nhúng như musl
cung cấp các tùy chọn liên kết tĩnh cho gần như tất cả nếu không phải tất cả các thư viện của họ.
Bây giờ strace
nhị phân bạn đã tạo và bạn có thể thấy rằng không có thư viện nào được truy cập trước khi chương trình bắt đầu:
strace ./simpleprog
Bây giờ so sánh với đầu ra của strace
chương trình được liên kết động và bạn sẽ thấy rằng bước của phiên bản được liên kết tĩnh ngắn hơn nhiều!
(Tôi không biết C # nhưng thật thú vị khi có khái niệm liên kết tĩnh cho ngôn ngữ VM)
Liên kết động liên quan đến việc biết cách tìm một chức năng cần thiết mà bạn chỉ có một tài liệu tham khảo từ chương trình của mình. Bạn chạy ngôn ngữ hoặc hệ điều hành tìm kiếm một đoạn mã trên hệ thống tập tin, mạng hoặc bộ đệm mã được biên dịch, khớp với tham chiếu và sau đó thực hiện một số biện pháp để tích hợp nó vào hình ảnh chương trình của bạn trong bộ nhớ, như di dời. Tất cả đều được thực hiện trong thời gian chạy. Nó có thể được thực hiện bằng tay hoặc bằng trình biên dịch. Có khả năng cập nhật với nguy cơ gây rối (cụ thể là địa ngục DLL).
Liên kết tĩnh được thực hiện tại thời điểm biên dịch, bạn báo cho trình biên dịch biết tất cả các phần chức năng và hướng dẫn nó tích hợp chúng. Không có tìm kiếm, không có sự mơ hồ, không có khả năng cập nhật mà không cần biên dịch lại. Tất cả các phụ thuộc của bạn là một vật lý với hình ảnh chương trình của bạn.