Линукс дээрх жишээ бүхий файлын тодорхойлогч

Нэг удаа ярилцлага өгөхдөө дискний зай дууссанаас болж үйлчилгээ ажиллахгүй байвал яах вэ гэж асуусан.

Мэдээж би энэ газар юу байгааг харна, болж өгвөл тэр газрыг цэвэрлэнэ гэж хариулсан.
Дараа нь ярилцлага авагч асуув: Хэрэв хуваалт дээр сул зай байхгүй, гэхдээ та бүх зайг эзлэх ямар ч файл харагдахгүй байвал яах вэ?

Үүний тулд би та үргэлж нээлттэй файлын тодорхойлогчдыг, жишээ нь lsof командын тусламжтайгаар харж, аль програм нь боломжтой бүх зайг эзэлснийг ойлгох боломжтой бөгөөд дараа нь өгөгдөл шаардлагатай эсэхээс хамааран нөхцөл байдлын дагуу ажиллах боломжтой гэж би хэлсэн. .

Ярилцлага авагч миний яриаг сүүлчийн үгээр таслан, "Бидэнд өгөгдөл хэрэггүй, энэ нь дибаг хийх бүртгэл байна гэж бодъё, гэхдээ програм дибаг бичих боломжгүй учраас ажиллахгүй байна" гэж нэмж хэлэв?

"За" гэж би хариулсан, "бид програмын тохиргооноос дибаг хийхийг унтрааж, дахин эхлүүлж болно."
Ярилцлага авагч: "Үгүй ээ, бид програмыг дахин эхлүүлэх боломжгүй, санах ойд чухал өгөгдөл хадгалагдсан хэвээр байгаа бөгөөд чухал үйлчлүүлэгчид үйлчилгээтэй холбогдсон тул бид дахин холбох боломжгүй" гэж эсэргүүцэв.

"За," гэж би хэлэв, "хэрэв бид програмыг дахин эхлүүлэх боломжгүй бөгөөд өгөгдөл нь бидэнд чухал биш бол бид ls команд дээр харагдахгүй байсан ч энэ нээлттэй файлыг файлын тодорхойлогчоор дамжуулан цэвэрлэж болно. файлын систем дээр."

Ярилцагч сэтгэл хангалуун байсан ч би тийм биш.

Тэгээд миний мэдлэгийг сорьж байгаа хүн яагаад илүү гүнзгийрдэггүй юм бол гэж бодсон. Гэхдээ өгөгдөл чухал бол яах вэ? Хэрэв бид процессыг дахин эхлүүлж чадахгүй ба процесс нь хоосон зайгүй хуваалт дээр файлын систем рүү бичдэг бол яах вэ? Хэрэв бид зөвхөн аль хэдийн бичигдсэн өгөгдөл төдийгүй энэ процессын бичсэн эсвэл бичихийг оролдсон өгөгдлийг алдаж чадахгүй бол яах вэ?

Тузик

Ажил мэргэжлийнхээ эхэн үед би хэрэглэгчийн мэдээллийг хадгалахад шаардлагатай жижиг програм бүтээх гэж оролдсон. Тэгээд би хэрэглэгчийг түүний өгөгдөлтэй хэрхэн тааруулах вэ гэж бодсон. Жишээлбэл, би Иванов Иван Ивановичтэй, түүнд зарим мэдээлэл байгаа, гэхдээ би тэдэнтэй яаж нөхөрлөх вэ? "Тузик" нэртэй нохой нь яг энэ Иванынх гэдгийг би шууд хэлж чадна. Гэхдээ тэр нэрээ сольж, Иванын оронд жишээлбэл Оля болвол яах вэ? Дараа нь манай Оля Ивановна Иванова нохойгүй болж, манай Тузик байхгүй Иванынх хэвээр байх болно. Хэрэглэгч бүрт өвөрмөц танигч (ID) өгсөн мэдээллийн сан нь энэ асуудлыг шийдвэрлэхэд тусалсан бөгөөд миний Тузик энэ ID-тай холбоотой байсан бөгөөд энэ нь үнэндээ серийн дугаар байсан юм. Ийнхүү хөзрийн эзэн 2 дугаартай байсан бөгөөд хэсэг хугацаанд Иван энэ үнэмлэхний дор байсан бөгөөд дараа нь Оля ижил үнэмлэхтэй болжээ. Хүн төрөлхтөн, мал аж ахуйн асуудал бодитоор шийдэгдсэн.

Файлын тодорхойлогч

