আমি অন্য প্রশ্নটি নিয়ে গবেষণা করছিলাম , যখন আমি বুঝতে পারি আমি বুঝতে পারছি না যে হুডের নীচে কী ঘটছে, সেই /dev/fd/*
ফাইলগুলি কী এবং কীভাবে শিশু প্রক্রিয়াগুলি সেগুলি খুলতে পারে।
আমি অন্য প্রশ্নটি নিয়ে গবেষণা করছিলাম , যখন আমি বুঝতে পারি আমি বুঝতে পারছি না যে হুডের নীচে কী ঘটছে, সেই /dev/fd/*
ফাইলগুলি কী এবং কীভাবে শিশু প্রক্রিয়াগুলি সেগুলি খুলতে পারে।
উত্তর:
ঠিক আছে, এটির অনেক দিক রয়েছে।
ফাইল বর্ণনাকারী
প্রতিটি প্রক্রিয়াটির জন্য, কার্নেল খোলা ফাইলগুলির একটি টেবিল বজায় রাখে (ভাল, এটি অন্যভাবে প্রয়োগ করা যেতে পারে, তবে যেহেতু আপনি এটি যে কোনওভাবে দেখতে সক্ষম নন, সুতরাং আপনি এটি কেবল একটি সাধারণ টেবিল ধরে নিতে পারেন)। সেই সারণীতে এটি কোন ফাইলটি / কোথায় তা পাওয়া যাবে, কোন মোডে আপনি এটি খোলেন, আপনি বর্তমানে কোন অবস্থানে পড়ছেন / লিখছেন এবং সেই ফাইলটিতে আই / ও ক্রিয়াকলাপ সম্পাদন করার জন্য অন্য যে কোনও জিনিস প্রয়োজন তা সম্পর্কিত তথ্য রয়েছে। এখন প্রক্রিয়াটি কখনই সেই টেবিলটি পড়তে (বা এমনকি লিখতেও) পায় না। প্রক্রিয়াটি কোনও ফাইল খুললে, এটি তথাকথিত ফাইল বিবরণকারীকে ফিরে পায়। যা কেবল টেবিলের মধ্যে একটি সূচক।
ডিরেক্টরি /dev/fd
এবং এর সামগ্রী
লিনাক্সে dev/fd
আসলে এর প্রতীকী লিঙ্ক /proc/self/fd
। /proc
একটি সিউডো ফাইল সিস্টেম যাতে কার্নেলটি ফাইল এপিআই দিয়ে অ্যাক্সেস করার জন্য কয়েকটি অভ্যন্তরীণ ডেটা স্ট্রাকচারের মানচিত্র তৈরি করে (যাতে তারা প্রোগ্রামগুলিতে নিয়মিত ফাইল / ডিরেক্টরি / সিমলিংকের মতো দেখায়)। বিশেষত সমস্ত প্রক্রিয়া সম্পর্কে তথ্য রয়েছে (যা এটি নাম দিয়েছিল)। প্রতীকী লিঙ্কটি /proc/self
সর্বদা চলমান প্রক্রিয়া সম্পর্কিত ডিরেক্টরিকে বোঝায় (এটি প্রক্রিয়াটির জন্য অনুরোধ করছে; বিভিন্ন প্রক্রিয়া অতএব বিভিন্ন মান দেখতে পাবে)। প্রক্রিয়াটির ডিরেক্টরিতে একটি উপ-ডিরেক্টরি রয়েছে oryfd
যার প্রতিটি উন্মুক্ত ফাইলের জন্য একটি প্রতীকী লিঙ্ক রয়েছে যার নাম কেবলমাত্র ফাইল বর্ণনাকারীর দশমিক প্রতিনিধিত্ব (প্রক্রিয়ার ফাইল টেবিলের সূচক, পূর্ববর্তী বিভাগটি দেখুন) এবং যার লক্ষ্য ফাইলটি এটি সম্পর্কিত s
শিশু প্রক্রিয়া তৈরি করার সময় ফাইল বর্ণনাকারী
একটি শিশু প্রক্রিয়া দ্বারা নির্মিত হয় fork
। এ fork
ফাইল বর্ণনাকারীদের একটি অনুলিপি তৈরি করে, যার অর্থ হল যে শিশু প্রক্রিয়াটি তৈরি হয়েছে তার পিতামাতার প্রক্রিয়ার মতোই ওপেন ফাইলগুলির খুব একই তালিকা রয়েছে। সুতরাং যতক্ষণ না ওপেন ফাইলগুলির একটি সন্তানের দ্বারা বন্ধ করা হয়, সন্তানের উত্তরাধিকারসূত্রে প্রাপ্ত ফাইল বর্ণনাকারীর অ্যাক্সেস পিতামাতার প্রক্রিয়াতে মূল ফাইল বিবরণকারীকে অ্যাক্সেস করার মতো একই ফাইলটি অ্যাক্সেস করবে।
নোট করুন যে কাঁটাচামচ করার পরে, আপনার কাছে একই প্রক্রিয়াটির দুটি কপি থাকে যা কেবল কাঁটাচামচ কল থেকে রিটার্ন মানের মধ্যে পৃথক হয় (পিতামাতার সন্তানের পিআইডি হয়, শিশু 0 পায়)। সাধারণত, একটি কাঁটাচামচ পরে একটি exec
অন্য এক্সিকিউটেবল দ্বারা একটি অনুলিপি প্রতিস্থাপন করা হয়। খোলা ফাইল বর্ণনাকারী যে চালানো বেঁচে আছে। এও লক্ষ করুন যে মৃত্যুদন্ড কার্যকর করার আগে প্রক্রিয়াটি অন্যান্য হেরফের করতে পারে (যেমন নতুন প্রক্রিয়াটি না পাওয়ার ফাইলগুলি বন্ধ করা, বা অন্যান্য ফাইলগুলি খোলার মতো)।
নামহীন পাইপ
একটি নামবিহীন পাইপ হ'ল কার্নেলের অনুরোধে ফাইল বর্ণনাকারীর একটি জুড়ি যা প্রথম ফাইল বর্ণনাকারীর কাছে লেখা সমস্ত কিছু দ্বিতীয়টিতে স্থানান্তরিত হয়। সবচেয়ে প্রচলিত ব্যবহার বংশীধ্বনিতুল্য কনস্ট্রাক্ট জন্য foo | bar
এর bash
, যেখানে মান আউটপুট foo
পাইপের লেখার অংশ দ্বারা প্রতিস্থাপিত হয়, এবং স্ট্যান্ডার্ড ইনপুট পড়তে অংশ দ্বারা প্রতিস্থাপিত হয়। স্ট্যান্ডার্ড ইনপুট এবং স্ট্যান্ডার্ড আউটপুট হ'ল ফাইল টেবিলের প্রথম দুটি প্রবেশিকা (এন্ট্রি 0 এবং 1; 2 হ'ল স্ট্যান্ডার্ড ত্রুটি), এবং সুতরাং এটির বদলির অর্থ অন্য ফাইল বর্ণনাকারীর সাথে সম্পর্কিত ডেটার সাথে সেই টেবিলের এন্ট্রিটি পুনরায় লেখার (আবার, প্রকৃত বাস্তবায়ন পৃথক হতে পারে)। যেহেতু প্রক্রিয়াটি সরাসরি টেবিলটিতে অ্যাক্সেস করতে পারে না তাই এটি করার জন্য একটি কার্নেল ফাংশন রয়েছে।
প্রক্রিয়া প্রতিস্থাপন
প্রক্রিয়া প্রতিস্থাপন কীভাবে কাজ করে তা বুঝতে এখন আমাদের একসাথে সমস্ত কিছুই রয়েছে:
echo
প্রক্রিয়া জন্য কাঁটাচামচ । শিশু প্রক্রিয়া (যা মূল bash
প্রক্রিয়ার হুবহু অনুলিপি হয় ) পাইপের পড়ার শেষটি বন্ধ করে দেয় এবং পাইপের লেখার শেষের সাথে নিজস্ব স্ট্যান্ডার্ড আউটপুট প্রতিস্থাপন করে। এটি echo
শেল বিল্টিন হিসাবে প্রদত্ত , কলটি bash
নিজেকে ছাড়িয়ে দিতে পারে exec
তবে তা কোনওভাবেই আসে না (শেল বিল্টিনটিও অক্ষম হতে পারে, এটি কার্যকর হলে /bin/echo
)।<(echo 1)
করে সিউডো ফাইল লিঙ্কের মাধ্যমে অভিব্যক্তিটি প্রতিস্থাপন করে /dev/fd
।/dev/fd/
। যেহেতু সম্পর্কিত ফাইল বর্ণনাকারী এখনও খোলা আছে, এটি এখনও পাইপের পড়ার শেষের সাথে মিলে যায়। সুতরাং যদি পিএইচপি প্রোগ্রামটি প্রদত্ত ফাইলটি পড়ার জন্য খোলে, এটি আসলে কী করে তা হল second
নামবিহীন পাইপের পাঠের শেষের জন্য একটি ফাইল বর্ণনাকারী তৈরি করা । তবে এটি কোনও সমস্যা নয়, এটি যে কোনও একটি থেকেই পড়তে পারে।echo
কমান্ডটির স্ট্যান্ডার্ড আউটপুট গ্রহণ করতে পারে যা একই পাইপের লেখার শেষ দিকে যায়।php
দৃশ্যের কথা বলছেন তবে php
পাইপগুলি ভালভাবে পরিচালনা করে না । কমান্ড বিবেচনা করেও cat <(echo test)
, এখানে আশ্চর্যের বিষয়টি হ'ল এটি bash
একবারের জন্য কাঁটাচামচ করে cat
তবে দুবারের জন্য echo test
।
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
।