ব্যাশে প্রক্রিয়া প্রতিস্থাপন কীভাবে প্রয়োগ করা হয়?


12

আমি অন্য প্রশ্নটি নিয়ে গবেষণা করছিলাম , যখন আমি বুঝতে পারি আমি বুঝতে পারছি না যে হুডের নীচে কী ঘটছে, সেই /dev/fd/*ফাইলগুলি কী এবং কীভাবে শিশু প্রক্রিয়াগুলি সেগুলি খুলতে পারে।


যে প্রশ্নের উত্তর দেওয়া হয় না?
পিএইচকে

উত্তর:


21

ঠিক আছে, এটির অনেক দিক রয়েছে।

ফাইল বর্ণনাকারী

প্রতিটি প্রক্রিয়াটির জন্য, কার্নেল খোলা ফাইলগুলির একটি টেবিল বজায় রাখে (ভাল, এটি অন্যভাবে প্রয়োগ করা যেতে পারে, তবে যেহেতু আপনি এটি যে কোনওভাবে দেখতে সক্ষম নন, সুতরাং আপনি এটি কেবল একটি সাধারণ টেবিল ধরে নিতে পারেন)। সেই সারণীতে এটি কোন ফাইলটি / কোথায় তা পাওয়া যাবে, কোন মোডে আপনি এটি খোলেন, আপনি বর্তমানে কোন অবস্থানে পড়ছেন / লিখছেন এবং সেই ফাইলটিতে আই / ও ক্রিয়াকলাপ সম্পাদন করার জন্য অন্য যে কোনও জিনিস প্রয়োজন তা সম্পর্কিত তথ্য রয়েছে। এখন প্রক্রিয়াটি কখনই সেই টেবিলটি পড়তে (বা এমনকি লিখতেও) পায় না। প্রক্রিয়াটি কোনও ফাইল খুললে, এটি তথাকথিত ফাইল বিবরণকারীকে ফিরে পায়। যা কেবল টেবিলের মধ্যে একটি সূচক।

ডিরেক্টরি /dev/fdএবং এর সামগ্রী

লিনাক্সে dev/fdআসলে এর প্রতীকী লিঙ্ক /proc/self/fd/procএকটি সিউডো ফাইল সিস্টেম যাতে কার্নেলটি ফাইল এপিআই দিয়ে অ্যাক্সেস করার জন্য কয়েকটি অভ্যন্তরীণ ডেটা স্ট্রাকচারের মানচিত্র তৈরি করে (যাতে তারা প্রোগ্রামগুলিতে নিয়মিত ফাইল / ডিরেক্টরি / সিমলিংকের মতো দেখায়)। বিশেষত সমস্ত প্রক্রিয়া সম্পর্কে তথ্য রয়েছে (যা এটি নাম দিয়েছিল)। প্রতীকী লিঙ্কটি /proc/selfসর্বদা চলমান প্রক্রিয়া সম্পর্কিত ডিরেক্টরিকে বোঝায় (এটি প্রক্রিয়াটির জন্য অনুরোধ করছে; বিভিন্ন প্রক্রিয়া অতএব বিভিন্ন মান দেখতে পাবে)। প্রক্রিয়াটির ডিরেক্টরিতে একটি উপ-ডিরেক্টরি রয়েছে oryfd যার প্রতিটি উন্মুক্ত ফাইলের জন্য একটি প্রতীকী লিঙ্ক রয়েছে যার নাম কেবলমাত্র ফাইল বর্ণনাকারীর দশমিক প্রতিনিধিত্ব (প্রক্রিয়ার ফাইল টেবিলের সূচক, পূর্ববর্তী বিভাগটি দেখুন) এবং যার লক্ষ্য ফাইলটি এটি সম্পর্কিত s

শিশু প্রক্রিয়া তৈরি করার সময় ফাইল বর্ণনাকারী

একটি শিশু প্রক্রিয়া দ্বারা নির্মিত হয় fork। এ forkফাইল বর্ণনাকারীদের একটি অনুলিপি তৈরি করে, যার অর্থ হল যে শিশু প্রক্রিয়াটি তৈরি হয়েছে তার পিতামাতার প্রক্রিয়ার মতোই ওপেন ফাইলগুলির খুব একই তালিকা রয়েছে। সুতরাং যতক্ষণ না ওপেন ফাইলগুলির একটি সন্তানের দ্বারা বন্ধ করা হয়, সন্তানের উত্তরাধিকারসূত্রে প্রাপ্ত ফাইল বর্ণনাকারীর অ্যাক্সেস পিতামাতার প্রক্রিয়াতে মূল ফাইল বিবরণকারীকে অ্যাক্সেস করার মতো একই ফাইলটি অ্যাক্সেস করবে।

নোট করুন যে কাঁটাচামচ করার পরে, আপনার কাছে একই প্রক্রিয়াটির দুটি কপি থাকে যা কেবল কাঁটাচামচ কল থেকে রিটার্ন মানের মধ্যে পৃথক হয় (পিতামাতার সন্তানের পিআইডি হয়, শিশু 0 পায়)। সাধারণত, একটি কাঁটাচামচ পরে একটি execঅন্য এক্সিকিউটেবল দ্বারা একটি অনুলিপি প্রতিস্থাপন করা হয়। খোলা ফাইল বর্ণনাকারী যে চালানো বেঁচে আছে। এও লক্ষ করুন যে মৃত্যুদন্ড কার্যকর করার আগে প্রক্রিয়াটি অন্যান্য হেরফের করতে পারে (যেমন নতুন প্রক্রিয়াটি না পাওয়ার ফাইলগুলি বন্ধ করা, বা অন্যান্য ফাইলগুলি খোলার মতো)।

নামহীন পাইপ

একটি নামবিহীন পাইপ হ'ল কার্নেলের অনুরোধে ফাইল বর্ণনাকারীর একটি জুড়ি যা প্রথম ফাইল বর্ণনাকারীর কাছে লেখা সমস্ত কিছু দ্বিতীয়টিতে স্থানান্তরিত হয়। সবচেয়ে প্রচলিত ব্যবহার বংশীধ্বনিতুল্য কনস্ট্রাক্ট জন্য foo | barএর bash, যেখানে মান আউটপুট fooপাইপের লেখার অংশ দ্বারা প্রতিস্থাপিত হয়, এবং স্ট্যান্ডার্ড ইনপুট পড়তে অংশ দ্বারা প্রতিস্থাপিত হয়। স্ট্যান্ডার্ড ইনপুট এবং স্ট্যান্ডার্ড আউটপুট হ'ল ফাইল টেবিলের প্রথম দুটি প্রবেশিকা (এন্ট্রি 0 এবং 1; 2 হ'ল স্ট্যান্ডার্ড ত্রুটি), এবং সুতরাং এটির বদলির অর্থ অন্য ফাইল বর্ণনাকারীর সাথে সম্পর্কিত ডেটার সাথে সেই টেবিলের এন্ট্রিটি পুনরায় লেখার (আবার, প্রকৃত বাস্তবায়ন পৃথক হতে পারে)। যেহেতু প্রক্রিয়াটি সরাসরি টেবিলটিতে অ্যাক্সেস করতে পারে না তাই এটি করার জন্য একটি কার্নেল ফাংশন রয়েছে।

প্রক্রিয়া প্রতিস্থাপন

প্রক্রিয়া প্রতিস্থাপন কীভাবে কাজ করে তা বুঝতে এখন আমাদের একসাথে সমস্ত কিছুই রয়েছে:

  1. বাশ প্রক্রিয়া পরে তৈরি দুটি প্রক্রিয়ার মধ্যে যোগাযোগের জন্য একটি নামবিহীন পাইপ তৈরি করে।
  2. echoপ্রক্রিয়া জন্য কাঁটাচামচ । শিশু প্রক্রিয়া (যা মূল bashপ্রক্রিয়ার হুবহু অনুলিপি হয় ) পাইপের পড়ার শেষটি বন্ধ করে দেয় এবং পাইপের লেখার শেষের সাথে নিজস্ব স্ট্যান্ডার্ড আউটপুট প্রতিস্থাপন করে। এটি echoশেল বিল্টিন হিসাবে প্রদত্ত , কলটি bashনিজেকে ছাড়িয়ে দিতে পারে execতবে তা কোনওভাবেই আসে না (শেল বিল্টিনটিও অক্ষম হতে পারে, এটি কার্যকর হলে /bin/echo)।
  3. বাশ (আসল, পিতা মাতার একজন) নামবিহীন পাইপের পড়ার শেষ উল্লেখ <(echo 1)করে সিউডো ফাইল লিঙ্কের মাধ্যমে অভিব্যক্তিটি প্রতিস্থাপন করে /dev/fd
  4. পিএইচপি প্রক্রিয়াটির জন্য বাশ এক্সিকিউট করে (নোট করুন কাঁটাচামচ পরে, আমরা এখনও বাশের ভিতরে [অনুলিপি করছি)। নতুন প্রক্রিয়াটি নামবিহীন পাইপের উত্তরাধিকার সূত্রে লেখা প্রান্তটি বন্ধ করে দেয় (এবং কিছু অন্যান্য প্রস্তুতিমূলক পদক্ষেপগুলি করে), তবে পঠনের শেষটি খোলা রাখে। তারপরে এটি পিএইচপি কার্যকর করে।
  5. পিএইচপি প্রোগ্রামটি নামটি গ্রহণ করে /dev/fd/। যেহেতু সম্পর্কিত ফাইল বর্ণনাকারী এখনও খোলা আছে, এটি এখনও পাইপের পড়ার শেষের সাথে মিলে যায়। সুতরাং যদি পিএইচপি প্রোগ্রামটি প্রদত্ত ফাইলটি পড়ার জন্য খোলে, এটি আসলে কী করে তা হল secondনামবিহীন পাইপের পাঠের শেষের জন্য একটি ফাইল বর্ণনাকারী তৈরি করা । তবে এটি কোনও সমস্যা নয়, এটি যে কোনও একটি থেকেই পড়তে পারে।
  6. এখন পিএইচপি প্রোগ্রামটি নতুন ফাইল বর্ণনাকারীর মাধ্যমে পাইপের পড়ার শেষটি পড়তে পারে এবং এইভাবে echoকমান্ডটির স্ট্যান্ডার্ড আউটপুট গ্রহণ করতে পারে যা একই পাইপের লেখার শেষ দিকে যায়।

অবশ্যই, আমি আপনার প্রচেষ্টার প্রশংসা করি তবে আমি বেশ কয়েকটি বিষয় তুলে ধরতে চেয়েছিলাম। প্রথমত, আপনি phpদৃশ্যের কথা বলছেন তবে phpপাইপগুলি ভালভাবে পরিচালনা করে না । কমান্ড বিবেচনা করেও cat <(echo test), এখানে আশ্চর্যের বিষয়টি হ'ল এটি bashএকবারের জন্য কাঁটাচামচ করে catতবে দুবারের জন্য echo test
এক্স-ইয়ুরি

13

celtschkএর উত্তর থেকে orrowণ নেওয়া এর /dev/fdপ্রতীকী লিঙ্ক /proc/self/fd। এবং /procএটি একটি সিউডো ফাইলসিস্টেম যা প্রক্রিয়া সম্পর্কিত পদ্ধতি এবং অন্যান্য সিস্টেমের তথ্যকে হায়ারারিকাল ফাইল-জাতীয় কাঠামোয় উপস্থাপন করে। ফাইলগুলির /dev/fdসাথে ফাইলগুলিতে কোনও প্রক্রিয়া খোলা থাকে এবং ফাইলগুলির বর্ণনাকারীর নাম থাকে এবং তাদের লক্ষ্য হিসাবে ফাইল করে। ফাইলটি খোলার বিষয়টি /dev/fd/Nডুপ্লিকেট করার বর্ণনাকারীর সমতুল্য N(অনুমান করে যে বর্ণনাকারী Nউন্মুক্ত)।

এবং এটি কীভাবে কাজ করে তা সম্পর্কে আমার তদন্তের ফলাফল এখানে রয়েছে ( straceআউটপুটটি অপ্রয়োজনীয় বিশদ থেকে মুক্তি পেয়ে কী ঘটছে তা আরও ভালভাবে প্রকাশ করার জন্য সংশোধন করা হয়):

$ cat 1.c
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char *argv[])
{
    char buf[100];
    int fd;
    fd = open(argv[1], O_RDONLY);
    read(fd, buf, 100);
    write(STDOUT_FILENO, buf, n_read);
    return 0;
}
$ gcc 1.c -o 1.out
$ cat 2.c
#include <unistd.h>
#include <string.h>

int main(void)
{
    char *p = "hello, world\n";
    write(STDOUT_FILENO, p, strlen(p));
    return 0;
}
$ gcc 2.c -o 2.out
$ strace -f -e pipe,fcntl,dup2,close,clone,close,execve,wait4,read,open,write bash -c './1.out <(./2.out)'
[bash] pipe([3, 4]) = 0
[bash] dup2(3, 63) = 63
[bash] close(3) = 0
[bash] clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f7c211fb9d0) = p2
Process p2 attached
[bash] close(4) = 0
[bash] clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f7c211fb9d0) = p1
Process p1 attached
[bash] close(63) = 0
[p2] dup2(4, 1) = 1
[p2] close(4) = 0
[p2] close(63) = 0
[bash] wait4(-1, <unfinished ...>
Process bash suspended
[p1] execve("/home/yuri/_/1.out", ["/home/yuri/_/1.out", "/dev/fd/63"], [/* 31 vars */]) = 0
[p2] clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f7c211fb9d0) = p22
Process p22 attached
[p22] execve("/home/yuri/_/2.out", ["/home/yuri/_/2.out"], [/* 31 vars */]) = 0
[p2] wait4(-1, <unfinished ...>
Process p2 suspended
[p1] open("/dev/fd/63", O_RDONLY) = 3
[p1] read(3,  <unfinished ...>
[p22] write(1, "hello, world\n", 13) = 13
[p1] <... read resumed> "hello, world\n", 100) = 13
Process p2 resumed
Process p22 detached
[p1] write(1, "hello, world\n", 13) = 13
hello, world
[p2] <... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = p22
[p2] --- SIGCHLD (Child exited) @ 0 (0) ---
[p2] wait4(-1, 0x7fff190f289c, WNOHANG, NULL) = -1 ECHILD (No child processes)
Process bash resumed
Process p1 detached
[bash] <... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = p1
[bash] --- SIGCHLD (Child exited) @ 0 (0) ---
Process p2 detached
[bash] wait4(-1, 0x7fff190f2bdc, WNOHANG, NULL) = 0
--- SIGCHLD (Child exited) @ 0 (0) ---
[bash] wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG, NULL) = p2
[bash] wait4(-1, 0x7fff190f299c, WNOHANG, NULL) = -1 ECHILD (No child processes)

মূলত, bashএকটি পাইপ তৈরি করে এবং তার বাচ্চাদের কাছে ফাইল বর্ণনাকারী হিসাবে পাঠায় (শেষের দিকে পড়ুন 1.out, এবং শেষে লিখুন 2.out)। এবং 1.out( /dev/fd/63) এর কমান্ড লাইন প্যারামিটার হিসাবে পঠন শেষ করে । এই 1.outউপায়টি খুলতে সক্ষম /dev/fd/63

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.