Tiedoston kuvaus Linuxissa esimerkein

Kerran haastattelussa minulta kysyttiin, mitä aiot tehdä, jos huomaat, että palvelu ei toimi, koska levytila ​​on loppunut?

Tietenkin vastasin, että katsoisin mitä tämä paikka on miehittänyt ja jos mahdollista, siivoan paikan.
Sitten haastattelija kysyi, mitä jos osiolla ei ole vapaata tilaa, mutta et myöskään näe tiedostoja, jotka vievät kaiken tilan?

Tähän sanoin, että aina voi katsoa avoimia tiedostokuvaajia esimerkiksi lsof-komennolla ja ymmärtää mikä sovellus on vienyt kaiken käytettävissä olevan tilan, ja sitten voi toimia olosuhteiden mukaan riippuen siitä, tarvitaanko tietoja. .

Haastattelija keskeytti minut viimeiseen sanaan ja lisäsi kysymykseensä: "Oletetaan, että emme tarvitse tietoja, se on vain virheenkorjausloki, mutta sovellus ei toimi, koska se ei voi kirjoittaa virheenkorjausta"?

"Okei", vastasin, "voimme poistaa virheenkorjauksen käytöstä sovelluksen asetuksista ja käynnistää sen uudelleen."
Haastattelija vastusti: "Ei, emme voi käynnistää sovellusta uudelleen, meillä on edelleen tärkeitä tietoja muistissa ja tärkeät asiakkaat ovat yhteydessä itse palveluun, joita emme voi pakottaa yhdistämään uudelleen."

"Okei", sanoin, "jos emme voi käynnistää sovellusta uudelleen ja tiedot eivät ole meille tärkeitä, voimme yksinkertaisesti tyhjentää tämän avoimen tiedoston tiedostokuvaajan kautta, vaikka emme näe sitä ls-komennossa tiedostojärjestelmässä."

Haastattelija oli tyytyväinen, mutta minä en.

Sitten ajattelin, miksi tietoni testaaja ei kaivaudu syvemmälle? Mutta entä jos tiedot ovat kuitenkin tärkeitä? Entä jos prosessia ei voida käynnistää uudelleen ja prosessi kirjoittaa tiedostojärjestelmään osiossa, jossa ei ole vapaata tilaa? Entä jos emme voi menettää jo kirjoitettuja tietoja, vaan myös tietoja, jotka tämä prosessi kirjoittaa tai yrittää kirjoittaa?

jolla

Urani alussa yritin luoda pienen sovelluksen, joka tarvitsi käyttäjätietojen tallentamista. Ja sitten ajattelin, kuinka voin yhdistää käyttäjän hänen tietoihinsa. Esimerkiksi minulla on Ivanov Ivan Ivanovich, ja hänellä on tietoa, mutta kuinka voin ystävystyä heidän kanssaan? Voin huomauttaa suoraan, että koira nimeltä "Tuzik" kuuluu juuri tälle Ivanille. Mutta entä jos hän vaihtaa nimensä ja Ivanin sijaan tulee esimerkiksi Olya? Sitten käy ilmi, että Olya Ivanovna Ivanovallamme ei enää ole koiraa, ja Tuzikimme kuuluu edelleen olemattomalle Ivanille. Tietokanta, joka antoi jokaiselle käyttäjälle yksilöllisen tunnisteen (ID), auttoi ratkaisemaan tämän ongelman, ja Tuzikni oli sidottu tähän tunnukseen, joka itse asiassa oli vain sarjanumero. Siten ässän omistajalla oli tunnusnumero 2, ja jossain vaiheessa Ivan oli tämän tunnuksen alla, ja sitten Olyasta tuli sama tunnus. Ihmiskunnan ja karjanhoidon ongelma ratkesi käytännössä.

Tiedoston kuvaus

Tiedoston ja sen kanssa toimivan ohjelman ongelma on suunnilleen sama kuin koirallamme ja ihmisellämme. Oletetaan, että avasin tiedoston nimeltä ivan.txt ja aloin kirjoittamaan siihen sanaa tuzik, mutta onnistuin kirjoittamaan vain ensimmäisen kirjaimen "t" tiedostoon ja joku nimesi tämän tiedoston uudelleen esimerkiksi olya.txt:ksi. Mutta tiedosto pysyy samana, ja haluan silti tallentaa ässäni siihen. Aina kun tiedosto avataan järjestelmäkutsulla avata millä tahansa ohjelmointikielellä saan yksilöllisen tunnuksen, joka osoittaa minut tiedostoon, tämä tunnus on tiedoston kuvaus. Ja sillä ei ole ollenkaan väliä mitä ja kuka tekee tälle tiedostolle seuraavaksi, sen voi poistaa, sen voi nimetä uudelleen, omistajaa voidaan vaihtaa tai luku- ja kirjoitusoikeudet voidaan ottaa pois, minulla on edelleen pääsy siihen, koska minulla oli tiedostoa avattaessa oikeudet lukea ja/tai kirjoittaa se ja onnistuin aloittamaan työskentelyn sen kanssa, mikä tarkoittaa, että minun on jatkettava niin.

Linuxissa libc-kirjasto avaa 3 kuvaustiedostoa kullekin käynnissä olevalle sovellukselle (prosessille), numeroitu 0,1,2. Lisätietoa löytyy linkeistä mies stdio и mies stdout

  • Tiedoston kuvaaja 0 on nimeltään STDIN, ja se liittyy sovelluksen syötteeseen
  • Tiedoston kuvaaja 1 on nimeltään STDOUT, ja sovellukset käyttävät sitä tietojen, kuten tulostuskomentojen, tulostamiseen
  • Tiedostokuvaajaa 2 kutsutaan nimellä STDERR, ja sovellukset käyttävät sitä virhesanomien tulostamiseen.

Jos ohjelmassasi avaat minkä tahansa tiedoston lukemista tai kirjoittamista varten, saat todennäköisesti ensimmäisen ilmaisen tunnuksen ja se on numero 3.

Voit tarkastella minkä tahansa prosessin tiedostokuvausluetteloa, jos tiedät sen PID:n.

Avataan esimerkiksi bash-konsoli ja katsotaan prosessimme PID-tunnusta

[user@localhost ]$ echo $$
15771

Toisessa konsolissa juostaan

[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

Voit turvallisesti jättää huomioimatta tiedostokuvaajan numero 255 tätä artikkelia varten; sen avasi tarpeitaan varten itse bash, ei linkitetty kirjasto.

Nyt kaikki 3 kuvaajatiedostoa on liitetty pseudopäätelaitteeseen /dev/pts, mutta voimme silti muokata niitä, esimerkiksi ajaa niitä toisessa konsolissa

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

Ja ensimmäisessä konsolissa näemme

[user@localhost ]$ hello world

Uudelleenohjaus ja putki

Voit helposti ohittaa nämä 3 kuvaajatiedostoa missä tahansa prosessissa, myös bashissa, esimerkiksi putken kautta, joka yhdistää kaksi prosessia, katso

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

Voit suorittaa tämän komennon itse viiva -f ja katso mitä sisällä tapahtuu, mutta kerron lyhyesti.

Vanhemman bash-prosessimme PID 15771:llä jäsentää komentomme ja ymmärtää tarkalleen kuinka monta komentoa haluamme suorittaa, meidän tapauksessamme niitä on kaksi: cat ja sleep. Bash tietää, että sen on luotava kaksi aliprosessia ja yhdistettävä ne yhdeksi putkeksi. Yhteensä bash tarvitsee 2 lapsiprosessia ja yhden putken.

Bash suorittaa järjestelmäkutsun ennen aliprosessien luomista putki ja vastaanottaa uudet tiedostokuvaajat väliaikaiseen putken puskuriin, mutta tämä puskuri ei vielä yhdistä kahta aliprosessiamme.

Pääprosessissa näyttää jo olevan putki, mutta aliprosesseja ei vielä ole:

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

Käytä sitten järjestelmäkutsua klooni bash luo kaksi aliprosessia, ja kolme prosessiamme näyttävät tältä:

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

Älä unohda, että klooni kloonaa prosessin ja kaikki tiedostokuvaajat, joten ne ovat samat pääprosessissa ja aliprosessissa. Pääprosessin PID 15771:n tehtävänä on seurata lapsiprosesseja, joten se vain odottaa vastausta lapsilta.

Siksi se ei tarvitse putkea, ja se sulkee tiedostokuvaajat, joiden numerot ovat 3 ja 4.

Ensimmäisessä lapsibash-prosessissa, jossa on PID 9004, järjestelmäkutsu dup2, muuttaa STDOUT-tiedostokuvaajan numero 1 putkeen osoittavaksi tiedostokuvaajaksi, meidän tapauksessamme se on numero 3. Siten kaikki, mitä ensimmäinen lapsiprosessi, jolla on PID 9004, kirjoittaa STDOUTiin, päätyy automaattisesti putken puskuriin.

Toisessa aliprosessissa, jossa on PID 9005, bash käyttää dup2:ta muuttaakseen tiedostokuvaajan STDIN-numeroa 0. Nyt kaikki, mitä toinen bash, jolla on PID 9005, lukee, luetaan putkesta.

Tämän jälkeen myös tiedostokuvaajat numeroilla 3 ja 4 suljetaan lapsiprosesseissa, koska niitä ei enää käytetä.

Jätän tietoisesti huomioimatta tiedostokuvaajan 255; bash itse käyttää sitä sisäisiin tarkoituksiin ja se suljetaan myös lapsiprosesseissa.

Seuraavaksi ensimmäisessä aliprosessissa, jossa on PID 9004, bash alkaa käyttää järjestelmäkutsua Exec suoritettava tiedosto, jonka määritimme komentorivillä, meidän tapauksessamme se on /usr/bin/cat.

Toisessa aliprosessissa, jossa on PID 9005, bash suorittaa toisen määrittämämme suoritettavan tiedoston, meidän tapauksessamme /usr/bin/sleep.

Exec-järjestelmäkutsu ei sulje tiedostokahvoja, ellei niitä ole avattu O_CLOEXEC-lipulla, kun avoin kutsu tehtiin. Meidän tapauksessamme suoritettavien tiedostojen käynnistämisen jälkeen kaikki nykyiset tiedostokuvaajat tallennetaan.

Tarkista konsolista:

[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

Kuten näet, putkemme yksilöllinen numero on sama molemmissa prosesseissa. Näin ollen meillä on yhteys kahden eri prosessin välillä saman vanhemman kanssa.

Niille, jotka eivät tunne bashin käyttämiä järjestelmäkutsuja, suosittelen komennon suorittamista stracen kautta ja katsomaan mitä tapahtuu sisäisesti, esimerkiksi näin:

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

Palataan ongelmaamme, jossa on vähän levytilaa ja yritämme tallentaa tietoja käynnistämättä prosessia uudelleen. Kirjoitetaan pieni ohjelma, joka kirjoittaa levylle noin 1 megatavu sekunnissa. Lisäksi, jos emme jostain syystä pystyneet kirjoittamaan tietoja levylle, jätämme tämän huomiotta ja yritämme kirjoittaa tiedot uudelleen sekunnissa. Esimerkissä, jossa käytän Pythonia, voit käyttää mitä tahansa muuta ohjelmointikieltä.

[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

Suoritetaan ohjelma ja tarkastellaan tiedostojen kuvauksia

[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

Kuten näet, meillä on kolme vakiotiedostokuvaajaamme ja vielä yksi, jonka avasimme. Katsotaanpa tiedoston kokoa:

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

Tietoja kirjoitetaan, yritämme muuttaa tiedoston käyttöoikeuksia:

[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

Näemme, että tietoja kirjoitetaan edelleen, vaikka käyttäjällämme ei ole oikeutta kirjoittaa tiedostoon. Yritetään poistaa se:

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

Missä tiedot on kirjoitettu? Ja onko niitä kirjoitettu ollenkaan? Tarkistamme:

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

Kyllä, tiedostokuvaajamme on edelleen olemassa ja voimme käsitellä tätä tiedostokuvaajaa kuin vanhaa tiedostoamme, voimme lukea, tyhjentää ja kopioida sen.

Katsotaanpa tiedoston kokoa:

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

Tiedoston koko on 19923457. Yritetään tyhjentää tiedosto:

[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

Kuten näette, tiedoston koko vain kasvaa ja runkomme ei toiminut. Katsotaanpa järjestelmäkutsun dokumentaatiota avata. Jos käytämme O_APPEND-lippua avattaessa tiedostoa, käyttöjärjestelmä tarkistaa jokaisen kirjoituksen yhteydessä tiedoston koon ja kirjoittaa tiedot tiedoston aivan loppuun, ja tekee tämän atomaarisesti. Tämä sallii useiden säikeiden tai prosessien kirjoittamisen samaan tiedostoon. Mutta koodissamme emme käytä tätä lippua. Näemme eri tiedostokoon lsofissa rungon jälkeen vain, jos avaamme tiedoston lisäkirjoitusta varten, mikä tarkoittaa sen sijaan koodissamme

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

meidän täytyy laittaa

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

Tarkistetaan "w"-lipulla

[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

ja "a"-lipulla

[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

Jo käynnissä olevan prosessin ohjelmointi

Usein ohjelmoijat käyttävät ohjelmia luodessaan ja testaaessaan debuggereita (esim. GDB) tai erilaisia ​​kirjautumistasoja sovelluksessa. Linux tarjoaa mahdollisuuden kirjoittaa ja muuttaa jo käynnissä olevaa ohjelmaa, esimerkiksi muuttaa muuttujien arvoja, asettaa keskeytyskohdan jne., jne.

Palatakseni alkuperäiseen kysymykseen siitä, ettei levytilaa ole tarpeeksi tiedoston kirjoittamiseen, yritetään simuloida ongelmaa.

Luodaan osiollemme tiedosto, jonka liitämme erilliseksi levyksi:

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

Luodaan tiedostojärjestelmä:

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

Liitä tiedostojärjestelmä:

[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

Luomme hakemiston omistajamme kanssa:

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

Avataan tiedosto vain kirjoittamista varten ohjelmassamme:

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

Tuoda markkinoille

[user@localhost ]$ python openforwrite.py 

Odotamme muutaman sekunnin

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

Meillä on siis tämän artikkelin alussa kuvattu ongelma. Vapaa tila 0, 100 % varattu.

Muistamme, että ongelman olosuhteiden mukaan yritämme tallentaa erittäin tärkeitä tietoja, joita ei voi menettää. Ja samaan aikaan meidän on korjattava palvelu käynnistämättä prosessia uudelleen.

Oletetaan, että meillä on vielä levytilaa, mutta eri osiossa, esimerkiksi /home.

Yritetään "ohjelmoida uudelleen" koodimme.

Katsotaanpa prosessimme PID:tä, joka on syönyt kaiken levytilan:

[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

Yhdistä prosessiin gdb:n kautta

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

Katsotaanpa avoimien tiedostojen kuvauksia:

(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

Tarkastelemme meitä kiinnostavia tietoja tiedostokuvauksesta numero 3

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

Kun muistamme, mitä järjestelmäkutsua Python tekee (katso yllä, missä suoritimme strace-kutsun ja löysimme avoimen kutsun), kun käsittelemme koodiamme avataksemme tiedoston, teemme saman itse prosessimme puolesta, mutta tarvitsemme O_WRONLY|O_CREAT| O_TRUNC-bitit korvataan numeerisella arvolla. Voit tehdä tämän avaamalla esimerkiksi ytimen lähteet täällä ja katso, mitkä liput ovat vastuussa mistä

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

Yhdistämme kaikki arvot yhdeksi, saamme 00001101

Suoritamme puhelumme gdb:stä

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

Joten saimme uuden tiedostokuvaajan numerolla 4 ja uuden avoimen tiedoston toisessa osiossa, tarkistamme:

(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

Muistamme esimerkin pipellä - kuinka bash muuttaa tiedostokuvauksia, ja olemme jo oppineet dup2-järjestelmäkutsun.

Yritämme korvata yhden tiedostokuvaajan toisella

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

tarkista:

(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

Suljemme tiedostokuvaajan 4, koska emme tarvitse sitä:

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

Ja poistu gdb:stä

(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

Uuden tiedoston tarkistaminen:

[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

Kuten näet, tiedot kirjoitetaan uuteen tiedostoon, tarkistetaan vanha:

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

Tietoja ei häviä, sovellus toimii, lokit kirjoitetaan uuteen paikkaan.

Monimutkaistaan ​​tehtävää hieman

Kuvitellaan, että tiedot ovat meille tärkeitä, mutta meillä ei ole levytilaa missään osioissa, emmekä voi yhdistää levyä.

Mitä voimme tehdä, on ohjata tietomme jonnekin, esimerkiksi putkeen, ja vuorostaan ​​ohjata tiedot putkesta verkkoon jonkin ohjelman, esimerkiksi netcatin, kautta.
Voimme luoda nimetyn putken komennolla mkfifo. Se luo pseudotiedoston tiedostojärjestelmään, vaikka siinä ei olisi vapaata tilaa.

Käynnistä sovellus uudelleen ja tarkista:

[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

Levytilaa ei ole, mutta luomme siihen onnistuneesti nimetyn putken:

[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

Nyt täytyy jotenkin kääriä kaikki tähän putkeen menevä data verkon kautta toiselle palvelimelle, sama netcat sopii tähän.

Käynnistämme palvelimella remote-server.example.com

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

Ongelmallisella palvelimellamme käynnistämme erillisen terminaalin

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

Nyt kaikki putkeen päätyvät tiedot menevät automaattisesti netcatin stdiniin, joka lähettää ne verkkoon portissa 7777.

Meidän tarvitsee vain alkaa kirjoittaa tietomme tähän nimettyyn putkeen.

Meillä on jo sovellus käynnissä:

[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

Kaikista lipuista tarvitsemme vain O_WRONLY, koska tiedosto on jo olemassa, eikä meidän tarvitse tyhjentää sitä

[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

Tarkistetaan etäpalvelin remote-server.example.com

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

Tietoja tulee, tarkistamme ongelmapalvelimen

[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

Tiedot tallennetaan, ongelma on ratkaistu.

Käytän tilaisuutta hyväkseni tervehtiäkseni kollegoitani Degirosta.
Kuuntele Radio-T podcasteja.

Hyvä kaikille.

Kotitehtävänä ehdotan, että mietit, mitä prosessitiedostojen kuvauksissa cat and sleep on, jos suoritat seuraavan komennon:

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

Lähde: will.com

Lisää kommentti