Linux-da nümunələrlə fayl təsviri

Bir dəfə müsahibə zamanı məndən soruşdular ki, diskdə yer bitdiyi üçün işləməyən xidmət tapsanız nə edəcəksiniz?

Təbii ki, mən cavab verdim ki, mən bu yerin nə ilə məşğul olduğunu görəcəm, imkan olsa, yeri təmizləyəcəm.
Sonra müsahib soruşdu ki, bölmədə boş yer yoxdursa, amma siz bütün yeri tutacaq heç bir fayl görmürsünüzsə necə?

Buna dedim ki, siz həmişə açıq fayl deskriptorlarına baxa bilərsiniz, məsələn, lsof əmri ilə və hansı proqramın bütün mövcud yeri tutduğunu başa düşə bilərsiniz və sonra məlumatların lazım olub-olmamasından asılı olaraq şəraitə uyğun hərəkət edə bilərsiniz. .

Müsahibim son sözümə sözümü kəsdi və sualına əlavə etdi: "Tutaq ki, məlumatlara ehtiyacımız yoxdur, bu, sadəcə olaraq debug jurnalıdır, lakin proqram düzəliş yaza bilmədiyi üçün işləmir"?

"Yaxşı," deyə cavab verdim, "biz proqram konfiqurasiyasında debug funksiyasını söndürüb yenidən başlada bilərik."
Müsahibimiz etiraz etdi: "Xeyr, tətbiqi yenidən başlada bilmərik, yaddaşımızda hələ də vacib məlumatlarımız var və vacib müştərilər xidmətin özünə qoşulub, biz onları yenidən qoşulmağa məcbur edə bilmərik."

“Yaxşı,” dedim, “əgər tətbiqi yenidən başlada bilmiriksə və məlumat bizim üçün vacib deyilsə, ls əmrində görməsək belə, bu açıq faylı sadəcə olaraq fayl deskriptoru vasitəsilə təmizləyə bilərik. fayl sistemində."

Müsahibim razı qaldı, mən yox.

Sonra fikirləşdim ki, niyə mənim biliyimi sınayan adam daha dərinə getmir? Bəs məlumat hər şeydən sonra vacibdirsə? Əgər prosesi yenidən başlada bilməsək və proses boş yer olmayan bölmədə fayl sisteminə yazırsa nə olar? Nəinki artıq yazılmış məlumatları, həm də bu prosesin yazdığı və ya yazmağa çalışdığı məlumatları itirə bilməsək?

Tuzik

Karyeramın əvvəllərində istifadəçi məlumatlarını saxlamaq üçün lazım olan kiçik bir proqram yaratmağa çalışdım. Və sonra düşündüm ki, istifadəçini onun məlumatlarına necə uyğunlaşdıra bilərəm? Məsələn, məndə İvanov İvan İvanoviç var, onun da bəzi məlumatları var, amma mən onlarla necə dostluq edə bilərəm? Birbaşa qeyd edə bilərəm ki, “Tuzik” adlı it də məhz bu İvana məxsusdur. Bəs adını dəyişsə və İvan əvəzinə, məsələn, Olya olarsa nə olar? Onda məlum olacaq ki, bizim Olya İvanovna İvanovanın daha iti olmayacaq, Tuzikimiz isə hələ də mövcud olmayan İvana məxsus olacaq. Hər bir istifadəçiyə unikal identifikator (ID) verən verilənlər bazası bu problemi həll etməyə kömək etdi və mənim Tuzikim bu ID-yə bağlandı, bu, əslində sadəcə seriya nömrəsi idi. Beləliklə, ace sahibinin şəxsiyyət nömrəsi 2 idi və bir anda İvan bu şəxsiyyət vəsiqəsinin altında idi, sonra Olya eyni şəxsiyyət vəsiqəsinin altında oldu. İnsanlıq və heyvandarlıq problemi praktiki olaraq həll olundu.

Fayl deskriptoru

