কম্পিউটার টিউটোরিয়াল

সি প্রোগ্রামিং-এ fork() এবং exec() দক্ষতা অর্জন: প্রসেস তৈরি ও প্রতিস্থাপন ব্যাখ্যা

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

সি ভাষায় Fork() ফাংশন

fork() ফাংশন কলিং প্রসেসের একটি ডুপ্লিকেট তৈরি করে। যদিও চাইল্ড প্রসেস প্যারেন্ট প্রসেসের একটি ডুপ্লিকেট, তবুও তারা কিছু প্রোপার্টি শেয়ার করে না যেমন অ্যালোকেট করা মেমরি এরিয়া, PID, ইত্যাদি। চলুন fork() ফাংশনের সিনট্যাক্স দেখি:

fork() ফাংশন প্যারেন্ট প্রসেসে ফলাফল হিসেবে চাইল্ড প্রসেসের PID রিটার্ন করে, در حالی যে একই কল চাইল্ড প্রসেসে কোনো প্রভাব ফেলে না এবং ফলাফল হিসেবে ০ রিটার্ন করে। এই মেকানিজম আমাদের একটি "if" কন্ডিশন দিয়ে দুটি প্রসেসে ভিন্ন কোড এক্সিকিউট করতে দেয় যেখানে কন্ডিশন হলো fork() এর রিটার্ন ভ্যালু। এই কনসেপ্টটি নিচে দেখানো হলো:

if (fork() == 0)
 {
 code for child process
 }
else
 {
 code for parent process
 }

এভাবে, প্যারেন্ট প্রসেস "else" স্টেটমেন্টের কার্লি ব্রেসেসের মাঝপথে কোড এক্সিকিউট করে, در حالی যে fork() দ্বারা তৈরি চাইল্ড প্রসেস "if" স্টেটমেন্টের কোড এক্সিকিউট করে।

এটি একটি সহজ উদাহরণ দিয়ে দেখি। নিচের কোডটি দুটি ইনফিনিট লুপ দিয়ে গঠিত। প্যারেন্ট প্রসেসে, প্রোগ্রাম "else" স্টেটমেন্টের লুপে পড়ে যায় যা "Write for parent process" মেসেজ প্রদর্শন করে, در حالی যে fork() দ্বারা তৈরি চাইল্ড প্রসেস "if" স্টেটমেন্টে পড়ে যায় যা "Write for child process" মেসেজ প্রদর্শন করে।

#include <stdio.h>
#include <unistd.h>

void main(){

if (fork() == 0)
 {
 while (1){
 printf ("Write by child process \n");
 sleep(5);}
 }

else
 {
 while (1){
 printf ("Write by parent process \n");
 sleep(2);}
 }
 }

নিচের ছবিতে এই কোডের কম্পাইলেশন ও এক্সিকিউশন দেখানো হয়েছে। কমান্ড কনসোলে দেখা যাচ্ছে, প্রতিটি প্রসেস ভিন্ন কোড এক্সিকিউট করছে:

সি প্রোগ্রামিং-এ fork() এবং exec() দক্ষতা অর্জন: প্রসেস তৈরি ও প্রতিস্থাপন ব্যাখ্যা

সি ভাষায় ExecXXX() ফাংশন

execXXX() ফাংশন ফ্যামিলি একটি রানিং প্রসেসকে একটি নতুন প্রসেস দিয়ে প্রতিস্থাপন করে। নতুন প্রসেসের ইমেজ সেই প্রসেসের অ্যালোকেট করা মেমরি এরিয়ায় কপি হয় যা প্রতিস্থাপন করা হচ্ছে, এর PID ও অ্যালোকেট করা রিসোর্স সংরক্ষণ করে।

এই গ্রুপের ফাংশনগুলো যা "unistd.h" হেডারে ডিফাইন করা আছে, তাদের ইনপুট অনুযায়ী ভিন্ন কল পদ্ধতি ব্যবহার করে এবং "variadics" টাইপের, তাই সেগুলো পুরানো প্রসেস থেকে নতুন প্রসেসে অনির্দিষ্ট আর্গুমেন্ট বা পয়েন্টার লিস্ট পাস করতে পারে। চলুন প্রতিটি ফাংশনের সিনট্যাক্স দেখি:

