تاريخ systemd: لماذا انتصر على SysV init؟
اكتشف ما عجز SysV init عن فعله، وما سبق أن جربه Upstart وlaunchd، ولماذا انتقلت التوزيعات إلى systemd خلال أربع سنوات، وأي الاعتراضات كانت محقّة.
لماذا انتصر systemd
بدأ تاريخ systemd بمشكلتين لم يتمكن SysV init من حلهما. لم يكن لدى SysV init (نظام System V init، وهو نظام بدء التشغيل الذي ورثه Linux من AT&T Unix) طريقة لوصف اعتماد خدمة على خدمات أخرى، ولا طريقة لمعرفة العمليات التي تنتمي إلى خدمة بعد تشغيلها. عالج systemd المشكلتين باستخدام ميزات في النواة لا يمكن لبرنامج shell النصي الوصول إليها: مجموعات التحكم لتتبّع العمليات، ومقابس الاستماع المفتوحة مسبقاً لترتيب بدء الخدمات. أما بقية القصة فتتعلق بانتشار هذين الحلين إلى بقية مساحة المستخدم، حيث بدأت الاعتراضات، وكان عدد من هذه الاعتراضات محقاً.
ما الذي كان يفعله SysV init فعلياً
في نظام SysV، كان PID 1 (معرّف العملية 1، وهي أول عملية تبدأها النواة) يقرأ /etc/inittab، ويختار مستوى تشغيل، ثم يشغّل البرامج النصية الخاصة بذلك المستوى. كانت البرامج النصية موجودة في /etc/init.d/. وكانت الروابط الرمزية في /etc/rc3.d/ تحدد البرامج التي تُشغّل وترتيب تشغيلها، لذلك كان /etc/rc3.d/S20nginx يشير إلى /etc/init.d/nginx ويُستدعى بالوسيط start.
#!/bin/sh
### BEGIN INIT INFO
# Provides: nginx
# Required-Start: $local_fs $remote_fs $network $syslog
# Required-Stop: $local_fs $remote_fs $network $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
### END INIT INFO
case "$1" in
start)
start-stop-daemon --start --quiet --pidfile /run/nginx.pid \
--exec /usr/sbin/nginx
;;
stop)
start-stop-daemon --stop --quiet --retry TERM/30/KILL/5 \
--pidfile /run/nginx.pid
;;
esacإن 20 في S20nginx هو موضع، وليس تبعية. وهو يعني أن هذا البرنامج النصي يُشغّل بعد S19 وقبل S21. لكنه لا يوضح السبب، لذلك لا يمكن لأي شيء التحقق من ذلك، ولا يمكن تشغيل برنامجين نصيين غير مرتبطين بأمان في الوقت نفسه من دون أن يقرر مسؤول بشري أن ذلك آمن.
كان البرنامج rc يشغّل كل برنامج نصي بالتتابع وينتظر انتهاءه. فإذا توقف برنامج نصي لمدة ثلاثين ثانية أثناء انتظار عنوان شبكة، توقفت عملية الإقلاع كاملة لمدة ثلاثين ثانية، حتى بالنسبة إلى الخدمات التي لا تستخدم الشبكة مطلقاً.
كان ترويس LSB (Linux Standard Base) في أعلى ذلك البرنامج النصي محاولة لمعالجة المشكلة من داخل هذا النظام. في Debian 6.0 عام 2011، أصبح insserv الإعداد الافتراضي: إذ كان يقرأ Required-Start من كل برنامج نصي، وينشئ رسماً بيانياً للتبعيات، ويعيد ترقيم الروابط الرمزية. عندها كان بإمكان Debian تشغيل البرامج النصية المستقلة في الوقت نفسه باستخدام startpar. ساعد ذلك، لكنه لم يعالج المشكلة الأعمق. فالتبعية كانت لا تزال مرتبطة بانتهاء برنامج نصي. يعني إرجاع S20nginx للقيمة 0 أن دالة shell قد عادت. ولا يعني أن nginx يستقبل الاتصالات.
الأمور الخمسة التي لم يكن أي init script قادراً على إصلاحها
- بدء التشغيل بالتوازي. يفرض ترتيب أسماء الملفات ترتيباً كلياً على كل خدمة في الجهاز، لذلك يستغرق الإقلاع مدة تساوي مجموع مدد مكوناته.
- الجاهزية. ينتهي start script عندما ينشئ daemon عملية فرعية، وليس عندما يصبح daemon قادراً على معالجة طلب. لذلك يبدأ script التالي غالباً في وقت مبكر جداً.
- الإشراف. ينشئ daemon عمليتين فرعيتين متتاليتين ثم تنتهي العملية الأب. يؤدي ذلك إلى فصل daemon عن الطرفية وإعادة إسناده إلى PID 1. يرى init انتهاء عملية فرعية، ولا يملك رابطاً موثوقاً بالعملية التي استمرت.
- البدء عند الطلب. كان inetd، وهو الخادم الفائق للإنترنت، قادراً على تشغيل daemon عند وصول اتصال. لكنه كان نظاماً منفصلاً بملف إعداد خاص به، ولم يكن يفعل شيئاً لمعالجة ترتيب بقية الخدمات عند الإقلاع.
- التحكم في الموارد. لم يكن أي init script قادراً على وضع حد لذاكرة خدمة أو لحصتها من CPU. كان
ulimitيطبَّق على عملية واحدة، بينما كانniceيقتصر على المجدول. لذلك كانت عملية فرعية خارجة عن السيطرة لخدمة ما تبدو مثل أي عملية أخرى على الجهاز.
كانت فجوة الإشراف هي المشكلة الأكثر تأثيراً في العمل اليومي. وكان ملف PID هو الحل البديل: يكتب daemon معرّف عمليته إلى /run/nginx.pid، ثم تقرأ دالة الإيقاف الملف. إذا أُنهِيت عملية daemon بالقوة، ظل الملف موجوداً. ثم تعيد النواة استخدام ذلك الرقم لشيء آخر، ويرسل start-stop-daemon --stop --pidfile إشارة إلى العملية التي أصبح الرقم يعرّفها. هكذا يمكن لملف PID قديم أن يجعل init script يقتل العملية الخطأ.
حلّ launchd مشكلة المقابس أولاً
أطلقت Apple الإصدار launchd في Mac OS X 10.4 عام 2005، وكتبه Dave Zarzycki. واستبدلت عملية واحدة كلّاً من init وrc وxinetd وcrond وwatchdogd.
كانت فكرة تفعيل المقابس جديرة بالاقتباس. ينشئ launchd كل مقبس في وضع الاستماع أولاً، ثم يبدأ تشغيل الخدمات الخفية. عندما يتصل عميل بخدمة خفية لم تبدأ بعد، لا يتلقى رفضاً للاتصال، لأن النواة تحتفظ بالاتصال في قائمة الانتظار الخاصة بذلك المقبس حتى تستدعي الخدمة الخفية accept(). لم يعد ترتيب بدء خدمتين خفيتين أمراً يحدده المسؤول يدوياً. يتولى المقبس ذلك.
بُني launchd على Mach IPC (الاتصال بين العمليات)، وهي آلية تابعة لنواة Apple XNU ولا يوجد لها مكافئ في Linux. لذلك لم يكن نقل الشيفرة عملياً قط. لكن الفكرة انتقلت رغم ذلك.
جعل Upstart الأحداث وحدة العمل
أصدرت Canonical نظام Upstart، الذي كتبه Scott James Remnant، ضمن Ubuntu 6.10 في October 2006. استخدمته الإصدارات Fedora 9 إلى Fedora 14، وكذلك RHEL 6 وChrome OS. استبدل مستويات التشغيل بالأحداث، وأوضحت كل مهمة الأحداث التي يجب أن تبدأها وتوقفها.
# /etc/init/example.conf
start on filesystem and net-device-up IFACE!=lo
stop on runlevel [!2345]
respawn
respawn limit 10 5
exec /usr/local/bin/exampledظهرت مشكلتان مع ازدياد عدد المهام. تتعلق الأولى باتجاه الاعتماديات. تقول المهمة: «ابدأني عند حدوث هذا»، ولذلك توجد معلومات الاعتماديات في الملف الخطأ: تعرف الخدمة ما تحتاج إليه، لكنها لا يمكن أن تعرف من سيحتاج إليها في العام المقبل. كانت إضافة خدمة غالباً تتطلب تعديل مهمة موجودة لكي تُصدر حدثاً جديداً.
تتعلق الثانية بالتتبع. كان Upstart يتتبع daemon متشعباً عبر عدّ استدعاءات fork() باستخدام ptrace، ويمكنك ضبط ذلك على expect fork أو expect daemon. إذا كان عدد التفرعات غير صحيح، فسيراقب Upstart عملية خرجت بالفعل، أو ينتظر تفرعاً حدث بالفعل. يتمثل العَرَض في تعليق initctl start من دون خطأ، ولا يتيح لك ملف المهمة تفسير ذلك.
كما تطلب Upstart من المساهمين توقيع اتفاقية مساهمي Canonical. لم يكن ذلك خللاً هندسياً، لكنه أثّر فعلاً في هوية الأشخاص الذين عملوا عليه.
إعادة التفكير في PID 1، أبريل 2010
في 30 أبريل 2010، نشر Lennart Poettering مقالاً بعنوان "إعادة التفكير في PID 1". عمل Kay Sievers معه في المشروع. استند الطرح إلى أربعة أجزاء.
- ابدأ عدداً أقل من الخدمات. يمكن أن تنتظر خدمات كثيرة حتى يطلبها شيء فعلياً.
- لا تُصرّح بترتيب التشغيل عندما يمكن لمقبس أن يحدده. افتح جميع المقابس في تمريرة واحدة، ثم ابدأ كل شيء في الوقت نفسه.
- تتبّع العمليات باستخدام مجموعات التحكم بدلاً من ملفات PID.
- صِف الخدمة في ملف تعريفي، بحيث يعمل الوصف نفسه على كل توزيعة.
صدر الإصدار الأول في العام نفسه. أطلقت Fedora 14 systemd كخيار في نوفمبر 2010، وجعلته Fedora 15 الخيار الافتراضي في مايو 2011.
لماذا جعلت cgroups الإشراف موثوقاً
إن cgroup (مجموعة التحكم) ميزة في النواة لتجميع العمليات، وقد أُدمجت في Linux 2.6.24 عام 2008. يضع systemd كل خدمة في cgroup خاصة بها. ترث العملية الابنة cgroup الخاصة بالعملية الأب، ولا يمكن لعملية غير ذات امتيازات أن تنقل نفسها خارجها. لذلك لا يخفي التفريع المزدوج أي شيء: يحتفظ PID 1 بالمجموعة الدقيقة للعمليات التابعة للوحدة في جميع الأوقات. يعني إيقاف خدمة قتل كل ما ينتمي إلى cgroup الخاصة بها، وهذا ما ينفذه KillMode=control-group الافتراضي.
يعرض systemctl status تلك المجموعة:
● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled)
Active: active (running) since Tue 2026-08-11 09:14:22 UTC; 3min ago
Main PID: 1042 (nginx)
Tasks: 3 (limit: 4653)
Memory: 6.1M (peak: 7.4M)
CGroup: /system.slice/nginx.service
├─1042 "nginx: master process /usr/sbin/nginx"
├─1043 "nginx: worker process"
└─1044 "nginx: worker process"يمثل ذلك الجزء الإجابة الكاملة عن ملف PID القديم. لا يوجد ملف يمكن أن يصبح قديماً، لأن القائمة هي حالة النواة.
تحمل الشجرة نفسها الحدود أيضاً، لأن cgroups صُممت للمحاسبة قبل أن يستخدمها أحد للتتبع. يتكون كل من MemoryMax= وCPUQuota= وTasksMax= من سطر واحد. يُعد فرض حد أقصى صارم للذاكرة ووحدة المعالجة المركزية على خدمة اليوم ملف drop-in، بينما كان ذلك في 2009 رقعةً لبرنامج shell نصي لم يكتبه أحد.
لماذا انتقلت كل توزيعة بين 2011 و2015
- Fedora 15، مايو 2011.
- openSUSE 12.1، نوفمبر 2011.
- Mageia 2، مايو 2012.
- Arch Linux، كخيار افتراضي للتثبيتات الجديدة منذ أكتوبر 2012.
- RHEL 7، يونيو 2014.
- SLES 12، أكتوبر 2014.
- Debian 8، أبريل 2015.
- Ubuntu 15.04، أبريل 2015.
استمر إصدار RHEL 7 لفترة أطول من الإصدارات الأخرى، لأن CentOS 7 أعاد بناءه، وهناك تعرّف معظم مسؤولي الأنظمة أول مرة إلى ملف وحدة، ضمن مرحلة في القصة الأطول الممتدة من Red Hat Linux عبر CentOS إلى Rocky وAlmaLinux.
كانت الأسباب مملة في معظمها، ولذلك حدث الانتقال بسرعة.
- يعمل ملف وحدة واحد على كل توزيعة، ولذلك بدأت المشاريع المصدرية في شحن ملف
.service، وتوقفت التوزيعات عن صيانة shell script لكل حزمة في كل إصدار. - انتقلت متابعة جلسات سطح المكتب إلى
systemd-logindبعد توقف صيانة ConsoleKit نحو عام 2012. كان GNOME يحتاج إلى logind، ولذلك كان على أي توزيعة لا تستخدم systemd أن تجد بديلاً. وكان ذلك البديل،elogind، هو logind المستخرج من systemd والذي تجري صيانته بصورة مستقلة. - دُمج udev، مدير الأجهزة، في شجرة مصدر systemd في أبريل 2012. وأصبحت التوزيعات التي تشحن udev تتابع مستودع systemd. وأنشأت Gentoo نسخة متفرعة من
eudevاستجابةً لذلك. - جعلت الحاويات تتبّع العمليات بصورة موثوقة وتطبيق الحدود لكل خدمة أكثر أهمية، لأن كليهما من ميزات cgroup. ولا يزال السؤال عن المشرف الذي يملك عملية الحاوية قائماً عند إعادة تشغيل مكدس Docker Compose تلقائياً بعد إعادة التشغيل.
كان قرار Debian هو الأبرز. صوّتت اللجنة الفنية في فبراير 2014، وتعادل التصويت، فأدلى الرئيس Bdale Garbee بالصوت الحاسم لمصلحة systemd. وأعلنت Ubuntu بعد أيام أنها ستتبع Debian بدلاً من الاستمرار في استخدام Upstart. وأنشأت مجموعة من مطوري Debian نسخة متفرعة من التوزيعة باسم Devuan في نوفمبر 2014، وأصدرت Devuan 1.0 في مايو 2017.
الاعتراضات بصياغة منصفة
النطاق. يضم مشروع واحد الآن PID 1، وdaemon السجلات، وإدارة جلسات تسجيل الدخول، ومدير الأجهزة، وdaemon لإعداد الشبكة، ومحلّل DNS (نظام أسماء النطاقات)، وعميل NTP (بروتوكول وقت الشبكة)، ومشغّل حاويات، ومحمّل إقلاع. الدفاع المعتاد، وهو أن هذه ملفات تنفيذية منفصلة لا يلزمك تثبيتها، صحيح لكنه لا يجيب عن الاعتراض. عندما يحتاج سطح المكتب إلى logind، ويُفصل logind عن شجرة systemd، لا يعود الاختيار حراً. هذا هو المقصود بالاقتران في الحجة، وقد حدث فعلاً.
السجل الثنائي. يكتب journald تنسيقاً ثنائياً مفهرساً بدلاً من النص العادي. تحصل بذلك على أمور لم يوفّرها النص من قبل: التصفية حسب الوحدة وحسب الأولوية، وحقولاً مهيكلة، وبيانات وصفية لا يستطيع البرنامج المُرسِل تزويرها، لأن journald يسجّل الوحدة وcgroup بنفسه. يستبدل journalctl -u nginx -p err --since "-1h" استخدام grep بتعبير نمطي للتاريخ. لكن التكلفة حقيقية أيضاً. على جهاز لا يقلع، لا يمكنك قراءة السجل باستخدام less من shell الإنقاذ. بدلاً من ذلك، وجّه journalctl إلى القرص الموصول:
sudo journalctl --directory /mnt/var/log/journal --boot -1 --priority errهناك مأزق ثانٍ لا ينتبه إليه الناس إلا بعد وقوعه مرة. يحتفظ journald بالسجلات في /run/log/journal، وهي الذاكرة، ما لم يكن /var/log/journal موجوداً. على جهاز لا يوجد فيه هذا المسار، لن يعرض journalctl -b -1 شيئاً بعد إعادة التشغيل، وهي اللحظة التي تحتاج فيها إلى السجل تحديداً. تحقّق من ذلك وأصلحه:
journalctl --disk-usage
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journaldيجب أن يعرض journalctl --disk-usage الآن السجلات المؤرشفة ضمن /var/log/journal. إذا أردت النص العادي أيضاً، اضبط ForwardToSyslog=yes في /etc/systemd/journald.conf، وأبقِ rsyslog مثبّتاً.
قابلية تصحيح الإقلاع. عندما تتعطل وحدة، تعرض وحدة التحكم سطراً واحداً ولا تعرض شيئاً آخر:
[ *** ] A start job is running for Wait for Network to be Configured (1min 32s / no limit)توجد أدوات تتيح التعمق في الفحص: systemctl list-jobs أثناء التعطل، وsystemd-analyze blame وsystemd-analyze critical-chain بعده، وsystemd.log_level=debug في سطر أوامر النواة. الصياغة المنصفة للاعتراض هي أن أي شخص يعرف sh كان يستطيع قراءة init script من أوله إلى آخره، بينما تتطلب الوحدة المتوقفة معرفة أي واحد من عشرات الأوامر يجب استخدامه. هذه تكلفة حقيقية. يدفعها كل مسؤول نظام مرة واحدة، وقد دفعها عدد كبير من مسؤولي الأنظمة في الوقت نفسه.
إعداد افتراضي يتغير للجميع. غيّر systemd 230 في 2016 الإعداد الافتراضي لـlogind، بحيث تُنهى عمليات المستخدم المتبقية عند تسجيل الخروج. كانت جلسات tmux وscreen المنفصلة تتوقف عندما تنتهي الجلسة التي أنشأتها. أرسلت التوزيعات KillUserProcesses=no في /etc/systemd/logind.conf، والإجابة المدعومة هي loginctl enable-linger <user>. غيّر إعداد افتراضي واحد في مشروع واحد عادةً اعتمد عليها ملايين الأشخاص، وهذا هو المقصود عملياً بعبارة «وجود قدر كبير جداً من userland في مكان واحد».
الاعتماد الافتراضي سطح أمني. في مارس 2024، استهدف الباب الخلفي في xz-utils خدمة sshd على Debian وUbuntu. لا يربط OpenSSH في المنبع libsystemd. أضافت تلك التوزيعات تصحيحاً يسمح لـsshd بإبلاغ systemd بجاهزيته، وأدى libsystemd إلى سحب liblzma، حيث كان الباب الخلفي موجوداً. بروتوكول الجاهزية نفسه عبارة عن datagram واحد يُرسل إلى socket المحدد في $NOTIFY_SOCKET، لذلك لم تكن هناك حاجة إلى أي مكتبة من الأساس. كان رد systemd هو تحميل مكتبات الضغط باستخدام dlopen، بحيث لا تعود مرتبطة افتراضياً. وتوضح فئة مرتبطة من الأخطاء الشكل نفسه: في 2017، عوملت قيمة User= تبدأ برقم على أنها غير صالحة، فشُغّلت الوحدة بصلاحيات root بدلاً من فشلها، وتحول خطأ مطبعي إلى تصعيد للصلاحيات. ترفض الإصدارات اللاحقة تشغيل الوحدة.
سجلّ التاريخ في موجّه systemctl الخاص بنظامك
أصبح كلّ من المشكلات السابقة الآن توجيهاً واحداً في ملف يمكنك قراءته.
- أصبح الإقلاع التسلسلي
After=وWants=، ويعرضsystemd-analyze critical-chainما عطّل إقلاعك فعلياً. - أصبح اختبار الجاهزية
Type=notify، حيث تكتب الخدمةREADY=1إلى$NOTIFY_SOCKETعندما تصبح قادرة على تقديم الخدمة. وما يزالType=forkingمعPIDFile=مستخدماً مع الخدمات القديمة، وهو النوع الذي يفشل معstart operation timed out. Terminating.عندما لا يظهر ملف PID. يؤدي اختيار النوع الخطأ إلى جعل الوحدة تبلغ عن حالتها على أنها نشطة بينما يكون البرنامج الخدمي الذي شغّلته قد توقف بالفعل، لذلك من المفيد معرفة أي Type= يطابق طريقة بدء البرنامج الخدمي فعلياً قبل كتابة ذلك السطر. - أصبحت المراقبة من اختصاص cgroup، لذلك يحل
Restart=on-failureمعRestartSec=محلّ برنامج غلاف، ويمنعStartLimitBurst=حلقة الأعطال من الاستمرار إلى ما لا نهاية. - أصبح inetd وحدة
.socketبجوار وحدة.service. - أصبح
ulimitهوMemoryMax=وCPUQuota=وTasksMax=. - أصبح السطر
su - appuser -cفي البرنامج النصي لـ init هوUser=وNoNewPrivileges=yesوProtectSystem=strict، لذلك فإن تشغيل خدمة باستخدام مستخدم غير ذي امتيازات هو الشكل الافتراضي للوحدة، وليس عملاً إضافياً.
[Unit]
Description=Example API
Wants=network-online.target
After=network-online.target postgresql.service
Requires=postgresql.service
[Service]
Type=notify
ExecStart=/usr/local/bin/exampled
Restart=on-failure
RestartSec=2
MemoryMax=512M
CPUQuota=50%
TasksMax=128
User=exampled
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
StateDirectory=exampled
[Install]
WantedBy=multi-user.targetهناك سطر واحد في ذلك الملف يمثّل الخطأ الذي يرتكبه الجميع مرة واحدة. إن Requires=postgresql.service متطلب وليس ترتيباً؛ فهو يقول إن وحدتك تفشل إذا فشل Postgres، ولا يقول ابدأ Postgres أولاً. من دون After=postgresql.service يبدأ الاثنان في الوقت نفسه، وتحاول خدمتك الاتصال بمنفذ لا يستمع عليه أي برنامج بعد. وُضع الخياران منفصلين عن قصد، لأنك قد تحتاج أحياناً إلى أحدهما دون الآخر. يجعل ProtectSystem=strict نظام الملفات للقراءة فقط بالنسبة إلى هذه الخدمة، ولذلك يوجد StateDirectory=: فهو يمنح الخدمة مساراً واحداً قابلاً للكتابة تحت /var/lib.
أوضح مكان لرؤية سلوك عام 2005 على خادم عام 2026 هو SSH في Ubuntu 24.04، الذي يأتي مع systemd 255 اعتباراً من August 2026. يكون ssh.service مفعّلاً عبر socket افتراضياً: يحتفظ ssh.socket بمقبس الاستماع، ويبدأ sshd عند وصول اتصال. لذلك لا تأثير لـ Port 2222 في /etc/ssh/sshd_config، لأن sshd ليس العملية التي فتحت المنفذ. يجب أن يكون التغيير في وحدة socket.
sudo systemctl edit ssh.socket[Socket]
ListenStream=
ListenStream=2222يمسح ListenStream= الفارغ القيمة الموروثة من الوحدة المضمّنة في الحزمة. إذا حذفته، فستحصل على المنفذين معاً، لأن systemd يضيف إلى القائمة بدلاً من استبدالها. بعد ذلك طبّق التغيير وتحقق منه، مع إبقاء جلسة SSH ثانية مفتوحة طوال العملية:
sudo systemctl daemon-reload
sudo systemctl restart ssh.socket
sudo ss -lntp | grep 2222يجب أن يعرض ss مقبساً واحداً على المنفذ 2222 مملوكاً لـ systemd، وليس لـ sshd. ذلك هو تصميم launchd بعد مرور عشرين عاماً، على VPS الخاص بك. إذا كنت تفضّل السلوك القديم، فإن sudo systemctl disable --now ssh.socket متبوعاً بـ sudo systemctl enable --now ssh.service يمنحك sshd طويل التشغيل الذي يقرأ Port من إعداداته الخاصة مرة أخرى.
تعتمد التفاصيل التي ستواجهها على الإصدار الذي تشغّله، لذلك من المفيد معرفة الفرق بين إصدار Ubuntu طويل الدعم وإصداره المرحلي قبل التخطيط للترقية. وعند العمل على أكثر من جهاز، فإن حقيقة تطابق ملف الوحدة في كل مكان هي سبب كون إدارة عدة خوادم من مكان واحد الآن مشكلة إعداد بدلاً من كونها مشكلة كتابة نصوص shell. وعندما تكتب وحداتك الخاصة، ينفّذ زوج الخدمة والمؤقت المهمة التي كنت ستقسّمها بين برنامج نصي لـ init وسطر cron في عام 2009.
FAQ
لماذا استبدلت توزيعات Linux نظام SysV init بـ systemd؟
لسببين هندسيين وسبب واحد متعلق بالصيانة. كان SysV init يرتّب الخدمات حسب اسم الملف، وهذا ترتيب موضعي وليس تبعية، كما كان يفقد تتبّع أي daemon يتفرع عن العملية الأصل، ولذلك كان يمكن لملفات PID القديمة أن تنهي العملية الخطأ. عالج systemd ترتيب التشغيل عبر تفعيل المقابس وتوجيهات التبعيات، وعالج التتبّع عبر مجموعات التحكم. أما سبب الصيانة فحسم سرعة الانتقال: يعمل ملف وحدة واحد على كل توزيعة، لذلك كانت المشاريع upstream توفّر ملف .service، وتوقف مشرفو التوزيعات عن كتابة shell script لكل حزمة. انتقلت Fedora 15 إلى systemd في May 2011، وكانت Ubuntu 15.04 آخر التوزيعات الكبيرة التي تأخرت، في April 2015.
هل systemd ملف ثنائي واحد ضخم؟
لا. تبني شجرة المصدر برامج منفصلة عديدة. العملية PID 1 هي /usr/lib/systemd/systemd، بينما journald وlogind وudevd عمليات منفصلة لها ملفات ثنائية خاصة بها؛ شغّل ls /usr/lib/systemd/ لرؤيتها على خادمك. أما الانتقاد المستمر فيتعلق بترابط الإصدارات وليس بحجم الملف الثنائي: تُصدر هذه البرامج معاً، وتتشارك واجهات خاصة، لذلك تميل التوزيعات إلى اعتمادها كمجموعة واحدة، كما أصبحت برامج مثل GNOME تتوقع logind تحديداً.
هل يمكنني تشغيل Linux من دون systemd؟
نعم. توفّر Devuan sysvinit، وتستخدم Gentoo OpenRC افتراضياً، وتستخدم Void runit، وتستخدم Alpine busybox init مع OpenRC، وتحافظ Slackware على scripts بأسلوب BSD. لكن ذلك يضيف أعمال توافق. تحتاج برامج سطح المكتب التي تتوقع logind إلى elogind، وهو logind الخاص بـsystemd والمُدار كحزمة مستقلة، كما أن مقداراً متزايداً من برامج الخادم لا يوفّر إلا ملف .service، لذلك تكتب startup script وتديره بنفسك.
لماذا يكون journal ثنائياً بدلاً من ملف نصي عادي؟
لأن journald يخزّن حقولاً منظمة مع فهرس، ما يوفّر التصفية حسب الوحدة، والتصفية حسب الأولوية، وبيانات وصفية لا يستطيع البرنامج المرسل تزويرها: يسجل journald الوحدة وcgroup وUID الحقيقي بنفسه بدلاً من الوثوق بسطر السجل. وتتمثل الكلفة في أنك تحتاج إلى journalctl لقراءته، بما في ذلك من rescue system، حيث توجّهه إلى القرص المركّب باستخدام journalctl --directory /mnt/var/log/journal. وإذا أردت النص أيضاً، فاضبط ForwardToSyslog=yes في /etc/systemd/journald.conf.
ما البديل عن تعديل script الخاص بي في /etc/init.d؟
ملفات drop-in. لا تعدّل الوحدة في /usr/lib/systemd/system/، لأن ترقية الحزمة ستستبدلها. شغّل sudo systemctl edit nginx.service، وينشئ systemd الملف /etc/systemd/system/nginx.service.d/override.conf، الذي يُدمج فوق الوحدة المقدّمة من الحزمة. يعرض systemctl cat nginx.service النتيجة المدمجة، ويسرد systemd-delta كل override على الخادم. بعد إجراء أي تعديل يدوياً، شغّل sudo systemctl daemon-reload، وإلا فسيطبع الأمر التالي Warning: The unit file, source configuration file or drop-ins of nginx.service changed on disk.