לשם שינוי...

nocgod

New member
לשם שינוי...

היי חבר'ה!
קיבלתי עבודת הגשה בקורס מערכות הפעלה...לומדים לכתוב קצת C תחת יוניקס (בתחלס משתמשים בלינוקס...) עם קצת system calls.
בכל מקרה, בעבודה הראשונה הייתי צריך לכתוב סימולטור של shell עם מערכת קבצים מדומה (כביכול היו מבנים של file ושל folder שיושבים בזיכרון בתוך מבנה נתונים)
הסתדרתי עם העבודה יופי, כמו עם שאר העבודות שהיו לי עד כה במהלך התואר.
העבודה החדשה שקיבלנו ביזארית, ואני מתקשה להבין איך בתחלס לתכנן את הדברים, מצד אחד אני מבין שמסובך זה לא, מצד שני...אני ממש רוצה לדעת מה הם עישנו כשכתבו את התרגיל.

התרגיל:
חברה המפתחת תוכנה עבור מרכזי מחקר קיבלה הצעה מ CERN. החברה התבקשה לבנות מערכת קבצים היכולה לעבד (לשמור ולטעון) כמות גדולה מאוד של אינפורמציה בכל רגע נתון. לשם כך החברה החליטה שלא תשמור את המערכת קבצים בדיסק (בשל האטיות) אלה תחזיק אותה בזיכרון (כמובן קיימים חסרונות גם בשיטה זו). אתם כמפתחי המערכת מתבקשים לממש אותה בצורה הבאה:
• כל תיקיה במערכת זו תיוצג על ידי תהליך. (????????????? איך הגעתם לרעיון הזה?!)
• כל קובץ במערכת זו תיוצג על ידי רשומה בתהליך מתאים (אין צורך להחזיק תוכן הקובץ אלה רק רשימה דינאמית של שמות הקבצים אשר נמצאים בתיקיה זו).
הערה: אסור להגביל את המערכת קבצים הן מבחינת כמות הקבצים וכן מבחינת כמות התיקיות (כלומר רשימות המחזיקות את הנתונים חייבים להיות דינאמיים ולהתאים את גודלם לכמות הנתונים) .
בנוסף לכך אתם צריכים לאפשר פעולות הבאות: mkdir, mkfile, cd, ls,rmdir, exit...
על ה shell להיות נוח למשתמש ו להראות את המסלול הנוכחי בעץ. כמו כן על מנת לבצע פעולה כלשהי במערכת קבצים נשלח הודעה מתאימה ב pipe אשר מחבר בין הרכיבים הנדרשים.
הערה: חובה לבדוק תקינות הקלט.
חובה לסיים את כל התהליכים בזמן סיום התוכנית.

אני לא מחפש פתרון ואני ממש לא מחפש שתכתבו קוד... אני רק רוצה שמישהו יתן לי כיוון של...איך בכלל להתייחס לדבר כזה? (או איך הם קיבלו רעיון של לייצג תיקיה כprocess?)
אני יודע איך עובד fork, אני למדתי איך עובדים pipes ואני חושב שמבחינת system calls זה כל מה שאני אזדקק לו פה.
השאלה איך לייצג את זה? איך לדאוג למעבר מידע? איך להשיג path לתיקייה נוכחית כשאין לי תקשורת דו כיוונית עם הprocesses?
מאוד קשה לי להתחיל לכתוב קוד מבלי לארגן בצורה כללית את העבודה בראש...

אשמח לכל כמה רעיונות :)
 

BravoMan

Active member
אני לא יודע איך המוח שלהם עובד, אבל

מישהו פה רצה כנראה לשים דגש על IPC, וזה מה שהם עשו.

עכשיו שאלה: למה אין לך תקשורת דו כיוונית בין תהליכים?
הרי לא בעיה להקים כזו.

