Disgrifydd ffeil yn Linux gydag enghreifftiau

Unwaith, yn ystod cyfweliad, gofynnwyd i mi, beth fyddwch chi'n ei wneud os gwelwch wasanaeth nad yw'n gweithio oherwydd bod y ddisg wedi rhedeg allan o le?

Wrth gwrs, atebais y byddwn yn gweld beth oedd yn cael ei feddiannu gan y lle hwn ac, os yn bosibl, byddwn yn glanhau'r lle.
Yna gofynnodd y cyfwelydd, beth os nad oes lle am ddim ar y rhaniad, ond hefyd nad ydych chi'n gweld unrhyw ffeiliau a fyddai'n cymryd yr holl le?

I hyn dywedais y gallwch chi bob amser edrych ar ddisgrifyddion ffeiliau agored, er enghraifft gyda'r gorchymyn lsof, a deall pa raglen sydd wedi cymryd yr holl le sydd ar gael, ac yna gallwch chi weithredu yn ôl yr amgylchiadau, yn dibynnu a oes angen y data .

Fe wnaeth y cyfwelydd dorri ar draws fi ar y gair olaf, gan ychwanegu at ei gwestiwn: “Tybiwch nad oes angen y data arnom, dim ond log dadfygio ydyw, ond nid yw'r cais yn gweithio oherwydd ni all ysgrifennu dadfyg”?

“Iawn,” atebais, “gallwn ddiffodd y dadfygio yng nghyfluniad y cymhwysiad a'i ailgychwyn.”
Gwrthwynebodd y cyfwelydd: “Na, ni allwn ailgychwyn y cais, mae gennym ni ddata pwysig wedi’i storio yn y cof o hyd, ac mae cleientiaid pwysig wedi’u cysylltu â’r gwasanaeth ei hun, na allwn orfodi i ailgysylltu eto.”

“iawn,” dywedais, “os na allwn ailgychwyn y rhaglen ac nad yw'r data'n bwysig i ni, yna gallwn glirio'r ffeil agored hon trwy'r disgrifydd ffeil, hyd yn oed os nad ydym yn ei weld yn y gorchymyn ls ar y system ffeiliau.”

Roedd y cyfwelydd yn falch, ond doeddwn i ddim.

Yna meddyliais, pam nad yw'r person sy'n profi fy ngwybodaeth yn cloddio'n ddyfnach? Ond beth os yw'r data'n bwysig wedi'r cyfan? Beth os na allwn ailgychwyn proses, a bod y broses yn ysgrifennu at y system ffeiliau ar raniad sydd heb le am ddim? Beth os na allwn golli nid yn unig y data sydd eisoes wedi'i ysgrifennu, ond hefyd y data y mae'r broses hon yn ei ysgrifennu neu'n ceisio ei ysgrifennu?

Tuzik

Yn gynnar yn fy ngyrfa, ceisiais greu cymhwysiad bach a oedd angen storio gwybodaeth defnyddwyr. Ac yna meddyliais, sut alla i baru'r defnyddiwr â'i ddata. Er enghraifft, mae gen i Ivanov Ivan Ivanovich, ac mae ganddo rywfaint o wybodaeth, ond sut alla i wneud ffrindiau gyda nhw? Gallaf nodi'n uniongyrchol bod y ci o'r enw “Tuzik” yn perthyn i'r union Ivan hwn. Ond beth os bydd yn newid ei enw ac yn lle Ivan yn dod, er enghraifft, Olya? Yna bydd yn troi allan na fydd gan ein Olya Ivanovna Ivanova gi mwyach, a bydd ein Tuzik yn dal i fod yn perthyn i'r Ivan nad yw'n bodoli. Fe wnaeth cronfa ddata a roddodd ddynodwr unigryw (ID) i bob defnyddiwr helpu i ddatrys y broblem hon, ac roedd fy Tuzik ynghlwm wrth yr ID hwn, a oedd, mewn gwirionedd, yn rhif cyfresol yn unig. Felly, roedd gan berchennog yr ace ID rhif 2, ac ar ryw adeg roedd Ivan o dan yr ID hwn, ac yna daeth Olya o dan yr un ID. Cafodd problem dynoliaeth a hwsmonaeth anifeiliaid ei datrys yn ymarferol.

Disgrifydd ffeil