int execl(const char *path, const char *arg, ...(char *) NULL );
int execlp(const char *file, const char *arg, ... (char *) NULL );
int execle(const char *path, const char *arg, ... , (char *) NULL, char * const envp[] );

int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],
char *const envp[]);

execl(), execle(), এবং execv() ফাংশনগুলো প্রথম ইনপুট আর্গুমেন্ট হিসেবে একটি পয়েন্টার ব্যবহার করে যেটি একটি স্ট্রিংকে নির্দেশ করে যেটিতে নতুন প্রসেসের এক্সিকিউটেবল ফাইলের অ্যাবসোলিউট পাথ থাকে, در حالی যে execlp(), execvp(), এবং execvpe() করেন্ট ডিরেক্টরির ফাইল নাম ব্যবহার করে। দ্বিতীয় ইনপুট হলো আর্গুমেন্টগুলো যা আপনি নতুন প্রসেসে পাস করেন। এগুলো অবশ্যই const char *arg স্ট্রিং বা char *const argv[] স্ট্রিং পয়েন্টার লিস্ট হতে হবে।

এখন, একটি উদাহরণ দেখি যা execv() ফাংশন ব্যবহার করে একটি প্রসেস প্রতিস্থাপন করে এবং একটি প্রোগ্রাম থেকে অন্য প্রোগ্রামে ইনপুট আর্গুমেন্ট পাস করে।

এ জন্য, আমরা দুটি খুব সিম্পল কোড তৈরি করব। একটি হলো প্যারেন্ট প্রসেস যা execv() ফাংশন কল করে চাইল্ড প্রসেস এক্সিকিউট করতে। যখন execv() ফাংশন চাইল্ড প্রসেস শুরু করে, এটি দুটি ইনপুট আর্গুমেন্ট স্ট্রিং রূপে পাস করে যা চাইল্ড প্রসেস রিট্রিভ করবে এবং শেলে প্রদর্শন করবে।

চাইল্ড প্রসেস

চাইল্ড প্রসেস একটি সিম্পল কোড যা "I am the child process" মেসেজ প্রিন্ট করে, প্যারেন্ট প্রসেস দ্বারা পাঠানো ইনপুট আর্গুমেন্ট রিট্রিভ করে, এবং সেগুলো শেলে প্রদর্শন করে। চাইল্ড প্রসেসের কোড নিচে:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
 printf ("I am the child process\n\n");
 printf ("Argument 1: %s\n", argv[1]);
 printf ("Argument 2: %s\n", argv[2]);
 }

এই কোড কম্পাইল করি এবং এর আউটপুট "Documents" ফোল্ডারে "child" নামে ".bin" এক্সটেনশন সহ সংরক্ষণ করি নিচের মত করে:

~$ gcc Documents/child.c -o Documents/child.bin

এভাবে, আমরা চাইল্ড এক্সিকিউটেবল ফাইল "Documents"-এ সংরক্ষণ করি। এই এক্সিকিউটেবলের পাথ হলো প্যারেন্ট প্রসেসে execv() কল করার সময় ইনপুট আর্গুমেন্ট পাথ।

প্যারেন্ট প্রসেস

প্যারেন্ট প্রসেস হলো সেটি যেখান থেকে আমরা execv() ফাংশন কল করি এটি চাইল্ড প্রসেস দিয়ে প্রতিস্থাপন করতে। এই কোডে, আমরা স্ট্রিং পয়েন্টারের একটি অ্যারে ডিফাইন করি যেটি execv() ফাংশন দ্বারা খোলা প্রসেসের ইনপুট আর্গুমেন্টকে রিপ্রেজেন্ট করে।