למיטב זיכרוני, אתה מגיע מתכנות מונחה עצמים, אז פשוט תחשוב על תוכנת ה-"תיקייה" כמחלקה.
היא תדע להגיד מה שמה, היא תדע להגיד מי "הילדים" שלה (ואם תראה בכך צורך גם מי ההורה שלה) והיא תדע להסתיים בקבלת פקודה מתאימה.

הכל עטוף באובייקט אחד, רק שהאובייקט הוא במקרה קובץ ריצה נפרד.

לגבי התקשורת חזרה לתהליך הראשי יש לך שתי אפשרויות:
אחת, היא להחזיק pipe נכנס עבור כל תיקייה פעילה.
זה יהיה מסורבל לדעתי.

שתיים: להחזיק pipe נכנס אחד שכל תיקייה בתורה תכתוב עליו.
היות ואין סיכוי ששתי תיקיות ינסו לתקשר בו זמנית (לפחות אין דרישה כזו בתרגיל) ואין בעיה להצמיד מזהה ייחודי (pid או כל דבר אחר) לכל תיקייה לא תהיה בעיה להבדיל עם מי "מדברים".

לפחות תהיה שמח שהתהליכים לא צריכים לתקשר דרך Quantum entanglement - בכל זאת CERN
.

ואגב, נראה לי שיצאתם די בזול - בקורס מערכות הפעלה בפתוחה נתנו לנו לכתוב assembler בתור פרויקט גמר.

(לא, הכוונה אינה לתכנות בשפת assembly אלא הגדירו שפה דמיונית כזו למחשב דמיוני ואנחנו היינו צריכים לכתוב את תוכנת ה-assembler שמתרגמת את קוד המקור, כלל תוויות והפניות לפונקציות חיצוניות, לשפה של אותה מכונה דמיונית).
 

nocgod

New member
תיארתי לעצמי

ברור לי שהדגש הוא על IPC וטיפה עבודה עם processes...
האמת אחרי שאתה כותב להחזיר pipe אחד ואז כל תיקיה תכתוב אליה הרעיון שלי נראה קצת דבילי עכשיו :)
ואני מאוד מבסוט שלא ביקשו quantum entanglement, אחרת האנשים אצלי במחלקה היו מתאבדים... רובם רוצים לשכוח את C ואת ++C כמה שיותר מהר, משום מה לי השפות האלה יותר קוסמות...
עד כמה שאני זוכר חברים שלי משנים קודמות סיפרו לי שעבודת הסוף בקורס הזה הייתה לכתוב shell מלא שבאמת עובד על המערכת קבצים, לא נשמע כל כך מטורף האמת אחרי שעשיתי את הסימולטור...כולה לשנות את התוכן של הפונקציות לכל מיני exec

עבודה מוזרה הביאו לכם במערכות הפעלה, נראה לי יותר מתאים לקורס קומפילציה...כי זה מה שאנחנו הולכים לעשות בסופו של דבר בקורס קומפילציה, לכתוב frontend של קומפיילר (איזה מזל שכתבתי כזה בantlr)

בכל מקרה מה אתה חושב על מבנה כזה של נתונים:
typedef struct _file
{
int size;
char* creationDate;
char name[128];
} File;

typedef struct _pipe
{
int fd[2];
} Pipe;

typedef struct _childFolder
{
pid_t id;
Pipe fd;
} ChildFolder;

typedef ChildFolder ParentFolder;

typedef struct _folder
{
char name[128];
char* creationDate;
ParentFolder* parentFolder;
ChildFolder** folderList;
File** fileList;
unsigned int numberOfFiles;
unsigned int numberOfFolders;
} Folder;


ההזחות יצאו משום מה לא משהו באתר...אבל עדיף מכלום...
 

BravoMan

Active member
כמה הערות קטנות:

אני חושב שהגדרת הטיפוסים ChildFolder ו-ParentFolder היא מיותרת וקצת מבלבלת.

אם הבנתי נכון את הכוונה, אז מתאים יותר שיהיה רק טיפוס אחד שיקרא משהו כמו FolderRef או FolderHolder או FolderNode.