Mae problem y ffeil a'r rhaglen sy'n gweithio gyda'r ffeil hon tua'r un peth â phroblem ein ci a'n dyn. Tybiwch imi agor ffeil o'r enw ivan.txt a dechrau ysgrifennu'r gair tuzik i mewn iddo, ond dim ond llwyddo i ysgrifennu'r llythyren gyntaf “t” yn y ffeil, ac ailenwyd y ffeil hon gan rywun, er enghraifft, i olya.txt. Ond mae'r ffeil yn aros yr un fath, a dwi dal eisiau cofnodi fy ace ynddo. Bob tro mae ffeil yn cael ei hagor gan alwad system agor mewn unrhyw iaith raglennu rwy'n derbyn ID unigryw sy'n fy nghyfeirio at ffeil, yr ID hwn yw'r disgrifydd ffeil. Ac nid oes ots o gwbl beth a phwy sy'n gwneud gyda'r ffeil hon nesaf, gellir ei dileu, gellir ei hailenwi, gellir newid y perchennog, neu gellir tynnu'r hawliau i ddarllen ac ysgrifennu i ffwrdd, byddaf yn dal i gael mynediad iddo, oherwydd ar adeg agor y ffeil, roedd gennyf yr hawl i'w darllen a/neu ei hysgrifennu a llwyddais i ddechrau gweithio gyda hi, sy'n golygu bod yn rhaid i mi barhau i wneud hynny.

Yn Linux, mae'r llyfrgell libc yn agor 3 ffeil disgrifydd ar gyfer pob rhaglen redeg (proses), wedi'i rhifo 0,1,2. Ceir rhagor o wybodaeth ar y dolenni stdio dyn и stdout dyn

  • Gelwir disgrifydd ffeil 0 yn STDIN ac mae'n gysylltiedig â mewnbwn cymhwysiad
  • Gelwir disgrifydd ffeil 1 yn STDOUT ac fe'i defnyddir gan gymwysiadau i allbynnu data, megis gorchmynion argraffu
  • Gelwir disgrifydd ffeil 2 yn STDERR ac fe'i defnyddir gan gymwysiadau i allbynnu negeseuon gwall.

Os byddwch chi'n agor unrhyw ffeil ar gyfer darllen neu ysgrifennu yn eich rhaglen, yna mae'n fwyaf tebygol y byddwch chi'n cael yr ID cyntaf am ddim a dyma fydd rhif 3.

Gellir gweld y rhestr o ddisgrifyddion ffeil ar gyfer unrhyw broses os ydych yn gwybod ei DCP.

Er enghraifft, gadewch i ni agor y consol bash ac edrych ar PID ein proses

[user@localhost ]$ echo $$
15771

Yn yr ail gonsol gadewch i ni redeg

