Мысалдармен Linux жүйесіндегі файл дескрипторы

Бірде сұхбат кезінде маған дискіде бос орын таусылғандықтан қызмет жұмыс істемей қалса, не істейсіз?

Әрине, мен бұл жердің не жатқанын көремін, мүмкін болса, сол жерді тазалаймын деп жауап бердім.
Содан кейін сұхбат алушы бөлімде бос орын болмаса, бірақ сіз барлық орынды алатын файлдарды көрмесеңіз ше?

Бұған мен әрқашан ашық файл дескрипторларын, мысалы, lsof пәрменімен қарауға болатынын және қандай қолданбаның барлық бос орынды алғанын түсінуге болатынын айттым, содан кейін деректер қажет пе, соған байланысты жағдайларға сәйкес әрекет ете аласыз. .

Интервьюер соңғы сөзімде мені тоқтатып, оның сұрағына қосты: «Бізге деректер қажет емес делік, бұл жай ғана жөндеу журналы, бірақ қолданба жұмыс істемейді, себебі ол жөндеуді жаза алмайды»?

«Жарайды, - деп жауап бердім мен, «біз қолданба конфигурациясында жөндеуді өшіріп, оны қайта іске қоса аламыз».
Сұхбат алушы қарсылық білдірді: «Жоқ, біз қолданбаны қайта іске қоса алмаймыз, бізде әлі де жадта сақталған маңызды деректер бар және маңызды клиенттер қызметтің өзіне қосылған, біз оны қайта қосуға мәжбүрлей алмаймыз».

«Жарайды» дедім мен, «егер біз қолданбаны қайта іске қоса алмасақ және деректер біз үшін маңызды болмаса, ls пәрменінде көрмесек те, бұл ашық файлды файл дескрипторы арқылы жай ғана тазалай аламыз. файлдық жүйеде».

Сұхбат беруші риза болды, бірақ мен емес.

Сонда мен ойладым, неге менің білімімді сынайтын адам тереңірек зерттемейді? Бірақ егер деректер өте маңызды болса ше? Егер процесті қайта іске қоса алмасақ және процесс бос орыны жоқ бөлімде файлдық жүйеге жазылса ше? Жазылған деректерді ғана емес, сонымен бірге осы процесс жазатын немесе жазуға тырысатын деректерді де жоғалта алмасақ ше?

Тузик

Мансаптың басында мен пайдаланушы ақпаратын сақтауға қажет шағын қосымшаны жасауға тырыстым. Содан кейін мен пайдаланушыны оның деректеріне қалай сәйкестендіруге болады деп ойладым. Мысалы, менде Иванов Иван Иванович бар, оның біраз ақпараты бар, бірақ мен олармен қалай достасамын? Мен «Тузик» деген иттің дәл осы Ивандікі екенін айта аламын. Бірақ ол атын өзгертіп, Иванның орнына, мысалы, Оля болса ше? Сонда біздің Оля Ивановна Иванованың иті болмайды, ал біздің Тузик әлі жоқ Ивандікі болып шығады. Әрбір пайдаланушыға бірегей идентификатор (ID) беретін дерекқор бұл мәселені шешуге көмектесті және менің Tuzik осы идентификаторға байланысты болды, бұл шын мәнінде жай сериялық нөмір болды. Осылайша, эйс иесінің жеке куәлігі 2 болды, ал бір уақытта Иван осы жеке куәліктің астында болды, содан кейін Оля сол жеке куәлікке ие болды. Адамгершілік пен мал шаруашылығының мәселесі іс жүзінде шешілді.

Файл дескрипторы

Файлдың және осы файлмен жұмыс істейтін бағдарламаның мәселесі шамамен біздің ит пен адамның проблемасымен бірдей. Мен ivan.txt деп аталатын файлды ашып, оған tuzik сөзін жаза бастадым делік, бірақ файлдағы бірінші «t» әрпін ғана жаза алдым және бұл файлдың атын біреу өзгертті, мысалы, olya.txt деп. Бірақ файл сол күйінде қалады, мен оған әлі де өзімнің эйсімді жазғым келеді. Файл жүйелік қоңырау арқылы ашылған сайын ашық кез келген бағдарламалау тілінде мені файлға көрсететін бірегей идентификаторды аламын, бұл идентификатор файл дескрипторы болып табылады. Бұл файлды ары қарай не істейтіні және кім жасайтыны маңызды емес, оны жоюға, атын өзгертуге, иесін өзгертуге немесе оқу мен жазу құқығын алып тастауға болады, мен әлі де рұқсат аламын. оған, өйткені файлды ашу кезінде мен оны оқуға және/немесе жазуға құқығым болды және мен онымен жұмыс істей бастадым, яғни мен мұны жалғастыруым керек.

