SSD Nodes Learn Hosting plans →
নির্দেশিকা Matt Connorদ্বারা Matt Connor · আপডেট করা হয়েছে 2026-08-26

systemd service Type: simple, forking ও notify এর পার্থক্য

আপনার systemd সার্ভিস active দেখালেও daemon বন্ধ হয়ে যাচ্ছে? simple, forking, notify বা oneshot টাইপ সঠিকভাবে বেছে নিন এবং মূল PID ট্র্যাক করার সঠিক পদ্ধতিটি জেনে নিন।

কেন systemd একটি প্রসেস মারা যাওয়ার পরেও ইউনিটকে active হিসেবে দেখায়

একটি systemd service ইউনিট ততক্ষণ active থাকে যতক্ষণ systemd-এর নির্দেশিত মূল প্রসেসটি জীবিত থাকে, এবং [Service] সেকশনের Type= নির্ধারণ করে সেই প্রসেসটি কোনটি। ভুল মান নির্বাচন করলে systemd একটি shell wrapper বা স্বল্পস্থায়ী parent প্রসেসকে পর্যবেক্ষণ করতে থাকে, অথচ আপনার কাঙ্ক্ষিত daemon প্রসেসটি সেই ইউনিটের ভেতরেই মারা যায়। ইউনিটটি আপনাকে সেই প্রসেস সম্পর্কেই তথ্য দিচ্ছে যা তাকে পর্যবেক্ষণ করতে বলা হয়েছে।

এখানে restart policy পরিবর্তন করে কোনো লাভ হবে না। Restart= তখনই কাজ করে যখন মূল প্রসেসটি বন্ধ হয়, তাই মূল PID (process identifier) যদি এমন কিছুর হয় যা এখনো চলছে, তবে Restart=always কখনোই কার্যকর হবে না। প্রথমে Type= ঠিক করুন। মূল প্রসেসটি প্রকৃতপক্ষে বন্ধ হওয়ার পর systemd কী করবে তা একটি আলাদা সিদ্ধান্ত, যা Restart= এবং RestartSec=-এর নির্দেশিকায় আলোচনা করা হয়েছে।

Type= আসলে কী নির্ধারণ করে

প্রতিটি Type= মান একই সাথে দুটি প্রশ্নের উত্তর দেয়। systemd কখন এই ইউনিটটিকে চালু হিসেবে বিবেচনা করবে এবং কোনটি মূল প্রসেস হবে।

প্রথম উত্তরটি অর্ডারিং বা ক্রম নিয়ন্ত্রণ করে। যে ইউনিটটি After=-এ আপনার ইউনিটের নাম উল্লেখ করে, সেটি systemd আপনার ইউনিটকে চালু ঘোষণা করা পর্যন্ত অপেক্ষা করে। একটি Type= যদি খুব দ্রুত "started" রিপোর্ট করে, তবে আপনার সার্ভিস প্রস্তুত হওয়ার আগেই নির্ভরশীল ইউনিটগুলো চলতে শুরু করতে পারে।

দ্বিতীয় উত্তরটি সুপারভিশন বা তদারকি নিয়ন্ত্রণ করে। systemd একটি ইউনিটের মাধ্যমে চালু হওয়া প্রতিটি প্রসেসকে একটি cgroup (control group)-এ রাখে। এটি কার্নেলের একটি ফিচার যা প্রসেসগুলোকে একত্রিত করে, যাতে সেগুলোকে একসাথে সীমাবদ্ধ করা বা বন্ধ করা যায়। এই cgroup-এর মাধ্যমেই systemctl stop ক্লিনআপ সম্পন্ন করে: KillMode= ডিফল্টভাবে control-group থাকে, তাই একটি ইউনিট বন্ধ করলে এর ভেতরের প্রতিটি প্রসেসে সিগন্যাল পাঠানো হয়। মূল PID বা main PID বিষয়টি আরও সুনির্দিষ্ট। এটি সেই একক প্রসেস যার সমাপ্তি ইউনিটটিকে বন্ধ করে দেয় এবং যার এক্সিট স্ট্যাটাস ইউনিটের ফলাফল হিসেবে গণ্য হয়। cgroup-কে মূল PID হিসেবে ভুলভাবে পড়ার কারণেই বিভ্রান্তি তৈরি হয়।