Faylın və bu faylla işləyən proqramın problemi itimiz və insanımızla təxminən eynidir. Tutaq ki, mən ivan.txt adlı faylı açıb içərisinə tuzik sözünü yazmağa başladım, ancaq faylda yalnız ilk “t” hərfini yaza bildim və bu faylın adını kimsə, məsələn olya.txt olaraq dəyişdirdi. Ancaq fayl eyni olaraq qalır və mən hələ də acemı orada qeyd etmək istəyirəm. Fayl hər dəfə sistem çağırışı ilə açıldıqda açmaq hər hansı bir proqramlaşdırma dilində məni fayla göstərən unikal ID alıram, bu ID fayl deskriptorudur. Və bundan sonra bu faylla nə və kimin məşğul olmasının heç bir əhəmiyyəti yoxdur, o silinə bilər, adı dəyişdirilə bilər, sahibi dəyişdirilə bilər və ya oxumaq və yazmaq hüququ əlindən alına bilər, hələ də girişim olacaq ona görə ki, faylı açarkən onu oxumaq və/yaxud yazmaq hüququm var idi və onunla işləməyə başladım, yəni bunu davam etdirməliyəm.

Linux-da libc kitabxanası hər bir işləyən proqram (proses) üçün 3 nömrəli 0,1,2 deskriptor faylı açır. Ətraflı məlumatı linklərdən əldə etmək olar man stdio и adam stdout

  • Fayl deskriptoru 0 STDIN adlanır və proqram daxiletməsi ilə əlaqələndirilir
  • Fayl deskriptoru 1 STDOUT adlanır və proqramlar tərəfindən çap əmrləri kimi məlumatları çıxarmaq üçün istifadə olunur
  • Fayl deskriptoru 2 STDERR adlanır və proqramlar tərəfindən səhv mesajlarını çıxarmaq üçün istifadə olunur.

Proqramınızda oxumaq və ya yazmaq üçün hər hansı bir faylı açsanız, çox güman ki, ilk pulsuz identifikatoru alacaqsınız və bu, 3 nömrə olacaq.

Əgər onun PID-ni bilirsinizsə, fayl deskriptorlarının siyahısı istənilən proses üçün baxıla bilər.

Məsələn, bash konsolunu açaq və prosesimizin PID-inə baxaq

[user@localhost ]$ echo $$
15771

İkinci konsolda qaçaq

