Table of Contents
(ب) أن بناء قذيفة من الخدش في (جيم) يظل أحد أفضل الطرق لتعميق فهمك لكيفية إدارة نظم التشغيل للعمليات، ومعالجة مدخلات المستخدمين، وتنفيذ البرامج، وفي حين أن القذائف الحديثة مثل (باش) و(زش) و(فيش) متطورة بشكل لا يصدق، فإن وظائفها الأساسية تغلي إلى حلقة بسيطة: قراءة القيادة، وقطعها، وخلق عملية جديدة، وانتظر الانتهاء من هذا الدليل الموسع.
ما الذي تفعله (شيل) في الواقع
At its heart, a shell is a command-line interpreter, it provides a textual interface where users type commands, and the shell translates those commands into actions performed by the operating system. When you type ls -la, the shell seem must find the ls executable
بناء قذيفة صغيرة يعلمك عنها
- قراءة المدخلات القياسية وتجميعها
- Creating and managing child processes with fork
- اعادة صياغة صورة الذاكرة العملية مع
- ترجمة:
- معالجة الأخطاء المشتركة ببراعة
فهم هذه المباني يعطيك نظرة عن كيفية عمل جميع النظم شبه الموحدة تحت غطاء الرأس، ويوفر أساسا صلبا للتعلم عن سمات أكثر تقدما مثل مناولة الإشارات، ومراقبة الوظائف، والاتصال بين العمليات.
وضع صمامات الشل
كل قذيفة مبنية حول حلقة رئيسية تتردد إلى ما لا نهاية حتى يطلب المستخدم الخروج من هذه الحلقة تطبع بسرعة وتقرأ خطا من المدخلات ثم تدقها ثم تتصرف على القيادة المقطعية
while (1) {
print_prompt();
read_input();
parse_input();
execute_command();
}
وفي جيم، تستخدم عادة ] فواكه لقراءة مدخلات لأنها تتعامل مع حدود خطية وتدفقات احتياطية أكثر أمانا من ] غايات ، ويمكن أن يكون العجلة بسيطة مثل
معالجة قضايا العصر في المدخلات
ونادرا ما تكون مدخلات المستخدمين نظيفة، إذ يجب أن تُعالج القصف القوي الخطوط الفارغة، والقيادة والمسار الأبيض، والقيادات الطويلة للغاية، وحالة نهاية العجلات (Ctrl+D). وإذا كان النبات ، فإن أي شيء ينبغي أن يُعادل ، إذا كان ينبغي أن يُثبت بشكل غير مباشر.
#define MAX_INPUT 1024
char input[MAX_INPUT];
if (fgets(input, MAX_INPUT, stdin) == NULL) {
printf("\n");
break; // EOF
}
// Remove trailing newline, if present
size_t len = strlen(input);
if (len > 0 && input[len-1] == '\n') {
input[len-1] = '\0';
} else {
// Input too long, flush remaining characters
int ch;
while ((ch = getchar()) != '\n' && ch != EOF);
}
أوامر الفرز في الحج
بمجرد أن يكون لديكِ خيط مدخل نظيف، يجب أن تقسميه إلى كسور، أول من يُدعى القائد (مثلاً، )
char *args[MAX_ARGS];
int arg_count = 0;
args[arg_count] = strtok(input, " \t");
while (args[arg_count] != NULL && arg_count < MAX_ARGS - 1) {
arg_count++;
args[arg_count] = strtok(NULL, " \t");
}
args[arg_count] = NULL; // execvp expects a NULL-terminated array
هذا المركب يقسم المدخلات على الأماكن والرسومات، بل يتعامل مع الخيوط المقتبسة، لذا فإن الأمر مثل ]echo:hello world]] سوف يخترق بشكل غير صحيح ثلاثة أكاذيب بدلا من اثنين.
بعد التكسير، يجب أن تتحققوا إذا كان أول من يُدعى (قوة القيادة الفارغة) وإذا كان الأمر كذلك، فإنهم سيستمرون في التكرار التالي للحلقة دون أن يُسرحوا.
بناء القيادة
لا يجب أن تُظهر جميع الأوامر عملية جديدة، بعض الأوامر، مثل cd و]
Exit
The exit command terminated the shell immediately. It is the simplest built-in to implement: just check if the first token equals `exit" and break out of the main cycle. Optionally, you can accept an exit status argument and pass it to the exit system call.
دليل التغيير (ج)
cd command requires you to call ]chdir . The target directory is the second argue. If no argue is provided, you might default to the user's home directory (available via the [FLT check:4]HOME[F value environment]
if (strcmp(args[0], "cd") == 0) {
const char *path = args[1];
if (path == NULL) {
path = getenv("HOME");
if (path == NULL) {
fprintf(stderr, "cd: HOME not set\n");
continue;
}
}
if (chdir(path) != 0) {
perror("cd");
}
continue; // skip fork/exec
}
عملية إنشاء الشوكة
أي قيادة ليست مبنية يجب أن تخلق قذيفة عملية طفل لتنفيذها نظام fork ينشئ عملية جديدة بتكرار عملية الاتصال العملية الجديدة تسمى الطفل، والوالد الأصلي هو بعد
pid_t pid = fork();
if (pid == -1) {
perror("fork");
continue;
}
if (pid == 0) {
// Child process
// ...
} else {
// Parent process
// ...
}
لماذا شوك؟
قد تتساءلون لماذا تحتاجون إلى إنشاء عملية منفصلة على الإطلاق، والسبب هو أن exec، الذي يحمّل برنامجاً جديداً إلى الذاكرة، يحل محل العملية الحالية تماماً، وإذا كانت القصف يُدعى ]exec] مباشرة، فإن برنامج القصف سيُستعاض عنه ولن يعود أبداً لقبول الأوامر الجديدة،
تنفيذ برنامج مع
exec family of functions replaces the current process with a new program. There are several variants: ]execl, execlp
if (pid == 0) {
// Child process
execvp(args[0], args);
// If execvp returns, an error occurred
perror("exec");
exit(EXIT_FAILURE);
}
ملاحظة النداء إلى ] بعد ]perror. وإذا ] ]] فشل (مثلا، لأن القيادة لا وجود لها)، فإن عملية الطفل يجب أن تنتهي، خلافا لذلك، أنها ستواصل تشغيل أي رمز يتبعه.
انتظار عملية الطفل
وبعد الاستغناء عن عملية الوالدين، تنتظر عادة الطفل أن ينتهي قبل أن يدفع مرة أخرى، ويتم ذلك بـ wait أو ] التي تنتظر .
int status;
waitpid(pid, &status, 0);
(أ) [(FLT:0]status)تغيير يتضمن معلومات عن كيفية إنهاء الطفل، ويمكنك استخدام الكليات مثل ]WIFEXITED، ]
Blocking vs. Non-Blocking
(أ) أن يُطلب من الأب قبل أن يخرج الطفل، وهذا هو السلوك الصحيح لعملية مُراعية، وإذا ما أضيفت لاحقاً الدعم لعمليات المعلومات الأساسية (تُدير قيادة مع ) تحتاج إلى قائمة، و[()، ستُديرون [الخيار 5]
وضع معاً الصدمة الكاملة
وهنا قذيفة كاملة ودنيا تدمج جميع القطع التي نوقشت حتى الآن، وهي تتناول exit] و](ج) ) البني - الزينات، والبرقيات المستخدمة [() [للإختبار:5]، وللتحقق من وضوحات الخارجية، وانتظر.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAX_INPUT 1024
#define MAX_ARGS 64
int main(void) {
char input[MAX_INPUT];
char *args[MAX_ARGS];
int should_run = 1;
while (should_run) {
printf("mysh> ");
fflush(stdout);
if (fgets(input, MAX_INPUT, stdin) == NULL) {
printf("\n");
break;
}
// Remove trailing newline
size_t len = strlen(input);
if (len > 0 && input[len-1] == '\n') {
input[len-1] = '\0';
}
// Tokenize
int i = 0;
args[i] = strtok(input, " \t");
while (args[i] != NULL && i < MAX_ARGS - 1) {
i++;
args[i] = strtok(NULL, " \t");
}
args[i] = NULL;
if (args[0] == NULL) {
continue; // empty line
}
// Handle built-in commands
if (strcmp(args[0], "exit") == 0) {
should_run = 0;
continue;
}
if (strcmp(args[0], "cd") == 0) {
const char *path = args[1];
if (path == NULL) {
path = getenv("HOME");
}
if (chdir(path) != 0) {
perror("cd");
}
continue;
}
// Fork and execute external command
pid_t pid = fork();
if (pid < 0) {
perror("fork");
continue;
}
if (pid == 0) {
// Child
execvp(args[0], args);
perror("exec");
exit(EXIT_FAILURE);
} else {
// Parent waits
int status;
waitpid(pid, &status, 0);
}
}
return 0;
}
[FLT] Myshell. [FLT:]
الشلالات المشتركة وسلاسل الديون
حتى مع هذا القدر الصغير من الرموز، هناك العديد من الأمور يمكن أن تسوء هنا أكثر القضايا شيوعاً وكيفية إصلاحها:
القيادة لم تجد
إذا قمت بكتابة أمر غير موجود (مثلاً، foobar])،
مفقودين من طراز Newline أو من طراز RUncated
وإذا ظهر سرعتك دون انتظار مدخلات، فإن السبب المحتمل هو أن تكون الشخصية المتبقية في مدخل احتياطي من نداء سابق، وتحقق دائما من أن فوجئ ] قد استهلكت الخط بأكمله (أي أن آخر طابع قبل أن يصبح المبيد الباطل خطا جديدا).
عمليات الزومبي
إذا نسيت أن تتصل في انتظار [أو ] في انتظار ، فإن عمليات الأطفال التي تنتهي تصبح زومبياً إلى أن تستعيدها، ولكن القصف في النموذج يدعو إلى التراكم ، ولكن ينبغي أن تظهر لاحقاً
توسيع نطاق الشيل
بمجرد أن تعملي القصف الأساسي يمكنكِ إضافة سمات تقرب من قذيفة العالم الحقيقي كل سمة تعلمكِ المزيد عن نظام التشغيل
توجيه المدخلات/النواتج
Supporting [FLT:] ], [FLT:[lt;, and inputgt; requires you to parse the command line for these operators, open the appropriate files using
القراصنة
(ب) إن وضع ناتج قيادة واحدة في مدخلات أخرى ()cmd1 ⁇ cmd2 ) هو أكثر مشاركة، تحتاج إلى إنشاء أنبوب مع ]pipe ، وشوك عمليتين من عمليات الطفل، وإعادة توجيه ناتج طفل واحد إلى نهاية الأنبوب والنهاية الأخرى، على أن يكتمل المدخلات.
مراقبة الوظائف
Adding background execution ( managemed;] at the end of a command) and the ability to bring jobs to the foreground requires managing a job table, handling ]SIGTSTP, SIGCONT
تاريخ القيادة
Implementation a simple history mechanism (up/down arrows to recall previous commands) involves capturing input using raw terminal mode (via tcgetattr and ]tcsetattr) or using the readline[FLT functioning:5]
المزيد من القراءة والموارد
ولتعميق فهمكم للمفاهيم المعروضة هنا، فإن الموارد التالية لا تقدر بثمن:
- GNU C Library manual: Process Creation examples] – Official documentation on fork, exec, and ]wait.
- The Open Group Base Specifications: Shell Command Language] – The formal specification for POSIX shell behavior; useful if you want to match a standard.
- Wikipedia: Unix shell] - A broad overview of shell history, variants, and features.
- دليل بيج للاتصال المتبادل بين العملاء
خاتمة
بناء قذيفة أساسية في (سي) هو أكثر من تدريب أكاديمي، يرغمك على المشاركة مباشرة في خلاصات نظام التشغيل الأساسي، لقد رأيت كيف تقرأ وتقطع مدخلات المستخدمين، كيف أن ]