Linux жүйесінде libc кітапханасы әрбір іске қосылған қолданба (процесс) үшін 3 нөмірленген 0,1,2 дескриптор файлын ашады. Қосымша ақпаратты сілтемелерден табуға болады man stdio и man stdout

  • 0 файл дескрипторы STDIN деп аталады және қолданба енгізуімен байланысты
  • 1-файл дескрипторы STDOUT деп аталады және оны қолданбалар басып шығару пәрмендері сияқты деректерді шығару үшін пайдаланады
  • Файл дескрипторы 2 STDERR деп аталады және оны қолданбалар қате туралы хабарларды шығару үшін пайдаланады.

Егер сіздің бағдарламаңызда оқу немесе жазу үшін кез келген файлды ашсаңыз, онда сіз бірінші тегін идентификаторды аласыз және ол 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 дескрипторлық файл псевдотерминал құрылғысымен байланысты /dev/pts, бірақ біз оларды әлі де басқара аламыз, мысалы, оларды екінші консольде іске қосамыз

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

Ал бірінші консольде біз көреміз

[user@localhost ]$ hello world

Қайта бағыттау және құбыр

Сіз бұл 3 дескриптор файлын кез келген процесте, соның ішінде bash бағдарламасында, мысалы, екі процесті байланыстыратын құбыр арқылы оңай ауыстыра аласыз, қараңыз.

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

Бұл пәрменді өзіңіз орындай аласыз strace -f және ішінде не болып жатқанын қараңыз, бірақ мен сізге қысқаша айтып беремін.

PID 15771 бар ата-аналық bash процесі біздің команданы талдайды және қанша пәрменді іске қосқымыз келетінін нақты түсінеді, біздің жағдайда олардың екеуі бар: мысық және ұйқы. 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

Содан кейін жүйелік қоңырауды пайдаланыңыз Clone 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
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 бар бірінші еншілес bash процесінде жүйелік қоңырау dup2, STDOUT файл дескрипторының нөмірі 1 құбырды көрсететін файл дескрипторына өзгертеді, біздің жағдайда ол 3 саны. Осылайша, PID 9004 бар бірінші еншілес процестің STDOUT жүйесіне жазғанының бәрі автоматты түрде құбыр буферінде аяқталады.

PID 9005 бар екінші еншілес процесте bash STDIN нөмірі 2 файл дескрипторын өзгерту үшін dup0 пайдаланады. Енді PID 9005 бар екінші bash оқылатын барлық нәрсе құбырдан оқылады.

Осыдан кейін 3 және 4 нөмірлі файл дескрипторлары да еншілес процестерде жабылады, өйткені олар енді пайдаланылмайды.

Мен 255 файл дескрипторын әдейі елемеймін; ол bash арқылы ішкі мақсаттар үшін пайдаланылады және сонымен қатар еншілес процестерде жабылады.

Содан кейін, PID 9004 бар бірінші еншілес процесте bash жүйелік қоңырауды пайдалана бастайды Exec пәрмен жолында біз көрсеткен орындалатын файл, біздің жағдайда бұл /usr/bin/cat.

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

Көріп отырғаныңыздай, файл өлшемі тек ұлғаюда және біздің магистраль жұмыс істемеді. Жүйелік шақыру құжаттамасын қарастырайық ашық. Егер файлды ашу кезінде O_APPEND жалаушасын қолданатын болсақ, онда әрбір жазу кезінде операциялық жүйе файл өлшемін тексереді және деректерді файлдың ең соңына дейін жазады және мұны атомдық түрде жасайды. Бұл бірнеше ағындарға немесе процестерге бір файлға жазуға мүмкіндік береді. Бірақ біздің кодымызда бұл жалауды қолданбаймыз. Біз файлды қосымша жазу үшін ашқанда ғана lsof файл өлшемін trunk файлынан кейін көре аламыз, яғни оның орнына біздің кодта

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 биттері сандық мәнмен ауыстырылады. Мұны істеу үшін, мысалы, ядро ​​көздерін ашыңыз осында және қандай жалаулар не үшін жауапты екенін қараңыз

#ТЕК 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 

Енді құбырда аяқталатын барлық деректер автоматты түрде netcat ішіндегі stdin-ге өтеді, ол оны 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 подкасттарын тыңдаңыз.

Бәріне жақсылық.

Үй тапсырмасы ретінде, егер сіз келесі пәрменді орындасаңыз, cat және sleep процессінің дескрипторлары файлында не болатыны туралы ойлануды ұсынамын:

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

Ақпарат көзі: www.habr.com

пікір қалдыру