[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

Bu məqalənin məqsədləri üçün 255 nömrəli fayl deskriptorunu etibarlı şəkildə rədd edə bilərsiniz; o, ehtiyacları üçün əlaqəli kitabxana tərəfindən deyil, bash tərəfindən açılmışdır.

İndi bütün 3 deskriptor faylı psevdo terminal cihazı ilə əlaqələndirilir /dev/pts, lakin biz hələ də onları manipulyasiya edə bilərik, məsələn, onları ikinci konsolda işlədə bilərik

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

Və ilk konsolda biz görəcəyik

[user@localhost ]$ hello world

Yönləndirmə və Boru

Siz bu 3 deskriptor faylını istənilən prosesdə, o cümlədən bash-da, məsələn, iki prosesi birləşdirən boru vasitəsilə asanlıqla ləğv edə bilərsiniz.

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

Bu əmri özünüz işlədə bilərsiniz strace -f və içəridə nə baş verdiyini görün, amma sizə qısaca danışacağam.

PID 15771 ilə ana bash prosesimiz əmrimizi təhlil edir və nə qədər əmr yerinə yetirmək istədiyimizi dəqiq başa düşür, bizim vəziyyətimizdə onlardan ikisi var: pişik və yuxu. Bash bilir ki, o, iki uşaq prosesi yaratmalı və onları bir boruda birləşdirməlidir. Ümumilikdə, bash-a 2 uşaq prosesi və bir boru lazımdır.

Bash uşaq prosesləri yaratmazdan əvvəl sistem çağırışını həyata keçirir boru və müvəqqəti boru buferində yeni fayl deskriptorlarını qəbul edir, lakin bu bufer hələ iki uşaq prosesimizi birləşdirmir.

Ana proses üçün artıq boru var kimi görünür, lakin hələ uşaq prosesləri yoxdur:

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

Sonra sistem zəngindən istifadə edin Clone bash iki uşaq prosesi yaradır və üç prosesimiz belə görünəcək:

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

Unutmayın ki, klon bütün fayl deskriptorları ilə birlikdə prosesi klonlayır, ona görə də onlar həm ana prosesdə, həm də uşaq prosesində eyni olacaq. PID 15771 ilə ana prosesin işi uşaq proseslərini izləməkdir, ona görə də o, sadəcə olaraq uşaqlardan cavab gözləyir.

Buna görə də boruya ehtiyac yoxdur və 3 və 4 nömrəli fayl deskriptorlarını bağlayır.

PID 9004 ilə ilk uşaq bash prosesində sistem çağırışı dup2, STDOUT fayl deskriptorumuzun 1 nömrəsini boruya işarə edən fayl deskriptoruna dəyişir, bizim vəziyyətimizdə bu, 3 nömrədir. Beləliklə, PID 9004 ilə ilk uşaq prosesinin STDOUT-a yazdığı hər şey avtomatik olaraq boru buferində bitəcək.

PID 9005 ilə ikinci uşaq prosesində bash fayl deskriptoru STDIN nömrəsini 2 dəyişmək üçün dup0 istifadə edir. İndi PID 9005 ilə ikinci bash-in oxuyacağı hər şey borudan oxunacaq.

Bundan sonra 3 və 4 nömrəli fayl deskriptorları da uşaq proseslərdə bağlanır, çünki onlar artıq istifadə olunmur.

Mən 255-ci fayl deskriptorunu bilərəkdən rədd edirəm; o, bash tərəfindən daxili məqsədlər üçün istifadə olunur və uşaq proseslərdə də bağlanacaq.

Sonra, PID 9004 ilə ilk uşaq prosesində bash sistem çağırışından istifadə etməyə başlayır exec əmr satırında göstərdiyimiz icra olunan fayl, bizim vəziyyətimizdə bu /usr/bin/cat.

PID 9005 ilə ikinci uşaq prosesində bash, bizim vəziyyətimizdə /usr/bin/sleep qeyd etdiyimiz ikinci icraedicini işə salır.

Açıq zəng edilən zaman O_CLOEXEC bayrağı ilə açılmamış halda, exec sistem çağırışı fayl tutacaqlarını bağlamır. Bizim vəziyyətimizdə, icra olunan faylları işə saldıqdan sonra bütün cari fayl deskriptorları saxlanacaq.

Konsolda yoxlayın:

[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

Gördüyünüz kimi, borularımızın unikal sayı hər iki prosesdə eynidir. Beləliklə, eyni valideynlə iki fərqli proses arasında əlaqəmiz var.

Bash-ın istifadə etdiyi sistem zəngləri ilə tanış olmayanlar üçün əmrləri strace vasitəsilə icra etməyi və daxildə nə baş verdiyini görməyi tövsiyə edirəm, məsələn:

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

Disk sahəsinin tükənməsi və prosesi yenidən başlatmadan məlumatları saxlamağa çalışmaq problemimizə qayıdaq. Diske saniyədə təxminən 1 meqabayt yazacaq kiçik bir proqram yazaq. Üstəlik, nədənsə diskə məlumat yaza bilmədiksə, sadəcə olaraq buna məhəl qoymayacağıq və bir saniyə ərzində məlumatları yenidən yazmağa çalışacağıq. Python istifadə etdiyim nümunədə siz hər hansı digər proqramlaşdırma dilindən istifadə edə bilərsiniz.

[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

Proqramı işə salaq və fayl deskriptorlarına baxaq

[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

Gördüyünüz kimi, bizim 3 standart fayl deskriptorumuz və açdığımız daha biri var. Faylın ölçüsünü yoxlayaq:

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

Məlumatlar yazılır, biz fayldakı icazələri dəyişdirməyə çalışırıq:

[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

İstifadəçimizin fayla yazmaq icazəsi olmasa da, verilənlərin hələ də yazıldığını görürük. Onu aradan qaldırmağa çalışaq:

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

Məlumatlar harada yazılır? Və ümumiyyətlə yazılıblar? Yoxlayırıq:

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

Bəli, fayl deskriptorumuz hələ də mövcuddur və biz bu fayl deskriptorunu köhnə faylımız kimi qəbul edə bilərik, onu oxuya, təmizləyə və kopyalaya bilərik.

Faylın ölçüsünə baxaq:

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

Faylın ölçüsü 19923457-dir. Gəlin faylı təmizləməyə çalışaq:

[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

Gördüyünüz kimi, fayl ölçüsü yalnız artır və magistralımız işləmədi. Sistem çağırışı sənədlərinə baxaq açmaq. Əgər faylı açarkən O_APPEND bayrağından istifadə etsək, onda hər yazanda əməliyyat sistemi faylın ölçüsünü yoxlayır və məlumatları faylın ən sonuna qədər yazır və bunu atomik şəkildə edir. Bu, eyni fayla çoxlu mövzu və ya proseslərin yazılmasına imkan verir. Amma kodumuzda bu bayraqdan istifadə etmirik. Biz faylı əlavə yazmaq üçün açsaq, lsof-da trunk-dan sonra fərqli bir fayl ölçüsünü görə bilərik, yəni kodumuzda

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

qoymalıyıq

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

“w” bayrağı ilə yoxlanılır

[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

və "a" bayrağı ilə

[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

Artıq işləyən prosesin proqramlaşdırılması

Tez-tez proqramçılar, proqramlar yaradarkən və sınaqdan keçirərkən, proqramda sazlayıcılardan (məsələn, GDB) və ya müxtəlif səviyyələrdə daxil olmaqdan istifadə edirlər. Linux, artıq işləyən proqramı həqiqətən yazmaq və dəyişdirmək imkanı verir, məsələn, dəyişənlərin dəyərlərini dəyişdirmək, kəsmə nöqtəsi təyin etmək və s. və s.

Fayl yazmaq üçün kifayət qədər disk sahəsinin olmaması ilə bağlı orijinal suala qayıdaraq, problemi simulyasiya etməyə çalışaq.

Gəlin bölməmiz üçün ayrıca disk kimi quraşdıracağımız bir fayl yaradaq:

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

Bir fayl sistemi yaradaq:

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

Fayl sistemini quraşdırın:

[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

Sahibimizlə bir kataloq yaradırıq:

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

Faylı yalnız proqramımızda yazmaq üçün açaq:

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

Başlat

[user@localhost ]$ python openforwrite.py 

Bir neçə saniyə gözləyirik

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

Beləliklə, bu məqalənin əvvəlində təsvir olunan problemimiz var. Boş yer 0, 100% doludur.

Xatırlayırıq ki, tapşırığın şərtlərinə uyğun olaraq, itirilməsi mümkün olmayan çox vacib məlumatları qeyd etməyə çalışırıq. Və eyni zamanda, prosesi yenidən başlatmadan xidməti düzəltmək lazımdır.

Tutaq ki, bizdə hələ də disk sahəsi var, lakin fərqli bölmədə, məsələn /home-də.

Gəlin kodumuzu “yenidən proqramlaşdırmağa” çalışaq.

Bütün disk yerini yeyən prosesimizin PID-inə baxaq:

[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 vasitəsilə prosesə qoşulun

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

Açıq fayl deskriptorlarına baxaq:

(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

Bizi maraqlandıran 3 nömrəli fayl deskriptoru haqqında məlumatlara baxırıq

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

Python-un hansı sistem çağırışını etdiyini nəzərə alaraq (yuxarıda strace işlədiyimiz və açıq çağırışı tapdığımız yerə baxın), faylı açmaq üçün kodumuzu emal edərkən, prosesimiz adından özümüz də eyni şeyi edirik, lakin bizə O_WRONLY|O_CREAT| lazımdır. O_TRUNC bitləri rəqəmli qiymətlə əvəz olunur. Bunu etmək üçün, məsələn, nüvə mənbələrini açın burada və görün hansı bayraqlar nəyə görə məsuliyyət daşıyır

#YALNIZCA O_YANLIŞ 00000001 təyin edin
#O_CREAT 00000100 təyin edin
#O_TRUNC 00001000 təyin edin

Bütün dəyərləri birləşdiririk, 00001101 alırıq

Zəngimizi gdb-dən edirik

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

Beləliklə, 4 nömrəli yeni bir fayl deskriptoru və başqa bir bölmədə yeni açıq fayl əldə etdik, yoxlayırıq:

(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

Biz boru ilə nümunəni xatırlayırıq - bash fayl deskriptorlarını necə dəyişdirir və biz artıq dup2 sistem çağırışını öyrənmişik.

Bir fayl deskriptorunu digəri ilə əvəz etməyə çalışırıq

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

Yoxlayırıq:

(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-cü fayl deskriptorunu bağlayırıq, çünki ona ehtiyacımız yoxdur:

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

Və gdb-dən çıxın

(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

Yeni fayl yoxlanılır:

[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

Gördüyünüz kimi, məlumatlar yeni bir fayla yazılır, köhnəni yoxlayaq:

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

Heç bir məlumat itirilmir, proqram işləyir, qeydlər yeni yerə yazılır.

Tapşırığı bir az çətinləşdirək

Təsəvvür edək ki, məlumat bizim üçün vacibdir, lakin heç bir bölmədə disk sahəsi yoxdur və diski birləşdirə bilmirik.

Edə biləcəyimiz şey, məlumatlarımızı harasa, məsələn, boruya yönləndirmək və öz növbəsində məlumatları bəzi proqramlar vasitəsilə, məsələn, netcat vasitəsilə borudan şəbəkəyə yönləndirməkdir.
Mkfifo əmri ilə adlandırılmış boru yarada bilərik. Boş yer olmasa belə, o, fayl sistemində psevdo fayl yaradacaq.

Tətbiqi yenidən başladın və yoxlayın:

[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

Disk sahəsi yoxdur, lakin biz orada uğurla adlandırılmış boru yaradırıq:

[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

İndi bu boruya daxil olan bütün məlumatları bir şəkildə şəbəkə vasitəsilə başqa bir serverə bağlamaq lazımdır; eyni netcat bunun üçün uyğundur.

Remote-server.example.com serverində işlədirik

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

Problemli serverimizdə ayrı bir terminalda işə salırıq

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

İndi boruda bitən bütün məlumatlar avtomatik olaraq netcat-da stdin-ə gedəcək, bu da onu 7777 portunda şəbəkəyə göndərəcək.

Etməli olduğumuz yeganə şey məlumatlarımızı bu adlandırılmış boruya yazmağa başlamaqdır.

Artıq tətbiqimiz işləyir:

[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

Bütün bayraqlardan bizə yalnız O_WRONLY lazımdır, çünki fayl artıq mövcuddur və onu təmizləməyə ehtiyac yoxdur

[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

Uzaq server remote-server.example.com yoxlanılır

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

Məlumatlar gəlir, problem serverini yoxlayırıq

[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

Məlumat saxlanılır, problem həll olunur.

Fürsətdən istifadə edərək Degirodan olan həmkarlarıma salam deyirəm.
Radio-T podkastlarına qulaq asın.

Hamıya yaxşıdır.

Ev tapşırığı olaraq, aşağıdakı əmri yerinə yetirsəniz, proses faylı deskriptorlarının pişik və yuxuda nə olacağını düşünməyi təklif edirəm:

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

Mənbə: www.habr.com

Добавить комментарий