Dosiera priskribilo en Linukso kun ekzemploj

Iam, dum intervjuo, oni demandis min, kion vi faros, se vi trovos servon, kiu ne funkcias pro tio, ke la disko elĉerpiĝis?

Kompreneble, mi respondis, ke mi vidos, kio estas okupata de ĉi tiu loko kaj, se eble, mi purigos la lokon.
Tiam la intervjuanto demandis, kio se ne estas libera spaco sur la vando, sed vi ankaŭ ne vidas dosierojn, kiuj okupas la tutan spacon?

Al tio mi diris, ke oni ĉiam povas rigardi malfermitajn dosierpriskribilojn, ekzemple per la komando lsof, kaj kompreni, kiu aplikaĵo okupis la tutan disponeblan spacon, kaj tiam oni povas agi laŭ la cirkonstancoj, depende ĉu la datumoj estas bezonataj. .

La intervjuanto interrompis min je la lasta vorto, aldonante al sia demando: "Supoze, ke ni ne bezonas la datumojn, ĝi estas nur sencimiga protokolo, sed la aplikaĵo ne funkcias ĉar ĝi ne povas skribi sencimigon"?

"Bone," mi respondis, "ni povas malŝalti sencimigon en la aplika agordo kaj rekomenci ĝin."
La intervjuanto kontraŭis: "Ne, ni ne povas rekomenci la aplikaĵon, ni ankoraŭ havas gravajn datumojn konservitajn en memoro, kaj gravaj klientoj estas konektitaj al la servo mem, kiun ni ne povas devigi rekonekti."

“Bone,” mi diris, “se ni ne povas rekomenci la aplikaĵon kaj la datumoj ne gravas por ni, tiam ni povas simple forigi ĉi tiun malfermitan dosieron per la dosierpriskribilo, eĉ se ni ne vidas ĝin en la komando ls. sur la dosiersistemo."

La intervjuanto estis kontenta, sed mi ne estis.

Tiam mi pensis, kial la homo, kiu testas miajn sciojn, ne fosas pli profunde? Sed kio se la datumoj finfine gravas? Kio se ni ne povas rekomenci procezon, kaj la procezo skribas al la dosiersistemo sur diskparto kiu ne havas liberan spacon? Kio se ni ne povas perdi ne nur la datumojn, kiuj jam estis skribitaj, sed ankaŭ la datumojn, kiujn ĉi tiu procezo skribas aŭ provas skribi?

Tuzik

Komence de mia kariero, mi provis krei malgrandan aplikaĵon, kiu bezonis stoki uzantajn informojn. Kaj tiam mi pensis, kiel mi povas kongrui la uzanton al liaj datumoj. Ekzemple, mi havas Ivanov Ivan Ivanoviĉ, kaj li havas kelkajn informojn, sed kiel mi povas amikiĝi kun ili? Mi povas rekte atentigi, ke la hundo nomita "Tuzik" apartenas al tiu mem Ivano. Sed kio se li ŝanĝas sian nomon kaj anstataŭ Ivan fariĝos, ekzemple, Olja? Tiam montriĝos, ke nia Olja Ivanovna Ivanova ne plu havos hundon, kaj nia Tuzik ankoraŭ apartenos al la neekzistanta Ivano. Datumaro, kiu donis al ĉiu uzanto unikan identigilon (ID) helpis solvi ĉi tiun problemon, kaj mia Tuzik estis ligita al ĉi tiu identigilo, kiu, fakte, estis nur seria numero. Tiel, la posedanto de la aso havis identigilon numeron 2, kaj iam Ivano estis sub ĉi tiu identigilo, kaj tiam Olya fariĝis sub la sama identigilo. La problemo de homaro kaj bredado praktike estis solvita.

Dosiera priskribilo

La problemo de la dosiero kaj la programo, kiu funkcias kun ĉi tiu dosiero, estas proksimume la sama kiel tiu de nia hundo kaj viro. Supozu, ke mi malfermis dosieron nomitan ivan.txt kaj komencis skribi la vorton tuzik en ĝin, sed nur sukcesis skribi la unuan literon "t" en la dosiero, kaj ĉi tiu dosiero estis renomita de iu, ekzemple, al olya.txt. Sed la dosiero restas la sama, kaj mi ankoraŭ volas registri mian ason en ĝi. Ĉiufoje kiam dosiero estas malfermita per sistemvoko malfermita en iu ajn programlingvo mi ricevas unikan identigilon kiu indikas min al dosiero, ĉi tiu identigilo estas la dosierpriskribilo. Kaj tute ne gravas kion kaj kiu faras kun ĉi tiu dosiero poste, ĝi povas esti forigita, ĝi povas esti renomita, la posedanto povas esti ŝanĝita, aŭ la rajtoj por legi kaj skribi povas esti forprenita, mi ankoraŭ havos aliron. al ĝi, ĉar en la momento de la malfermo de la dosiero, mi havis la rajtojn legi kaj/aŭ skribi ĝin kaj mi sukcesis eklabori kun ĝi, kio signifas, ke mi devas plu fari tion.

En Linukso, la libc-biblioteko malfermas 3 priskribajn dosierojn por ĉiu ruliĝanta aplikaĵo (procezo), numeritaj 0,1,2. Pliaj informoj troveblas sur la ligiloj man stdio и viro stdout

  • Dosierpriskribilo 0 nomiĝas STDIN kaj estas rilata al aplikaĵa enigo
  • Dosierpriskribilo 1 nomiĝas STDOUT kaj estas uzata de aplikoj por eligi datumojn, kiel presi komandojn
  • Dosiera priskribilo 2 nomiĝas STDERR kaj estas uzata de aplikaĵoj por eligi erarmesaĝojn.

Se en via programo vi malfermas iun dosieron por legi aŭ skribi, tiam plej verŝajne vi ricevos la unuan senpagan identigilon kaj ĝi estos numero 3.

La listo de dosierpriskribiloj povas esti vidita por ajna procezo se vi konas ĝian PID.

Ekzemple, ni malfermu la bash-konzolon kaj rigardu la PID de nia procezo

[user@localhost ]$ echo $$
15771

En la dua konzolo ni kuru

[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

Vi povas sekure ignori dosierpriskribilon numeron 255 por la celoj de ĉi tiu artikolo; ĝi estis malfermita por siaj bezonoj de bash mem, kaj ne de la ligita biblioteko.

Nun ĉiuj 3 priskribaj dosieroj estas asociitaj kun la pseŭda fina aparato /dev/pts, sed ni ankoraŭ povas manipuli ilin, ekzemple, ruli ilin en dua konzolo

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

Kaj en la unua konzolo ni vidos

[user@localhost ]$ hello world

Alidirekti kaj Pipo

Vi povas facile superregi ĉi tiujn 3 priskribajn dosierojn en iu ajn procezo, inkluzive en bash, ekzemple per tubo liganta du procezojn, vidu

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

Vi povas ruli ĉi tiun komandon mem per strace -f kaj vidu, kio okazas interne, sed mi rakontos al vi mallonge.

Nia gepatra bash-procezo kun PID 15771 analizas nian komandon kaj komprenas precize kiom da komandoj ni volas ruli, en nia kazo estas du el ili: kato kaj dormo. Bash scias, ke ĝi bezonas krei du infanajn procezojn, kaj kunfandi ilin en unu tubon. Entute, bash bezonos 2 infanajn procezojn kaj unu tubon.

Bash rulas sistemvokon antaŭ krei infanajn procezojn pipo kaj ricevas novajn dosierpriskribilojn sur la provizora pipa bufro, sed ĉi tiu bufro ankoraŭ ne ligas niajn du infanajn procezojn.

Por la gepatra procezo, ŝajnas, ke jam ekzistas pipo, sed ankoraŭ ne ekzistas infanaj procezoj:

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

Poste uzante la sistemvokon klono bash kreas du infanajn procezojn, kaj niaj tri procezoj aspektos jene:

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 forgesu, ke klono klonas la procezon kune kun ĉiuj dosierpriskribiloj, do ili estos samaj en la gepatra procezo kaj en la infanaj. La laboro de la gepatra procezo kun PID 15771 estas kontroli la infanajn procezojn, do ĝi simple atendas respondon de la infanoj.

Tial ĝi ne bezonas pipon, kaj ĝi fermas dosierpriskribilojn numeritajn 3 kaj 4.

En la unua infanbash procezo kun PID 9004, la sistemvoko dup2, ŝanĝas nian STDOUT-dosierpriskribilon numeron 1 al dosierpriskribilo indikanta pipon, en nia kazo ĝi estas numero 3. Tiel, ĉio, kion la unua infanprocezo kun PID 9004 skribas al STDOUT, aŭtomate finiĝos en la pipa bufro.

En la dua infana procezo kun PID 9005, bash uzas dup2 por ŝanĝi la dosierpriskribilon STDIN numeron 0. Nun ĉio, kion nia dua bash kun PID 9005 legos, estos legita el la pipo.

Post tio, dosierpriskribiloj numeritaj 3 kaj 4 ankaŭ estas fermitaj en la infanaj procezoj, ĉar ili ne plu estas uzataj.

Mi intence ignoras dosierpriskribilon 255; ĝi estas uzata por internaj celoj de bash mem kaj ankaŭ estos fermita en infanaj procezoj.

Poste, en la unua infana procezo kun PID 9004, bash komencas uzi sisteman vokon exec la rulebla dosiero, kiun ni specifis sur la komandlinio, en nia kazo ĝi estas /usr/bin/cat.

En la dua infana procezo kun PID 9005, bash rulas la duan ruleblan, kiun ni specifis, en nia kazo /usr/bin/sleep.

La exec-sistemvoko ne fermas dosiertenilojn krom se ili estis malfermitaj kun la O_CLOEXEC-flago en la tempo kiam la malferma voko estis farita. En nia kazo, post lanĉo de la plenumeblaj dosieroj, ĉiuj aktualaj dosierpriskribiloj estos konservitaj.

Kontrolu en la konzolo:

[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

Kiel vi povas vidi, la unika nombro de nia pipo estas la sama en ambaŭ procezoj. Tiel ni havas ligon inter du malsamaj procezoj kun la sama gepatro.

Por tiuj, kiuj ne konas la sistemvokojn, kiujn bash uzas, mi tre rekomendas ruli la komandojn per strace kaj vidi kio okazas interne, ekzemple jene:

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

Ni revenu al nia problemo kun malalta diskospaco kaj provante ŝpari datumojn sen rekomenci la procezon. Ni skribu malgrandan programon, kiu skribos proksimume 1 megabajton je sekundo al disko. Krome, se ial ni ne povis skribi datumojn al la disko, ni simple ignoros ĉi tion kaj provos skribi la datumojn denove en sekundo. En la ekzemplo mi uzas Python, vi povas uzi ajnan alian programlingvon.

[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

Ni rulu la programon kaj rigardu la dosierpriskribilojn

[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

Kiel vi povas vidi, ni havas niajn 3 normajn dosierpriskribilojn kaj unu pli, kiun ni malfermis. Ni kontrolu la grandecon de dosiero:

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

La datumoj estas skribitaj, ni provas ŝanĝi la permesojn sur la dosiero:

[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

Ni vidas, ke la datumoj ankoraŭ estas skribitaj, kvankam nia uzanto ne havas permeson skribi al la dosiero. Ni provu forigi ĝin:

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

Kie estas skribitaj la datumoj? Kaj ĉu ili entute estas skribitaj? Ni kontrolas:

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

Jes, nia dosierpriskribilo ankoraŭ ekzistas kaj ni povas trakti ĉi tiun dosierpriskribilon kiel nian malnovan dosieron, ni povas legi, malplenigi kaj kopii ĝin.

Ni rigardu la grandecon de dosiero:

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

La dosiergrandeco estas 19923457. Ni provu forigi la dosieron:

[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

Kiel vi povas vidi, la dosiergrandeco nur pliiĝas kaj nia trunko ne funkciis. Ni rigardu la sistemvokan dokumentadon malfermita. Se ni uzas la O_APPEND flagon kiam oni malfermas dosieron, tiam kun ĉiu skribo, la operaciumo kontrolas la dosiergrandecon kaj skribas datumojn ĝis la fino de la dosiero, kaj faras tion atome. Ĉi tio permesas al pluraj fadenoj aŭ procezoj skribi al la sama dosiero. Sed en nia kodo ni ne uzas ĉi tiun flagon. Ni povas vidi malsaman dosiergrandecon en lsof post trunko nur se ni malfermas la dosieron por plia skribo, kio signifas en nia kodo anstataŭe

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

ni devas meti

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

Kontrolante kun la "w" flago

[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

kaj kun la "a" flago

[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

Programado de jam funkcianta procezo

Ofte programistoj, kiam ili kreas kaj testas programojn, uzas erarserĉilojn (ekzemple GDB) aŭ diversajn nivelojn de ensalutu en la aplikaĵo. Linukso disponigas la kapablon vere skribi kaj ŝanĝi jam funkciantan programon, ekzemple, ŝanĝi la valorojn de variabloj, agordi rompopunkton ktp., ktp.

Revenante al la originala demando pri ne sufiĉa diskospaco por skribi dosieron, ni provu simuli la problemon.

Ni kreu dosieron por nia diskparto, kiun ni montos kiel aparta disko:

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

Ni kreu dosiersistemon:

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

Muntu la dosiersistemon:

[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

Ni kreas dosierujon kun nia posedanto:

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

Ni malfermu la dosieron por skribi nur en nia programo:

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

Lanĉo

[user@localhost ]$ python openforwrite.py 

Ni atendas kelkajn sekundojn

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

Do, ni havas la problemon priskribitan komence de ĉi tiu artikolo. Libera spaco 0, 100% okupata.

Ni memoras, ke laŭ la kondiĉoj de la tasko, ni provas registri tre gravajn datumojn, kiuj ne povas esti perditaj. Kaj samtempe ni devas ripari la servon sen rekomenci la procezon.

Ni diru, ke ni ankoraŭ havas diskospacon, sed en alia diskparto, ekzemple en /home.

Ni provu "reprogrami sur la flugo" nian kodon.

Ni rigardu la PID de nia procezo, kiu manĝis la tutan diskospacon:

[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

Konekti al la procezo per gdb

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

Ni rigardu la malfermitajn dosierpriskribilojn:

(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

Ni rigardas la informojn pri dosierpriskribilo numero 3, kiu interesas nin

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

Konsiderante, kian sistemvokon Python faras (vidu supre kie ni kuris strace kaj trovis la malfermitan vokon), kiam ni prilaboras nian kodon por malfermi dosieron, ni mem faras la samon nome de nia procezo, sed ni bezonas la O_WRONLY|O_CREAT| O_TRUNC bitoj anstataŭigas per nombra valoro. Por fari tion, malfermu la kernfontojn, ekzemple tie kaj rigardu, kiuj flagoj respondecas pri kio

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

Ni kombinas ĉiujn valorojn en unu, ni ricevas 00001101

Ni rulas nian vokon de gdb

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

Do ni ricevis novan dosierpriskribilon kun numero 4 kaj novan malfermitan dosieron sur alia sekcio, ni kontrolas:

(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

Ni memoras la ekzemplon kun pipo - kiel bash ŝanĝas dosierpriskribilojn, kaj ni jam lernis la sistemvokon dup2.

Ni provas anstataŭigi unu dosierpriskribilon per alia

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

Ni kontrolas:

(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

Ni fermas dosierpriskribilon 4, ĉar ni ne bezonas ĝin:

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

Kaj eliru 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

Kontrolante la novan dosieron:

[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

Kiel vi povas vidi, la datumoj estas skribitaj al nova dosiero, ni kontrolu la malnovan:

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

Neniuj datumoj estas perditaj, la aplikaĵo funkcias, protokoloj estas skribitaj al nova loko.

Ni iom kompliku la taskon

Ni imagu, ke la datumoj estas gravaj por ni, sed ni ne havas diskospacon en iu ajn el la sekcioj kaj ni ne povas konekti la diskon.

Kion ni povas fari estas alidirekti niajn datumojn ien, ekzemple al pipo, kaj siavice alidirekti datumojn de pipo al la reto per iu programo, ekzemple netcat.
Ni povas krei nomitan tubon per la komando mkfifo. Ĝi kreos pseŭdodosieron sur la dosiersistemo eĉ se ne estas libera spaco sur ĝi.

Rekomencu la aplikaĵon kaj kontrolu:

[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

Ne estas diskospaco, sed ni sukcese kreas nomitan tubon tie:

[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

Nun ni devas iel envolvi ĉiujn datumojn, kiuj eniras ĉi tiun tubon al alia servilo per la reto; la sama netcat taŭgas por ĉi tio.

Sur la servilo remote-server.example.com ni lanĉas

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

Sur nia problema servilo ni lanĉas en aparta terminalo

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

Nun ĉiuj datumoj, kiuj finiĝas en la tubo, aŭtomate iros al stdin en netcat, kiu sendos ĝin al la reto ĉe la haveno 7777.

Ĉio, kion ni devas fari, estas komenci skribi niajn datumojn en ĉi tiun nomitan tubon.

Ni jam havas la aplikaĵon funkciantan:

[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

El ĉiuj flagoj, ni bezonas nur O_WRONLY ĉar la dosiero jam ekzistas kaj ni ne bezonas forigi ĝin.

[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

Kontrolante la fora servilo remote-server.example.com

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

La datumoj venas, ni kontrolas la problemservilon

[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

La datumoj estas konservitaj, la problemo estas solvita.

Mi profitas por saluti miajn kolegojn el Degiro.
Aŭskultu podkastojn de Radio-T.

Bonan al ĉiuj.

Kiel hejmtasko, mi sugestas, ke vi pripensu, kio estos en la procezdosierpriskribiloj kato kaj dormo se vi rulas la jenan komandon:

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

fonto: www.habr.com

Aldoni komenton