Մի անգամ հարցազրույցի ժամանակ ինձ հարցրին՝ ի՞նչ եք անելու, եթե հայտնաբերեք, որ ծառայությունը չի աշխատում սկավառակի տարածքի սպառվելու պատճառով:
Ես, իհարկե, պատասխանեցի, որ կտեսնեմ, թե ինչ է զբաղեցրել այս տեղը, և հնարավորության դեպքում կմաքրեմ այդ տեղը։
Այնուհետև հարցազրուցավարը հարցրեց՝ ի՞նչ անել, եթե բաժանման վրա ազատ տարածք չկա, բայց դուք նաև չեք տեսնում որևէ ֆայլ, որը կզբաղեցնի ամբողջ տարածքը:
Սրա համար ես ասացի, որ դուք միշտ կարող եք դիտել բաց ֆայլերի նկարագրիչները, օրինակ՝ lsof հրամանով, և հասկանալ, թե որ հավելվածն է զբաղեցրել ողջ հասանելի տարածքը, այնուհետև կարող եք գործել ըստ հանգամանքների՝ կախված տվյալների անհրաժեշտությունից: .
Հարցազրուցավարն ընդհատեց ինձ վերջին բառի ժամանակ՝ ավելացնելով իր հարցին. «Ենթադրենք, մեզ պետք չեն տվյալները, դա պարզապես վրիպազերծման մատյան է, բայց հավելվածը չի աշխատում, քանի որ չի կարող վրիպազերծում գրել»:
«Լավ,- պատասխանեցի ես,- մենք կարող ենք անջատել վրիպազերծումը հավելվածի կազմաձևում և վերագործարկել այն»:
Հարցազրուցավարն առարկեց. «Ոչ, մենք չենք կարող վերագործարկել հավելվածը, մենք դեռ կարևոր տվյալներ ունենք պահված հիշողության մեջ, և կարևոր հաճախորդները միացված են հենց ծառայությանը, որը մենք չենք կարող ստիպել նորից միացնել»:
«Լավ», - ասացի ես, - եթե մենք չենք կարող վերագործարկել հավելվածը, և տվյալները մեզ համար կարևոր չեն, ապա մենք կարող ենք պարզապես մաքրել այս բաց ֆայլը ֆայլի նկարագրիչի միջոցով, նույնիսկ եթե այն չենք տեսնում ls հրամանում: ֆայլային համակարգի վրա»:
Հարցազրուցավարը գոհ էր, իսկ ես՝ ոչ:
Հետո մտածեցի՝ ինչո՞ւ իմ գիտելիքները ստուգողն ավելի խորը չի փորում: Բայց ի՞նչ, եթե տվյալներն ի վերջո կարևոր են: Ի՞նչ անել, եթե մենք չկարողանանք վերագործարկել գործընթացը, և գործընթացը գրի ֆայլային համակարգին մի բաժանման վրա, որը ազատ տարածություն չունի: Իսկ եթե մենք չկարողանանք կորցնել ոչ միայն արդեն գրված տվյալները, այլև այն տվյալները, որոնք այս գործընթացը գրում կամ փորձում է գրել:
Տուզիկ
Իմ կարիերայի սկզբում ես փորձեցի ստեղծել մի փոքրիկ հավելված, որը պետք է պահպաներ օգտատերերի տեղեկատվությունը: Եվ հետո ես մտածեցի, թե ինչպես կարող եմ համապատասխանեցնել օգտվողին իր տվյալների հետ: Օրինակ, ես ունեմ Իվանով Իվան Իվանովիչ, և նա որոշակի տեղեկություններ ունի, բայց ինչպե՞ս կարող եմ ընկերանալ նրանց հետ: Կարող եմ ուղղակիորեն նշել, որ «Տուզիկ» անունով շունը հենց այս Իվանին է պատկանում։ Բայց եթե նա փոխի իր անունը և Իվանի փոխարեն դառնա, օրինակ, Օլյա՞ն։ Հետո կպարզվի, որ մեր Օլյա Իվանովնա Իվանովան այլեւս շուն չի ունենա, իսկ մեր Տուզիկը դեռ գոյություն չունեցող Իվանին է պատկանում։ Տվյալների բազան, որը յուրաքանչյուր օգտվողին տալիս էր եզակի նույնացուցիչ (ID) օգնեց լուծել այս խնդիրը, և իմ Tuzik-ը կապված էր այս ID-ի հետ, որն իրականում ընդամենը սերիական համար էր: Այսպիսով, էսի սեփականատերը ուներ ID համար 2, և ժամանակի ինչ-որ պահի Իվանը գտնվում էր այս ID-ի տակ, իսկ հետո Օլյան դարձավ նույն ID-ի տակ: Մարդկության ու անասնապահության խնդիրը գործնականում լուծվեց։
Ֆայլի նկարագրիչ
Ֆայլի և այս ֆայլի հետ աշխատող ծրագրի խնդիրը մոտավորապես նույնն է, ինչ մեր շան և մարդու խնդիրը: Ենթադրենք, ես բացեցի ivan.txt անունով մի ֆայլ և սկսեցի դրա մեջ գրել tuzik բառը, բայց միայն հաջողվեց գրել առաջին «t» տառը ֆայլում, և այս ֆայլը ինչ-որ մեկի կողմից վերանվանվեց, օրինակ, olya.txt: Բայց ֆայլը մնում է նույնը, և ես դեռ ուզում եմ դրանում գրանցել իմ ace-ը: Ամեն անգամ, երբ ֆայլը բացվում է համակարգային զանգի միջոցով
Linux-ում libc գրադարանը յուրաքանչյուր գործող հավելվածի (գործընթացի) համար բացում է 3 նկարագրող ֆայլ՝ 0,1,2 համարներով։ Լրացուցիչ տեղեկություններ կարելի է գտնել հղումներով
- Ֆայլի նկարագրիչը 0 կոչվում է STDIN և կապված է հավելվածի մուտքագրման հետ
- Ֆայլի նկարագրիչը 1 կոչվում է STDOUT և օգտագործվում է հավելվածների կողմից տվյալների ելքի համար, ինչպիսիք են տպման հրամանները
- Ֆայլի նկարագրիչ 2-ը կոչվում է STDERR և օգտագործվում է հավելվածների կողմից՝ սխալի հաղորդագրություններ ուղարկելու համար:
Եթե ձեր ծրագրում բացեք որևէ ֆայլ՝ կարդալու կամ գրելու համար, ապա, ամենայն հավանականությամբ, կստանաք առաջին անվճար ID-ն և այն կլինի համարը 3։
Ֆայլերի նկարագրիչների ցանկը կարող է դիտվել ցանկացած գործընթացի համար, եթե դուք գիտեք դրա PID-ը:
Օրինակ, եկեք բացենք bash վահանակը և նայենք մեր գործընթացի PID-ին
[user@localhost ]$ echo $$
15771
Երկրորդ կոնսոլում եկեք գործարկենք
[user@localhost ]$ ls -lah /proc/15771/fd/
total 0
dr-x------ 2 user user 0 Oct 7 15:42 .
dr-xr-xr-x 9 user user 0 Oct 7 15:42 ..
lrwx------ 1 user user 64 Oct 7 15:42 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 255 -> /dev/pts/21
Դուք կարող եք ապահով կերպով անտեսել ֆայլի համար 255 նկարագրիչը այս հոդվածի նպատակների համար, այն բացվել է իր կարիքների համար հենց bash-ի կողմից, և ոչ թե կապված գրադարանի կողմից:
Այժմ բոլոր 3 նկարագրիչ ֆայլերը կապված են կեղծ տերմինալ սարքի հետ
[user@localhost ]$ echo "hello world" > /proc/15771/fd/0
Իսկ առաջին կոնսոլում մենք կտեսնենք
[user@localhost ]$ hello world
Վերահղում և խողովակ
Դուք կարող եք հեշտությամբ վերացնել այս 3 նկարագրիչ ֆայլերը ցանկացած գործընթացում, ներառյալ bash-ում, օրինակ՝ երկու պրոցեսներ միացնող խողովակի միջոցով, տես.
[user@localhost ]$ cat /dev/zero | sleep 10000
Դուք կարող եք ինքներդ գործարկել այս հրամանը strace -f և տեսեք, թե ինչ է կատարվում ներսում, բայց ես ձեզ կարճ կասեմ.
Մեր ծնողական bash գործընթացը PID 15771-ով վերլուծում է մեր հրամանը և հստակ հասկանում, թե քանի հրաման ենք ուզում գործարկել, մեր դեպքում դրանք երկուսն են՝ cat և sleep: Bash-ը գիտի, որ պետք է ստեղծի երկու երեխա գործընթացներ և դրանք միաձուլի մեկ խողովակի մեջ: Ընդհանուր առմամբ, bash-ին անհրաժեշտ կլինի 2 երեխայի պրոցես և մեկ խողովակ:
Bash-ն իրականացնում է համակարգային զանգ՝ նախքան երեխայի գործընթացները ստեղծելը
Ծնողական գործընթացի համար, կարծես, արդեն խողովակ կա, բայց դեռևս երեխա գործընթացներ չկան.
PID command
15771 bash
lrwx------ 1 user user 64 Oct 7 15:42 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:42 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:42 255 -> /dev/pts/21
Այնուհետև օգտագործելով համակարգային զանգը
PID command
15771 bash
lrwx------ 1 user user 64 Oct 7 15:42 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:42 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:42 255 -> /dev/pts/21
PID command
9004 bash
lrwx------ 1 user user 64 Oct 7 15:57 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:57 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:57 255 -> /dev/pts/21
PID command
9005 bash
lrwx------ 1 user user 64 Oct 7 15:57 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:57 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:57 255 -> /dev/pts/21
Մի մոռացեք, որ կլոնավորումը կլոնավորում է գործընթացը բոլոր ֆայլերի նկարագրիչների հետ միասին, այնպես որ դրանք նույնը կլինեն ծնողական և մանկական գործընթացներում: PID 15771-ով ծնող գործընթացի աշխատանքը երեխայի գործընթացները վերահսկելն է, ուստի այն պարզապես սպասում է երեխաների արձագանքին:
Հետևաբար, այն խողովակի կարիք չունի, և այն փակում է 3 և 4 թվով ֆայլերի նկարագրիչները:
PID 9004-ով առաջին երեխայի բախման գործընթացում համակարգային զանգը
PID 9005-ով երկրորդ երեխայի գործընթացում bash-ը օգտագործում է dup2՝ փոխելու ֆայլի նկարագրիչ STDIN համարը 0: Այժմ այն ամենը, ինչ կկարդա մեր երկրորդ bash-ը PID 9005-ով, կկարդացվի խողովակից:
Դրանից հետո 3 և 4 համարներով ֆայլերի նկարագրիչները նույնպես փակվում են երեխայի գործընթացներում, քանի որ դրանք այլևս չեն օգտագործվում:
Ես միտումնավոր անտեսում եմ ֆայլի նկարագրիչ 255-ը, այն օգտագործվում է ներքին նպատակների համար բաշի կողմից և կփակվի նաև երեխայի գործընթացներում:
Հաջորդը, PID 9004-ով առաջին երեխայի գործընթացում, bash-ը սկսում է օգտագործել համակարգային զանգ
PID 9005-ով երկրորդ երեխայի գործընթացում bash-ը գործարկում է մեր նշած երկրորդ գործարկիչը, մեր դեպքում /usr/bin/sleep:
Exec համակարգային զանգը չի փակում ֆայլերի բռնակները, եթե բաց կանչի պահին դրանք բացված չեն եղել O_CLOEXEC դրոշով: Մեր դեպքում գործարկվող ֆայլերը գործարկելուց հետո բոլոր ընթացիկ ֆայլերի նկարագրիչները կպահպանվեն:
Ստուգեք վահանակում.
[user@localhost ]$ pgrep -P 15771
9004
9005
[user@localhost ]$ ls -lah /proc/15771/fd/
total 0
dr-x------ 2 user user 0 Oct 7 15:42 .
dr-xr-xr-x 9 user user 0 Oct 7 15:42 ..
lrwx------ 1 user user 64 Oct 7 15:42 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 255 -> /dev/pts/21
[user@localhost ]$ ls -lah /proc/9004/fd
total 0
dr-x------ 2 user user 0 Oct 7 15:57 .
dr-xr-xr-x 9 user user 0 Oct 7 15:57 ..
lrwx------ 1 user user 64 Oct 7 15:57 0 -> /dev/pts/21
l-wx------ 1 user user 64 Oct 7 15:57 1 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:57 2 -> /dev/pts/21
lr-x------ 1 user user 64 Oct 7 15:57 3 -> /dev/zero
[user@localhost ]$ ls -lah /proc/9005/fd
total 0
dr-x------ 2 user user 0 Oct 7 15:57 .
dr-xr-xr-x 9 user user 0 Oct 7 15:57 ..
lr-x------ 1 user user 64 Oct 7 15:57 0 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:57 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 2 -> /dev/pts/21
[user@localhost ]$ ps -up 9004
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
user 9004 0.0 0.0 107972 620 pts/21 S+ 15:57 0:00 cat /dev/zero
[user@localhost ]$ ps -up 9005
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
user 9005 0.0 0.0 107952 360 pts/21 S+ 15:57 0:00 sleep 10000
Ինչպես տեսնում եք, մեր խողովակի եզակի թիվը նույնն է երկու գործընթացներում: Այսպիսով, մենք կապ ունենք երկու տարբեր գործընթացների միջև նույն ծնողի հետ:
Նրանց համար, ովքեր ծանոթ չեն համակարգային զանգերին, որոնք օգտագործում է bash-ը, ես խորհուրդ եմ տալիս հրամանները գործարկել strace-ի միջոցով և տեսնել, թե ինչ է կատարվում ներսում, օրինակ՝ այսպես.
strace -s 1024 -f bash -c "ls | grep hello"
Եկեք վերադառնանք մեր խնդրին՝ կապված ցածր սկավառակի տարածության հետ և փորձում ենք պահպանել տվյալները՝ առանց գործընթացը վերագործարկելու: Եկեք գրենք մի փոքր ծրագիր, որը սկավառակի վրա կգրի մոտավորապես 1 մեգաբայթ վայրկյանում։ Ավելին, եթե ինչ-ինչ պատճառներով մենք չկարողացանք տվյալներ գրել սկավառակի վրա, մենք պարզապես անտեսելու ենք դա և կփորձենք նորից գրել տվյալները մեկ վայրկյանում: Օրինակում, որ ես օգտագործում եմ Python-ը, կարող եք օգտագործել ցանկացած այլ ծրագրավորման լեզու:
[user@localhost ]$ cat openforwrite.py
import datetime
import time
mystr="a"*1024*1024+"n"
with open("123.txt", "w") as f:
while True:
try:
f.write(str(datetime.datetime.now()))
f.write(mystr)
f.flush()
time.sleep(1)
except:
pass
Եկեք գործարկենք ծրագիրը և նայենք ֆայլերի նկարագրիչներին
[user@localhost ]$ python openforwrite.py &
[1] 3762
[user@localhost ]$ ps axuf | grep [o]penforwrite
user 3762 0.0 0.0 128600 5744 pts/22 S+ 16:28 0:00 | _ python openforwrite.py
[user@localhost ]$ ls -la /proc/3762/fd
total 0
dr-x------ 2 user user 0 Oct 7 16:29 .
dr-xr-xr-x 9 user user 0 Oct 7 16:29 ..
lrwx------ 1 user user 64 Oct 7 16:29 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 7 16:29 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 7 16:29 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 7 16:29 3 -> /home/user/123.txt
Ինչպես տեսնում եք, մենք ունենք մեր 3 ստանդարտ ֆայլի նկարագրիչները և ևս մեկը, որը մենք բացել ենք: Եկեք ստուգենք ֆայլի չափը.
[user@localhost ]$ ls -lah 123.txt
-rw-rw-r-- 1 user user 117M Oct 7 16:30 123.txt
Տվյալները գրվում են, մենք փորձում ենք փոխել ֆայլի թույլտվությունները.
[user@localhost ]$ sudo chown root: 123.txt
[user@localhost ]$ ls -lah 123.txt
-rw-rw-r-- 1 root root 168M Oct 7 16:31 123.txt
[user@localhost ]$ ls -lah 123.txt
-rw-rw-r-- 1 root root 172M Oct 7 16:31 123.txt
Մենք տեսնում ենք, որ տվյալները դեռ գրվում են, չնայած մեր օգտատերը ֆայլում գրելու թույլտվություն չունի։ Փորձենք հեռացնել այն.
[user@localhost ]$ sudo rm 123.txt
[user@localhost ]$ ls 123.txt
ls: cannot access 123.txt: No such file or directory
Որտե՞ղ են գրված տվյալները: Իսկ դրանք ընդհանրապես գրվա՞ծ են։ Մենք ստուգում ենք.
[user@localhost ]$ ls -la /proc/3762/fd
total 0
dr-x------ 2 user user 0 Oct 7 16:29 .
dr-xr-xr-x 9 user user 0 Oct 7 16:29 ..
lrwx------ 1 user user 64 Oct 7 16:29 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 7 16:29 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 7 16:29 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 7 16:29 3 -> /home/user/123.txt (deleted)
Այո, մեր ֆայլի նկարագրիչը դեռ գոյություն ունի, և մենք կարող ենք այս ֆայլի նկարագրիչին վերաբերվել ինչպես մեր հին ֆայլին, կարող ենք կարդալ, մաքրել և պատճենել այն:
Եկեք նայենք ֆայլի չափին.
[user@localhost ]$ lsof | grep 123.txt
python 31083 user 3w REG 8,5 19923457 2621522 /home/user/123.txt
Ֆայլի չափը 19923457 է: Փորձենք մաքրել ֆայլը.
[user@localhost ]$ truncate -s 0 /proc/31083/fd/3
[user@localhost ]$ lsof | grep 123.txt
python 31083 user 3w REG 8,5 136318390 2621522 /home/user/123.txt
Ինչպես տեսնում եք, ֆայլի չափը միայն ավելանում է, և մեր բեռնախցիկը չի աշխատում: Եկեք նայենք համակարգային զանգերի փաստաթղթերին
with open("123.txt", "w") as f:
մենք պետք է դնենք
with open("123.txt", "a") as f:
Ստուգում «w» դրոշակով
[user@localhost ]$ strace -e trace=open python openforwrite.py 2>&1| grep 123.txt
open("123.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
եւ «ա» դրոշակով
[user@localhost ]$ strace -e trace=open python openforwrite.py 2>&1| grep 123.txt
open("123.txt", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
Արդեն գործող գործընթացի ծրագրավորում
Հաճախ ծրագրավորողները ծրագրեր ստեղծելիս և փորձարկելիս օգտագործում են վրիպազերծիչներ (օրինակ՝ GDB) կամ հավելվածում գրանցման տարբեր մակարդակներ։ Linux-ը հնարավորություն է տալիս իրականում գրել և փոխել արդեն գործող ծրագիրը, օրինակ՝ փոխել փոփոխականների արժեքները, սահմանել ընդմիջման կետ և այլն և այլն:
Վերադառնալով ֆայլ գրելու համար սկավառակի ոչ բավարար տարածության մասին սկզբնական հարցին, փորձենք մոդելավորել խնդիրը:
Եկեք ստեղծենք ֆայլ մեր բաժանման համար, որը մենք կտեղադրենք որպես առանձին սկավառակ.
[user@localhost ~]$ dd if=/dev/zero of=~/tempfile_for_article.dd bs=1M count=10
10+0 records in
10+0 records out
10485760 bytes (10 MB) copied, 0.00525929 s, 2.0 GB/s
[user@localhost ~]$
Եկեք ստեղծենք ֆայլային համակարգ.
[user@localhost ~]$ mkfs.ext4 ~/tempfile_for_article.dd
mke2fs 1.42.9 (28-Dec-2013)
/home/user/tempfile_for_article.dd is not a block special device.
Proceed anyway? (y,n) y
...
Writing superblocks and filesystem accounting information: done
[user@localhost ~]$
Տեղադրեք ֆայլային համակարգը.
[user@localhost ~]$ sudo mount ~/tempfile_for_article.dd /mnt/
[sudo] password for user:
[user@localhost ~]$ df -h | grep mnt
/dev/loop0 8.7M 172K 7.9M 3% /mnt
Մենք ստեղծում ենք գրացուցակ մեր սեփականատիրոջ հետ.
[user@localhost ~]$ sudo mkdir /mnt/logs
[user@localhost ~]$ sudo chown user: /mnt/logs
Եկեք բացենք ֆայլը միայն մեր ծրագրում գրելու համար.
with open("/mnt/logs/123.txt", "w") as f:
Մենք մեկնարկում ենք
[user@localhost ]$ python openforwrite.py
Մենք սպասում ենք մի քանի վայրկյան
[user@localhost ~]$ df -h | grep mnt
/dev/loop0 8.7M 8.0M 0 100% /mnt
Այսպիսով, մենք ունենք այս հոդվածի սկզբում նկարագրված խնդիրը: Ազատ տարածք 0, 100% զբաղված։
Հիշում ենք, որ ըստ առաջադրանքի պայմանների՝ փորձում ենք արձանագրել շատ կարևոր տվյալներ, որոնք հնարավոր չէ կորցնել։ Եվ միևնույն ժամանակ մենք պետք է շտկենք ծառայությունը՝ առանց գործընթացը վերագործարկելու։
Ենթադրենք, մենք դեռ ունենք սկավառակի տարածություն, բայց այլ բաժանման մեջ, օրինակ /home-ում:
Փորձենք «վերածրագրավորել» մեր կոդը:
Եկեք նայենք մեր գործընթացի PID-ին, որը խլել է սկավառակի ամբողջ տարածքը.
[user@localhost ~]$ ps axuf | grep [o]penfor
user 10078 27.2 0.0 128600 5744 pts/22 R+ 11:06 0:02 | _ python openforwrite.py
Միացեք գործընթացին gdb-ի միջոցով
[user@localhost ~]$ gdb -p 10078
...
(gdb)
Եկեք նայենք բաց ֆայլերի նկարագրիչներին.
(gdb) shell ls -lah /proc/10078/fd/
total 0
dr-x------ 2 user user 0 Oct 8 11:06 .
dr-xr-xr-x 9 user user 0 Oct 8 11:06 ..
lrwx------ 1 user user 64 Oct 8 11:09 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:09 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:06 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:09 3 -> /mnt/logs/123.txt
Մենք դիտում ենք 3-րդ ֆայլի նկարագրիչի մասին տեղեկատվությունը, որը մեզ հետաքրքրում է
(gdb) shell cat /proc/10078/fdinfo/3
pos: 8189952
flags: 0100001
mnt_id: 482
Նկատի ունենալով, թե ինչ համակարգային կանչ է անում Python-ը (տես վերևում, որտեղ մենք գործարկեցինք strace-ը և գտանք բաց կանչը), երբ մշակում ենք մեր կոդը՝ ֆայլ բացելու համար, մենք ինքներս նույնն ենք անում մեր գործընթացի անունից, բայց մեզ անհրաժեշտ է O_WRONLY|O_CREAT| O_TRUNC բիթերը փոխարինվում են թվային արժեքով: Դա անելու համար, օրինակ, բացեք միջուկի աղբյուրները
#սահմանել O_WRONLY 00000001
#սահմանել O_CREAT 00000100
#սահմանել O_TRUNC 00001000
Մենք միավորում ենք բոլոր արժեքները մեկում, ստանում ենք 00001101
Մենք կատարում ենք մեր զանգը gdb-ից
(gdb) call open("/home/user/123.txt", 00001101,0666)
$1 = 4
Այսպիսով, մենք ստացանք նոր ֆայլի նկարագրիչ 4 համարով և նոր բաց ֆայլ մեկ այլ բաժանման վրա, մենք ստուգում ենք.
(gdb) shell ls -lah /proc/10078/fd/
total 0
dr-x------ 2 user user 0 Oct 8 11:06 .
dr-xr-xr-x 9 user user 0 Oct 8 11:06 ..
lrwx------ 1 user user 64 Oct 8 11:09 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:09 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:06 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:09 3 -> /mnt/logs/123.txt
l-wx------ 1 user user 64 Oct 8 11:15 4 -> /home/user/123.txt
Մենք հիշում ենք խողովակի օրինակը՝ ինչպես է bash-ը փոխում ֆայլերի նկարագրությունները, և մենք արդեն սովորել ենք dup2 համակարգի կանչը։
Մենք փորձում ենք մի ֆայլի նկարագրիչը փոխարինել մյուսով
(gdb) call dup2(4,3)
$2 = 3
Մենք ստուգում ենք.
(gdb) shell ls -lah /proc/10078/fd/
total 0
dr-x------ 2 user user 0 Oct 8 11:06 .
dr-xr-xr-x 9 user user 0 Oct 8 11:06 ..
lrwx------ 1 user user 64 Oct 8 11:09 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:09 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:06 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:09 3 -> /home/user/123.txt
l-wx------ 1 user user 64 Oct 8 11:15 4 -> /home/user/123.txt
Մենք փակում ենք ֆայլի նկարագրիչը 4, քանի որ մեզ դա պետք չէ.
(gdb) call close (4)
$1 = 0
Եվ դուրս եկեք gdb-ից
(gdb) quit
A debugging session is active.
Inferior 1 [process 10078] will be detached.
Quit anyway? (y or n) y
Detaching from program: /usr/bin/python2.7, process 10078
Նոր ֆայլի ստուգում.
[user@localhost ~]$ ls -lah /home/user/123.txt
-rw-rw-r-- 1 user user 5.1M Oct 8 11:18 /home/user/123.txt
[user@localhost ~]$ ls -lah /home/user/123.txt
-rw-rw-r-- 1 user user 7.1M Oct 8 11:18 /home/user/123.txt
Ինչպես տեսնում եք, տվյալները գրված են նոր ֆայլում, եկեք ստուգենք հինը.
[user@localhost ~]$ ls -lah /mnt/logs/123.txt
-rw-rw-r-- 1 user user 7.9M Oct 8 11:08 /mnt/logs/123.txt
Ոչ մի տվյալ չի կորչում, հավելվածն աշխատում է, տեղեկամատյանները գրվում են նոր վայրում:
Եկեք մի փոքր բարդացնենք խնդիրը
Եկեք պատկերացնենք, որ տվյալները մեզ համար կարևոր են, բայց մենք բաժանմունքներից որևէ մեկում սկավառակի տարածություն չունենք և չենք կարող միացնել սկավառակը:
Այն, ինչ մենք կարող ենք անել, մեր տվյալները վերահղելն է ինչ-որ տեղ, օրինակ՝ խողովակին, և իր հերթին տվյալները խողովակից դեպի ցանց վերահղել ինչ-որ ծրագրի միջոցով, օրինակ՝ netcat:
mkfifo հրամանով կարող ենք ստեղծել անունով խողովակ։ Այն կստեղծի կեղծ ֆայլ ֆայլային համակարգում, նույնիսկ եթե դրա վրա ազատ տարածք չկա:
Վերագործարկեք հավելվածը և ստուգեք.
[user@localhost ]$ python openforwrite.py
[user@localhost ~]$ ps axuf | grep [o]pen
user 5946 72.9 0.0 128600 5744 pts/22 R+ 11:27 0:20 | _ python openforwrite.py
[user@localhost ~]$ ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user 0 Oct 8 11:27 .
dr-xr-xr-x 9 user user 0 Oct 8 11:27 ..
lrwx------ 1 user user 64 Oct 8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:28 3 -> /mnt/logs/123.txt
[user@localhost ~]$ df -h | grep mnt
/dev/loop0 8.7M 8.0M 0 100% /mnt
Սկավառակի տարածք չկա, բայց մենք այնտեղ հաջողությամբ ստեղծում ենք անունով խողովակ.
[user@localhost ~]$ mkfifo /mnt/logs/megapipe
[user@localhost ~]$ ls -lah /mnt/logs/megapipe
prw-rw-r-- 1 user user 0 Oct 8 11:28 /mnt/logs/megapipe
Այժմ մենք պետք է ինչ-որ կերպ փաթեթավորենք այս խողովակի մեջ մտնող բոլոր տվյալները ցանցի միջոցով մեկ այլ սերվերի մեջ, նույն netcat-ը հարմար է դրա համար:
Remote-server.example.com սերվերում մենք աշխատում ենք
[user@localhost ~]$ nc -l 7777 > 123.txt
Մեր խնդրահարույց սերվերի վրա մենք գործարկում ենք առանձին տերմինալում
[user@localhost ~]$ nc remote-server.example.com 7777 < /mnt/logs/megapipe
Այժմ բոլոր տվյալները, որոնք հայտնվում են խողովակում, ավտոմատ կերպով կգնան stdin netcat-ում, որը դրանք կուղարկի ցանց 7777 նավահանգստի վրա:
Մեզ մնում է միայն սկսել գրել մեր տվյալները այս անունով խողովակում:
Մենք արդեն ունենք գործարկվող հավելվածը՝
[user@localhost ~]$ ps axuf | grep [o]pen
user 5946 99.8 0.0 128600 5744 pts/22 R+ 11:27 169:27 | _ python openforwrite.py
[user@localhost ~]$ ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user 0 Oct 8 11:27 .
dr-xr-xr-x 9 user user 0 Oct 8 11:27 ..
lrwx------ 1 user user 64 Oct 8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:28 3 -> /mnt/logs/123.txt
Բոլոր դրոշներից մեզ անհրաժեշտ է միայն O_WRONLY, քանի որ ֆայլն արդեն գոյություն ունի, և մենք այն մաքրելու կարիք չունենք
[user@localhost ~]$ gdb -p 5946
...
(gdb) call open("/mnt/logs/megapipe", 00000001,0666)
$1 = 4
(gdb) shell ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user 0 Oct 8 11:27 .
dr-xr-xr-x 9 user user 0 Oct 8 11:27 ..
lrwx------ 1 user user 64 Oct 8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:28 3 -> /mnt/logs/123.txt
l-wx------ 1 user user 64 Oct 8 14:20 4 -> /mnt/logs/megapipe
(gdb) call dup2(4,3)
$2 = 3
(gdb) shell ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user 0 Oct 8 11:27 .
dr-xr-xr-x 9 user user 0 Oct 8 11:27 ..
lrwx------ 1 user user 64 Oct 8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:28 3 -> /mnt/logs/megapipe
l-wx------ 1 user user 64 Oct 8 14:20 4 -> /mnt/logs/megapipe
(gdb) call close(4)
$3 = 0
(gdb) shell ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user 0 Oct 8 11:27 .
dr-xr-xr-x 9 user user 0 Oct 8 11:27 ..
lrwx------ 1 user user 64 Oct 8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:28 3 -> /mnt/logs/megapipe
(gdb) quit
A debugging session is active.
Inferior 1 [process 5946] will be detached.
Quit anyway? (y or n) y
Detaching from program: /usr/bin/python2.7, process 5946
Ստուգվում է հեռավոր սերվերի remote-server.example.com
[user@localhost ~]$ ls -lah 123.txt
-rw-rw-r-- 1 user user 38M Oct 8 14:21 123.txt
Տվյալները գալիս են, մենք ստուգում ենք խնդրահարույց սերվերը
[user@localhost ~]$ ls -lah /mnt/logs/
total 7.9M
drwxr-xr-x 2 user user 1.0K Oct 8 11:28 .
drwxr-xr-x 4 root root 1.0K Oct 8 10:55 ..
-rw-rw-r-- 1 user user 7.9M Oct 8 14:17 123.txt
prw-rw-r-- 1 user user 0 Oct 8 14:22 megapipe
Տվյալները պահպանված են, խնդիրը լուծված է։
Օգտվելով առիթից՝ ողջունում եմ Դեժիրոյի իմ գործընկերներին:
Լսեք Radio T podcasts:
Ամենայն բարիք։
Որպես տնային աշխատանք, ես առաջարկում եմ ձեզ մտածել այն մասին, թե ինչ կլինի գործընթացի ֆայլի նկարագրիչներ cat and sleep, եթե գործարկեք հետևյալ հրամանը.
[user@localhost ~]$ cat /dev/zero 2>/dev/null| sleep 10000
Source: www.habr.com