Type=simple যখন বাইনারি চলার আগেই স্টার্ট রিপোর্ট করে

যখন ExecStart= সেট করা থাকে এবং Type= বা BusName= এর কোনোটিই উপস্থিত থাকে না, তখন Type=simple ডিফল্ট হিসেবে কাজ করে। systemd প্রসেসটি তৈরি করে, ইউনিটটিকে সাথে সাথে স্টার্ট হয়েছে বলে ধরে নেয় এবং সেই প্রসেসটিকে মেইন PID হিসেবে গণ্য করে। সার্ভিস বাইনারি কার্যকর হওয়ার আগেই পরবর্তী ইউনিটগুলো কাজ শুরু করে দেয়।

এই শেষ বিষয়টি একটি সাধারণ বিভ্রান্তির কারণ ব্যাখ্যা করে। ExecStart= পাথে কোনো টাইপো থাকলেও স্টার্ট জব সফল হয়, এবং বাইনারি কার্যকর করতে ব্যর্থ হলে কিছুক্ষণ পরেই ত্রুটি দেখা দেয়। systemd এই ক্ষেত্রটিকে exit code 203 দিয়ে রেকর্ড করে, যা তাদের নিজস্ব টেবিলে EXEC নামে পরিচিত এবং সার্ভিস বাইনারি কার্যকর করতে ব্যর্থ হওয়া হিসেবে সংজ্ঞায়িত। তাই systemctl start কোনো ত্রুটি ছাড়া ফিরে আসা মানেই এই নয় যে আপনার বাইনারিটি বিদ্যমান।

যেসব প্রোগ্রাম ফোরগ্রাউন্ডে থাকে এবং কখনোই ব্যাকগ্রাউন্ডে যায় না, সেগুলোর জন্য simple ব্যবহার করুন। আধুনিক ডেমনের বেশিরভাগ এবং আপনার নিজের লেখা প্রায় যেকোনো প্রোগ্রামের ক্ষেত্রে এটি প্রযোজ্য।

Type=exec প্রোগ্রামটি চালু হওয়া পর্যন্ত অপেক্ষা করে

Type=exec হলো simple-এর একটি উন্নত সংস্করণ। systemd তখনই একটি ইউনিটকে চালু হিসেবে গণ্য করে যখন fork এবং binary-এর execution উভয়ই সফল হয়। কোনো binary অনুপস্থিত থাকলে বা কোনো User= সমাধান করা না গেলে, তা এখন নীরবে ব্যর্থ হওয়ার পরিবর্তে সরাসরি start job-কে ব্যর্থ করে দেয়।

Type=exec প্রথম systemd 240 সংস্করণে যুক্ত করা হয়েছিল, তাই বর্তমানে প্রচলিত প্রতিটি সার্ভার ডিস্ট্রিবিউশনে এটি রয়েছে। আগস্ট 2026 অনুযায়ী, Ubuntu 24.04-এ systemd 255 এবং Debian 13-এ systemd 257 ব্যবহৃত হচ্ছে। আপনার সিস্টেমের সংস্করণটি systemctl --version দিয়ে যাচাই করুন।

এর বিনিময়ে শুরুতে একটি অতিরিক্ত synchronisation ধাপ সম্পন্ন করতে হয়। তবে এর সুবিধা হলো, systemctl start থেকে একটি সঠিক exit status পাওয়া যায়। foreground-এ চলা প্রোগ্রামের ক্ষেত্রে simple-এর পরিবর্তে exec ব্যবহার করা শ্রেয়।

Type=forking এবং মূল PID হারিয়ে যাওয়ার কারণ

