ملف واصف Linux مع أمثلة

مرة واحدة ، في مقابلة ، سألوني ماذا ستفعل إذا وجدت خدمة لا تعمل لأن القرص نفدت مساحته؟

بالطبع ، أجبت أنني سأرى ما الذي كان يشغله هذا المكان ، وإذا أمكن ، فسوف أقوم بتنظيف المكان.
ثم سأل القائم بإجراء المقابلة ، ماذا إذا لم تكن هناك مساحة خالية في القسم ، لكنك أيضًا لا ترى الملفات التي ستشغل المكان كله؟

قلت لذلك ، يمكنك دائمًا النظر إلى واصفات الملفات المفتوحة ، على سبيل المثال ، باستخدام الأمر lsof وفهم التطبيق الذي شغل كل المساحة المتاحة ، ثم يمكنك المتابعة وفقًا للظروف ، اعتمادًا على ما إذا كانت البيانات مطلوبة.

قاطعني القائم بإجراء المقابلة في آخر كلمة ، مضيفًا سؤالي: "لنفترض أننا لسنا بحاجة إلى البيانات ، إنه مجرد سجل تصحيح ، لكن التطبيق لا يعمل لأنه لا يمكنه تسجيل تصحيح الأخطاء"؟

أجبت "حسنًا ،" يمكننا إيقاف تشغيل تصحيح الأخطاء في تهيئة التطبيق وإعادة تشغيله. "
اعترض القائم بإجراء المقابلة: "لا ، لا يمكننا إعادة تشغيل التطبيق ، ولا تزال البيانات الهامة مخزنة في ذاكرتنا ، ويتم توصيل عملاء مهمين بالخدمة نفسها ، والتي لا يمكننا فرض إعادة الاتصال بها".

قلت: "حسنًا ، إذا لم نتمكن من إعادة تشغيل التطبيق والبيانات ليست مهمة بالنسبة لنا ، فيمكننا ببساطة مسح هذا الملف المفتوح من خلال ملف واصف ، حتى لو لم نراه في الأمر ls في نظام الملفات."

كان القائم بإجراء المقابلة مسروراً ، لكنني لم أكن كذلك.

ثم فكرت ، لماذا لا يقوم الشخص الذي يختبر معرفتي بحفر أعمق؟ ولكن ماذا لو كانت البيانات لا تزال مهمة؟ ماذا لو لم نتمكن من إعادة تشغيل العملية ، وفي نفس الوقت تكتب هذه العملية إلى نظام الملفات في قسم لا توجد فيه مساحة خالية؟ ماذا لو لم نتمكن من فقد البيانات المسجلة بالفعل ، ولكن أيضًا البيانات التي تكتبها أو تحاول تسجيلها؟

قارب


في بداية حياتي المهنية ، حاولت إنشاء تطبيق صغير كان من الضروري فيه تخزين المعلومات عن المستخدمين. ثم فكرت ، كيف يمكنني تعيين المستخدم لبياناته. على سبيل المثال ، لدي إيفان إيفانوف إيفانيتش ، ولديه بعض البيانات ، ولكن كيف يمكن تكوين صداقات معهم؟ يمكنني أن أشير بشكل مباشر إلى أن الكلب المسمى "Tuzik" ينتمي إلى هذا إيفان. لكن ماذا لو غيّر اسمه وبدلاً من أن يصبح إيفان ، على سبيل المثال ، عليا؟ ثم اتضح أن لدينا أوليا إيفانوفنا إيفانوفا لم يعد لديها كلب ، وسوف تظل لدينا Tuzik إلى إيفان غير موجود. ساعدت قاعدة البيانات هذه قاعدة بيانات أعطت كل مستخدم معرفًا فريدًا (ID) ، وتم إرفاق Tuzik الخاص بي بهذا المعرف ، والذي كان في الحقيقة مجرد رقم ترتيبي. وهكذا ، كان مالك tuzik يحمل رقم الهوية 2 ، وفي وقت ما كان إيفان تحت هذا المعرف ، ثم أصبح أوليا هو نفسه المعرف. تم حل مشكلة الإنسانية وتربية الحيوانات من الناحية العملية.

واصف الملف


مشكلة الملف والبرنامج الذي يعمل مع هذا الملف هو نفس مشكلة كلبنا وشخصنا. افترض أنني فتحت ملفًا باسم ivan.txt وبدأت في كتابة الكلمة tuzik فيه ، لكنني تمكنت من كتابة الحرف الأول "t" فقط في الملف ، وتمت إعادة تسمية هذا الملف بواسطة شخص ما ، على سبيل المثال ، olya.txt. لكن الملف ظل كما هو ، وما زلت أريد أن أكتب الآس فيه. في كل مرة أقوم بفتح ملف باستخدام اتصال النظام المفتوح بأي لغة برمجة ، أحصل على معرف فريد يشير لي إلى ملف ، وهذا المعرّف هو واصف للملف. ولا يهم ما يفعله أي شخص بعد ذلك بهذا الملف ، يمكنه حذفه أو يمكنه إعادة تسميته أو يمكنه تغيير المالك أو سحب أذونات القراءة والكتابة ، ما زلت سأتمكن من الوصول إليه ، لأنه في وقت فتح الملف كان لي الحق في القراءة و / أو كتابتها ، وتمكنت من البدء في العمل معه ، مما يعني أنه يجب علي الاستمرار في القيام بذلك.