ואני לא ממש מבין למה אתה רוצה להחזיק רשימות של קבצים ותיקיות "בת" בתור מצביע למצביע.
אתה מתכוון להשתמש במערך דינמי ו-realloc?
זה עדיין לא מצדיק את זה.

אני ממליץ לך בחום לממש רשימה מקושרת פשוטה, שה-data שלה הוא מצביע ל-void ולהשתמש בה כדי להחזיק רשימות שונות.
מטקסט התרגיל נראה כאילו הם ממש מתחננים שתעשה את זה.

וטיפ קטן: אם משתמשים ב-typedef לא חייבים לתת שם למבנה.
אפשר פשוט לכתוב struct אלא אם אחד מאיברי המבנה אמור להיות מצביע למבנה עצמו.

סתם כדי לחסוך הקלדה ולא לזבל את ה-namespace.
אלא אם המרצה שלכם מתעקש שתעשו את זה...
 

nocgod

New member
בסוד אני אספר

לרוב לא בודקים את הקוד
אם היו בודקים באמת את הקוד (העתקות, סגנון קוד עקום, קוד זבל וזליגות זיכרון) אנשים היו מקבלים 40 בממוצע...
כן אני מתכוון להשתמש ב malloc realloc כי הכל צריך להיות דיי דינאמי...
למה בתור מצביע למצביע? כדי לא להעתיק תוכן של struct אלא מצביע אליו, נראה לי יותר נכון מלעשות העתקה יבשה של נתונים מstruct לstruct...
לא בדיוק הבנתי איפה אתה מציע להכניס רשימה מקושרת...

אני יודע שאם אני עושה typedef לstruct אני לא צריך לתת לו שם, סתם אוהב לעשות fold לכל הדברים האלה ובלי שם לstruct אני רואה שורה typedef struct... בלי שם, וזה מציק לי :)

לגבי הchildfolder parentfolder ידעתי שזה רעיון גרוע, זה סיבה למה לא כותבים דברים כאלה ב 11 בלילה...

סמסטר שעבר הם הנפיצו עבודה הכי לא טבעית לעבוד בה עם threads בג'אווה, הייתי צריך לשבור את הראש ולאנוס בכוח את הthreads לתוך התוכנה... הסמסטר העבודה המוזרה הזאת...אפשר לחשוב חסרים רעיונות נורמלים לתרגול IPC

תודה על הטיפים!
 

BravoMan

Active member
הממ...

בוא נראה אם הבנתי נכון לגבי הרשימות:

בכל פעם שאתה מוסיף פריט לרשימה (לא משנה אם תיקיות או קבצים), אתה מתכוון לעשות את הפעולות הבאות:
1. להקצות מחדש את מערך המצביעים עם realloc כדי להוסיף תא למצביע נוסף.
2. להקצות עם malloc מקום למבנה חדש ולשמור את המצביע בתא החדש שהקצת.

זו אכן הגישה שנהוגה ב-Java אבל לא כ"כ ב-C.

לא מומלץ להגזים עם realloc.
בגלל שהוא כל פעם מבקש מקטע זיכרון רציף הוא עלול לגרור צריכת זיכרון גבוהה (באופן זמני) והעתקה אינטנסיבית.
הרי כאשר לא נשאר מקום פנוי אחרי סוף המערך שמנסים להקצות מחדש, הפקודה מקצה מחדש מקום באזור אחר, מעתיקה הכל ורק אז משחררת את הקודם.

לכן, בד"כ ב-C ו-++C ממליצים להשתמש ברשימה מקושרת במקום הקצעה מחדש ולמיטב ידיעתי, מחלקות שממשות מערכים דינמיים למעשה משתמשות ברשימה מקושרת מאחורי הקלעים.

זה יותר עבודה לכתוב אבל יעיל יותר מבחינת ביצועים וזיכרון.
והיות שלא סביר שתצטרך לגשת לפי אינדקס מספרי לאיברי הרשימה (הרי תחפש קבצים ותיקיות לפי שם או בסדר הופעתם) זה לא ישפיע על הגישה.