Type=forking systemd-কে জানায় যে ExecStart=-এ থাকা প্রসেসটি একটি চাইল্ড প্রসেস তৈরি করে নিজে বন্ধ হয়ে যাবে। systemd সেই প্রথম প্রসেসটির বন্ধ হওয়ার জন্য অপেক্ষা করে এবং সেটি বন্ধ হওয়ার পরই ইউনিটটিকে 'started' হিসেবে গণ্য করে। পেছনে রয়ে যাওয়া চাইল্ড প্রসেসটিই হলো মূল daemon। এই অভ্যাসটি SysV যুগের, যখন init script শেষ হওয়ার পর daemon-কে পর্যবেক্ষণ করার মতো কিছু ছিল না এবং একটি PID ফাইলই ছিল একমাত্র প্রমাণ যে কী চলছে। এই সীমাবদ্ধতাটিই কেন systemd init script-কে প্রতিস্থাপন করেছে তার মূল কারণগুলোর একটি।

এর প্রধান সমস্যা হলো পরিচয় শনাক্ত করা। systemd যে প্রসেসটি চালু করেছিল তা এখন আর নেই, তাই systemd-কে খুঁজে বের করতে হয় যে বেঁচে থাকা প্রসেসগুলোর মধ্যে কোনটি মূল। PIDFile=-কে সেই ফাইলের পাথে সেট করুন যা daemon তৈরি করে, সাধারণত এটি /run-এর অধীনে থাকে। systemd সেখান থেকে PID পড়ে নেয়। systemd এটিও যাচাই করে যে ওই ফাইলে থাকা PID-টি এই সার্ভিসেরই কোনো প্রসেসের কি না। ফলে ভুল কোনো প্রসেসের নাম থাকা পুরনো ফাইলকে বিশ্বাস না করে বাতিল করে দেওয়া হয়।

PIDFile= ছাড়া GuessMainPID= কার্যকর হয় এবং এর ডিফল্ট মান হলো yes। এই অনুমানটি কেবল তখনই নির্ভরযোগ্য যখন সার্ভিসটি একটি মাত্র প্রসেসে স্থির থাকে। ম্যানুয়াল অনুযায়ী সীমাবদ্ধতাটি স্পষ্ট: যদি daemon একাধিক প্রসেস নিয়ে গঠিত হয়, তবে এই অনুমান ভুল হতে পারে এবং ফেইলুর ডিটেকশন কাজ করা বন্ধ করে দেয়। একটি ইউনিটের মূল PID 0 হয়ে যেতে পারে, যার অর্থ হলো systemd-এর পর্যবেক্ষণ করার মতো কিছুই নেই।

অধিকাংশ daemon যা fork করে, সেগুলোর foreground-এ থাকার জন্য একটি সুইচ থাকে। Type=exec-এর সাথে সেই সুইচটি ব্যবহার করুন এবং PIDFile= লাইনটি মুছে ফেলুন। যত কম জটিলতা থাকবে, PID হারিয়ে যাওয়ার সম্ভাবনা তত কম হবে।

Type=oneshot যে কাজ শেষ হয়ে যায় তার জন্য

Type=oneshot আশা করে যে প্রসেসটি চলবে এবং তারপর বন্ধ হয়ে যাবে। systemd তখনই ইউনিটটিকে 'started' হিসেবে গণ্য করে যখন প্রসেসটি সফলভাবে সম্পন্ন হয়, যা oneshot-কে এমন কাজের জন্য উপযুক্ত করে তোলে যার জন্য অন্য কোনো ইউনিট অপেক্ষা করে থাকে। যখন কোনো ইউনিটে Type= বা ExecStart= উল্লেখ করা থাকে না, তখন এটিই ডিফল্ট হিসেবে কাজ করে।

oneshot-এর ক্ষেত্রে দুটি আচরণ বিশেষভাবে প্রযোজ্য। এটিই একমাত্র টাইপ যা একাধিক ExecStart= লাইন গ্রহণ করতে পারে এবং সেই লাইনগুলো ক্রমানুসারে কার্যকর হয়। এর স্টার্ট টাইমআউট ডিফল্টভাবে নিষ্ক্রিয় থাকে, তাই কোনো oneshot যদি আটকে যায় তবে তা অনির্দিষ্টকাল ধরে অপেক্ষা করবে, যদি না আপনি নিজে TimeoutStartSec= সেট করেন।

