בעיה עם waitpid (לינוקס)

zagzagzag

New member
בעיה עם waitpid (לינוקס)

יש לי תוכנית שתופסת את הסיגנל SIGCHLD ועושה fork. ב-signal handler יש לי את הקוד הבא:
pid_t pid; do pid = waitpid(-1, NULL, WNOHANG); while( pid > 0 ); if ( pid < 0 ) error_exit("wait error",ERROR_CODE​
לאחר שנסגרים כל התהליכים הבנים, התוכנית עפה על "No child processes", כאשר הערך של pid הוא -1 והערך של errno הוא ECHILD. לפי מה שקראתי ב-man, כאשר אני משתמש ב-WNOHANG הערך החוזר מ-waitpid הוא 0 אם אין בנים. יש למישהו רעיון למה אני מקבל את ההודעה הזו ?
 

voguemaster

New member
מעניין מאוד אפילו

זה לא אמור לקרות כמובן. בד"כ ECHILD מוחזר כשאתה מבקש פעולת IGNORE על הסיגנל SIGCHLD. ממש תמוה. תסביר בבקשה, מה ז"א התוכנית עפה ? מה אתה רואה אם אתה מריץ אותה תחת gdb ? אני מקווה אגב שאתה לא מחכה על פרוססים שהם בעצם threads כי זה יוצר בלאגן לא קטן
. אולי תראה לנו את קטע הקוד שבו אתה "מתקין" את ה-signal handler שלך ?
 

zagzagzag

New member
הקוד

error_exit מדפיסה הודעת שגיאה עם perror ויוצאת עם exit. אז ב"עפה" התכוונתי להודעה שאני מקבל. אם אני מתעלם מ-ECHILD התוכנית מתפקדת ללא בעיות. לגבי gdb אני אצטרך לקרוא קצת על עבודה עם מספר תהליכים, אז זה יאלץ להדחות למחר ועכשיו לקוד: כל מה שעושים התהליכים הבנים הוא לפתוח fifo (קיים) לכתיבה, לבצע redirection ל-stdout ו-stderr ל-fifo הזה ולבצע execvp. אני מקבל את התוצאות הנ"ל גם כשאני בטוח שאין לי threads בתהליכים הבנים (למשל אם אני עושה exec ל-cp בלי פרמטרים). תהליך האב מבצע את הקוד הבא (הורדתי חלקים לא חשובים) בלולאת while אינסופית:
// wait until a message arrives do ret_value = select(fifo_fd+1, &fifo_fd_set, NULL, NULL, tvptr); while ( ret_value < 0 && errno == EINTR ); if ( ret_value < 0 ) error_exit("select error", ERROR_CODE); // if a message arrived, handle it and continue if ( ret_value > 0 ) { handle_message(fifo_fd); continue; } /* else - its time to execute a program */ // catch SIGCHLD if ( signal(SIGCHLD, sigchld_handler) == SIG_ERR ) error_exit("Signal error",ERROR_CODE); // fork off a child pid = fork(); if ( pid < 0 ) // fork error error_exit("Fork error",ERROR_CODE); else if ( pid == 0 ) { // child process signal(SIGCHLD, SIG_DFL); // See remarks below exec_next(); // execute } // parent process FD_ZERO(&fifo_fd_set); FD_SET(fifo_fd, &fifo_fd_set); // do some stuff, and start the while loop again​
לגבי ההערה על signal בתהליך הבן (הקריאה השנייה ל-signal) - התוכנית מתנהגת באותה צורה עם השורה הזאת ובלעדיה. הסיבה לקיום שלה היא שאני לא יודע אם התהליכים הבנים מבצעים fork. יכול להיות שהיא מיותרת כי אני מבצע exec ? תודה מראש
 

voguemaster

New member
העלילה מסתבכת

חשבתי שדיברת על "עפה" במובן של איזה קריסה עסיסית, לכן רציתי לראות מה קורה ב-gdb (כי הוא תופס את זה באותו רגע). אם אין לך קרנל 2.6 אין לך מה לנסות אפילו לדבג תוכנית multiprocessed (לא מדבר על multithreaded) כי אין תמיכה בקרנל לאפשר ל-gdb לעשות tracing בתוך פרוסס בן. (כן אני יודע שיש את האופציה follow-fork-mode - היא לא עובדת
). מעבר למה שכבר אמרתי אני די אובד עצות
. אני יכול לנסות להעלות לכאן קוד שמתקין sig handler שאני השתמשתי בו ואולי זה יעזור אבל אני די בספק.
 

zagzagzag

New member
המשך

אני משתמש בקרנל 2.6, אבל בדקתי את התוכנית שלי גם עם knoppix (קרנל 2.4) וקיבלתי אותן תוצאות. בכל מקרה, התוכנית הבאה מחזירה אצלי את אותו פלט:
#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #define N 5 #define ERROR_EXIT(str) { \ perror(str); \ exit(1); \ } void sigchld(int sig) { pid_t pid; do { pid = waitpid(-1, NULL, WNOHANG); printf("child %d\n",pid); } while ( pid > 0 ); if ( errno == ECHILD ) puts("errno=ECHILD"); if ( pid < 0 ) ERROR_EXIT("waitpid"); if ( signal(SIGCHLD, sigchld) == SIG_ERR ) ERROR_EXIT("signal") } int main() { int i; pid_t pid; char *args[2]; args[0] = strdup("/bin/echo"); args[1] = NULL; if ( signal(SIGCHLD, sigchld) == SIG_ERR ) ERROR_EXIT("signal") for ( i = 0; i < N; i += 1 ) { pid = fork(); if ( pid < 0 ) // fork error ERROR_EXIT("fork") else if ( pid == 0 ) { // child process sleep(N-i+1); exit(0); } } while(1); return 0; }​
התוכנית לא אמורה לעצור, אבל אצלי אני מקבל את הפלט הבא:
child 3032 child 0 child 3031 child 0 child 3030 child 0 child 3029 child 0 child 3028 child -1 errno=ECHILD waitpid: No child processes​
אם מישהו יוכל לבדוק את הקוד אצלו ולדווח (כולל גרסת קומפיילר וקרנל) אני אשמח. אני משתמש ב-GCC 3.3.3 ובקרנל 2.6.4 (עם טלאי בשם WOLK, אבל לא נראה לי שזה משנה).
 

voguemaster

New member
יש סיכוי שיהיה לי משהו בשבילך בערב

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

zagzagzag

New member
תודה. הגעתי במקרה לעמוד באתר

של GNU, ולפי הדוגמה בסוף העמוד הקוד שלי אמור לעבוד. הדוגמה שלהם לא עובדת אצלי (אני מקבל את אותה הודעה). אני אנסה את גם את פורום לינוקס.
 

MotiAd

New member
הסבר קצרצר...

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

voguemaster

New member
מה שאמרו לי

מישהו שאני מכיר שנתקל בבעיה זה שיש פשוט באג במימוש של waitpid.
 

zagzagzag

New member
לא, אני אשם ../images/Emo6.gif

כנראה שלא הבנתי נכון את הדקויות של ה-man. תשובה מ-experts exchange מה שהכי מוזר לי הוא שהדרך לכתוב את הקוד הזה (ש"אוסף" את כל הבנים) היא להתעלם מ-ECHILD. אין משהו שלא מערב שגיאות? בכל מקרה, תודה לך ולמוטי
 

voguemaster

New member
אני מתקשה להאמין ידידי

ואני אסביר לך למה. ה-MAN PAGE אומר בפירוש שאם אין יותר ילדים הוא מחזיר 0 (כשעובדים עם WNOHANG). לפחות בזה שאני הסתכלתי בו.
 

zagzagzag

New member
בדקתי גם ב-cygwin ../images/Emo13.gif

במקרה יצא לי לעבוד קצת עם חלונות אז ניסיתי ב-cygwin, אותן תוצאות. אני חושב שמיציתי את הפלטפורמות שאני יכול לבדוק עליהן, אלא אם אני אמצא את ה-live cd של FreeBSD שזרוק אצלי איפשהו.
אז או שיש באג במימוש של GNU (הפונקציה waitpid היא חלק מ-glibc, נכון?), או שהכוונה ב-man ל-"availible childs" היא רק לתהליכים בנים קיימים (כלומר אם אין אף תהליך בן, הוא אפילו לא נחשב בתור non-availible child - זו שגיאה).
 
למעלה