Một lần, trong một cuộc phỏng vấn, tôi được hỏi, bạn sẽ làm gì nếu thấy một dịch vụ không hoạt động do đĩa đã hết dung lượng?
Tất nhiên, tôi trả lời rằng tôi sẽ xem nơi này có gì và nếu có thể, tôi sẽ dọn dẹp nơi này.
Sau đó, người phỏng vấn hỏi, nếu không có dung lượng trống trên phân vùng nhưng bạn cũng không thấy bất kỳ tệp nào chiếm hết dung lượng thì sao?
Về điều này, tôi đã nói rằng bạn luôn có thể xem các bộ mô tả tệp đang mở, chẳng hạn như bằng lệnh lsof và hiểu ứng dụng nào đã chiếm hết dung lượng có sẵn, sau đó bạn có thể hành động tùy theo trường hợp, tùy thuộc vào việc dữ liệu có cần thiết hay không .
Người phỏng vấn ngắt lời tôi ở lời cuối cùng, thêm vào câu hỏi của anh ta: “Giả sử chúng ta không cần dữ liệu, nó chỉ là nhật ký gỡ lỗi, nhưng ứng dụng không hoạt động vì không thể viết bản gỡ lỗi”?
“Được rồi,” tôi trả lời, “chúng ta có thể tắt tính năng gỡ lỗi trong cấu hình ứng dụng và khởi động lại nó.”
Người phỏng vấn phản đối: “Không, chúng tôi không thể khởi động lại ứng dụng, chúng tôi vẫn còn dữ liệu quan trọng được lưu trong bộ nhớ và các máy khách quan trọng đã được kết nối với chính dịch vụ mà chúng tôi không thể buộc kết nối lại”.
“Được rồi,” tôi nói, “nếu chúng tôi không thể khởi động lại ứng dụng và dữ liệu không quan trọng đối với chúng tôi thì chúng tôi có thể chỉ cần xóa tệp đang mở này thông qua bộ mô tả tệp, ngay cả khi chúng tôi không thấy nó trong lệnh ls trên hệ thống tập tin.”
Người phỏng vấn hài lòng, còn tôi thì không.
Sau đó tôi nghĩ, tại sao người kiểm tra kiến thức của tôi không đào sâu hơn? Nhưng nếu dữ liệu đó quan trọng thì sao? Điều gì sẽ xảy ra nếu chúng ta không thể khởi động lại một quy trình và quy trình đó ghi vào hệ thống tệp trên một phân vùng không còn dung lượng trống? Điều gì sẽ xảy ra nếu chúng ta không thể mất không chỉ dữ liệu đã được ghi mà còn cả dữ liệu mà quá trình này ghi hoặc cố gắng ghi?
Tuzik
Khi mới bắt đầu sự nghiệp, tôi đã cố gắng tạo một ứng dụng nhỏ cần lưu trữ thông tin người dùng. Và sau đó tôi nghĩ, làm cách nào để khớp người dùng với dữ liệu của anh ấy. Ví dụ, tôi có Ivanov Ivan Ivanovich, anh ấy có một số thông tin, nhưng làm sao tôi có thể kết bạn với họ? Tôi có thể chỉ ra trực tiếp rằng con chó tên “Tuzik” thuộc về chính Ivan này. Nhưng điều gì sẽ xảy ra nếu anh ta đổi tên và thay vì Ivan lại trở thành Olya chẳng hạn? Sau đó, hóa ra Olya Ivanovna Ivanova của chúng ta sẽ không còn con chó nữa, và Tuzik của chúng ta vẫn sẽ thuộc về Ivan không tồn tại. Cơ sở dữ liệu cung cấp cho mỗi người dùng một mã định danh (ID) duy nhất đã giúp giải quyết vấn đề này và Tuzik của tôi được gắn với ID này, trên thực tế, chỉ là một số sê-ri. Do đó, chủ nhân của quân át có ID số 2, và tại một thời điểm nào đó Ivan có ID này, và sau đó Olya trở thành có cùng ID. Vấn đề nhân loại và chăn nuôi đã được giải quyết trên thực tế.
Bộ mô tả tập tin
Vấn đề của tệp và chương trình hoạt động với tệp này gần giống như vấn đề của con chó và con người của chúng ta. Giả sử tôi mở một tệp có tên ivan.txt và bắt đầu viết từ tuzik vào đó, nhưng chỉ viết được chữ cái đầu tiên “t” trong tệp và tệp này đã được ai đó đổi tên, chẳng hạn như thành olya.txt. Nhưng tập tin vẫn giữ nguyên và tôi vẫn muốn ghi con át chủ bài của mình vào đó. Mỗi lần một tập tin được mở bằng lệnh gọi hệ thống
Trong Linux, thư viện libc mở 3 tệp mô tả cho mỗi ứng dụng (tiến trình) đang chạy, được đánh số 0,1,2. Thông tin thêm có thể được tìm thấy trên các liên kết
- Bộ mô tả tệp 0 được gọi là STDIN và được liên kết với đầu vào ứng dụng
- Bộ mô tả tệp 1 được gọi là STDOUT và được các ứng dụng sử dụng để xuất dữ liệu, chẳng hạn như lệnh in
- Bộ mô tả tệp 2 được gọi là STDERR và được các ứng dụng sử dụng để xuất thông báo lỗi.
Nếu trong chương trình của bạn, bạn mở bất kỳ tệp nào để đọc hoặc ghi, thì rất có thể bạn sẽ nhận được ID miễn phí đầu tiên và nó sẽ là số 3.
Danh sách các bộ mô tả tệp có thể được xem cho bất kỳ quy trình nào nếu bạn biết PID của nó.
Ví dụ: hãy mở bảng điều khiển bash và xem PID của quy trình của chúng tôi
[user@localhost ]$ echo $$
15771
Trong bảng điều khiển thứ hai hãy chạy
[user@localhost ]$ ls -lah /proc/15771/fd/
total 0
dr-x------ 2 user user 0 Oct 7 15:42 .
dr-xr-xr-x 9 user user 0 Oct 7 15:42 ..
lrwx------ 1 user user 64 Oct 7 15:42 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 255 -> /dev/pts/21
Bạn có thể yên tâm bỏ qua phần mô tả tệp số 255 vì mục đích của bài viết này; nó được mở theo nhu cầu của chính nó bằng chính bash chứ không phải bởi thư viện được liên kết.
Bây giờ cả 3 tệp mô tả đều được liên kết với thiết bị đầu cuối giả
[user@localhost ]$ echo "hello world" > /proc/15771/fd/0
Và trong bảng điều khiển đầu tiên chúng ta sẽ thấy
[user@localhost ]$ hello world
Chuyển hướng và ống
Bạn có thể dễ dàng ghi đè 3 tệp mô tả này trong bất kỳ quy trình nào, kể cả trong bash, chẳng hạn như thông qua một đường ống kết nối hai quy trình, xem
[user@localhost ]$ cat /dev/zero | sleep 10000
Bạn có thể tự chạy lệnh này với dấu vết -f và xem chuyện gì đang xảy ra bên trong, nhưng tôi sẽ kể cho bạn nghe ngắn gọn.
Quá trình bash gốc của chúng tôi với PID 15771 phân tích lệnh của chúng tôi và hiểu chính xác có bao nhiêu lệnh chúng tôi muốn chạy, trong trường hợp của chúng tôi có hai trong số đó: cat và sleep. Bash biết rằng nó cần tạo hai tiến trình con và hợp nhất chúng thành một ống. Tổng cộng, bash sẽ cần 2 tiến trình con và một đường ống.
Bash chạy lệnh gọi hệ thống trước khi tạo tiến trình con
Đối với quy trình gốc, có vẻ như đã có một đường ống, nhưng chưa có quy trình con nào:
PID command
15771 bash
lrwx------ 1 user user 64 Oct 7 15:42 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:42 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:42 255 -> /dev/pts/21
Sau đó sử dụng lệnh gọi hệ thống
PID command
15771 bash
lrwx------ 1 user user 64 Oct 7 15:42 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:42 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:42 255 -> /dev/pts/21
PID command
9004 bash
lrwx------ 1 user user 64 Oct 7 15:57 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:57 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:57 255 -> /dev/pts/21
PID command
9005 bash
lrwx------ 1 user user 64 Oct 7 15:57 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:57 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:57 255 -> /dev/pts/21
Đừng quên rằng bản sao sẽ sao chép quy trình cùng với tất cả các bộ mô tả tệp, vì vậy chúng sẽ giống nhau trong quy trình cha và quy trình con. Công việc của tiến trình cha với PID 15771 là giám sát các tiến trình con nên nó chỉ chờ phản hồi từ tiến trình con.
Do đó, nó không cần pipe và nó đóng các bộ mô tả tệp được đánh số 3 và 4.
Trong quy trình bash con đầu tiên với PID 9004, lệnh gọi hệ thống
Trong tiến trình con thứ hai với PID 9005, bash sử dụng dup2 để thay đổi bộ mô tả tệp STDIN số 0. Bây giờ mọi thứ mà bash thứ hai của chúng ta với PID 9005 sẽ đọc sẽ được đọc từ đường ống.
Sau đó, các bộ mô tả tệp được đánh số 3 và 4 cũng bị đóng trong các tiến trình con vì chúng không còn được sử dụng nữa.
Tôi cố tình bỏ qua bộ mô tả tập tin 255; nó được sử dụng cho mục đích nội bộ bởi chính bash và cũng sẽ bị đóng trong các tiến trình con.
Tiếp theo, trong tiến trình con đầu tiên với PID 9004, bash bắt đầu sử dụng lệnh gọi hệ thống
Trong quy trình con thứ hai với PID 9005, bash chạy tệp thực thi thứ hai mà chúng tôi đã chỉ định, trong trường hợp của chúng tôi là /usr/bin/sleep.
Lệnh gọi hệ thống exec không đóng các thẻ điều khiển tệp trừ khi chúng được mở bằng cờ O_CLOEXEC tại thời điểm lệnh gọi mở được thực hiện. Trong trường hợp của chúng tôi, sau khi khởi chạy các tệp thực thi, tất cả các bộ mô tả tệp hiện tại sẽ được lưu.
Kiểm tra trong bảng điều khiển:
[user@localhost ]$ pgrep -P 15771
9004
9005
[user@localhost ]$ ls -lah /proc/15771/fd/
total 0
dr-x------ 2 user user 0 Oct 7 15:42 .
dr-xr-xr-x 9 user user 0 Oct 7 15:42 ..
lrwx------ 1 user user 64 Oct 7 15:42 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 255 -> /dev/pts/21
[user@localhost ]$ ls -lah /proc/9004/fd
total 0
dr-x------ 2 user user 0 Oct 7 15:57 .
dr-xr-xr-x 9 user user 0 Oct 7 15:57 ..
lrwx------ 1 user user 64 Oct 7 15:57 0 -> /dev/pts/21
l-wx------ 1 user user 64 Oct 7 15:57 1 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:57 2 -> /dev/pts/21
lr-x------ 1 user user 64 Oct 7 15:57 3 -> /dev/zero
[user@localhost ]$ ls -lah /proc/9005/fd
total 0
dr-x------ 2 user user 0 Oct 7 15:57 .
dr-xr-xr-x 9 user user 0 Oct 7 15:57 ..
lr-x------ 1 user user 64 Oct 7 15:57 0 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:57 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 2 -> /dev/pts/21
[user@localhost ]$ ps -up 9004
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
user 9004 0.0 0.0 107972 620 pts/21 S+ 15:57 0:00 cat /dev/zero
[user@localhost ]$ ps -up 9005
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
user 9005 0.0 0.0 107952 360 pts/21 S+ 15:57 0:00 sleep 10000
Như bạn có thể thấy, số lượng ống duy nhất của chúng tôi là như nhau trong cả hai quy trình. Do đó, chúng ta có một kết nối giữa hai tiến trình khác nhau có cùng một tiến trình gốc.
Đối với những người không quen với các lệnh gọi hệ thống mà bash sử dụng, tôi thực sự khuyên bạn nên chạy các lệnh thông qua strace và xem điều gì đang xảy ra bên trong, ví dụ như thế này:
strace -s 1024 -f bash -c "ls | grep hello"
Hãy quay lại vấn đề dung lượng ổ đĩa thấp và cố gắng lưu dữ liệu mà không cần khởi động lại quá trình. Hãy viết một chương trình nhỏ có tốc độ ghi khoảng 1 megabyte mỗi giây vào đĩa. Hơn nữa, nếu vì lý do nào đó mà chúng tôi không thể ghi dữ liệu vào đĩa, chúng tôi sẽ đơn giản bỏ qua điều này và cố gắng ghi lại dữ liệu sau một giây. Trong ví dụ tôi đang sử dụng Python, bạn có thể sử dụng bất kỳ ngôn ngữ lập trình nào khác.
[user@localhost ]$ cat openforwrite.py
import datetime
import time
mystr="a"*1024*1024+"n"
with open("123.txt", "w") as f:
while True:
try:
f.write(str(datetime.datetime.now()))
f.write(mystr)
f.flush()
time.sleep(1)
except:
pass
Hãy chạy chương trình và xem phần mô tả tập tin
[user@localhost ]$ python openforwrite.py &
[1] 3762
[user@localhost ]$ ps axuf | grep [o]penforwrite
user 3762 0.0 0.0 128600 5744 pts/22 S+ 16:28 0:00 | _ python openforwrite.py
[user@localhost ]$ ls -la /proc/3762/fd
total 0
dr-x------ 2 user user 0 Oct 7 16:29 .
dr-xr-xr-x 9 user user 0 Oct 7 16:29 ..
lrwx------ 1 user user 64 Oct 7 16:29 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 7 16:29 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 7 16:29 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 7 16:29 3 -> /home/user/123.txt
Như bạn có thể thấy, chúng tôi có 3 bộ mô tả tệp tiêu chuẩn và một bộ mô tả tệp khác mà chúng tôi đã mở. Hãy kiểm tra kích thước tập tin:
[user@localhost ]$ ls -lah 123.txt
-rw-rw-r-- 1 user user 117M Oct 7 16:30 123.txt
Dữ liệu đang được ghi, chúng tôi cố gắng thay đổi quyền trên tệp:
[user@localhost ]$ sudo chown root: 123.txt
[user@localhost ]$ ls -lah 123.txt
-rw-rw-r-- 1 root root 168M Oct 7 16:31 123.txt
[user@localhost ]$ ls -lah 123.txt
-rw-rw-r-- 1 root root 172M Oct 7 16:31 123.txt
Chúng tôi thấy rằng dữ liệu vẫn đang được ghi, mặc dù người dùng của chúng tôi không có quyền ghi vào tệp. Hãy thử loại bỏ nó:
[user@localhost ]$ sudo rm 123.txt
[user@localhost ]$ ls 123.txt
ls: cannot access 123.txt: No such file or directory
Dữ liệu được viết ở đâu? Và chúng có được viết không? Chung ta kiểm tra:
[user@localhost ]$ ls -la /proc/3762/fd
total 0
dr-x------ 2 user user 0 Oct 7 16:29 .
dr-xr-xr-x 9 user user 0 Oct 7 16:29 ..
lrwx------ 1 user user 64 Oct 7 16:29 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 7 16:29 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 7 16:29 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 7 16:29 3 -> /home/user/123.txt (deleted)
Có, bộ mô tả tệp của chúng tôi vẫn tồn tại và chúng tôi có thể coi bộ mô tả tệp này giống như tệp cũ của mình, chúng tôi có thể đọc, xóa và sao chép nó.
Hãy nhìn vào kích thước tập tin:
[user@localhost ]$ lsof | grep 123.txt
python 31083 user 3w REG 8,5 19923457 2621522 /home/user/123.txt
Kích thước tệp là 19923457. Hãy thử xóa tệp:
[user@localhost ]$ truncate -s 0 /proc/31083/fd/3
[user@localhost ]$ lsof | grep 123.txt
python 31083 user 3w REG 8,5 136318390 2621522 /home/user/123.txt
Như bạn có thể thấy, kích thước tệp ngày càng tăng và đường trung kế của chúng tôi không hoạt động. Hãy xem tài liệu cuộc gọi hệ thống
with open("123.txt", "w") as f:
chúng ta phải đặt
with open("123.txt", "a") as f:
Kiểm tra bằng cờ “w”
[user@localhost ]$ strace -e trace=open python openforwrite.py 2>&1| grep 123.txt
open("123.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
và với cờ "a"
[user@localhost ]$ strace -e trace=open python openforwrite.py 2>&1| grep 123.txt
open("123.txt", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
Lập trình một tiến trình đang chạy
Thông thường, các lập trình viên, khi tạo và thử nghiệm chương trình, sử dụng trình gỡ lỗi (ví dụ GDB) hoặc nhiều cấp độ đăng nhập khác nhau vào ứng dụng. Linux cung cấp khả năng thực sự viết và thay đổi một chương trình đang chạy, ví dụ: thay đổi giá trị của các biến, đặt điểm dừng, v.v., v.v.
Quay trở lại câu hỏi ban đầu về việc không đủ dung lượng ổ đĩa để ghi một tập tin, chúng ta hãy thử mô phỏng vấn đề.
Hãy tạo một tệp cho phân vùng của chúng tôi, chúng tôi sẽ gắn tệp này dưới dạng một đĩa riêng:
[user@localhost ~]$ dd if=/dev/zero of=~/tempfile_for_article.dd bs=1M count=10
10+0 records in
10+0 records out
10485760 bytes (10 MB) copied, 0.00525929 s, 2.0 GB/s
[user@localhost ~]$
Hãy tạo một hệ thống tập tin:
[user@localhost ~]$ mkfs.ext4 ~/tempfile_for_article.dd
mke2fs 1.42.9 (28-Dec-2013)
/home/user/tempfile_for_article.dd is not a block special device.
Proceed anyway? (y,n) y
...
Writing superblocks and filesystem accounting information: done
[user@localhost ~]$
Gắn kết hệ thống tập tin:
[user@localhost ~]$ sudo mount ~/tempfile_for_article.dd /mnt/
[sudo] password for user:
[user@localhost ~]$ df -h | grep mnt
/dev/loop0 8.7M 172K 7.9M 3% /mnt
Chúng tôi tạo một thư mục với chủ sở hữu của chúng tôi:
[user@localhost ~]$ sudo mkdir /mnt/logs
[user@localhost ~]$ sudo chown user: /mnt/logs
Hãy mở tệp chỉ để viết trong chương trình của chúng tôi:
with open("/mnt/logs/123.txt", "w") as f:
Phóng
[user@localhost ]$ python openforwrite.py
Chúng tôi đợi vài giây
[user@localhost ~]$ df -h | grep mnt
/dev/loop0 8.7M 8.0M 0 100% /mnt
Vì vậy, chúng tôi có vấn đề được mô tả ở đầu bài viết này. Không gian trống 0, đã chiếm 100%.
Chúng tôi nhớ rằng tùy theo điều kiện của nhiệm vụ, chúng tôi đang cố gắng ghi lại những dữ liệu rất quan trọng không thể bị mất. Và đồng thời, chúng ta cần sửa dịch vụ mà không cần khởi động lại quy trình.
Giả sử chúng ta vẫn còn dung lượng ổ đĩa, nhưng ở một phân vùng khác, chẳng hạn như trong /home.
Hãy thử “lập trình lại nhanh chóng” mã của chúng ta.
Hãy xem xét PID của quy trình của chúng tôi, nó đã chiếm hết dung lượng ổ đĩa:
[user@localhost ~]$ ps axuf | grep [o]penfor
user 10078 27.2 0.0 128600 5744 pts/22 R+ 11:06 0:02 | _ python openforwrite.py
Kết nối với quá trình thông qua gdb
[user@localhost ~]$ gdb -p 10078
...
(gdb)
Hãy xem các mô tả tệp đang mở:
(gdb) shell ls -lah /proc/10078/fd/
total 0
dr-x------ 2 user user 0 Oct 8 11:06 .
dr-xr-xr-x 9 user user 0 Oct 8 11:06 ..
lrwx------ 1 user user 64 Oct 8 11:09 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:09 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:06 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:09 3 -> /mnt/logs/123.txt
Chúng tôi xem thông tin về bộ mô tả tập tin số 3 mà chúng tôi quan tâm
(gdb) shell cat /proc/10078/fdinfo/3
pos: 8189952
flags: 0100001
mnt_id: 482
Hãy ghi nhớ lệnh gọi hệ thống mà Python thực hiện (xem ở trên nơi chúng tôi chạy strace và tìm thấy lệnh gọi mở), khi xử lý mã của chúng tôi để mở một tệp, chúng tôi tự thực hiện điều tương tự thay mặt cho quy trình của mình, nhưng chúng tôi cần O_WRONLY|O_CREAT| Các bit O_TRUNC thay thế bằng một giá trị số. Để thực hiện việc này, hãy mở nguồn kernel, ví dụ:
#define O_WRONLY 00000001
#define O_CREAT 00000100
#define O_TRUNC 00001000
Chúng tôi kết hợp tất cả các giá trị thành một, chúng tôi nhận được 00001101
Chúng tôi thực hiện cuộc gọi của mình từ gdb
(gdb) call open("/home/user/123.txt", 00001101,0666)
$1 = 4
Vì vậy, chúng tôi có một bộ mô tả tệp mới với số 4 và một tệp đang mở mới trên một phân vùng khác, chúng tôi kiểm tra:
(gdb) shell ls -lah /proc/10078/fd/
total 0
dr-x------ 2 user user 0 Oct 8 11:06 .
dr-xr-xr-x 9 user user 0 Oct 8 11:06 ..
lrwx------ 1 user user 64 Oct 8 11:09 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:09 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:06 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:09 3 -> /mnt/logs/123.txt
l-wx------ 1 user user 64 Oct 8 11:15 4 -> /home/user/123.txt
Chúng ta nhớ lại ví dụ về pipe - cách bash thay đổi bộ mô tả tệp và chúng ta đã học được lệnh gọi hệ thống dup2.
Chúng tôi cố gắng thay thế một bộ mô tả tập tin bằng một bộ mô tả tập tin khác
(gdb) call dup2(4,3)
$2 = 3
Chúng tôi kiểm tra:
(gdb) shell ls -lah /proc/10078/fd/
total 0
dr-x------ 2 user user 0 Oct 8 11:06 .
dr-xr-xr-x 9 user user 0 Oct 8 11:06 ..
lrwx------ 1 user user 64 Oct 8 11:09 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:09 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:06 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:09 3 -> /home/user/123.txt
l-wx------ 1 user user 64 Oct 8 11:15 4 -> /home/user/123.txt
Chúng tôi đóng bộ mô tả tệp 4 vì chúng tôi không cần nó:
(gdb) call close (4)
$1 = 0
Và thoát gdb
(gdb) quit
A debugging session is active.
Inferior 1 [process 10078] will be detached.
Quit anyway? (y or n) y
Detaching from program: /usr/bin/python2.7, process 10078
Kiểm tra tập tin mới:
[user@localhost ~]$ ls -lah /home/user/123.txt
-rw-rw-r-- 1 user user 5.1M Oct 8 11:18 /home/user/123.txt
[user@localhost ~]$ ls -lah /home/user/123.txt
-rw-rw-r-- 1 user user 7.1M Oct 8 11:18 /home/user/123.txt
Như bạn có thể thấy, dữ liệu được ghi vào một tệp mới, hãy kiểm tra tệp cũ:
[user@localhost ~]$ ls -lah /mnt/logs/123.txt
-rw-rw-r-- 1 user user 7.9M Oct 8 11:08 /mnt/logs/123.txt
Không có dữ liệu nào bị mất, ứng dụng hoạt động, nhật ký được ghi vào vị trí mới.
Hãy phức tạp hóa nhiệm vụ một chút
Hãy tưởng tượng rằng dữ liệu quan trọng đối với chúng ta nhưng chúng ta không có dung lượng ổ đĩa trong bất kỳ phân vùng nào và chúng ta không thể kết nối ổ đĩa.
Những gì chúng ta có thể làm là chuyển hướng dữ liệu của mình đến một nơi nào đó, chẳng hạn như đến đường ống, và lần lượt chuyển hướng dữ liệu từ đường ống sang mạng thông qua một số chương trình, chẳng hạn như netcat.
Chúng ta có thể tạo một đường ống được đặt tên bằng lệnh mkfifo. Nó sẽ tạo một tệp giả trên hệ thống tệp ngay cả khi không có dung lượng trống trên đó.
Khởi động lại ứng dụng và kiểm tra:
[user@localhost ]$ python openforwrite.py
[user@localhost ~]$ ps axuf | grep [o]pen
user 5946 72.9 0.0 128600 5744 pts/22 R+ 11:27 0:20 | _ python openforwrite.py
[user@localhost ~]$ ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user 0 Oct 8 11:27 .
dr-xr-xr-x 9 user user 0 Oct 8 11:27 ..
lrwx------ 1 user user 64 Oct 8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:28 3 -> /mnt/logs/123.txt
[user@localhost ~]$ df -h | grep mnt
/dev/loop0 8.7M 8.0M 0 100% /mnt
Không có dung lượng ổ đĩa, nhưng chúng tôi đã tạo thành công một đường ống có tên ở đó:
[user@localhost ~]$ mkfifo /mnt/logs/megapipe
[user@localhost ~]$ ls -lah /mnt/logs/megapipe
prw-rw-r-- 1 user user 0 Oct 8 11:28 /mnt/logs/megapipe
Bây giờ chúng ta cần bằng cách nào đó bọc tất cả dữ liệu đi vào đường ống này đến một máy chủ khác qua mạng; cùng một netcat phù hợp cho việc này.
Trên máy chủ remote-server.example.com chúng tôi chạy
[user@localhost ~]$ nc -l 7777 > 123.txt
Trên máy chủ có vấn đề của chúng tôi, chúng tôi khởi chạy trong một thiết bị đầu cuối riêng biệt
[user@localhost ~]$ nc remote-server.example.com 7777 < /mnt/logs/megapipe
Bây giờ tất cả dữ liệu kết thúc trong đường ống sẽ tự động chuyển đến stdin trong netcat, dữ liệu này sẽ gửi đến mạng trên cổng 7777.
Tất cả những gì chúng ta phải làm là bắt đầu ghi dữ liệu của mình vào đường dẫn có tên này.
Chúng tôi đã có ứng dụng đang chạy:
[user@localhost ~]$ ps axuf | grep [o]pen
user 5946 99.8 0.0 128600 5744 pts/22 R+ 11:27 169:27 | _ python openforwrite.py
[user@localhost ~]$ ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user 0 Oct 8 11:27 .
dr-xr-xr-x 9 user user 0 Oct 8 11:27 ..
lrwx------ 1 user user 64 Oct 8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:28 3 -> /mnt/logs/123.txt
Trong tất cả các cờ, chúng tôi chỉ cần O_WRONLY vì tệp đã tồn tại và chúng tôi không cần xóa nó
[user@localhost ~]$ gdb -p 5946
...
(gdb) call open("/mnt/logs/megapipe", 00000001,0666)
$1 = 4
(gdb) shell ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user 0 Oct 8 11:27 .
dr-xr-xr-x 9 user user 0 Oct 8 11:27 ..
lrwx------ 1 user user 64 Oct 8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:28 3 -> /mnt/logs/123.txt
l-wx------ 1 user user 64 Oct 8 14:20 4 -> /mnt/logs/megapipe
(gdb) call dup2(4,3)
$2 = 3
(gdb) shell ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user 0 Oct 8 11:27 .
dr-xr-xr-x 9 user user 0 Oct 8 11:27 ..
lrwx------ 1 user user 64 Oct 8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:28 3 -> /mnt/logs/megapipe
l-wx------ 1 user user 64 Oct 8 14:20 4 -> /mnt/logs/megapipe
(gdb) call close(4)
$3 = 0
(gdb) shell ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user 0 Oct 8 11:27 .
dr-xr-xr-x 9 user user 0 Oct 8 11:27 ..
lrwx------ 1 user user 64 Oct 8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:28 3 -> /mnt/logs/megapipe
(gdb) quit
A debugging session is active.
Inferior 1 [process 5946] will be detached.
Quit anyway? (y or n) y
Detaching from program: /usr/bin/python2.7, process 5946
Kiểm tra máy chủ từ xa remote-server.example.com
[user@localhost ~]$ ls -lah 123.txt
-rw-rw-r-- 1 user user 38M Oct 8 14:21 123.txt
Dữ liệu đang đến, chúng tôi kiểm tra máy chủ có vấn đề
[user@localhost ~]$ ls -lah /mnt/logs/
total 7.9M
drwxr-xr-x 2 user user 1.0K Oct 8 11:28 .
drwxr-xr-x 4 root root 1.0K Oct 8 10:55 ..
-rw-rw-r-- 1 user user 7.9M Oct 8 14:17 123.txt
prw-rw-r-- 1 user user 0 Oct 8 14:22 megapipe
Dữ liệu được lưu, vấn đề được giải quyết.
Tôi nhân cơ hội này để gửi lời chào đến các đồng nghiệp của tôi từ Degiro.
Nghe podcast Radio-T.
Tốt cho tất cả.
Để làm bài tập về nhà, tôi khuyên bạn nên suy nghĩ về những gì sẽ có trong bộ mô tả tệp tiến trình cat và sleep nếu bạn chạy lệnh sau:
[user@localhost ~]$ cat /dev/zero 2>/dev/null| sleep 10000
Nguồn: www.habr.com