প্রসেসটি বন্ধ হয়ে যাওয়ার পর, ইউনিটটি inactive অবস্থায় ফিরে আসে। RemainAfterExit=yes এটিকে active অবস্থায় রাখে যেখানে কোনো প্রসেস চলমান থাকে না। এই পৃষ্ঠার শুরুতে উল্লিখিত সমস্যার এটিই হলো পরিকল্পিত সংস্করণ, এবং যখন ইউনিটের কাজ কোনো কিছু চলমান রাখা নয় বরং কোনো অবস্থা (state) তৈরি করে রাখা হয়, তখন এটি সঠিক পদ্ধতি: যেমন ফায়ারওয়াল রুলসেট লোড করা বা কন্টেইনার স্ট্যাক চালু করা। এটি একটি Docker Compose স্ট্যাক যা রিবুটের পর ফিরে আসে-এর পেছনের প্যাটার্ন, যেখানে ইউনিটটি compose কমান্ড চালায়, শেষ হয়ে যায় এবং active থাকে কারণ এটি যে কন্টেইনারগুলো চালু করেছে সেগুলো ইউনিটের চেয়েও বেশি সময় ধরে চলে। একটি oneshot ইউনিট হলো সেটি যা কোনো শিডিউল ট্রিগার করে, যা cron-এর পরিবর্তে systemd টাইমার ব্যবহার করে কোনো জব চালানো-এর অন্য অর্ধেক।

Type=notify lets the service say when it is ready

Type=notify moves the decision to the service. systemd holds the start job open until the process sends READY=1 over a Unix socket whose path it receives in the NOTIFY_SOCKET environment variable. The C interface is sd_notify(3), and many servers support it already.

This is the accurate answer to the question "is it started". simple and exec report started before the service has read its configuration or opened its listening socket, so a dependent unit can start too early and fail its first connection. notify reports started at the moment the service itself says it is ready.

systemd accepts that message from the main process only, which is what NotifyAccess=main means, and Type=notify implies it. If the message comes from a child or a helper, set NotifyAccess=all. A shell script can call systemd-notify --ready, but that runs as a separate short lived process, so it needs NotifyAccess=all and systemd may not be able to attribute a message whose sender has already exited. A service that speaks the protocol itself is more reliable.

Two related settings are worth knowing. Type=notify-reload, available since systemd 253, extends the same handshake to reloads, so systemctl reload returns when the service reports the reload is finished instead of returning when the signal was sent. WatchdogSec= asks a notifying service to send a keep-alive message on an interval, and systemd treats a missed deadline as a failure.

Type=dbus এবং Type=idle

Type=dbus ততক্ষণ অপেক্ষা করে যতক্ষণ না সার্ভিসটি D-Bus-এ একটি নাম গ্রহণ করে, যা হলো এমন একটি মেসেজ বাস যা সিস্টেম এবং ডেস্কটপ সার্ভিসগুলো একে অপরের সাথে যোগাযোগের জন্য ব্যবহার করে। এর জন্য BusName= প্রয়োজন এবং BusName= সেট করা মাত্রই এটি ডিফল্ট হিসেবে কাজ করে। এটি শুধুমাত্র সেই সব সার্ভিসের জন্য ব্যবহার করুন যা প্রকৃতপক্ষে একটি বাস নাম রেজিস্টার করে।

Type=idle অনেকটা simple-এর মতো কাজ করে, কিন্তু এটি প্রোগ্রাম চালানোকে ততক্ষণ বিলম্বিত করে যতক্ষণ না কিউতে থাকা জবগুলো সম্পন্ন হয়, যার সর্বোচ্চ সময়সীমা পাঁচ সেকেন্ড। এটি এই কারণে রাখা হয়েছে যাতে বুট হওয়ার সময় কনসোল আউটপুট স্ট্যাটাস মেসেজের সাথে মিশে না যায়। এটি কোনো অর্ডারিং টুল নয় এবং সাধারণ কোনো সার্ভিসে এটি ব্যবহার করা উচিত নয়।

কেন একটি র‍্যাপার স্ক্রিপ্ট systemd-কে ভুল PID-তে আটকে রাখে

