102.3 Bài 1
Chứng chỉ: |
LPIC-1 |
---|---|
Phiên bản: |
5.0 |
Chủ đề: |
102 Cài đặt Linux và Quản lý Gói |
Mục tiêu: |
102.3 Cài đặt Trình Quản lý Khởi động |
Bài: |
1 trên 1 |
Giới thiệu
Trong bài học này, chúng ta sẽ thảo luận về thư viện chia sẻ hay còn được gọi là đối tượng chia sẻ: các đoạn mã được biên dịch, có thể tái sử dụng như các hàm hoặc hạng và được nhiều chương trình khác nhau thường xuyên sử dụng.
Để bắt đầu, chúng ta sẽ tìm hiểu thư viện chia sẻ là gì, cách xác định chúng và nơi ta có thể tìm thấy chúng. Tiếp theo, chúng ta sẽ đi vào cách định cấu hình các vị trí lưu trữ của chúng. Cuối cùng, chúng ta sẽ tìm hiểu về cách tìm kiếm các thư viện chia sẻ mà một chương trình cụ thể sẽ phụ thuộc vào.
Khái niệm về Thư viện Chia sẻ
Tương tự như phiên bản vật lý của chúng, thư viện phần mềm là bộ sưu tập mã nhằm phục vụ cho nhiều chương trình khác nhau, cũng giống như các thư viện vật lý giữ sách và các tài nguyên để nhiều người có thể sử dụng.
Để tạo một tệp thực thi từ mã nguồn của chương trình, ta cần có hai bước quan trọng. Đầu tiên, trình biên dịch sẽ biến mã nguồn thành mã máy được lưu trữ trong cái gọi là tệp đối tượng. Thứ hai, trình liên kết sẽ kết hợp các tệp đối tượng và liên kết chúng với các thư viện để tạo ra tệp thực thi cuối cùng. Liên kết này có thể được thực hiện theo phương thức tĩnh hoặc động. Tùy thuộc vào phương thức được sử dụng, chúng ta sẽ thảo luận về thư viện tĩnh hoặc về thư viện chia sẻ trong trường hợp liên kết động. Hãy cùng giải thích sự khác biệt giữa chúng.
- Thư viện tĩnh
-
Một thư viện tĩnh được hợp nhất với chương trình tại thời điểm liên kết. Một bản sao của mã thư viện sẽ được nhúng vào chương trình và trở thành một phần của nó. Do đó, chương trình sẽ không phụ thuộc vào thư viện trong thời gian chạy vì chương trình đã có chứa mã thư viện. Không có phần phụ thuộc có thể được coi là một lợi thế vì chúng ta sẽ không phải lo lắng về việc đảm bảo các thư viện được sử dụng luôn luôn sẵn sàng. Mặt khác, các chương trình được liên kết tĩnh thường sẽ nặng hơn.
- Thư viện chia sẻ (hoặc động)
-
Trong trường hợp của thư viện chia sẻ, trình liên kết chỉ cần đảm bảo rằng chương trình tham chiếu được chính xác các thư viện. Tuy nhiên, trình liên kết sẽ không sao chép bất kỳ mã thư viện nào vào tệp chương trình. Dù vậy, trong thời gian chạy, thư viện chia sẻ sẽ phải có sẵn để đáp ứng các phần phụ thuộc của chương trình. Đây là một cách tiếp cận mang tính kinh tế để quản lý tài nguyên hệ thống vì nó sẽ giúp giảm kích thước tệp chương trình và chỉ duy nhất một bản sao của thư viện được tải vào bộ nhớ ngay cả khi nó được sử dụng bởi nhiều chương trình.
Quy ước đặt tên Tệp Đối tượng Chia sẻ
Tên của một thư viện chia sẻ (hay còn được gọi là soname) được đặt tuân theo một mẫu tạo bởi ba thành phần:
-
Tên thư viện (thường có tiền tố là
lib
trong library - tức thư viện) -
so
(viết tắt của “shared object” - tức đối tượng chia sẻ) -
Số phiên bản của thư viện
Ví dụ: libpthread.so.0
Ngược lại, tên của thư viện tĩnh sẽ kết thúc bằng .a
, ví dụ như libpthread.a
.
Note
|
Bởi vì các tệp chứa thư viện chia sẻ phải có sẵn khi chương trình được thực thi, hầu hết các hệ thống Linux đều có chứa thư viện chia sẻ. Vì các thư viện tĩnh chỉ được yêu cầu trong một tệp chuyên dụng khi một chương trình được liên kết nên chúng có thể sẽ không có trên hệ thống người dùng cuối. |
glibc
(thư viện GNU C) là một ví dụ điển hình về thư viện chia sẻ. Trên hệ thống Debian GNU/Linux 9.9, tệp của nó được đặt tên là libc.so.6
. Các tên tệp chung chung như vậy thường là các liên kết tượng trưng trỏ đến tệp thực chứa một thư viện, tên của tệp sẽ chứa số phiên bản chính xác. Trong trường hợp của glibc
, liên kết tượng trưng này sẽ giống như sau:
$ ls -l /lib/x86_64-linux-gnu/libc.so.6 lrwxrwxrwx 1 root root 12 feb 6 22:17 /lib/x86_64-linux-gnu/libc.so.6 -> libc-2.24.so
Việc tham chiếu các tệp thư viện chia sẻ (được đặt tên theo một phiên bản cụ thể) tới các tên tệp chung chung hơn là khá phổ biến.
Các ví dụ khác về thư viện chia sẻ bao gồm libreadline
(cho phép người dùng chỉnh sửa các dòng lệnh khi chúng được nhập và bao gồm hỗ trợ cho cả chế độ chỉnh sửa Emacs và vi), libcrypt
(chứa các chức năng liên quan đến mã hóa, hàm băm và mã hóa) hoặc libcurl
(là thư viện truyền tệp đa giao thức).
Các vị trí phổ biến cho các thư viện chia sẻ trong hệ thống Linux là:
-
/lib
-
/lib32
-
/lib64
-
/usr/lib
-
/usr/local/lib
Note
|
Khái niệm thư viện chia sẻ không chỉ dành riêng cho Linux. Ví dụ như trong Windows, chúng được gọi là DLL, viết tắt của dynamic linked libraries, tức thư viện được liên kết động. |
Cấu hình của Đường dẫn Thư viện Chia sẻ
Các tham chiếu trong các chương trình được liên kết động sẽ được giải quyết bởi trình liên kết động (ld.so
hoặc ld-linux.so
) khi chương trình được chạy. Trình liên kết động sẽ tìm kiếm các thư viện trong một số thư mục. Các thư mục này được chỉ định bởi đường dẫn thư viện. Đường dẫn thư viện được định cấu hình trong thư mục /etc
, cụ thể là trong tệp /etc/ld.so.conf
và phổ biến hơn hiện nay là trong các tệp trong thư mục /etc/ld.so.conf.d
. Thông thường, /etc/ld.so.conf
chỉ bao gồm một dòng đơn include
cho các tệp *.conf
trong /etc/ld.so.conf.d
:
$ cat /etc/ld.so.conf include /etc/ld.so.conf.d/*.conf
Thư mục /etc/ld.so.conf.d
có chứa các tệp *.conf
:
$ ls /etc/ld.so.conf.d/ libc.conf x86_64-linux-gnu.conf
Các tệp *.conf
này phải bao gồm các đường dẫn tuyệt đối đến các thư mục thư viện chia sẻ:
$ cat /etc/ld.so.conf.d/x86_64-linux-gnu.conf # Multiarch support /lib/x86_64-linux-gnu /usr/lib/x86_64-linux-gnu
Lệnh ldconfig
đảm nhiệm việc đọc các tệp cấu hình này, tạo tập hợp các liên kết tượng trưng như đã nói ở trên giúp định vị các thư viện riêng lẻ và cuối cùng là cập nhật tệp bộ nhớ đệm /etc/ld.so.cache
. Do đó, ldconfig
phải được chạy mỗi khi tệp cấu hình được thêm hoặc cập nhật.
Các tùy chọn hữu ích cho ldconfig
là:
-v
,--verbose
-
Hiển thị số phiên bản thư viện, tên của từng thư mục và các liên kết được tạo:
$ sudo ldconfig -v /usr/local/lib: /lib/x86_64-linux-gnu: libnss_myhostname.so.2 -> libnss_myhostname.so.2 libfuse.so.2 -> libfuse.so.2.9.7 libidn.so.11 -> libidn.so.11.6.16 libnss_mdns4.so.2 -> libnss_mdns4.so.2 libparted.so.2 -> libparted.so.2.0.1 (...)
Vì vậy, chúng ta có thể thấy được cách
libfuse.so.2
được liên kết với tệp đối tượng chia sẻ thực tếlibfuse.so.2.9.7
. -p
,--print-cache
-
In danh sách các thư mục và thư viện ứng cử viên được lưu trữ trong bộ nhớ đệm hiện tại:
$ sudo ldconfig -p 1094 libs found in the cache `/etc/ld.so.cache' libzvbi.so.0 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libzvbi.so.0 libzvbi-chains.so.0 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libzvbi-chains.so.0 libzmq.so.5 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libzmq.so.5 libzeitgeist-2.0.so.0 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libzeitgeist-2.0.so.0 (...)
Hãy lưu ý cách bộ nhớ đệm sử dụng soname đủ điều kiện của các liên kết:
$ sudo ldconfig -p |grep libfuse libfuse.so.2 (libc6,x86-64) => /lib/x86_64-linux-gnu/libfuse.so.2
Nếu liệt kê dài /lib/x86_64-linux-gnu/libfuse.so.2
, chúng ta sẽ tìm thấy tham chiếu đến tệp đối tượng chia sẻ thực tế libfuse.so.2.9.7
được lưu trữ trong cùng một thư mục:
$ ls -l /lib/x86_64-linux-gnu/libfuse.so.2 lrwxrwxrwx 1 root root 16 Aug 21 2018 /lib/x86_64-linux-gnu/libfuse.so.2 -> libfuse.so.2.9.7
Note
|
Vì nó yêu cầu quyền ghi vào |
Ngoài các tệp cấu hình được mô tả ở trên, biến môi trường LD_LIBRARY_PATH
có thể được sử dụng để tạm thời thêm đường dẫn mới cho thư viện chia sẻ. Nó được tạo thành từ một tập hợp các thư mục được phân tách bằng dấu hai chấm (:
) nơi các thư viện được tra cứu. Ví dụ: để thêm /usr/local/mylib
vào đường dẫn thư viện trong phiên vỏ hiện tại, ta có thể nhập:
$ LD_LIBRARY_PATH=/usr/local/mylib
Bây giờ, ta có thể kiểm tra giá trị của nó:
$ echo $LD_LIBRARY_PATH /usr/local/mylib
Để thêm /usr/local/mylib
vào đường dẫn thư viện trong phiên vỏ hiện tại và xuất nó sang tất cả các tiến trình con được sinh ra từ vỏ đó, ta nhập:
$ export LD_LIBRARY_PATH=/usr/local/mylib
Để xóa biến môi trường LD_LIBRARY_PATH
, ta chỉ cần gõ:
$ unset LD_LIBRARY_PATH
Để làm cho thay đổi trở thành vĩnh viễn, ta có thể viết:
export LD_LIBRARY_PATH=/usr/local/mylib
vào trong một trong các tập lệnh khởi tạo của Bash, chẳng hạn như /etc/bash.bashrc
hoặc ~/.bashrc
.
Note
|
|
Tìm kiếm các Phần Phụ thuộc của một Tệp Thực thi cụ thể
Để tra cứu các thư viện chia sẻ theo yêu cầu của một chương trình cụ thể, hãy sử dụng lệnh ldd
, theo sau là đường dẫn tuyệt đối tới chương trình. Đầu ra sẽ hiển thị đường dẫn của tệp thư viện chia sẻ cũng như địa chỉ bộ nhớ hệ thập lục phân nơi nó được tải:
$ ldd /usr/bin/git linux-vdso.so.1 => (0x00007ffcbb310000) libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f18241eb000) libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f1823fd1000) libresolv.so.2 => /lib/x86_64-linux-gnu/libresolv.so.2 (0x00007f1823db6000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f1823b99000) librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f1823991000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f18235c7000) /lib64/ld-linux-x86-64.so.2 (0x00007f182445b000)
Tương tự như vậy, chúng ta có thể sử dụng ldd
để tìm kiếm các phần phụ thuộc của một đối tượng chia sẻ:
$ ldd /lib/x86_64-linux-gnu/libc.so.6 /lib64/ld-linux-x86-64.so.2 (0x00007fbfed578000) linux-vdso.so.1 (0x00007fffb7bf5000)
Với tùy chọn -u
(hoặc --unused
), ldd
sẽ in các phần phụ thuộc trực tiếp không được sử dụng (nếu chúng tồn tại):
$ ldd -u /usr/bin/git Unused direct dependencies: /lib/x86_64-linux-gnu/libz.so.1 /lib/x86_64-linux-gnu/libpthread.so.0 /lib/x86_64-linux-gnu/librt.so.1
Lý do cho các phần phụ thuộc không được sử dụng có liên quan đến các tùy chọn được trình liên kết sử dụng khi xây dựng tệp nhị phân. Mặc dù chương trình không cần tới một thư viện không được sử dụng, nó vẫn sẽ được liên kết và gắn nhãn là NEEDED
trong thông tin về tệp đối tượng. Ta có thể xem kỹ điều này bằng cách sử dụng các lệnh như readelf
hoặc objdump
mà chúng ta sẽ sớm phải áp dụng trong phần bài tập mở rộng.
Bài tập Hướng dẫn
-
Hãy chia các tên thư viện chia sẻ sau thành các phần của chúng:
Tên Tệp đầy đủ Tên Thư viện Hậu tố so Số phiên bản linux-vdso.so.1
libprocps.so.6
libdl.so.2
libc.so.6
libsystemd.so.0
ld-linux-x86-64.so.2
-
Bạn đã phát triển một phần mềm và muốn thêm một thư mục thư viện chia sẻ mới vào hệ thống của mình (
/opt/lib/mylib
). Bạn đã viết đường dẫn tuyệt đối của nó trong tệp có tênmylib.conf
.-
Bạn nên đặt tệp này trong thư mục nào?
-
Bạn nên chạy lệnh nào để các thay đổi có hiệu lực hoàn toàn?
-
-
Bạn sẽ sử dụng lệnh nào để liệt kê các thư viện chia sẻ theo yêu cầu của
kill
?
Bài tập Mở rộng
-
objdump
là một tiện ích dòng lệnh hiển thị thông tin từ các tệp đối tượng. Hãy kiểm tra xem nó đã được cài đặt trong hệ thống hay chưa vớiwhich objdump
. Nếu chưa thì bạn hãy cài đặt nó ngay bây giờ.-
Hãy sử dụng
objdump
với tùy chọn-p
(hoặc--private-headers
) vàgrep
để in các phần phụ thuộc củaglibc
: -
Hãy sử dụng
objdump
với tùy chọn-p
(hoặc--private-headers
) vàgrep
để in soname củaglibc
: -
Hãy sử dụng
objdump
với tùy chọn-p
(hoặc--private-headers
) vàgrep
để in các phần phụ thuộc của Bash:
-
Tóm tắt
Trong bài học này, chúng ta đã học về:
-
Thư viện chia sẻ (hoặc thư viện động) là gì.
-
Sự khác nhau giữa thư viện chia sẻ và thư viện tĩnh.
-
Tên của các thư viện chia sẻ (sonames).
-
Các vị trí thích hợp dành cho thư viện chia sẻ trong hệ thống Linux, chẳng hạn như
/lib
hoặc/usr/lib
. -
Mục đích của trình liên kết động
ld.so
(hoặcld-linux.so
). -
Cách định cấu hình đường dẫn thư viện chia sẻ bằng các tệp trong
/etc/
, chẳng hạn nhưld.so.conf
hoặc các tệp trong thư mụcld.so.conf.d
. -
Cách định cấu hình đường dẫn thư viện chia sẻ bằng biến môi trường
LD_LIBRARY_PATH
. -
Cách tra cứu các phần phụ thuộc thư viện chia sẻ và thực thi được.
Các lệnh đã được dùng trong bài học này:
ls
-
Liệt kê nội dung thư mục.
cat
-
Nối các tệp và in trên đầu ra tiêu chuẩn.
sudo
-
Yêu cầu siêu người dùng thực thi lệnh với quyền của quản trị viên hệ thống.
ldconfig
-
Định cấu hình liên kết thời gian chạy trình liên kết động.
echo
-
Hiển thị giá trị của biến môi trường.
export
-
Xuất giá trị của biến môi trường sang vỏ con.
unset
-
Xóa biến môi trường.
ldd
-
In các phần phụ thuộc đối tượng chia sẻ của một chương trình.
readelf
-
Hiển thị thông tin về các tệp ELF (ELF là viết tắt của executable and linkable format - tức định dạng thực thi và có thể liên kết).
objdump
-
In thông tin từ tệp đối tượng.
Đáp án Bài tập Hướng dẫn
-
Hãy chia các tên thư viện chia sẻ sau thành các phần của chúng:
Tên Tệp đầy đủ Tên Thư viện Hậu tố so Số phiên bản linux-vdso.so.1
linux-vdso
so
1
libprocps.so.6
libprocps
so
6
libdl.so.2
libdl
so
2
libc.so.6
libc
so
6
libsystemd.so.0
libsystemd
so
0
ld-linux-x86-64.so.2
ld-linux-x86-64
so
2
-
Bạn đã phát triển một phần mềm và muốn thêm một thư mục thư viện chia sẻ mới vào hệ thống của mình (
/opt/lib/mylib
). Bạn đã viết đường dẫn tuyệt đối của nó trong tệp có tênmylib.conf
.-
Bạn nên đặt tệp này trong thư mục nào?
/etc/ld.so.conf.d
-
Bạn nên chạy lệnh nào để các thay đổi có hiệu lực hoàn toàn?
ldconfig
-
-
Bạn sẽ sử dụng lệnh nào để liệt kê các thư viện chia sẻ theo yêu cầu của
kill
?ldd /bin/kill
Đáp án Bài tập Mở rộng
-
objdump
là một tiện ích dòng lệnh hiển thị thông tin từ các tệp đối tượng. Hãy kiểm tra xem nó đã được cài đặt trong hệ thống hay chưa vớiwhich objdump
. Nếu chưa thì bạn hãy cài đặt nó ngay bây giờ.-
Hãy sử dụng
objdump
với tùy chọn-p
(hoặc--private-headers
) vàgrep
để in các phần phụ thuộc củaglibc
:objdump -p /lib/x86_64-linux-gnu/libc.so.6 | grep NEEDED
-
Hãy sử dụng
objdump
với tùy chọn-p
(hoặc--private-headers
) vàgrep
để in soname củaglibc
:objdump -p /lib/x86_64-linux-gnu/libc.so.6 | grep SONAME
-
Hãy sử dụng
objdump
với tùy chọn-p
(hoặc--private-headers
) vàgrep
để in các phần phụ thuộc của Bash:objdump -p /bin/bash | grep NEEDED
-