ذات مرة ، في مقابلة ، سئلت ماذا ستفعل إذا وجدت خدمة معطلة بسبب حقيقة أن القرص قد نفد المساحة؟
بالطبع ، أجبته أنني سأرى ما كان يفعله هذا المكان ، وإذا أمكن ، سأقوم بتنظيف المكان.
ثم سأل القائم بإجراء المقابلة ، ماذا لو لم تكن هناك مساحة خالية على القسم ، لكنك أيضًا لا ترى الملفات التي من شأنها أن تشغل كل المساحة؟
لهذا ، قلت إنه يمكنك دائمًا إلقاء نظرة على واصفات الملفات المفتوحة ، على سبيل المثال ، باستخدام الأمر lsof وفهم التطبيق الذي شغل كل المساحة المتاحة ، ومن ثم يمكنك التصرف وفقًا للظروف ، اعتمادًا على ما إذا كانت البيانات مطلوبة .
قاطعني القائم بإجراء المقابلة عند الكلمة الأخيرة ، مضيفًا إلى سؤاله: "لنفترض أننا لسنا بحاجة إلى البيانات ، إنها مجرد سجل تصحيح ، ولكن التطبيق معطل لأنه لا يمكنه كتابة التصحيح"؟
أجبته "حسنًا" ، "يمكننا إيقاف تصحيح الأخطاء في تهيئة التطبيق وإعادة تشغيله".
اعترض القائم بإجراء المقابلة: "لا ، لا يمكننا إعادة تشغيل التطبيق ، لا يزال لدينا بيانات مهمة في الذاكرة ، والعملاء المهمون متصلون بالخدمة نفسها ، ولا يمكننا فرض إعادة الاتصال".
قلت "حسنًا" ، "إذا لم نتمكن من إعادة تشغيل التطبيق ولا نهتم بالبيانات ، فيمكننا مسح هذا الملف المفتوح عبر واصف الملف ، حتى لو لم نراه في ls الأمر على نظام الملفات. "
كان القائم بإجراء المقابلة راضيًا ، لكنني لم أكن كذلك.
ثم فكرت ، لماذا لا يتعمق الشخص الذي يختبر معرفتي؟ ولكن ماذا لو كانت البيانات مهمة بعد كل شيء؟ ماذا لو لم نتمكن من إعادة العملية ، وفي نفس الوقت تكتب هذه العملية إلى نظام الملفات على قسم لا يحتوي على مساحة خالية؟ ماذا لو لم نتمكن من فقدان ليس فقط البيانات المكتوبة بالفعل ، ولكن أيضًا البيانات التي تكتبها هذه العملية أو تحاول كتابتها؟
قارب
في بداية مسيرتي المهنية ، كنت أحاول إنشاء تطبيق صغير يحتاج إلى تخزين معلومات حول المستخدمين. ثم فكرت كيف يمكنني مطابقة المستخدم مع بياناته. على سبيل المثال ، لدي إيفانوف إيفان إيفانوفيتش ، ولديه بعض البيانات ، ولكن كيف أقوم بتكوين صداقات معهم؟ أستطيع أن أشير مباشرة إلى أن الكلب المسمى "Tuzik" ينتمي إلى نفس إيفان. ولكن ماذا لو غير اسمه وأصبح على سبيل المثال أوليا بدلاً من إيفان؟ بعد ذلك سيتضح أن أوليا إيفانوفنا إيفانوفا لن يكون لديها كلب بعد الآن ، وسيظل توزيك لدينا ينتمي إلى إيفان غير الموجود. ساعدت قاعدة البيانات في حل هذه المشكلة ، والتي أعطت لكل مستخدم معرفًا فريدًا (ID) ، وتم ربط Tuzik الخاص بي بهذا المعرف ، والذي كان في الواقع مجرد رقم تسلسلي. وهكذا ، كان مالك التوزيك يحمل رقم الهوية 2 ، وفي وقت ما كان إيفان يحمل هذا الرقم التعريفي ، ثم أصبحت أوليا تحت نفس المعرف. تم حل مشكلة الجنس البشري وتربية الحيوانات عمليا.
واصف الملف
مشكلة الملف والبرنامج الذي يعمل مع هذا الملف هي تقريباً نفس مشكلة كلبنا وإنساننا. لنفترض أنني فتحت ملفًا باسم ivan.txt وبدأت في كتابة كلمة tuzik فيه ، لكنني تمكنت من كتابة الحرف الأول فقط "t" في الملف ، وتمت إعادة تسمية هذا الملف من قِبل شخص ما ، على سبيل المثال ، إلى olya.txt. لكن الملف هو نفسه وما زلت أريد أن أكتب آصلي إليه. في كل مرة تفتح فيها ملفًا باستدعاء نظام
في نظام Linux ، تفتح مكتبة libc 3 ملفات واصفات لكل تطبيق قيد التشغيل (عملية) ، بأرقام 0,1,2،XNUMX،XNUMX. يمكنك العثور على مزيد من المعلومات على الروابط
- يسمى واصف الملف 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 نفسها ، وليس بواسطة المكتبة المرتبطة.
الآن جميع ملفات الوصف الثلاثة مرتبطة بالجهاز الطرفي الزائف
[user@localhost ]$ echo "hello world" > /proc/15771/fd/0
وسنرى في وحدة التحكم الأولى
[user@localhost ]$ hello world
إعادة التوجيه والأنابيب
يمكنك بسهولة تجاوز ملفات الوصف الثلاثة هذه في أي عملية ، بما في ذلك في bash ، على سبيل المثال ، من خلال أنبوب (أنبوب) يربط بين عمليتين ، راجع
[user@localhost ]$ cat /dev/zero | sleep 10000
يمكنك تشغيل هذا الأمر بنفسك باستخدام ستراس -f وأرى ما يجري في الداخل ، لكنني سأختصر الأمر.
تقوم عملية bash الأصل الخاصة بنا مع PID 15771 بتحليل أمرنا وتفهم بالضبط عدد الأوامر التي نريد تشغيلها ، وفي حالتنا يوجد اثنان منهم: القط والنوم. يعرف Bash أنه يحتاج إلى إنشاء عمليتين فرعيتين ، ودمجهما في أنبوب واحد. في المجموع ، ستحتاج bash إلى عمليتين فرعيتين وأنبوب واحد.
قبل إنشاء عمليات فرعية ، يقوم 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.
في أول عملية bash child باستخدام PID 9004 ، استدعاء النظام
في العملية الفرعية الثانية مع PID 9005 ، bash dup2s الملف إلى رقم واصف STDIN رقم 0. الآن كل شيء ستقرأه bash الثانية مع PID 9005 سيقرأ من الأنبوب.
بعد ذلك ، يتم أيضًا إغلاق واصفات الملفات ذات الرقمين 3 و 4 في العمليات الفرعية ، نظرًا لأنها لم تعد مستخدمة.
لقد تجاهلت عمدًا واصف الملف 255 ، يتم استخدامه داخليًا بواسطة bash نفسه وسيتم إغلاقه أيضًا في العمليات الفرعية.
بعد ذلك ، في أول عملية فرعية باستخدام 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 بقيمة رقمية. للقيام بذلك ، افتح مصادر kernel ، على سبيل المثال
#تعريف 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
يتم حفظ البيانات ، تم حل المشكلة.
أغتنم هذه الفرصة لألقي التحية على زملائي من ديجيرو.
استمع إلى راديو تي بودكاست.
الامور جيدة.
كواجب منزلي ، أقترح التفكير فيما سيكون موجودًا في واصفات ملفات القط وعملية النوم إذا قمت بتشغيل الأمر التالي:
[user@localhost ~]$ cat /dev/zero 2>/dev/null| sleep 10000
المصدر: www.habr.com