এখানে সেই গঠনটি দেওয়া হলো যা মূল সমস্যার সৃষ্টি করে।

[Service]
Type=simple
ExecStart=/opt/app/run.sh
#!/bin/bash
export APP_ENV=production
/opt/app/bin/server --config /etc/app.yaml &
/opt/app/bin/exporter --port 9101

systemd শেলটিকে মূল PID হিসেবে রেকর্ড করে। যখন exporter ফোরগ্রাউন্ডে চলে, তখন শেলটি জীবিত থাকে। যদি server মারা যায়, তবে শেলটি তা বুঝতে পারে না, তাই মূল PID তখনও জীবিত থাকে, ইউনিটটি তখনও active অবস্থায় থাকে এবং Restart=-এর কাজ করার মতো কিছু থাকে না। উভয় প্রসেসই পুরো সময় ইউনিটের cgroup-এ অবস্থান করে, তাই systemctl stop তখনও সঠিকভাবে ক্লিনআপ করতে পারে। এখানে সুপারভিশন বা তত্ত্বাবধান ব্যবস্থাটি ভেঙে পড়ে, ক্লিনআপ নয়।

সমাধানটি নির্ভর করে ইউনিটে আসলে কয়টি দীর্ঘস্থায়ী প্রসেস চলছে তার ওপর।

যদি একটি মাত্র প্রসেস থাকে, তবে শেলটিকে সেটি দিয়ে প্রতিস্থাপন করুন।

#!/bin/bash
export APP_ENV=production
exec /opt/app/bin/server --config /etc/app.yaml

exec শেলটিকে নির্দিষ্ট প্রোগ্রাম দিয়ে প্রতিস্থাপন করে এবং একই PID বজায় রাখে, ফলে systemd যে PID রেকর্ড করেছিল তা এখন ডেমনের অধীনে থাকে। আরও ভালো হয় যদি র‍্যাপারটি সরিয়ে ফেলেন। Environment= এবং EnvironmentFile= ভেরিয়েবলগুলো বহন করে এবং ExecStartPre= সেটআপ ধাপটি সম্পন্ন করে, ফলে systemd সরাসরি ডেমোনটি চালু করতে পারে এবং শুরু থেকেই এর PID জানতে পারে।

যদি দুটি প্রসেস থাকে, তবে কোনো একক PID পুরো ইউনিটকে প্রতিনিধিত্ব করতে পারে না। সেগুলোকে দুটি আলাদা ইউনিটে ভাগ করুন এবং After=Wants= ব্যবহার করে সেগুলোর ক্রম নির্ধারণ করুন। প্রতি প্রসেসের জন্য একটি ইউনিট হলো সেই ব্যবস্থা যা systemd ভালোভাবে তত্ত্বাবধান করতে পারে এবং এটিই একমাত্র উপায় যার মাধ্যমে প্রতিটি প্রসেস নিজস্ব রিস্টার্ট আচরণ পায়।

ExitType=cgroup পরিবর্তনের কারণ

ExitType= systemd 250 সংস্করণে যুক্ত করা হয়েছে। এর ডিফল্ট মান হলো main: যখন মূল প্রসেসটি বন্ধ হয়ে যায়, তখন ইউনিটটিকেও বন্ধ হিসেবে গণ্য করা হয়। ExitType=cgroup ব্যবহার করলে, যতক্ষণ পর্যন্ত cgroup-এর ভেতরে যেকোনো একটি প্রসেস সচল থাকে, ততক্ষণ ইউনিটটিকে চলমান হিসেবে বিবেচনা করা হয়।

[Service]
Type=simple
ExitType=cgroup
ExecStart=/opt/app/launcher

এটি একটি নির্দিষ্ট সমস্যার সমাধান করে। এমন কোনো লঞ্চার যা মূল কাজ শুরু করার পর নিজে বন্ধ হয়ে যায়, সেটি ExitType=main-এর অধীনে থাকলে systemd ইউনিটটিকে বন্ধ মনে করে এবং বাকি প্রসেসগুলোকে কিল (kill) করে দেয়। ExitType=cgroup ব্যবহার করলে ইউনিটটি পুরো গ্রুপটিকে অনুসরণ করে।