على نظام Linux ، تفتح مكتبة libc لكل ملف واصف للتطبيق قيد التشغيل (العملية) ، بأرقام 0،1،2. يمكنك العثور على مزيد من المعلومات على الروابط man stdio و man stdout

  • يسمى واصف الملف 0 STDIN ويرتبط بإدخال البيانات من التطبيق
  • يسمى واصف الملف 1 STDOUT ويتم استخدامه بواسطة التطبيقات لإخراج البيانات ، على سبيل المثال ، أوامر الطباعة
  • يسمى واصف الملف 2 STDERR ويتم استخدامه بواسطة التطبيقات لإخراج بيانات الإبلاغ عن الأخطاء.

إذا قمت في ملفك بفتح ملف للقراءة أو الكتابة ، فستحصل على الأرجح على أول معرّف مجاني وسيكون الرقم 3.

يمكن عرض قائمة بملفات الواصف من أي عملية إذا كنت تعرف معرف المنتج الخاص بها.

على سبيل المثال ، افتح وحدة تحكم مع bash وشاهد معرّف العملية الخاص بنا

[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 نفسه ، وليس عن طريق مكتبة مرتبطة.

الآن يتم ربط جميع الملفات الوصفية الثلاثة بجهاز / dev / pseudo-terminal ، لكن لا يزال بإمكاننا معالجتها ، على سبيل المثال ، التشغيل في وحدة التحكم الثانية

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

وفي وحدة التحكم الأولى سوف نرى

 [user@localhost ]$ hello world 

إعادة توجيه والأنابيب


يمكنك بسهولة تجاوز هذه الملفات الوصفية الثلاثة في أي عملية ، بما في ذلك bash ، على سبيل المثال ، من خلال توجيه يربط بين عمليتين ، راجع

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

يمكنك تشغيل هذا الأمر بنفسك باستخدام شرائط -f ومعرفة ما يحدث في الداخل ، لكنني سأخبرك بإيجاز.

يقوم تحليل العملية الأصل الخاص بنا باستخدام PID 15771 بتوزيع أمرنا ويفهم بالضبط عدد الأوامر التي نريد تشغيلها ، وفي حالتنا هناك اثنان منهم: القط والنوم. يعرف 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 

ثم ، باستخدام مكالمة النظام ، يُنشئ 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.

في أول عملية تابعة لـ bash مع PID 9004 ، يغيّر استدعاء نظام dup2 لدينا واصف ملف STDOUT رقم 1 إلى واصف الملف الذي يشير إلى توجيه الإخراج ، وفي حالتنا يكون الرقم 3. وبالتالي ، كل شيء ستكتبه العملية الفرعية الأولى مع PID 9004 إلى STDOUT سوف تسقط تلقائيا في الأنابيب العازلة.

في العملية الفرعية الثانية مع PID 9005 ، يغير bash واصف الملف STDIN برقم 0 باستخدام dup2 ، والآن كل ما سوف تقرأه باش الثانية مع PID 9005 ستقرأ من توجيه الإخراج.

بعد ذلك ، يتم غلق واصفات الأرقام 3 و 4 أيضًا في العمليات الفرعية ، حيث لم تعد مستخدمة.

أتجاهل عمدا واصف الملف 255 ، ويستخدم bash للاحتياجات الداخلية وسيتم إغلاقه أيضًا في العمليات الفرعية.

علاوة على ذلك ، في العملية الفرعية الأولى مع PID 9004 ، يبدأ bash الملف القابل للتنفيذ الذي حددناه في سطر الأوامر مع exec call system ، في حالتنا هو / 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 -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 بعد trankate فقط إذا فتحنا الملف للتسجيل الإضافي ، مما يعني أنه بدلاً من رمزنا

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

علينا أن نضع

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

التحقق مع العلم "ث"

 [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 ٪.

نتذكر أنه وفقًا لشروط المهمة ، نحاول تسجيل بيانات مهمة للغاية لا يمكن فقدانها. وفي الوقت نفسه ، نحتاج إلى إصلاح الخدمة دون إعادة تشغيل العملية.

افترض أنه لا يزال لدينا مساحة على القرص ، ولكن في قسم مختلف ، على سبيل المثال في / المنزل.

دعنا نحاول "إعادة برمجة على الطاير" رمزنا.

ننظر إلى معرّف العملية الخاص بعملية لدينا ، والتي أكلت كل مساحة القرص:

 [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 (انظر أعلاه ، حيث ركضنا شدًا ووجدنا المكالمة المفتوحة) ، ونقوم بمعالجة الكود الخاص بنا لفتح الملف ، ونفعل نفس الشيء نيابة عنا ، لكننا نحتاج إلى O_WRONLY | O_CREAT | O_TRUNC استبدال بقيمة رقمية. للقيام بذلك ، افتح مصادر kernel ، على سبيل المثال ، هنا وانظر ما هي الأعلام المسؤولة عن ماذا

#define O_WRONLY 00000001
#define O_CREAT 00000100
#define 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 

الآن ستنتقل جميع البيانات التي تدخل الأنبوب تلقائيًا إلى شبكة الإنترنت ، مما سيرسلها إلى الشبكة على المنفذ 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 

تم حفظ البيانات ، حل المشكلة.

أغتنم هذه الفرصة لأعرب عن تحياتي لزملائي في ديجيرو.
الاستماع إلى راديو T دبليو.

جيد للجميع.

كواجب منزلي ، أقترح عليك التفكير فيما سيكون في ملف واصفات عملية القط والنوم إذا قمت بتشغيل هذا الأمر:

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

Source: https://habr.com/ru/post/ar471038/


All Articles