Linux 中的文件描述符及其示例

有一次,在一次採訪中,有人問我,如果由於磁盤空間不足而導致服務損壞,您會怎麼做?

當然,我回答說我會看看這個地方在做什麼,如果可能的話,我會清理這個地方。
然後面試官問,如果分區沒有可用空間,但你也沒有看到佔用所有空間的文件怎麼辦?

對此,我說你可以隨時查看打開的文件描述符,例如使用 lsof 命令,了解哪個應用程序佔用了所有可用空間,然後你可以根據情況採取行動,具體取決於是否需要數據。

面試官在最後一句話中打斷了我,並補充了他的問題:“假設我們不需要數據,它只是一個調試日誌,但應用程序因為無法寫入調試而關閉了”?

“好吧,”我回答道,“我們可以在應用程序配置中關閉調試並重新啟動它。”
面試官反對:“不,我們不能重新啟動應用程序,我們內存中仍然有重要數據,重要的客戶端連接到服務本身,我們不能強制重新連接。”

“好吧,”我說,“如果我們無法重新啟動應用程序並且我們不關心數據,那麼我們可以通過文件描述符清除這個打開的文件,即使我們在 ls 中沒有看到它文件系統上的命令。 ”

面試官很滿意,但我不滿意。

然後我想,為什麼測試我知識的人不深入挖掘呢? 但如果數據畢竟很重要怎麼辦? 如果我們無法重新啟動該進程,並且同時該進程向沒有可用空間的分區上的文件系統寫入數據怎麼辦? 如果我們不僅不能丟失已寫入的數據,而且不能丟失該進程正在寫入或嘗試寫入的數據怎麼辦?

圖濟克

在我職業生涯的初期,我試圖創建一個需要存儲用戶信息的小型應用程序。 然後我想,如何將用戶與其數據相匹配。 比如我有伊万諾維奇伊万諾維奇,他有一些數據,但是如何和他們交朋友呢? 我可以直接指出,這只名叫“Tuzik”的狗屬於同一個伊万。 但是,如果他改了名字,不再是伊万,而是變成了奧利亞(Olya)呢? 那麼我們的奧利亞·伊万諾夫娜·伊万諾娃將不再有狗,而我們的圖茲克仍然屬於不存在的伊万。 數據庫幫助解決了這個問題,它為每個用戶提供了一個唯一的標識符(ID),而我的 Tuzik 與這個 ID 綁定在一起,實際上,它只是一個序列號。 因此,tuzik 的所有者的 ID 號為 2,並且在某個時間點 Ivan 處於該 ID 下,然後 Olya 也處於同一 ID 下。 人類和畜牧業的問題得到切實解決。

文件描述符

文件和處理該文件的程序的問題與我們的狗和人類的問題大致相同。 假設我打開了一個名為ivan.txt 的文件,並開始向其中寫入單詞tuzik,但設法只將第一個字母“t”寫入該文件,並且該文件被某人重命名,例如,重命名為olya.txt。 但文件是一樣的,我仍然想向它寫入我的王牌。 每次使用系統調用打開文件時 打開 在任何編程語言中,我都會得到一個指向文件的唯一 ID,這個 ID 就是文件描述符。 接下來誰對該文件做什麼並不重要,它可以被刪除,可以重命名,可以更改其所有者或剝奪讀寫權限,我仍然可以訪問它,因為在打開文件時,我有權讀取和/或寫入它,並且我設法開始使用它,這意味著我必須繼續這樣做。

在Linux上,libc庫為每個正在運行的應用程序(進程)打開3個描述符文件,編號為0,1,2、XNUMX、XNUMX。 您可以在鏈接中找到更多信息 男人工作室 и 人標準輸出

  • 文件描述符 0 稱為 STDIN,與應用程序輸入相關。
  • 文件描述符 1 稱為 STDOUT,由輸出應用程序(例如打印命令)使用。
  • 文件描述符 2 名為 STDERR,應用程序使用它來輸出錯誤消息。

如果在您的程序中您打開任何文件進行讀取或寫入,那麼您很可能會獲得第一個空閒 ID,並且它將是數字 3。

如果您知道任何進程的 PID,則可以查看該進程的文件描述符列表。

例如,讓我們用 bash 打開控制台並查看進程的 PID

[user@localhost ]$ echo $$
15771

在第二個控制台中,運行