এটি কী সমাধান করে না, সে বিষয়ে স্পষ্ট ধারণা থাকা প্রয়োজন। ExitType=cgroup ইউনিটটিকে ততক্ষণ সক্রিয় রাখে যতক্ষণ অন্তত একটি প্রসেস জীবিত থাকে, তাই দুটি ডেমনের (daemon) একটি মারা গেলেও ইউনিটটি সক্রিয় থেকে যায়। এটি শুধুমাত্র লঞ্চারের সমস্যাটি সমাধান করে। এটি একটি ইউনিটকে একাধিক স্বাধীন প্রসেসের সুপারভাইজারে রূপান্তর করে না। এছাড়া ExitType=-কে Type=oneshot-এর সাথে ব্যবহার করা যায় না।

cgroup-এর মধ্যেই রিসোর্স অ্যাকাউন্টিং সম্পন্ন হয়, তাই MemoryMax= এবং CPUQuota=-এর মতো সীমাবদ্ধতাগুলো ইউনিট দ্বারা তৈরি প্রতিটি প্রসেসের ওপর প্রযোজ্য হয়, মূল PID সম্পর্কে Type= যা-ই বলুক না কেন। এই বিষয়টি systemd ব্যবহার করে সার্ভিসের মেমরি ও CPU সীমাবদ্ধ করা অংশে বিস্তারিত আলোচনা করা হয়েছে।

systemd আসলে কোন প্রসেসটি পর্যবেক্ষণ করছে তা খুঁজে বের করার উপায়

ডিবাগিংয়ের সময় আপনার ইউনিটে ধারাবাহিকভাবে নিচের ধাপগুলো অনুসরণ করুন। প্রথমে দেখুন systemd কী লোড করেছে, তারপর দেখুন এটি কী ট্র্যাক করছে এবং সবশেষে প্রসেস টেবিলের সাথে তা মিলিয়ে দেখুন।

systemctl cat app.service
systemctl show -p Type,ExitType,GuessMainPID,PIDFile,MainPID,RemainAfterExit app.service

systemctl cat কমান্ডটি ইউনিট ফাইল এবং এর সাথে প্রযোজ্য প্রতিটি ড্রপ-ইন ফাইল প্রদর্শন করে, ফলে আপনি সেই ফাইলটিই পড়তে পারেন যা systemd লোড করেছে, আপনার মনে রাখা ফাইলটি নয়। systemctl show কমান্ডটি ডিফল্ট মানসহ কার্যকর মানগুলো প্রদর্শন করে, যা আপনি হয়তো কোথাও লেখেননি। পরবর্তী ধাপে যাওয়ার আগে MainPID-এর মানটি খেয়াল করুন।

systemd-cgls --unit=app.service
ps -o pid,ppid,stat,etime,args -p "$(systemctl show -p MainPID --value app.service)"

systemd-cgls কমান্ডটি ইউনিটের cgroup-এ থাকা প্রতিটি প্রসেসের তালিকা দেখায়। ps লাইনটি সেই একক প্রসেসটিকে বর্ণনা করে যা systemd পর্যবেক্ষণ করছে। এই দুটি একসাথে পড়ুন। MainPID যদি 0 হয়, তবে এর অর্থ systemd-এর পর্যবেক্ষণ করার মতো কোনো প্রসেস নেই। যদি cgroup-এ আপনার ডেমনের পাশাপাশি MainPID কোনো শেল নির্দেশ করে, তবে এটি উপরের উল্লিখিত র‍্যাপার (wrapper) কেস। প্রত্যাশার চেয়ে বেশি প্রসেস cgroup-এ থাকলে বুঝতে হবে সেখানে কোনো লঞ্চার বা ফর্কিং ডেমনের সংশ্লিষ্টতা রয়েছে।

systemctl status app.service
journalctl -u app.service -b

