হুম, নাম দেখে চমকে গেলেন? প্রোগ্রামের আবার বাবা-মা কি, বাচ্চা-কাচ্চাই বা আসলো কোথা থেকে? না ভয় পাবেন না। কোন যুগান্তকারী আবিষ্কারের কথা বলতে আসিনি, আমি প্যারেন্ট প্রসেস আর চাইল্ড প্রসেস এর কথা বলছি।
আগে একটু হালকা ভূমিকা দিয়ে নেই। একসময় কম্পিউটার কেবল একটি প্রোগ্রাম একবারে হ্যান্ডেল করতে পারত। সেটা অনেক আগের কথা। এখনকার কম্পিউটার আর আধুনিক ওএস (লিনাক্স,উইন্ডোজ,ম্যাক) গুলো এক সাথে অনেকগুলো প্রোগ্রাম হ্যান্ডেল করতে পারে। এগুলোকে আমরা বলি মাল্টিটাস্কিং ওএস। তো কাজের কথা হল মাল্টিটাস্কিং ওএস গুলো কিন্ত প্রকৃতপক্ষে মাল্টিটাস্কিং না। আধুনিক প্রসেসর এর ক্ষমতাকে কাজে লাগিয়ে ওএস গুলো একটি বিষেশ প্রক্রিয়ায় সবগুলো চলমান প্রোগ্রাম বা প্রসেস এর মধ্যে সময়টা ভাগ করে দেয়। আসলে একবারে একটি প্রসেসই চলে কিন্তু প্রসেস গুলো এত দ্রুত একটার পরে আরেকটা চক্রাকারে চলতে থাকে যে মনে হয় সবগুলো একই সাথে চলছে।
লিনাক্সে প্রত্যেকটি চলমান প্রোগ্রামকে বলা হয় প্রসেস। আর প্রসেসগুলোর মধ্যে সময় ভাগ করে দেবার জন্য একটি প্রসেস শিডিউলার ব্যাবহার করা হয়।এর কাজ হল প্রত্যেকটি চলমান প্রসেস এর মধ্যে সময় ভাগ করে দেয়া। প্রতিটি প্রসেস এর জন্য সময় নির্দিস্ট করে দেয়া হয়। এই সময়ের মধ্যে প্রসেসটি শেষ হোক বা না হোক সময় শেষ হলে শিডিউলার পরের প্রসেস এ চলে যায়। এভাবে ঘুরে ঘুরে প্রসেসগুলো প্রচন্ড দ্রুত এক্সিকিউট হতে থাকে। আর আমাদের প্রসেসর এত দ্রুত ডাটাগুলো হ্যন্ডেল করে যে আমরা বুঝতেই পারি না আসলে একটি কাজই হচ্ছে। আমাদের মনে হয় মেশিনটা মাল্টিটাস্কিং।
আমি মনে হয় ভূমিকাটা লম্বা করে ফেললাম। প্রতিটা প্রসেস কে ঠিকঠাক মত চালানোর জন্য বা একটার পর একটার ধারাবিহিকতা ঠিক রাখার জন্য প্রসেস শিডিউলার বেশ কিছু জটিল পদ্ধতি অনুসরন করে থাকে। লিনাক্সে প্রতিটা চলমান প্রসেসকে একটি প্রসেস আইডি অ্যালোকেট করে দেয়া হয়। প্রসেস শিডিউলার মূলত সিপিইউ রেজিস্টার ভ্যালু, সিস্টেম স্ট্যাক, আপ্লিকেশন স্ট্যাক, প্রসেস আইডি এসব হিসেব করে প্রতিটি প্রসেস এর জন্য সময় নির্ধারন করে। একটার পর আরেকটা প্রসেস এ সুইচ করার এই পদ্ধতিকা বলা হয় কন্টটেস্কট সুইচিং। লিনাক্সের ক্ষেত্রে এটি হল প্রি-এম্পটিভ যার মানে হল একটি প্রসেস এর জন্য নির্ধারিত সময় শেষ হওয়া মাত্রই পরের প্রসেস এ সুইচ করা হবে, এক্ষেত্রে প্রথম প্রসেসটি শেষ হোক বা না হোক সেটির সময় শেষ হলেই পরেরটি চালু হবে। ফলে কোন একটি প্রসেস হ্যাং করলে শুধু সেটিই হ্যাং করা ত্থাকবে, বাকিগুলো সফল ভাবেই চলতে থাকে।
এবার কাজের কথায় আসি, আজ লিখব প্রসেস আইডি, প্যারেন্ট প্রসেস, চাইল্ড প্রসেস নিয়ে।
প্রথমেই নিচের কোডটি লিখুন। কোডগুলো সি তে লিখা। টার্মিনালে কম্পাইল করে রান করতে পারেন। এটার সাহায্য নিতে পারেন প্রয়োজনে।
#include<stdio.h>
int main()
{
printf(“Process ID = %d. \n”,getpid());
return 0;
}
রান করানো হলে দেখবেন একটি প্রসেস আইডি প্রিন্ট হচ্ছে। এখানে getpid() হল একটি লাইব্রেরি ফাংশন যেটির কাজ হল প্রসেস আইডি জানানো। দেখবেন প্রতিবার রান করালে প্রোগ্রামটি ভিন্ন ভিন্ন প্রসেস আইডি দিচ্ছে।
বাবা মা, বাচ্চা-কাচ্চাঃ
আগেই বলেছি সব চলমান প্রোগ্রামই এক একটি প্রসেস। মজার ব্যাপার হল একটি চলমান প্রসেস চাইলেই আরেকটি প্রসেসকে তার অধীনে রেখে চালু করতে ও থামাতে পারে। অর্থাৎ একটি প্রসেস থেকে আপনি চাইলে এক বা একাধিক সাবপ্রসেস জন্ম দিতে পারেন। এক্ষেত্রে প্রথম প্রসেস এবং চালু করা প্রসেস এর মধ্যে একটি প্যারেন্ট-চাইল্ড সম্পর্ক তৈরী হবে। চাইল্ড প্রসেস বানানোর জন্য fork() নামক একটি লাইব্রেরি ফাংশন কাজে লাগানো যায়। নিচের কোডটি দেখুনঃ
#include<stdio.h>
#include<sys/types.h>
int main()
{
printf(“Before Forking. \n”);
fork();
printf(“After Forking. \n”);
return 0;
}
এই কোডটি রান করালে কি আউটপুট আসতে পারে বলুন তো? রান করিয়ে দেখুন। নিচের মত আউটপুট আসার কথা।
Before Forking. After Forking. After Forking.
দেখুন, fork() ফাংশনের পর যা কিছু আছে সবই দুইবার করে এসেছে। কেন বলুন তো? কারন fork() ফাংশন একটি চাইল্ড প্রসেস তৈরী করেছে, ফলে পরের লাইনগুলো একবার প্যারেন্ট থেকে ও আরেকবার চাইল্ড থেকে এক্সিকিউট হয়েছে।
আচ্ছা আপনার মনে কি প্রশ্ন আসছে এত কিছুর কি দরকার? আসলে fork() ফাংশন যে ধরনের কাজ করতে পারে তার একটি প্রচলিত উদাহরন হল, ধরুন আপনি একই সাথে দুটি কাজ করতে চাইছেন, যেমন একটি ফাইল কপি হবে এবং কপি হবার সময় একটি চলমান ইমেজ ফাইল মনিটরে দেখাতে থাকবে। কপি শেষ হলেই ইমেজ ফাইলটি থেমে যাবে। এখানে দুটি কাজ, একটি হল কপি করা আরেকটি হল ইমেজটি চলা। ইমেজটি দেখানোর কাজটা আমরা চাইলেই আলাদা প্রোগ্রাম লিখে করতে পারি। কিন্তু একই কপি প্রোগ্রামের ভেতর একটি চাইল্ড প্রসেস বানিয়ে কাজটি করলে এটি যেমন সহজ হয় তেমনি দুটি প্রোগ্রাম আলাদা রান করানোর চাইতে কম ঝামেলার হয়। অর্থাৎ যদি দুটি বা তার বেশি কাজ একই সাথে একই প্রোগ্রামের অধীনে করার প্রয়োজন হয় তখনই চাইল্ড প্রসেস তৈরীর প্রয়োজন পড়ে।
ইমেজ ফাইল ও কপি করার কাজটি একই সাথে করার জন্য এধরনের কোড লিখা যেতে পারেঃ
#include<stdio.h>
#include<sys/types.h>
int main()
{
int pid;
pid=fork();
if(pid==0)
{
printf(“In Child Process.\n”);
/* Code to play animated GIF goes here */
}
else
{
printf(“In Parent Process.\n”);
/*Code to copy file goes here */
}
return 0;
}
প্রোগ্রামটি লক্ষ্য করুন। fork() এর কাজ হল একটি চাইল্ড প্রসেস তৈরী করা এবং এর পরের কোডগুলোর পুনরাবৃত্তি করা প্যারেন্ট চাইল্ড উভয় প্রসেস এর জন্য।fork() এর কাজ প্যারেন্ট আর চাইল্ডে সমানতালেই চলতে থাকে, অর্থাৎ fork() ফাংশনটি একবার কল হলেও কন্ট্রোল এটির কাছে দুবার আসবে। এবং কন্ট্রোল যখন চাইল্ড প্রসেসে এ রিটার্ন করবে তখন এটি সবসময় শূন্য রিটার্ন করবে। তাই উপরের প্রোগ্রামে প্যারেন্টের জন্য else ব্লক এক্সিকিউট হবে এবং চাইল্ড এর জন্য if ব্লক এক্সিকিউট হবে।
আসুন প্যরেন্ট-চাইল্ড প্রসেস ও fork() ফাংশনের কাজ বুঝতে আরেকটি প্রোগ্রাম দেখি।
#include<stdio.h>
#include<sys/types.h>
int main()
{
int pid;
pid=fork();
if(pid==0)
{
printf("CHILD: In Child Process. \n");
printf("CHILD: Child'd PID = %d.\n", getpid());
printf("CHILD: Parent's PID = %d.\n", getppid());
}
else
{
printf("PARENT: In Parent Process. \n");
printf("PARENT: Parent's PID = %d.\n",getpid());
printf("PARENT: Child's PID = %d.\n",pid);
}
return 0;
}
এই প্রোগ্রামটি চালালে নিচের মত আউটপুট আসবে। অবস্যই আপনার পিসিতে ভিন্ন প্রসেস আইডি আসবে ।
PARENT: In Parent Process. PARENT: Parent's PID = 4980. PARENT: Child's PID = 4981. CHILD: In Child Process. CHILD: Child'd PID = 4981. CHILD: Parent's PID = 4980.
getpid() এর সাথে সাথে এখানে getppid() নামের আরো একটি ফাংশন ব্যাবহার করা হয়েছে। এটির কাজ হল প্যারেন্ট এর প্রসেস আইডি বের করা। প্যারেন্ট ও চাইল্ড এর প্রসেস আইডি দেখে আশা করি ব্যাপারটা বুঝতে পারছেন।
এবং আরো একটিঃ
শেষ প্রোগ্রামটি একটু দেখুন। ধরুন আমরা চাই আমাদের প্রোগ্রামের অংশ হিসেবে মানে চাইল্ড হিসেবে পিসিতে থাকা কোন একটি প্রোগ্রাম বা কমান্ড এক্সিকিউট হবে। এই কাজটি বেশ কয়েক ভাবে করা যায়। এর একটি হল exec() ফাংশন ব্যাবহার। exec() ফাংশন দিয়ে আমরা কোন কমান্ড লাইন আর্গুমেন্ট কে এক্সিকিউট করাতে পারি। একই কাজ করা যায় execv() ফাংশন দিয়েও। তবে execv() ফাংশনে কমান্ড লাইন ফাংশনটিকে স্ট্রিং অ্যারে আকারে পাঠাতে হয়। একই রকম আরো কয়েকটি ফাংশন আছে, যেমন execle(), execvp() ইত্যাদি। তাহলে চলুন দেখে নিই পরের প্রোগ্রামটি।
#include
#include
#include
int main()
{
int pid;
pid=fork();
if(pid==0)
{
execl("/bin/ls", "-al", "/etc", NULL);
printf("Child after exec() \n");
}
else
printf("In Parent Process.\n");
return 0;
}
এখানে fork() করার পর আমরা চাইল্ড ফাংশনে execl() লাইব্রেরি ফাংশন দিয়ে একটি কমান্ড এক্সিকিউট করিয়েছি। execl() ফাংশনের প্যারামিটার এর মধ্যে প্রথমটি হল প্রোগ্রামটির পাথ বা ডিরেক্টরি। বাকি গুলো ওই প্রোগ্রামের কমান্ড লাইন আর্গুমেন্ট। এবং শেষ প্যারামিটারটি সবসময়ই NULL হবে। তাহলে দেখা যাচ্ছে আমরা ececl() দিয়ে যা কমান্ডটি চালিয়েছি সেটি হল
ls -al /etc
বুঝতেই পারছেন, /etc ডিরেকটরিতে যা যা ছিল সবই টার্মিনালে দেখাচ্ছে। তবে একটি জিনিস কি খেয়াল করছেন? Execl() এর পরে থাকা printf টি কিন্তু আউটপুটে কিছু দেখায় নি। এর কারণ হল exec ফ্যামেলির ফংশনগুলো কলিং প্রসেস এর ইমেজগুলোকে এটির ডাটা বা কোড দিয়ে ওভাররাইট করে ফেলে। এখানেও চাইল্ড প্রসেস এর মেমরি ওভাররিটেন হয়ে গেছে তাই printf এর আউটপুট দেখায় নি। যাক এই বিষয়ে আরেকদিন বলব।
ধন্যবাদ।
Valo hoise.
ধন্যবাদ ভাইয়া
Pingback: মাল্টি থ্রেডেড প্রোগ্রামিং (অ্যাডভান্সড প্রোগ্রামিং) | ACMSolver - Bangla
ভালো লিখেছেন… এরকম আরো লিখা চাই… চালিয়ে যান…
ধন্যবাদ ভাইয়া !
>>>khub valo laglo pore.chalie jau rashed.
সব বুজি নাই
এটা লিনাক্স প্রসেস কন্ট্রোল নিয়ে প্রোগ্রামিং বিষয়ে। অপারেটিং সিস্টেম নিয়ে আমাদের একটা কোর্স আছে, ওটায় লাগে। আমি অবস্য অনেক আগে লিখেছি এটা। বেশি কঠিন করে লিখেছি নাকি?