নিচের ইলাস্ট্রেশনে আপনি দেখতে পাবেন স্ট্রিং পয়েন্টারের অ্যারে সঠিকভাবে কীভাবে তৈরি করতে হয়। এই ক্ষেত্রে, এতে চারটি পয়েন্টার আছে এবং এটিকে "arg_Ptr[]" বলা হয়।

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

অতএব, আমরা প্রতিটি পয়েন্টারকে সংশ্লিষ্ট আর্গুমেন্ট স্ট্রিং ফরম্যাটে অ্যাসাইন করি:

arg_Ptr[0] = "child.bin";
arg_Ptr[1] = " Hello from ";
arg_Ptr[2] = " process 2 ";
arg_Ptr[3] = NULL;

পরবর্তী ধাপ হলো execv() ফাংশন কল করা, প্রথম আর্গুমেন্ট হিসেবে এক্সিকিউটেবল ফাইলের অ্যাবসোলিউট পাথ ধারণকারী স্ট্রিং এবং দ্বিতীয় আর্গুমেন্ট হিসেবে স্ট্রিং অ্যারে arg_Ptr[] পাস করা। প্যারেন্ট প্রসেসের সম্পূর্ণ কোড নিচে দেখানো হলো:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

int main (){
 printf ("I am the parent process");
 char *arg_Ptr[4];
 arg_Ptr[0] = " child.c";
 arg_Ptr[1] = " Hello from ";
 arg_Ptr[2] = " process 2 ";
 arg_Ptr[3] = NULL;

 execv("/home/linuxhint/Documents/child.bin", arg_Ptr);
 }

এই কোড কম্পাইল করি যা ".c" ফাইলের পাথ এবং আউটপুটের নাম স্পেসিফাই করে:

~$ gcc Documents/parent.c -o pattern

তারপর, আউটপুট রান করি:

প্যারেন্ট প্রসেস "I am the parent process" মেসেজ প্রদর্শন করে, স্ট্রিং অ্যারে তৈরি করে প্রতিটি ইনপুট আর্গুমেন্ট যা পরবর্তী প্রসেসে পাস করা হবে স্ট্রিং অ্যাসাইন করে, এবং execv() ফাংশন কল করে।

যদি execv() ফাংশন সফলভাবে এক্সিকিউট হয়, "child.bin" এক্সিকিউটেবল প্যারেন্ট প্রসেসকে প্রতিস্থাপন করে এবং এর ID ও অ্যালোকেট করা মেমরি দখল করে। সুতরাং, এই অ্যাকশন অন-ডু করা যায় না।

চাইল্ড প্রসেস "I am the child process" মেসেজ প্রদর্শন করে এবং প্যারেন্ট প্রসেস দ্বারা পাস করা প্রতিটি ইনপুট আর্গুমেন্ট রিট্রিভ করে কমান্ড কনসোলে প্রদর্শন করার জন্য।

সি প্রোগ্রামিং-এ fork() এবং exec() দক্ষতা অর্জন: প্রসেস তৈরি ও প্রতিস্থাপন ব্যাখ্যা

লিনাক্সে নতুন প্রসেস তৈরি করতে Fork() এবং Execve() ফাংশন কম্বিনেশন

আমরা এখন পর্যন্ত দেখেছি, fork() ফাংশন একটি প্রসেস ডুপ্লিকেট করে, در حالی যে execve() একটি প্রসেস প্রতিস্থাপন করে। এই উদাহরণে, আমরা দেখব কীভাবে এই দুটি ফাংশন কম্বিনেশন ব্যবহার করে একটি নতুন প্রসেস খোলা যায় একটি ডুপ্লিকেট থেকে যা পরে প্রতিস্থাপন হয়। এ জন্য, আমরা আগে দেখানো দুটি ফাংশনের উদাহরণের কোড কম্বাইন করি যাতে fork() প্যারেন্ট প্রসেস ডুপ্লিকেট করে এবং execve() সেটিকে একটি এক্সিকিউটেবল দিয়ে প্রতিস্থাপন করে যা এই ক্ষেত্রে পূর্বের "child.bin" উদাহরণে ব্যবহার করা একই।