Файлын асуудал болон энэ файлтай ажилладаг программ нь манай нохой, хүнийхтэй ойролцоогоор ижил байна. Би ivan.txt нэртэй файл нээгээд түүнд tuzik гэдэг үгийг бичиж эхэлсэн ч файлын эхний “t” үсгийг л бичиж чадсан ба энэ файлын нэрийг хэн нэгэн, жишээ нь olya.txt болгон өөрчилсөн гэж бодъё. Гэхдээ энэ файл хэвээрээ байгаа бөгөөд би хөзрийн хөзрийг түүн дотор бичихийг хүсч байна. Системийн дуудлагаар файл нээгдэх болгонд нээлттэй Ямар ч програмчлалын хэл дээр би намайг файл руу заадаг өвөрмөц ID хүлээн авдаг, энэ ID нь файлын тодорхойлогч юм. Тэгээд энэ файлыг дараа нь юу, хэн хийх нь огт хамаагүй, устгаж болно, нэрийг нь өөрчилж болно, эзэмшигчийг нь сольж болно, эсвэл унших бичих эрхийг хасаж болно, надад хандах боломжтой хэвээр байх болно. Учир нь файлыг нээх үед би үүнийг унших, бичих эрхтэй байсан бөгөөд би түүнтэй ажиллаж эхэлсэн тул үүнийг үргэлжлүүлэн хийх ёстой гэсэн үг юм.

Линукс дээр libc номын сан нь ажиллаж байгаа програм (процесс) бүрийн хувьд 3 дугаартай 0,1,2 тодорхойлогч файлыг нээдэг. Дэлгэрэнгүй мэдээллийг линкүүдээс авах боломжтой man stdio и хүн stdout

  • Файлын тодорхойлогч 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 тодорхойлогч файлууд псевдо терминал төхөөрөмжтэй холбоотой байна /dev/pts, гэхдээ бид тэдгээрийг удирдаж болно, жишээлбэл, хоёр дахь консол дээр ажиллуулж болно

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

Эхний консол дээр бид харах болно

[user@localhost ]$ hello world

Redirect болон Pipe

Та эдгээр 3 тодорхойлогч файлыг ямар ч процесст, тухайлбал bash-д, жишээ нь хоёр процессыг холбосон хоолойгоор дамжуулан хялбархан дарж болно.

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

Та энэ тушаалыг өөрөө ажиллуулж болно strace -f мөн дотор нь юу болж байгааг хараарай, гэхдээ би танд товчхон хэлье.

PID 15771-тэй манай эцэг эхийн 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

Дараа нь системийн дуудлагыг ашиглана уу хуулбар 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 нь dup2-г ашиглан STDIN дугаар 0 файлын тодорхойлогчийг өөрчилдөг. Одоо PID 9005-тай хоёр дахь bash-ын унших бүх зүйлийг хоолой дээрээс унших болно.

Үүний дараа 3 ба 4-р дугаартай файлын тодорхойлогчдыг ашиглахаа больсон тул хүүхдийн процессуудад бас хаагдана.

Би 255 файлын тодорхойлогчийг санаатайгаар үл тоомсорлодог; үүнийг bash өөрөө дотоод хэрэгцээнд ашигладаг бөгөөд мөн хүүхэд процессуудад хаагдах болно.

Дараа нь PID 9004-тэй эхний хүүхэд процесст bash системийн дуудлагыг ашиглаж эхэлнэ ажиллуулах Бидний командын мөрөнд заасан гүйцэтгэх файл бол манай тохиолдолд /usr/bin/cat.

PID 9005-тай хоёрдахь хүүхэд процесст bash нь бидний заасан хоёр дахь гүйцэтгэгдэх файлыг ажиллуулдаг бөгөөд бидний тохиолдолд /usr/bin/sleep.

Нээлттэй дуудлага хийх үед O_CLOEXEC тугаар нээгээгүй л бол exec системийн дуудлага нь файлын бариулыг хаадаггүй. Манай тохиолдолд гүйцэтгэгдэх файлуудыг ажиллуулсны дараа одоогийн бүх файлын тодорхойлогч хадгалагдах болно.

Консол дээр шалгана уу:

[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) эсвэл програмын янз бүрийн түвшний бүртгэлийг ашигладаг. Линукс нь аль хэдийн ажиллаж байгаа програмыг бичих, өөрчлөх боломжийг олгодог, жишээлбэл, хувьсагчийн утгыг өөрчлөх, таслах цэгийг тохируулах гэх мэт.

Файл бичихэд хангалттай дискний зай байхгүй гэсэн анхны асуулт руу буцаж очоод асуудлыг дуурайж үзье.

Бид тусдаа диск болгон холбох хуваалтдаа файл үүсгэцгээе.

[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_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 and sleep процессын файлын тодорхойлогчдод юу байх талаар бодохыг зөвлөж байна.

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

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх