Fájlleíró Linuxban példákkal

Egyszer egy interjúban megkérdezték tőlem, hogy mit tenne, ha megszakadt a szolgáltatás amiatt, hogy a lemezen elfogy a hely?

Természetesen azt válaszoltam, hogy megnézem, mit csinál ez a hely, és ha lehet, kitakarítom a helyet.
Aztán a kérdező megkérdezte, mi van akkor, ha nincs szabad hely a partíción, de nem látod azokat a fájlokat sem, amelyek minden helyet elfoglalnának?

Erre azt mondtam, hogy mindig meg lehet nézni a megnyitott fájlleírókat, például az lsof paranccsal, és megérteni, hogy melyik alkalmazás foglalt el minden rendelkezésre álló helyet, majd a körülményeknek megfelelően lehet cselekedni, attól függően, hogy szükség van-e az adatokra. .

A kérdező az utolsó szónál félbeszakított, kérdéséhez hozzáfűzve: „Tegyük fel, hogy nincs szükségünk az adatokra, ez csak egy hibakeresési napló, de az alkalmazás leállt, mert nem tudja megírni a hibakeresést”?

"Rendben" - válaszoltam - "kikapcsolhatjuk a hibakeresést az alkalmazás konfigurációjában, és újraindíthatjuk."
A kérdező kifogásolta: „Nem, nem tudjuk újraindítani az alkalmazást, még mindig vannak fontos adatok a memóriában, és magához a szolgáltatáshoz csatlakoznak fontos kliensek, amelyeket nem tudunk rákényszeríteni az újracsatlakozásra.”

– Oké – mondtam –, ha nem tudjuk újraindítani az alkalmazást, és nem törődünk az adatokkal, akkor egyszerűen törölhetjük ezt a megnyitott fájlt a fájlleíró segítségével, még akkor is, ha nem látjuk az ls-ben. parancsot a fájlrendszeren.”

A kérdező elégedett volt, én viszont nem.

Aztán arra gondoltam, miért nem ás mélyebbre az, aki teszteli a tudásomat? De mi van akkor, ha az adatok mégis fontosak? Mi van akkor, ha nem tudjuk újraindítani a folyamatot, és ugyanakkor ez a folyamat olyan partíción ír a fájlrendszerbe, amelyen nincs szabad hely? Mi van akkor, ha nem csak a már megírt adatokat nem veszíthetjük el, hanem azokat az adatokat sem, amelyeket ez a folyamat ír vagy próbál írni?

Tuzik

Pályám kezdetén megpróbáltam létrehozni egy kis alkalmazást, amiben információkat kellett tárolni a felhasználókról. És akkor arra gondoltam, hogyan tudnám a felhasználót az adataihoz illeszteni. Például van Ivanov Ivan Ivanovicsom, és van néhány adata, de hogyan lehet velük barátkozni? Közvetlenül kijelenthetem, hogy a "Tuzik" nevű kutya ugyanahhoz az Ivánhoz tartozik. De mi van, ha megváltoztatja a nevét, és Ivan helyett például Olya lesz? Aztán kiderül, hogy Olya Ivanovna Ivanovánknak már nem lesz kutyája, a mi Tuzikunk pedig továbbra is a nem létező Iváné lesz. Az adatbázis segített megoldani ezt a problémát, amely minden felhasználóhoz egyedi azonosítót (ID) adott, és ehhez az azonosítóhoz volt kötve a Tuzikom, ami valójában csak egy sorozatszám volt. Így a tuzik tulajdonosa 2-es azonosítószámú volt, és valamikor Iván is ezen az igazolványon volt, majd Olya is ugyanazon az azonosító alatt állt. Az emberiség és az állattenyésztés problémája gyakorlatilag megoldódott.

Fájlleíró

A fájl és az ezzel a fájllal működő program problémája nagyjából ugyanaz, mint a kutyánknak és az embernek. Tegyük fel, hogy megnyitottam egy ivan.txt nevű fájlt, és elkezdtem beleírni a tuzik szót, de csak az első „t” betűt sikerült beleírnom a fájlba, és ezt a fájlt valaki átnevezte például olya.txt-re. De a fájl ugyanaz, és még mindig rá akarom írni az ászomat. Minden alkalommal, amikor rendszerhívással megnyit egy fájlt nyitva bármilyen programozási nyelvben kapok egy egyedi azonosítót, ami egy fájlra mutat, ez az azonosító a fájlleíró. És egyáltalán nem mindegy, hogy mit és ki csinál ezután ezzel a fájllal, törölhető, átnevezhető, megváltoztathatja a tulajdonosát, vagy elveheti az olvasási és írási jogokat, továbbra is hozzáférek, mert a fájl megnyitásakor jogom volt olvasni és/vagy írni, és sikerült elkezdenem vele dolgozni, vagyis folytatnom kell.