systemctl status কমান্ডটি স্টেট লাইন এবং cgroup ট্রি একসাথে প্রদর্শন করে, তাই এটি প্রায়শই উভয় প্রশ্নের উত্তর একসাথে দিয়ে দেয়। -b ফ্ল্যাগসহ journalctl -u কমান্ডটি বর্তমান বুটের জন্য সীমাবদ্ধ থেকে systemd-এর রেকর্ড করা স্টার্ট এবং স্টপ ইভেন্টগুলো দেখায়, সাথে থাকে এর দেখা এক্সিট কোডগুলো। যদি ডেমোনটি জার্নালের পরিবর্তে নিজস্ব লগ ফাইলে লেখে, তবে সেই ফাইলটিও পড়ুন, কারণ systemd কেবল তার কাছে পৌঁছানো তথ্যই রেকর্ড করতে পারে।

আপনি যখন Type= পরিবর্তন করবেন, তখন রিলোড এবং রিস্টার্ট করুন।

systemd-analyze verify /etc/systemd/system/app.service
sudo systemctl daemon-reload
sudo systemctl restart app.service

systemd-analyze verify কমান্ডটি ফাইলটি পার্স করে এবং যেসব সেটিংস গ্রহণ করা সম্ভব নয় তা রিপোর্ট করে। daemon-reload কমান্ডটি systemd-কে ডিস্ক থেকে ইউনিট ফাইলগুলো পুনরায় পড়তে বাধ্য করে। পরিবর্তিত Type= ইতিমধ্যে চলমান ইউনিটে কার্যকর হয় না, তাই রিস্টার্ট করা বাধ্যতামূলক, ঐচ্ছিক নয়।

এরপর পরিবর্তনটি পরীক্ষা করুন। systemd-cgls থেকে আপনার কাঙ্ক্ষিত প্রসেসের PID নিন এবং সেটিকে কিল করুন। এরপরই systemctl is-active app.service চালান। যদি Type= সঠিক হয়, তবে ইউনিটটি সক্রিয় অবস্থা থেকে বেরিয়ে যাবে। যদি এটি সক্রিয়ই থাকে, তবে বুঝতে হবে systemd অন্য কোনো কিছু পর্যবেক্ষণ করছে।

কোন systemd service Type= ব্যবহার করা উচিত

  • যে প্রোগ্রামটি ফোরগ্রাউন্ডে চালু থাকে: Type=exec
  • যে প্রোগ্রামটি readiness notification সমর্থন করে: Type=notify, এবং যদি এটি রিলোড নিশ্চিত করে তবে notify-reload
  • যে ডেমনটি ব্যাকগ্রাউন্ডে চলে যেতে চায়: Type=forking এবং সাথে PIDFile=, অথবা এর ফোরগ্রাউন্ড সুইচসহ Type=exec
  • যে স্ক্রিপ্টটি কাজ শেষ করে বেরিয়ে যায়: Type=oneshot, এবং যদি উদ্দেশ্য স্টেট ধরে রাখা হয় তবে RemainAfterExit=yes
  • যে লঞ্চারটি বেরিয়ে যায় কিন্তু এর চাইল্ড প্রসেসগুলো চলতে থাকে: Type=simple এবং সাথে ExitType=cgroup

কোনো থার্ড-পার্টি ডেমনের জন্য কোনটি প্রয়োজন তা নিশ্চিত না হলে, প্রথমে এর প্যাকেজ করা unit file পড়ুন। ডিস্ট্রিবিউশনের সাথে আসা কোনো ইউনিটে systemctl cat চালালে আপস্ট্রিম থেকে নির্ধারিত Type= দেখা যায়, এবং এই পছন্দটি আপনার চেয়েও বেশি মানুষের দ্বারা পরীক্ষিত।

FAQ

প্রসেস মারা যাওয়ার পরেও আমার systemd unit কেন active থাকে?