בנוסף, זה יקל על הכנסת פריטים לרשימה בצורה שתשמור עליה ממוינת, במידה ויש דרישה כזו עבור פקודת ls.
 

nocgod

New member
תודה על הטיפים

אני כמובן מוצא הגיון בדברים שלך. כנראה שאתה צודק בקשר לעניין הrealloc/malloc
אני מתבאס לממש מבנה נתונים באותו הקובץ עם שאר התוכנית זה נראה מסורבל ודוחה...(כן הם מבקשים שנגיש הכל בקובץ אחד...go figure) מצד שני אתה צודק, זה יקצר תהליכים בהכנסה/הוצאה/מציאה של איבר כלשהו במערך
אני עדיין לא יודע איך בדיוק אני אמצא path בין תהליכים שונים...אבל הגיע הזמן לכתוב קצת קוד צריך להגיש את זה עוד שבוע וחצי...אז נסתדר on the way מה שנקרא...גם ככה לא בודקים כמו בני אדם...:)
 

BravoMan

Active member
כל מוסד והשיגעונות שלו...

בקורס בפתוחה הדגישו לנו 20 פעם להפריד לקבצים שונים לפי פונקציונליות כדי לשמור על הכל מסודר, ואפילו אמרו שיורידו ניקוד אם לא נעשה הפרדה רעויה.

לעניין ה-path: תחשוב מתי אתה צריך למצוא אותו, או יותר נכון לאיזו מטרה.
זה אמור להגדיר את הדרך הכי נוחה לנהל אותו.

באופן כללי, כל עוד כל תיקייה שומרת "קישור" (מספר PID או כל דרך אחרת) לאבא שלה, אין בעיה לעקוב ממנה לשורש העץ - פשוט תתשאל כל ספרייה על ההורה שלה.

בכל מקרה, ה-shell שלך צריך לשמור על pwd (כלומר על הנתיב שבו אתה עובד).
יש כמה אפשרויות, כשהתריוויאלית היא פשוט מחרוזת, שנוחה להצגה אבל לא נוחה לעבודה, והמורכבת יותר היא להחזיק כל רכיב בנתיב כאובייקט עצמאי (תא במערך או איבר ברשימה מקושרת).
מה שמחזיקים יכול להיות:
* PID של תיקייה ואז צריך כל פעם לשאול מה שמה.
* שם של תיקייה אבל אז צריך למצוא את ה-PID שלה אם רוצים לתמרן אותה.
* שניהם, אבל אז צריך קצת יותר זיכרון לכל אובייקט (לדעתי זה בכל זאת עדיף מבחינת יעילות עבודה).

אגב, יש בלינוקס פונקציה שמחזירה את ה-PID של תהליך האב (זה שממנו עשית fork):
http://linux.die.net/man/2/getppid

אבל, כדי שהיא תהיה שימושית למציאת נתיב, כל ספריה צריכה להיות אחראית על יצירת תתי הספריות שלה, וזו נראית לי דרך עבודה עקומה.
(לדעתי, התהליך הראשי אמור להיות אחראי על כל היצירות והמחיקות)

ורק נקודה קטנה לסיום:
קיוויתי, שעוד אנשים יצטרפו לדיון הזה.
הדברים שאני כותב כאן הם די Off the top of my head כמו שאומרים, וחלקם נכתבו מאוחר בערב אחרי יום עבודה עמוס.

נקודת מבט אחרת (או שניים או שלושה) בהחלט לא יזיקו.
 

nocgod

New member
לצערי כל עוד לא מדובר ב...

שאלות: "סיקסיק אנס אותנו בעבודה, יש פה 2 forים אחד בשני ואפילו יש if..." או "אני לא מבין/ה שאלה 5, בואו פתרו לי אני אגיש ואקבל אחלה ציון..."
זה לא מעניין אף אחד...
אבל לא נורא הבאת לי מספיק חומר למחשבה...עכשיו רק צריך זמן לחשוב ויהיה מצויין
, מקווה שעד סוף שבוע אני אתקדם לאנשהו עם זה...תודה!