[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

Gallwch anwybyddu disgrifydd ffeil rhif 255 yn ddiogel at ddibenion yr erthygl hon; fe'i hagorwyd ar gyfer ei anghenion gan bash ei hun, ac nid gan y llyfrgell gysylltiedig.

Nawr mae pob un o'r 3 ffeil disgrifydd yn gysylltiedig â'r ddyfais derfynell ffug /dev/pts, ond gallwn eu trin o hyd, er enghraifft, eu rhedeg mewn ail gonsol

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

Ac yn y consol cyntaf byddwn yn gweld

[user@localhost ]$ hello world

Ailgyfeirio a Pipe

Gallwch chi ddiystyru'r 3 ffeil disgrifydd hyn yn hawdd mewn unrhyw broses, gan gynnwys mewn bash, er enghraifft trwy bibell sy'n cysylltu dwy broses, gweler

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

Gallwch chi redeg y gorchymyn hwn eich hun gyda strap -f a gweld beth sy'n digwydd y tu mewn, ond byddaf yn dweud wrthych yn fyr.

Mae ein proses bash rhiant gyda PID 15771 yn dosrannu ein gorchymyn ac yn deall yn union faint o orchmynion yr ydym am eu rhedeg, yn ein hachos ni mae dau ohonyn nhw: cath a chysgu. Mae Bash yn gwybod bod angen iddo greu prosesau dau blentyn, a'u huno yn un bibell. Yn gyfan gwbl, bydd angen 2 broses plentyn ac un bibell ar bash.

Mae Bash yn rhedeg galwad system cyn creu prosesau plentyn bibell ac yn derbyn disgrifyddion ffeil newydd ar y byffer pibell dros dro, ond nid yw'r byffer hwn yn cysylltu ein prosesau dau blentyn eto.

Ar gyfer y broses rhiant, mae'n edrych fel bod pibell eisoes, ond nid oes prosesau plentyn eto:

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

Yna defnyddio'r alwad system clonio mae bash yn creu proses dau blentyn, a bydd ein tair proses yn edrych fel hyn:

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

Peidiwch ag anghofio bod clôn yn clonio'r broses ynghyd â'r holl ddisgrifyddion ffeil, felly byddant yr un peth yn y broses rhiant ac yn y rhai plentyn. Gwaith y broses rhiant gyda PID 15771 yw monitro prosesau'r plentyn, felly mae'n aros am ymateb gan y plant.

Felly, nid oes angen pibell arno, ac mae'n cau disgrifyddion ffeiliau rhif 3 a 4.

Yn y broses bash plentyn cyntaf gyda PID 9004, y system alwad dwp2, yn newid ein disgrifydd ffeil STDOUT rhif 1 i ddisgrifydd ffeil sy'n pwyntio at bibell, yn ein hachos ni mae'n rhif 3. Felly, bydd popeth y mae'r broses plentyn cyntaf gyda PID 9004 yn ei ysgrifennu i STDOUT yn dod i ben yn awtomatig yn y byffer pibell.

Yn y broses ail blentyn gyda PID 9005, mae bash yn defnyddio dup2 i newid y disgrifydd ffeil rhif STDIN 0. Nawr bydd popeth y bydd ein hail bash gyda PID 9005 yn ei ddarllen yn cael ei ddarllen o'r bibell.

Ar ôl hyn, mae disgrifyddion ffeiliau rhif 3 a 4 hefyd ar gau yn y prosesau plentyn, gan nad ydynt yn cael eu defnyddio mwyach.

Rwy’n anwybyddu disgrifydd ffeil 255 yn fwriadol; caiff ei ddefnyddio at ddibenion mewnol gan bash ei hun a bydd hefyd yn cael ei gau mewn prosesau plant.

Nesaf, yn y broses plentyn cyntaf gyda PID 9004, mae bash yn dechrau defnyddio galwad system exec y ffeil gweithredadwy a nodwyd gennym ar y llinell orchymyn, yn ein hachos ni yw /usr/bin/cat.

Yn y broses ail blentyn gyda PID 9005, mae bash yn rhedeg yr ail weithredadwy a nodwyd gennym, yn ein hachos ni /usr/bin/cwsg.

Nid yw'r alwad system exec yn cau dolenni ffeiliau oni bai eu bod wedi'u hagor gyda'r faner O_CLOEXEC ar yr adeg y gwnaed yr alwad agored. Yn ein hachos ni, ar ôl lansio'r ffeiliau gweithredadwy, bydd yr holl ddisgrifyddion ffeil cyfredol yn cael eu cadw.

Gwiriwch yn y consol:

[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

Fel y gwelwch, mae nifer unigryw ein pibell yr un peth yn y ddwy broses. Felly mae gennym gysylltiad rhwng dwy broses wahanol gyda'r un rhiant.

I'r rhai nad ydynt yn gyfarwydd â'r galwadau system y mae bash yn eu defnyddio, rwy'n argymell yn fawr rhedeg y gorchmynion trwy strace a gweld beth sy'n digwydd yn fewnol, er enghraifft fel hyn:

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

Gadewch i ni ddychwelyd at ein problem gyda gofod disg isel a cheisio arbed data heb ailgychwyn y broses. Gadewch i ni ysgrifennu rhaglen fach a fydd yn ysgrifennu tua 1 megabeit yr eiliad i ddisg. Ar ben hynny, os nad oeddem yn gallu ysgrifennu data i'r ddisg am ryw reswm, byddwn yn anwybyddu hyn ac yn ceisio ysgrifennu'r data eto mewn eiliad. Yn yr enghraifft rydw i'n defnyddio Python, gallwch chi ddefnyddio unrhyw iaith raglennu arall.

[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

Gadewch i ni redeg y rhaglen ac edrych ar y disgrifyddion ffeil

[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

Fel y gallwch weld, mae gennym ein 3 disgrifydd ffeil safonol ac un arall a agorwyd gennym. Gadewch i ni wirio maint y ffeil:

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

Mae'r data yn cael ei ysgrifennu, rydym yn ceisio newid y caniatâd ar y ffeil:

[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

Gwelwn fod y data yn dal i gael ei ysgrifennu, er nad oes gan ein defnyddiwr ganiatâd i ysgrifennu at y ffeil. Gadewch i ni geisio cael gwared arno:

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

Ble mae'r data wedi'i ysgrifennu? Ac a ydynt yn ysgrifenedig o gwbl? Rydym yn gwirio:

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

Ydy, mae ein disgrifydd ffeil yn dal i fodoli a gallwn drin y disgrifydd ffeil hwn fel ein hen ffeil, gallwn ei darllen, ei chlirio a'i chopïo.

Edrychwn ar faint y ffeil:

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

Maint y ffeil yw 19923457. Gadewch i ni geisio clirio'r ffeil:

[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

Fel y gallwch weld, dim ond cynyddu maint y ffeil ac ni weithiodd ein boncyff. Edrychwn ar ddogfennaeth galwadau'r system agor. Os byddwn yn defnyddio'r faner O_APPEND wrth agor ffeil, yna gyda phob ysgrifen, mae'r system weithredu yn gwirio maint y ffeil ac yn ysgrifennu data i ddiwedd y ffeil, ac yn gwneud hyn yn atomig. Mae hyn yn caniatáu i edafedd neu brosesau lluosog ysgrifennu i'r un ffeil. Ond yn ein cod nid ydym yn defnyddio'r faner hon. Gallwn weld maint ffeil gwahanol yn lsof after trunk dim ond os ydym yn agor y ffeil ar gyfer ysgrifennu ychwanegol, sy'n golygu yn ein cod yn lle hynny

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

mae'n rhaid i ni roi

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

Gwirio gyda'r faner “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 chyda baner "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

Rhaglennu proses sydd eisoes yn rhedeg

Yn aml, mae rhaglenwyr, wrth greu a phrofi rhaglenni, yn defnyddio dadfygwyr (er enghraifft GDB) neu lefelau amrywiol o fewngofnodi yn y rhaglen. Mae Linux yn darparu'r gallu i ysgrifennu a newid rhaglen sydd eisoes yn rhedeg, er enghraifft, newid gwerthoedd newidynnau, gosod torbwynt, ac ati, ac ati.

Gan ddychwelyd at y cwestiwn gwreiddiol am ddim digon o le ar ddisg i ysgrifennu ffeil, gadewch i ni geisio efelychu'r broblem.

Gadewch i ni greu ffeil ar gyfer ein rhaniad, y byddwn yn ei osod fel disg ar wahân:

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

Gadewch i ni greu system ffeiliau:

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

Gosodwch y system ffeiliau:

[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

Rydym yn creu cyfeiriadur gyda'n perchennog:

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

Gadewch i ni agor y ffeil i'w hysgrifennu yn ein rhaglen yn unig:

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

Lansio

[user@localhost ]$ python openforwrite.py 

Rydym yn aros ychydig eiliadau

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

Felly, mae gennym y broblem a ddisgrifir ar ddechrau'r erthygl hon. Lle am ddim 0, 100% wedi'i feddiannu.

Cofiwn, yn ôl amodau’r dasg, ein bod yn ceisio cofnodi data pwysig iawn na ellir ei golli. Ac ar yr un pryd, mae angen i ni drwsio'r gwasanaeth heb ailgychwyn y broses.

Gadewch i ni ddweud bod gennym le ar ddisg o hyd, ond mewn rhaniad gwahanol, er enghraifft yn / cartref.

Gadewch i ni geisio “ailraglennu ar y hedfan” ein cod.

Edrychwn ar PID ein proses, sydd wedi bwyta'r holl le ar y ddisg:

[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

Cysylltwch â'r broses trwy gdb

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

Edrychwn ar y disgrifyddion ffeil agored:

(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

Edrychwn ar y wybodaeth am ddisgrifydd ffeil rhif 3, sydd o ddiddordeb i ni

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

Gan gadw mewn cof pa alwad system y mae Python yn ei gwneud (gweler uchod lle rhedon ni'n ddirybudd a dod o hyd i'r alwad agored), wrth brosesu ein cod i agor ffeil, rydyn ni'n gwneud yr un peth ein hunain ar ran ein proses, ond mae angen yr O_WRONLY|O_CREAT| Mae didau O_TRUNC yn disodli gyda gwerth rhifol. I wneud hyn, agorwch y ffynonellau cnewyllyn, er enghraifft yma ac edrych pa fflagiau sy'n gyfrifol am beth

#diffinio O_WRONLY 00000001
#diffinio O_CREAT 00000100
#diffinio O_TRUNC 00001000

Rydym yn cyfuno'r holl werthoedd yn un, rydym yn cael 00001101

Rydym yn rhedeg ein galwad o gdb

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

Felly cawsom ddisgrifydd ffeil newydd gyda rhif 4 a ffeil agored newydd ar raniad arall, rydym yn gwirio:

(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

Rydyn ni'n cofio'r enghraifft gyda phibell - sut mae bash yn newid disgrifyddion ffeil, ac rydyn ni eisoes wedi dysgu'r alwad system dup2.

Rydym yn ceisio disodli un disgrifydd ffeil am un arall

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

Rydym yn gwirio:

(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

Rydym yn cau disgrifydd ffeil 4, gan nad oes ei angen arnom:

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

Ac ymadael 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

Wrthi'n gwirio'r ffeil newydd:

[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

Fel y gallwch weld, mae'r data wedi'i ysgrifennu i ffeil newydd, gadewch i ni wirio'r hen un:

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

Nid oes unrhyw ddata yn cael ei golli, mae'r rhaglen yn gweithio, mae logiau'n cael eu hysgrifennu i leoliad newydd.

Gadewch i ni gymhlethu'r dasg ychydig

Dychmygwn fod y data yn bwysig i ni, ond nid oes gennym le ar ddisg yn unrhyw un o'r rhaniadau ac ni allwn gysylltu'r ddisg.

Yr hyn y gallwn ei wneud yw ailgyfeirio ein data yn rhywle, er enghraifft i bibell, ac yn ei dro ailgyfeirio data o bibell i'r rhwydwaith drwy ryw raglen, er enghraifft netcat.
Gallwn greu pibell a enwir gyda'r gorchymyn mkfifo. Bydd yn creu ffeil ffug ar y system ffeiliau hyd yn oed os nad oes lle am ddim arno.

Ailgychwyn y cais a gwirio:

[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

Nid oes lle ar y ddisg, ond rydym yn llwyddo i greu pibell a enwir yno:

[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

Nawr mae angen i ni rywsut lapio'r holl ddata sy'n mynd i'r bibell hon i weinydd arall trwy'r rhwydwaith; mae'r un netcat yn addas ar gyfer hyn.

Ar y gweinydd pell-server.example.com rydym yn rhedeg

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

Ar ein gweinydd problemus rydym yn lansio mewn terfynell ar wahân

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

Nawr bydd yr holl ddata sy'n dod i ben yn y bibell yn mynd yn awtomatig i stdin yn netcat, a fydd yn ei anfon i'r rhwydwaith ar borthladd 7777.

Y cyfan sy'n rhaid i ni ei wneud yw dechrau ysgrifennu ein data i'r bibell hon a enwir.

Mae gennym y cais yn rhedeg yn barod:

[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'r holl fflagiau, dim ond O_WRONLY sydd ei angen arnom gan fod y ffeil eisoes yn bodoli ac nid oes angen i ni ei chlirio

[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

Gwirio'r gweinydd pell remote-server.example.com

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

Mae'r data yn dod, rydym yn gwirio'r gweinydd problem

[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

Mae'r data yn cael ei arbed, mae'r broblem yn cael ei datrys.

Manteisiaf ar y cyfle hwn i ddweud helo wrth fy nghydweithwyr o Degiro.
Gwrandewch ar bodlediadau Radio-T.

Pob peth yn dda.

Fel gwaith cartref, rwy'n awgrymu eich bod chi'n meddwl beth fydd yn y disgrifiadau ffeil proses cath a chysgu os ydych chi'n rhedeg y gorchymyn canlynol:

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

Ffynhonnell: hab.com

Ychwanegu sylw