কারণ systemd যে প্রসেসটিকে মূল প্রসেস হিসেবে গণ্য করে, সেটি এখনো সচল। systemd প্রতিটি ইউনিটের cgroup-এর সব প্রসেস না দেখে, Type= অনুযায়ী প্রতিটি সার্ভিসের জন্য একটি নির্দিষ্ট PID পর্যবেক্ষণ করে। Type=simple দিয়ে শুরু করা কোনো wrapper script সাধারণত এর কারণ হয়: শেলটিই মূল PID হিসেবে থাকে, তাই শেল যে daemon-টিকে ব্যাকগ্রাউন্ডে চালু করে সেটি বন্ধ হয়ে গেলেও ইউনিটটি active থেকে যায়। systemctl show -p MainPID app.service চালান, তারপর systemd-cgls --unit=app.service দিয়ে ইউনিটের cgroup তালিকাভুক্ত করুন এবং উভয়টির তুলনা করুন।

Type=simple এবং Type=exec এর মধ্যে পার্থক্য কী?

Type=simple ইউনিটটিকে তখনই চালু হয়েছে বলে ধরে নেয় যখন systemd প্রসেসটি তৈরি করে, বাইনারিটি কার্যকর হওয়ার আগেই। তাই ExecStart=-এ ভুল পাথ থাকলে সেটি সফলভাবে চালু হয়েছে বলে দেখায় এবং পরে ব্যর্থ হয়। Type=exec কার্যকর হওয়া পর্যন্ত অপেক্ষা করে, ফলে ব্যর্থতা সরাসরি স্টার্ট জবের মাধ্যমেই জানা যায়। উভয়ই একই প্রসেসকে মূল PID হিসেবে গণ্য করে। Type=exec ব্যবহারের জন্য systemd 240 বা তার নতুন সংস্করণ প্রয়োজন।

Type=forking এর সাথে কি এখনো PIDFile= প্রয়োজন?

হ্যাঁ, যখনই daemon কোনো ফাইল লেখে তখনই এটি প্রয়োজন। এটি ছাড়া systemd GuessMainPID=-এ ফিরে যায়, যা একটি অনুমান মাত্র এবং শুধুমাত্র সেই সব সার্ভিসের জন্য নির্ভরযোগ্য যা একটি মাত্র প্রসেসে স্থির থাকে। যখন এই অনুমান ভুল হয় বা অসম্ভব হয়, তখন সেই ইউনিটের জন্য ব্যর্থতা শনাক্তকরণ এবং স্বয়ংক্রিয়ভাবে পুনরায় চালু হওয়ার প্রক্রিয়া কাজ করা বন্ধ করে দেয়। PIDFile=-কে সেই সঠিক পাথে নির্দেশ করুন যেখানে daemon ফাইলটি লেখে, যা সাধারণত /run-এর অধীনে থাকে।

কখন RemainAfterExit=yes ব্যবহার করা উচিত?

যখন ইউনিটের উদ্দেশ্য কোনো প্রসেস চালু রাখা নয়, বরং সিস্টেমের অবস্থা পরিবর্তন করা হয়। একটি Type=oneshot ইউনিট যা firewall রুল লোড করে বা কন্টেইনার স্ট্যাক চালু করে, কাজ শেষ হওয়ার সাথে সাথেই তা বন্ধ হয়ে যায়। RemainAfterExit=yes ছাড়া ইউনিটটি inactive হয়ে যায়, যার ফলে systemctl stop-এর কাছে থামানোর মতো কিছু থাকে না এবং ExecStop= ক্লিনআপ চালানোর কোনো উপায় থাকে না। এটি ব্যবহার করলে কোনো প্রসেস ছাড়াই ইউনিটটি active থাকে, যা এখানে উদ্দেশ্যপ্রণোদিত।

Type= পরিবর্তন করলে কি daemon-reload প্রয়োজন?

হ্যাঁ, এবং সেই সাথে ইউনিটটি রিস্টার্ট করাও প্রয়োজন। systemctl daemon-reload ডিস্কে থাকা ইউনিট ফাইলগুলোকে systemd-কে পুনরায় পড়তে বাধ্য করে, কিন্তু একটি চলমান ইনস্ট্যান্স সেই Type=-ই বজায় রাখে যা দিয়ে সেটি শুরু হয়েছিল। পরীক্ষা করার আগে sudo systemctl daemon-reload এবং তারপর sudo systemctl restart app.service চালান, অন্যথায় আপনি এখনো পুরনো সুপারভিশন আচরণই পর্যবেক্ষণ করবেন।