לעניין הpath חשבתי עבור כל תיקיה להחזיק חוץ מpipe אליה ואת ה pId שלה (אני מרגיש דפוק כשאני כותב את זה) להחזיק גם את השם שלה, ככה יהיה לי הרבה יותר קל לעשות פעולות כמו cd או ls...
בעבודה הקודמת שלי עשיתי מעין פונקציה רקורסיבית שהולכת אחורנית ל"אבא" עד שאין יותר אבות (כאילו root) ואז מתחילה להדפיס את השמות של התיקיה, הבעיה שלי היא ההמתנה בין הprocesses אני לא בדיוק סגור איך אני יכול לעשות yeild לטובת process אחר תוך כדי שליחה של ההודעה בpipe בסגנון של printPath שיהוה מעין טריגר להמשך התהליך עד שנגיע לשורש...

בכל מקרה אלו לא השעות הנכונות לחשוב על תיקיות בתור תהליכים, pipeים ושאר הסלטים...
 

mandymo

New member
אסמבלר המערכות הפעלה?

זה לא ב"תכנון מערכות" או משהו כזה? (הקורס שבו מקבלים את K&R.
במערכות הפעלה אני זוכר את ה shell ואת scheduler.

הזכרת לי נישכחות....
 

פרסאוס

New member
השנקל שלי לעניין

כל תיקיה היא פרוסס כי זה מה קיבלנו ועם זה ננצח.
מחלק את זה מהקטן לגדול:
קובץ - צ"ל מבנה שמייצג קובץ וזאת היחידה הקטנה ביותר בתוכנית.
שם וגודל הם פרמטרים מינימליים, אבל אפשר להוסיף תאריכים (יצירה, שינוי) והרשאות.
לא הייתי מציין שדה תאריך כמחרוזת.
הלאה. רשימת הקבצים תהיה רשימה מקושרת מן הסתם.
רשימת ילדים של הפרוסס - ילד והפייפ שלו לתקשורת, זיהוי לתקשורת חוזרת, פייפ של האבא.
בפרוסס הראשי מן הסתם NULL יהיה הפייפ של האב.
שם הפרוסס כתיקייה - מחרוזת.
מיקום נוכחי בעץ ישמר כנראה בפרוסס הראשי - כרגע לא עולה לי משהו טוב יותר.
mkdir שולח בקשה לפרוסס מיקום נוכחי.
CD משנה מיקום נוכחי אם קיים.
פונקציית סגירה:
כל פרוסס יסגור את הפרוססים הכפופים לו.
צריך להחליט מה לעשות במקרה של פרוסס נסגר שמייצג את התיקייה הנוכחית (לעבור? לשלול סגירה?).
אין בעיה של תקשורת, היות ויש פייפ גם לאב וגם לבנים.
מיקום נוכחי מושג באמצעות לולאה או רקורסיה ושמות התיקיות עד לאב.
ביציאה צריך לסמן דגל שיש לסגור בכל מחיר ולא צריך לשנות תיקייה נוכחית.
 

nocgod

New member
השנקל שלך ממש דומה למה שאני מתכנן לעשות

גיליתי משהו מעניין היום
מסתבר שאם אני עושה read מpipe שלא כתבו אליו כלום התהליך שעושה את הread עושה wait לתהליך ממנו ניסה לעשות read... מוזר שלא דאגו להזכיר את זה בשום מקום ובאף הרצאה...
בכל מקרה אני עדיין מתעצל לממש רשימה מקושרת באותו הקובץ יחד עם שאר הקוד (כמו שאמרתי הם רוצים שנגיש להם קובץ אחד בלבד משום מה)

בקשר ליציאה, אני מאמין שהמצב יהיה שכל פרוסס נוכחי שאני בו יהיה במצב שכל הבנים שלו עושים read לpipe שמקשר ביניהם וכך גם הפרוסס אב, לכן מה שאני אעשה זה אעביר מעין קריאה לאבא שתגיע עד השורש ומשם תישלך באופן סדרתי קריאה ל kill process שתגיע עד העלים של העץ המעוות הזה ופרוססים פשוט יתחילו למות אחד אחרי השני עד שהשורש ימות משהו בסגנון:

void _killAllProcesses(FolderRefNode* refList)
{
FolderRefNode* cur = refList;

while (cur != NULL)
{
write(fd[0], "kill", 5);
cur = nextNode(cur);
}

exit(0);
}

כאשר כתוצאה מהקריאה הזאת יהיה איזה שהוא dispatch בכל הבנים שיקרא לאותה הפונקציה...סוג של cascade on kill :) נשמע מרושע...
 