[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

在本文的框架內,您可以安全地忽略編號為 255 的文件描述符,它是由 bash 本身根據您的需要打開的,而不是由鏈接庫打開的。

現在所有 3 個描述符文件都與偽終端設備關聯 /dev/點,但我們仍然可以操作它們,例如在第二個控制台中運行

[user@localhost ]$ echo "hello world" > /proc/15771/fd/0

在第一個控制台中我們將看到

[user@localhost ]$ hello world

重定向和管道

你可以在任何進程中輕鬆覆蓋這3個描述符文件,包括在bash中,例如通過管道(pipe)連接兩個進程,參見

[user@localhost ]$ cat /dev/zero | sleep 10000

您可以自己運行此命令 strace-f 看看裡面發生了什麼,但我會長話短說。

我們的 PID 為 15771 的父 bash 進程解析我們的命令並準確地理解我們想要運行多少個命令,在我們的例子中有兩個命令:cat 和 sleep。 Bash 知道它需要創建兩個子進程,並將它們合併到一個管道中。 bash 總共需要 2 個子進程和 XNUMX 個管道。

在創建子進程之前,bash 運行系統調用 並在臨時管道緩衝區上接收新的文件描述符,但該緩衝區尚未以任何方式連接我們的兩個子進程。

對於父進程,看起來管道已經存在,但還沒有子進程:

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

然後使用系統調用 克隆 bash 創建兩個子進程,我們的三個進程將如下所示:

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

不要忘記克隆會克隆進程以及所有文件描述符,因此它們在父進程和子進程中將是相同的。 PID為15771的父進程的任務是監視子進程,因此它只是等待子進程的響應。

因此,他不需要管道,他關閉了編號為3和4的文件描述符。

在第一個PID為9004的bash子進程中,系統調用 杜普2,將STDOUT 文件描述符編號1 更改為指向管道的文件描述符,在我們的示例中為編號3。因此,PID 為9004 的第一個子進程寫入STDOUT 的所有內容都將自動落入管道緩衝區。

在 PID 9005 的第二個子進程中,bash 將文件複製到 STDIN 描述符編號 2。現在 PID 0 的第二個 bash 將讀取的所有內容都將從管道中讀取。

此後,編號為 3 和 4 的文件描述符也會在子進程中關閉,因為它們不再被使用。

我故意忽略文件描述符 255,它由 bash 本身在內部使用,並且也會在子進程中關閉。

接下來,在第一個PID為9004的子進程中,bash以系統調用啟動 EXEC 我們在命令行上指定的可執行文件,在我們的例子中是 /usr/bin/cat。

在 PID 為 9005 的第二個子進程中,bash 運行我們指定的第二個可執行文件,在我們的例子中是 /usr/bin/sleep。

exec 系統調用不會關閉文件描述符,除非在執行 open 調用時使用 O_CLOEXEC 標誌打開文件描述符。 在我們的例子中,運行可執行文件後,所有當前文件描述符將被保存。

在控制台中檢查:

[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

正如您所看到的,我們的管道的唯一編號在兩個進程中是相同的。 因此,我們在具有相同父進程的兩個不同進程之間建立了連接。

對於那些不熟悉 bash 使用的系統調用的人,我強烈建議通過 strace 運行命令並查看內部發生的情況,例如,如下所示:

strace -s 1024 -f bash -c "ls | grep hello"

讓我們回到磁盤空間不足並嘗試在不重新啟動進程的情況下保存數據的問題。 讓我們編寫一個小程序,每秒向磁盤寫入約 1 MB 的數據。 此外,如果由於某種原因我們無法將數據寫入磁盤,我們將簡單地忽略這一點並嘗試在一秒鐘內再次寫入數據。 在我使用 Python 的示例中,您可以使用任何其他編程語言。

[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

運行程序並查看文件描述符

[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

正如您所看到的,我們有 3 個標准文件描述符和另一個已打開的文件描述符。 讓我們檢查一下文件大小:

[user@localhost ]$ ls -lah 123.txt 
-rw-rw-r-- 1 user user 117M Oct  7 16:30 123.txt

數據寫入後,我們嘗試更改文件的權限:

[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

我們看到數據仍在寫入,儘管我們的用戶沒有寫入文件的權限。 讓我們嘗試刪除它:

[user@localhost ]$ sudo rm 123.txt 
[user@localhost ]$ ls 123.txt
ls: cannot access 123.txt: No such file or directory

數據寫在哪裡? 它們到底有沒有被寫下來? 我們檢查:

[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)

是的,我們的文件描述符仍然存在,我們可以像舊文件一樣使用這個文件描述符,我們可以讀取它,清理它並複制它。

查看文件大小:

[user@localhost ]$ lsof | grep 123.txt
python    31083             user    3w      REG                8,5   19923457   2621522 /home/user/123.txt

文件大小為19923457。嘗試清除文件:

[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

正如您所看到的,文件大小只會增加,而我們的主幹不起作用。 我們來看看系統調用的文檔 打開。 如果我們在打開文件時使用 O_APPEND 標誌,則每次寫入時,操作系統都會檢查文件大小並將數據寫入文件的最末尾,並以原子方式執行此操作。 這允許多個線程或進程寫入同一個文件。 但在我們的代碼中我們不使用這個標誌。 僅當我們打開文件進行寫入時,我們才能在 trunk 之後的 lsof 中看到不同的文件大小,這意味著在我們的代碼中,而不是

with open("123.txt", "w") as f:

我們必須把

with open("123.txt", "a") as f:

使用“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

並帶有“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

對已經運行的進程進行編程

通常,在創建和測試程序時,程序員會在應用程序中使用調試器(例如 GDB)或各種級別的日誌記錄。 Linux提供了實際編寫和更改已經運行的程序的能力,例如更改變量的值、設置斷點等等。

回到最初關於磁盤空間不足來寫入文件的問題,讓我們嘗試模擬一下這個問題。

讓我們為分區創建一個文件,我們將其作為單獨的驅動器安裝:

[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 ~]$

讓我們創建一個文件系統:

[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 ~]$

讓我們掛載文件系統:

[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

與我們的所有者創建一個目錄:

[user@localhost ~]$ sudo mkdir /mnt/logs
[user@localhost ~]$ sudo chown user: /mnt/logs

讓我們在程序中打開僅用於寫入的文件:

with open("/mnt/logs/123.txt", "w") as f:

發射

[user@localhost ]$ python openforwrite.py 

等待幾秒鐘

[user@localhost ~]$ df -h | grep mnt
/dev/loop0      8.7M  8.0M     0 100% /mnt

於是,我們就得到了本文開頭所描述的問題。 可用空間0,已佔用100%。

我們記得根據問題的情況,我們正在嘗試記錄非常重要且不能丟失的數據。 為此,我們需要修復服務而不重新啟動進程。

假設我們仍然有磁盤空間,但在不同的分區中,例如在 /home 中。

讓我們嘗試“動態重新編程”我們的代碼。

我們查看進程的 PID,它佔用了所有磁盤空間:

[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

使用 gdb 連接到進程

[user@localhost ~]$ gdb -p 10078
...
(gdb) 

我們查看打開的文件描述符:

(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

我們查看有關我們感興趣的編號為 3 的文件描述符的信息

(gdb) shell cat /proc/10078/fdinfo/3
pos:    8189952
flags:  0100001
mnt_id: 482

請記住Python 的系統調用是做什麼的(參見上面我們運行strace 並發現一個open 調用的地方),在處理打開文件的代碼時,我們自己代表我們的進程執行相同的操作,但我們需要O_WRONLY| O_CREAT| O_TRUNC 位替換為數值。 為此,請打開內核源代碼,例如 這裡 看看哪些標誌負責什麼

#定義O_WRONLY 00000001
#定義O_CREAT 00000100
#定義O_TRUNC 00001000

我們將所有值\u00001101b\uXNUMXbin合為一,得到XNUMX

從 gdb 運行我們的調用

(gdb) call open("/home/user/123.txt", 00001101,0666)
$1 = 4

所以我們得到了一個新的文件描述符,編號為 4,並且在另一個分區上有一個新的打開文件,檢查:

(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

我們還記得管道的示例 - bash 如何更改文件描述符,並且已經學習了 dup2 系統調用。

嘗試用另一個文件描述符替換一個文件描述符

(gdb) call dup2(4,3)
$2 = 3

我們檢查:

(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

關閉文件描述符 4,因為我們不需要它:

(gdb) call close (4)
$1 = 0

並退出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

檢查新文件:

[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

正如你所看到的,數據被寫入一個新文件,我們檢查舊文件:

[user@localhost ~]$ ls -lah /mnt/logs/123.txt 
-rw-rw-r-- 1 user user 7.9M Oct  8 11:08 /mnt/logs/123.txt

數據不會丟失,應用程序可以運行,日誌會寫入新位置。

讓我們讓事情變得更困難一點

想像一下,數據對我們很重要,但我們的任何分區都沒有磁盤空間,並且無法連接磁盤。

我們能做的就是將數據重定向到某個地方,例如重定向到管道,並通過某些程序(例如netcat)將數據從管道重定向到網絡。
我們可以使用 mkfifo 命令創建命名管道。 它將在文件系統上創建一個偽文件,即使其上沒有可用空間。

重新啟動應用程序並檢查:

[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

沒有磁盤空間,但我們成功地創建了一個命名管道:

[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

現在我們需要以某種方式將進入該管道的所有數據通過網絡包裝到另一台服務器,相同的 netcat 適合於此。

在remote-server.example.com服務器上,運行

[user@localhost ~]$ nc -l 7777 > 123.txt 

在我們的問題服務器上,在單獨的終端中運行

[user@localhost ~]$ nc remote-server.example.com 7777 < /mnt/logs/megapipe 

現在,進入管道的所有數據都會自動發送到 netcat 中的 stdin,後者會將其發送到端口 7777 上的網絡。

我們所要做的就是開始將數據寫入這個命名管道。

我們已經有一個正在運行的應用程序:

[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

在所有標誌中,我們只需要 O_WRONLY,因為文件已經存在並且我們不需要清除它

[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

檢查遠程服務器remote-server.example.com

[user@localhost ~]$ ls -lah 123.txt 
-rw-rw-r-- 1 user user 38M Oct  8 14:21 123.txt

數據來了,我們檢查有問題的服務器

[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

數據保存,問題解決。

我藉此機會向德吉羅的同事們問好。
收聽 Radio-T 播客。

都好。

作為作業,我建議考慮一下如果運行以下命令,cat 和 sleep 進程的文件描述符中將包含什麼內容:

[user@localhost ~]$ cat /dev/zero 2>/dev/null| sleep 10000

來源: www.habr.com

添加評論