এখন, আমরা ".c" এক্সটেনশন সহ একটি খালি ফাইল নিই এবং fork() ফাংশন উদাহরণে দেখানো প্রোগ্রাম কোড ইনসার্ট করি।

এই প্রোগ্রামে, আমরা শুধু চাইল্ড প্রসেসের কোড মডিফাই করি যাতে execv() ফাংশন সেটিকে "child.bin" এক্সিকিউটেবল দিয়ে প্রতিস্থাপন করে, در حالی যে মূল টাস্ক fork() ফাংশন উদাহরণের সাথে সাদৃশ্য রেখে।

এ জন্য, আমরা execv() ফাংশন উদাহরণের main() ফাংশনের কন্টেন্ট কপি করি এবং "if" স্টেটমেন্টের কন্টেন্ট بهذا কোড দিয়ে প্রতিস্থাপন করি। এখন, সম্পূর্ণ প্রোগ্রাম কেমন দেখে তা দেখি:

#include <stdio.h>
#include <unistd.h>

void main(){

if (fork() == 0)
 {
 printf ("I am the child process\n");
 char *arg_Ptr[5];
 arg_Ptr[0] = " child.c";
 arg_Ptr[1] = " Hello from ";
 arg_Ptr[2] = " process 2 ";
 arg_Ptr[3] = NULL;
 execv(/home/linuxhint/Documents/child.bin", arg_Ptr);
 }

 else
 {
 while (1){
 printf ("
Write by parent process \n");
 sleep(3);}
 }
 }

এভাবে, সিস্টেমে একটি নতুন প্রসেস তৈরি করে ডুপ্লিকেট করা হয় যা execv() ফাংশন পরবর্তীতে "child.bin" এক্সিকিউটেবল থেকে প্রসেস দিয়ে প্রতিস্থাপন করে। নিচে এই কোডের কম্পাইলেশন ছবি দেওয়া হলো।

সি প্রোগ্রামিং-এ fork() এবং exec() দক্ষতা অর্জন: প্রসেস তৈরি ও প্রতিস্থাপন ব্যাখ্যা

ছবিতে দেখা যাচ্ছে, fork() ফাংশন একটি নতুন প্রসেস তৈরি করে ডুপ্লিকেট করে, যা execv() ফাংশন পরবর্তীতে "child.bin" এক্সিকিউটেবল দিয়ে প্রতিস্থাপন করে।

উপসংহার

এই লিনাক্সহিন্ট আর্টিকেলে, আমরা আপনাকে দেখিয়েছি কীভাবে fork() এবং execv() ফাংশন ব্যবহার করে লিনাক্সে নতুন প্রসেস খোলা যায়। আমরা এই দুটি ফাংশনের সংক্ষিপ্ত বর্ণনা, তাদের সিনট্যাক্স, এবং ইনপুট ও আউটপুট আর্গুমেন্ট দেখিয়েছি। আপনাকে আরও ভালোভাবে বুঝতে সাহায্য করার জন্য, আমরা প্রতিটি ফাংশনের একটি উদাহরণ দিয়েছি যেখানে আমরা তাদের কল পদ্ধতি এবং প্রতিটির দ্বারা সম্পাদিত টাস্ক শিখেছি। এই দুটি ফাংশনকে আলাদা আলাদা দেখার পর, আমরা ব্যাখ্যা করেছি কীভাবে সেগুলো কম্বিনেশন ব্যবহার করে লিনাক্সে নতুন প্রসেস তৈরি করার পদ্ধতি বাস্তবায়ন করা যায়।

  1. HTML শিখুন: ডেভেলপারদের জন্য অপরিহার্য সেরা অনুশীলন, নির্দেশিকা এবং সংস্থান

  2. Tkinter-এ উইন্ডো দেখানো ছাড়া ক্লিপবোর্ড থেকে কপি করা

  3. PHP-এ পারসেন্টেজ ক্যালকুলেট করুন: একটি স্টেপ-বাই-স্টেপ গাইড

  4. CSS-এ max-height প্রপার্টি