פרסאוס

New member
ציפו שתקרא את החומר

דרך אגב, הוא לא חייב לחכות, אבל במקרה של המימוש הספציפי הזה,
עדיף שיחכה.
 

nocgod

New member
ציפו?

פייר בקורס שיש בו: הרצאה, מעבדה, תרגול הייתי מצפה לקצת יותר מלהקריא לי שקופיות...אבל נו מילא בכל מקרה תכנות למדתי לבד...למה שזה יהיה שונה
אגב לדעתי מדובר במשהו יחסית טריוויאלי ומשהו שהכרחי להסביר, בדיוק כמו שהם דאגו לחפור לנו 2 הרצאות על סמפורים ועד כמה הם מגניבים בבעיות producer-consumer...

אחרי שאגיש את העבודה אולי אעלה אותה לפה כדי שתוכלו לשפד אותי, אולי לתת כמה טיפים לכתיבת קוד...
בכל מקרה כבר כתבתי את הרשימה המקושרת והתחלתי כבר לטפל בקוד של mkdir, rmdir כאשר קוד טריויאלי כמו פירסור של פקודות ויצירה/מחיקה של קובץ מוכנים כבר (האמת העתקתי מעבודה קודמת שלי)
הבעיה הרצינית שלי תהיה התיאום בIPC אבל אחרי שבוע תכנון אני מאמין עוד 6-8 שעות אני אסיים...אולי 10 אחרי debug+valgrind
 

פרסאוס

New member
קובץ אחד?

אפשר. כי אחרת פרוייקטים צריך להעלות למקום אחר.
כל תחינותי לא עזרו, המקום הזה לא יהיה קוד פרינדלי.
 

nocgod

New member
גם ככה זה יוגש כקובץ אחד

אני אמחק ממנו סממני זיהוי (שם תעודת זהות תאריכי הגשה וכו') אשנה לו שם לmain.txt ואעלה אותו לפה...
כבר הרבה זמן שאף אחד לא נתן לי פידבאק על העבודות שלו, הן תמיד נבדקות בצורה של: "בוא תראה לי איך כתבת את פונקציה mkfile..." ואז אני צריך להראות פונקצייה builder ל file והבודק מגרד את הראש ואומר טוב סבבה מבלי לראות בכלל איפה אני מכניס את התוצר של הbuilder למערך...מוזר :)

יש סיכוי שאתה מכיר סביבת עבודה חברותית ל ++C או C מעל לינוקס שתתן לי GUI ל GDB? אקליפס נחמד אבל יש לו קריזות של ילדה בת 10.
 

BravoMan

Active member
זה היחס???

כתבתי לך בזמנו על 2 כאלה: Anjuta ו-Jeany (יותר קלילה ופשטנית).
אבל אולי זה בגלל שאני לא כחול


וסתם לידע כללי: ל-GDB יש GUI משלו:
http://www.gnu.org/software/ddd
 

BravoMan

Active member
לכבוד הוא לי!

אני רק מקווה שמישהו לא חשב בטעות שבאמת נעלבתי או משהו.
הפתיחה של ההודעה הקודמת כוונה להיות בדיחה.
 
למעלה