Linuxon a libc könyvtár 3 leíró fájlt nyit meg minden futó alkalmazáshoz (folyamathoz), 0,1,2, XNUMX, XNUMX számokkal. További információkat a linkeken találhat férfi stdio и férfi stdout

  • A 0 fájlleíró neve STDIN, és az alkalmazás bemenetéhez van társítva.
  • Az 1. fájlleíró neve STDOUT, és a kimeneti alkalmazások, például a nyomtatási parancsok használják.
  • A 2. fájlleíró neve STDERR, és az alkalmazások hibaüzenetek kiadására használják.

Ha a programjában megnyit egy fájlt olvasásra vagy írásra, akkor valószínűleg megkapja az első ingyenes azonosítót, és ez a 3-as lesz.

Bármely folyamat fájlleíróinak listáját megtekintheti, ha ismeri annak PID-jét.

Például nyissunk meg egy konzolt a bash segítségével, és nézzük meg a folyamatunk PID-jét

[user@localhost ]$ echo $$
15771

A második konzolon futtassa

[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

A cikk keretein belül nyugodtan figyelmen kívül hagyhatja a 255-ös számú fájlleírót, azt maga a bash nyitotta meg, nem a hivatkozott könyvtár.

Most mind a 3 leíró fájl hozzá van rendelve a pszeudoterminál eszközhöz /dev/pts, de továbbra is manipulálhatjuk őket, például futtathatjuk a második konzolon

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

És az első konzolon látni fogjuk

[user@localhost ]$ hello world

Átirányítás és cső

Könnyedén felülírhatja ezt a 3 leíró fájlt bármely folyamatban, beleértve a bash-t is, például két folyamatot összekötő cső (pipe) segítségével, lásd

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

Ezt a parancsot saját maga is futtathatja strace -f és nézd meg, mi történik odabent, de röviden leírom.

A PID 15771-es szülő bash folyamatunk elemzi a parancsunkat, és pontosan megérti, hány parancsot akarunk futtatni, esetünkben kettő van belőlük: cat és sleep. A Bash tudja, hogy két gyermekfolyamatot kell létrehoznia, és egy csőbe kell egyesítenie őket. Összességében a bash-hoz 2 gyermek folyamatra és egy pipere lesz szüksége.

Az utódfolyamatok létrehozása előtt a bash rendszerhívást futtat cső és új fájlleírókat kap egy ideiglenes pipe pufferen, de ez a puffer még semmilyen módon nem köti össze a két gyermekfolyamatunkat.

A szülőfolyamat esetében úgy tűnik, hogy a cső már megvan, de még nincsenek alárendelt folyamatok:

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

Ezután használja a rendszerhívást klón A bash két gyermekfolyamatot hoz létre, és a mi három folyamatunk így fog kinézni:

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

Ne felejtsük el, hogy a klón klónozza a folyamatot az összes fájlleíróval együtt, így azok ugyanazok lesznek a szülő folyamatban és a gyermek folyamatban. A PID 15771-es szülőfolyamat feladata a gyermekfolyamatok figyelése, tehát csak a gyerekek válaszára vár.

Ezért nincs szüksége csőre, a fájlleírókat pedig a 3-as és 4-es számmal zárja.

A 9004-es PID-vel rendelkező első bash gyermekfolyamatban a rendszerhívás dup2, megváltoztatja az 1-es számú STDOUT fájlleírót egy csőre mutató fájlleíróra, esetünkben ez a 3. Így minden, amit az első PID 9004-es gyermekfolyamat STDOUT-ra ír, automatikusan a pipe pufferbe kerül.

A második, PID 9005-ös utódfolyamatban a bash dup2s a fájlt a 0 STDIN-leíró számra írja be. Most minden, amit a 9005-ös PID-vel rendelkező második bash olvasni fog, a csőből fog olvasni.

Ezt követően a 3-as és 4-es számú fájlleírók is bezáródnak a gyermekfolyamatokban, mivel már nem használják őket.

Szándékosan figyelmen kívül hagyom a 255-ös fájlleírót, maga a bash használja belsőleg, és a gyermekfolyamatok is bezárják.

Ezután a 9004-es PID-vel rendelkező első gyermekfolyamatban a bash rendszerhívással indul exec a végrehajtható fájl, amelyet a parancssorban adtunk meg, esetünkben ez a /usr/bin/cat.

A 9005-ös PID-vel rendelkező második gyermekfolyamatban a bash az általunk megadott második végrehajtható fájlt futtatja, esetünkben /usr/bin/sleep.

Az exec rendszerhívás csak akkor zárja be a fájlleírókat, ha azokat az O_CLOEXEC jelzővel nyitották meg a nyitott hívás végrehajtásakor. Esetünkben a futtatható fájlok futtatása után az összes aktuális fájlleíró mentésre kerül.

Ellenőrzés a konzolon:

[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

Mint látható, a csövünk egyedi száma mindkét folyamatban megegyezik. Így két különböző folyamat között van kapcsolatunk ugyanazzal a szülővel.

Azok számára, akik nem ismerik a bash által használt rendszerhívásokat, erősen ajánlom a parancsok futtatását a strace-en keresztül, és nézzék meg, mi történik belül, például így:

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

Térjünk vissza a problémánkhoz, miszerint kifogyott a lemezterület, és megpróbáljuk elmenteni az adatokat a folyamat újraindítása nélkül. Írjunk egy kis programot, ami körülbelül 1 megabájtot ír a lemezre másodpercenként. Sőt, ha valamilyen oknál fogva nem tudtunk adatokat írni a lemezre, egyszerűen figyelmen kívül hagyjuk ezt, és egy másodperc múlva megpróbáljuk újra írni az adatokat. A Python-t használó példában bármilyen más programozási nyelvet használhat.

[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

Futtassa a programot, és nézze meg a fájlleírókat

[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

Amint látja, van 3 szabványos fájlleírónk, és egy másik, amelyet megnyitottunk. Ellenőrizzük a fájl méretét:

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

az adatok írásra kerülnek, megpróbáljuk megváltoztatni a fájl jogait:

[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

Azt látjuk, hogy az adatok írása még folyamatban van, bár felhasználónknak nincs joga írni a fájlba. Próbáljuk meg eltávolítani:

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

Hol vannak az adatok írva? És egyáltalán meg vannak írva? Ellenőrizzük:

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

Igen, a fájlleírónk továbbra is létezik, és ezzel a fájlleíróval is dolgozhatunk, mint a régi fájlunkkal, elolvashatjuk, tisztíthatjuk és másolhatjuk.

Nézze meg a fájl méretét:

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

A fájl mérete 19923457. A fájl törlése:

[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

Amint látja, a fájl mérete csak nő, és a törzsünk nem működött. Lapozzuk át a rendszerhívás dokumentációját nyitva. Ha egy fájl megnyitásakor az O_APPEND jelzőt használjuk, akkor az operációs rendszer minden írásnál ellenőrzi a fájl méretét és a fájl legvégére írja az adatokat, és ezt atomosan teszi. Ez lehetővé teszi több szál vagy folyamat írását ugyanabba a fájlba. De a kódunkban nem használjuk ezt a jelzőt. Csak akkor láthatunk más fájlméretet az lsof-ban a trunk után, ha megnyitjuk a fájlt írásra, ami azt jelenti, hogy a kódunkban nem

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

tennünk kell

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

Ellenőrzés "w" zászlóval

[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

és "a" zászlóval

[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

Egy már futó folyamat programozása

Egy program létrehozásakor és tesztelésekor a programozók gyakran hibakeresőket (például GDB) vagy különféle szintű naplózást használnak az alkalmazásban. A Linux lehetővé teszi egy már futó program tényleges megírását és módosítását, például a változók értékeinek megváltoztatását, töréspont beállítását, és így tovább.

Visszatérve a fájl írásához szükséges lemezterület hiányára vonatkozó eredeti kérdéshez, próbáljuk meg szimulálni a problémát.

Hozzon létre egy fájlt a partíciónkhoz, amelyet külön meghajtóként csatolunk:

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

Hozzunk létre egy fájlrendszert:

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

Csatlakoztassuk a fájlrendszert:

[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

Hozzon létre egy könyvtárat tulajdonosunkkal:

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

Nyissuk meg a fájlt írásra csak a programunkban:

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

Dob

[user@localhost ]$ python openforwrite.py 

Várjon néhány másodpercig

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

Tehát megkaptuk a cikk elején leírt problémát. Szabad hely 0, 100%-ban foglalt.

Emlékezzünk arra, hogy a probléma körülményeinek megfelelően nagyon fontos adatokat próbálunk rögzíteni, amelyek nem veszhetnek el. Ennek során ki kell javítanunk a szolgáltatást a folyamat újraindítása nélkül.

Tegyük fel, hogy még mindig van lemezterületünk, de egy másik partícióban, például a / home-ban.

Próbáljuk meg "menet közben átprogramozni" a kódunkat.

Megnézzük a folyamatunk PID-jét, amely felemésztette az összes lemezterületet:

[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

Csatlakozás egy folyamathoz a gdb segítségével

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

Megnézzük a megnyitott fájlleírókat:

(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

A 3-as számú fájlleíróra vonatkozó információkat nézzük meg, amelyek érdekelnek

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

Szem előtt tartva, hogy a Python milyen rendszerhívást végez (lásd fent, ahol a strace-t futtattuk és nyitott hívást találtunk), miközben a kódunkat feldolgozzuk egy fájl megnyitásához, mi magunk is megtesszük ugyanezt a folyamat nevében, de szükségünk van az O_WRONLY|O_CREAT| Az O_TRUNC bitek helyébe numerikus érték lép. Ehhez nyissa meg például a kernelforrásokat itt és nézze meg, hogy mely zászlók miért felelősek

#define O_WRONLY 00000001
#define O_CREAT 00000100
#define O_TRUNC 00001000

Összevonjuk az összes értéket egy, 00001101-et kapunk

A gdb hívása fut

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

Tehát kaptunk egy új fájlleírót 4-es számmal és egy új megnyitott fájlt egy másik partíción, ellenőrizze:

(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

Emlékszünk a pipe példára – hogyan változtatja meg a bash a fájlleírókat, és már megtanultuk a dup2 rendszerhívást.

Megpróbál egy fájlleírót lecserélni egy másikra

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

Ellenőrizzük:

(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

Zárjuk be a 4-es fájlleírót, mert nincs rá szükségünk:

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

És kilép a gdb-ből

(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

Az új fájl ellenőrzése:

[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

Amint látja, az adatok egy új fájlba íródnak, ellenőrizzük a régit:

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

Az adatok nem vesznek el, az alkalmazás működik, a naplók új helyre íródnak.

Nehezítsük meg egy kicsit a dolgokat

Képzeljük el, hogy az adatok fontosak számunkra, de nincs lemezterületünk egyik partícióban sem, és nem tudjuk csatlakoztatni a lemezt.

Annyit tehetünk, hogy átirányítjuk az adatainkat valahova, például egy pipere, és a csőből az adatokat átirányítjuk a hálózatra valamilyen programon, például a netcaten keresztül.
Az mkfifo paranccsal létrehozhatunk egy nevű csövet. Létrehoz egy pszeudofájlt a fájlrendszeren, még akkor is, ha nincs rajta szabad hely.

Indítsa újra az alkalmazást, és ellenőrizze:

[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

Nincs lemezterület, de sikeresen létrehoztunk ott egy nevű csövet:

[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

Most valahogy minden adatot, ami ebbe a csőbe kerül, át kell csomagolnunk egy másik szerverre a hálózaton keresztül, erre ugyanaz a netcat alkalmas.

A remote-server.example.com kiszolgálón futtassa

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

A problémás szerverünkön egy külön terminálon fut

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

Mostantól minden adat, amely a csőbe kerül, automatikusan a netcat stdin-jába kerül, amely a 7777-es porton küldi el a hálózatnak.

Nincs más dolgunk, mint elkezdeni az adatainkat írni ebbe a nevezett csőbe.

Már van egy futó alkalmazásunk:

[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

Az összes zászló közül csak az O_WRONLY-ra van szükségünk, mivel a fájl már létezik, és nem kell törölnünk

[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

A távoli szerver ellenőrzése remote-server.example.com

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

Jönnek az adatok, ellenőrizzük a problémás szervert

[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

Az adatok mentésre kerülnek, a probléma megoldódott.

Megragadom az alkalmat, hogy köszöntsem a degirói kollégákat.
Hallgassa meg a Radio-T podcastokat.

Minden rendben.

Házi feladatként azt javaslom, hogy gondolja át, mi lesz a macska és alvás folyamat fájlleíróiban, ha futtatja a következő parancsot:

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

Forrás: will.com

Hozzászólás