אנגליתצרפתיתספרדי

Ad


סמל OnWorks

explain_lca2010 - מקוון בענן

הפעל את explain_lca2010 בספק אירוח חינמי של OnWorks על גבי Ubuntu Online, Fedora Online, אמולטור מקוון של Windows או אמולטור מקוון של MAC OS

זוהי הפקודה explain_lca2010 שניתן להפעיל בספק האירוח החינמי של OnWorks באמצעות אחת מתחנות העבודה המקוונות המרובות שלנו, כגון Ubuntu Online, Fedora Online, אמולטור מקוון של Windows או אמולטור מקוון של MAC OS

תָכְנִית:

שֵׁם


explain_lca2010 - לא נמצא מדיום: כאשר הגיע הזמן להפסיק לנסות לקרוא שטרורשל (3).
אכפת.

מוֹטִיבָצִיָה


הרעיון ל-libexplain עלה בדעתי בתחילת שנות ה-1980. בכל פעם שיחת מערכת
מחזיר שגיאה, הקרנל יודע בדיוק מה השתבש... ודוחס את זה לתוך
פחות מ-8 סיביות של ארנו. למרחב המשתמש יש גישה לאותם נתונים כמו הקרנל, זה
אמור להיות אפשרי עבור שטח המשתמש כדי להבין בדיוק מה קרה כדי לעורר את השגיאה
החזר, והשתמש בזה כדי לכתוב הודעות שגיאה טובות.

האם זה יכול להיות כל כך פשוט?

שְׁגִיאָה הודעות as עֲדִינוּת
הודעות שגיאה טובות הן לעתים קרובות אותן משימות של "אחוז אחד" שנפלות בזמן לוח הזמנים
לחץ סוחט את הפרויקט שלך. עם זאת, הודעת שגיאה טובה יכולה להפוך
שיפור לא פרופורציונלי לחוויית המשתמש, כאשר המשתמש נודד לתוך מפחיד
טריטוריה לא ידועה שלא נתקלים בה בדרך כלל. זו משימה לא פשוטה.

כמתכנת זחל, המחבר לא ראה את הבעיה בשגיאה (מדויקת לחלוטין).
הודעות כמו זו:
חריג צף (הליבה הושלכה)
עד שהצביעה על הפרשנות האלטרנטיבית שאינה מתכנת. אבל זה לא ה
הדבר היחיד שגוי בהודעות שגיאה של יוניקס. באיזו תדירות אתה רואה הודעות שגיאה כמו:
$ ./מְטוּפָּשׁ
לא יכול לפתוח קובץ
$
ישנן שתי אפשרויות עבור מפתח בשלב זה:

1.
אתה יכול להפעיל מאתר באגים, כגון gdb(1), או

2.
אתה יכול להשתמש שטרס(1) או מִסבָּך(1) להסתכל פנימה.

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

בדוגמה זו, לעומת זאת, באמצעות שטרס(1) מגלה
$ שטרס -e trace=פתוח ./מְטוּפָּשׁ
open("חלק/קובץ", O_RDONLY) = -1 ENOENT (אין קובץ או ספרייה כאלה)
לא יכול לפתוח קובץ
$
זה הרבה יותר מידע ממה שהודעת השגיאה מספקת. בדרך כלל, ה
קוד מקור טיפשי נראה כך
int fd = פתוח("משהו", O_RDONLY);
if (fd < 0)
{
fprintf(stderr, "לא יכול לפתוח קובץ\n");
יציאה(1);
}
למשתמש לא אומרים אשר קובץ, וגם לא מצליח לספר למשתמש אשר שְׁגִיאָה. היה הקובץ
אפילו שם? האם הייתה בעיית הרשאות? זה כן אומר לך שהוא ניסה לפתוח א
קובץ, אבל זה כנראה היה במקרה.

תפוס את מקל הרמז שלך ולך לנצח איתו את המתכנת הזחל. ספר לו על טעות(3).
בפעם הבאה שתשתמש בתוכנית תראה הודעת שגיאה אחרת:
$ ./מְטוּפָּשׁ
פתוח: אין קובץ או ספרייה כאלה
$
התקדמות, אבל לא מה שציפינו. כיצד המשתמש יכול לתקן את הבעיה אם הודעת השגיאה
לא אומר לו מה הייתה הבעיה? בהסתכלות על המקור, אנו רואים
int fd = פתוח("משהו", O_RDONLY);
if (fd < 0)
{
perror("פתוח");
יציאה(1);
}
הגיע הזמן לעוד ריצה עם מקל הרמזים. הפעם, הודעת השגיאה לוקחת צעד אחד
קדימה וצעד אחד אחורה:
$ ./מְטוּפָּשׁ
משהו: אין כזה קובץ או תקייה
$
עכשיו אנחנו יודעים את הקובץ שהוא ניסה לפתוח, אבל כבר לא מודיעים לנו שזה היה לפתוח(2)
זה נכשל. במקרה הזה זה כנראה לא משמעותי, אבל זה יכול להיות משמעותי עבור
שיחות מערכת אחרות. זה היה יכול להיות creat(2) במקום זאת, פעולה המרמזת על כך
יש צורך בהרשאות שונות.
const char *שם קובץ = "משהו";
int fd = open(שם קובץ, O_RDONLY);
if (fd < 0)
{
perror(שם קובץ);
יציאה(1);
}
הקוד לדוגמה לעיל אופייני למרבה הצער גם למתכנתים שאינם זחלים. זְמַן
לספר ללומד הפדאוואן שלנו על שטרור(3) שיחת מערכת.
$ ./מְטוּפָּשׁ
לפתוח משהו: אין כזה קובץ או תקייה
$
זה ממקסם את המידע שניתן להציג למשתמש. הקוד נראה כך
זֶה:
const char *שם קובץ = "משהו";
int fd = open(שם קובץ, O_RDONLY);
if (fd < 0)
{
fprintf(stderr, "פתח %s: %s\n", שם קובץ, strerror(errno));
יציאה(1);
}
כעת יש לנו את קריאת המערכת, את שם הקובץ ואת מחרוזת השגיאה. זה מכיל את כל
מידע ש שטרס(1) מודפס. זה טוב ככל שיהיה.

או שזה?

מגבלות of טעות ו שטרור
הבעיה שהמחבר ראה, עוד בשנות ה-1980, הייתה שהודעת השגיאה אינה שלמה.
האם "אין קובץ או ספרייה כאלה" מתייחס ל"כמה", או אל "דבר" קובץ ב
ה "כמה"ספרייה?

מבט מהיר בדף האיש עבור שטרור(3) אומר:
strerror - מחרוזת החזרה המתארת ​​את מספר השגיאה
שימו לב היטב: זה מתאר את השגיאה מספר, לא השגיאה.

מצד שני, הגרעין יודע מה הייתה השגיאה. הייתה נקודה ספציפית ב
קוד הליבה, הנגרם ממצב ספציפי, שבו קוד הליבה הסתעף ואמר "לא".
האם תוכנית מרחב משתמש יכולה להבין את המצב הספציפי ולכתוב שגיאה טובה יותר
הודעה?

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

והנה הקטע שמגרר: הקרנל כבר יודע מה שם הקובץ של הקובץ
מתאר משויך ל. למה מתכנת צריך להעביר נתונים מיותרים כולם
הדרך למטה בערימת השיחות רק כדי לשפר הודעת שגיאה שאולי לעולם לא תונפק? ב
במציאות, מתכנתים רבים אינם טורחים, והודעות השגיאה המתקבלות הן גרועות יותר
זה.

אבל אלה היו שנות ה-1980, על PDP11, עם משאבים מוגבלים וללא ספריות משותפות. חזור
לאחר מכן, אין טעם של יוניקס כלול / proc אפילו בצורה ראשונית, וה לסוף(1) תוכנית
היה במרחק של למעלה מעשור. אז הרעיון נגנז כבלתי מעשי.

רמה אין סוף תמיכה
תאר לעצמך שאתה תמיכה ברמה אינסוף. תיאור התפקיד שלך אומר שאתה אף פעם לא
אֵיִ פַּעַם צריך לדבר עם משתמשים. מדוע, אם כן, יש עדיין זרם קבוע של אנשים שרוצים
אתה, גורו יוניקס המקומי, לפענח עוד הודעת שגיאה?

באופן מוזר, 25 שנים מאוחר יותר, למרות מערכת הרשאות פשוטה, מיושמת במלואה
עקביות, לרוב משתמשי יוניקס עדיין אין מושג איך לפענח "אין קובץ או ספרייה כאלה",
או כל אחת מהודעות השגיאה החשוכות האחרות שהם רואים מדי יום. או, לפחות, מסתורי ל
אותם.

האם לא יהיה נחמד אם תמיכה טכנית ברמה ראשונה לא תצטרך לפענח הודעות שגיאה?
האם לא יהיה נחמד לקבל הודעות שגיאה שמשתמשים יוכלו להבין מבלי להתקשר
תמיכה טכנית?

בימים אלה / proc ב-Linux היא יותר ממסוגלת לספק את המידע הדרוש לפענוח
הרוב המכריע של הודעות השגיאה, ומכוונים את המשתמש לסיבה הקרובה להן
בְּעָיָה. במערכות עם מוגבלות / proc יישום, לסוף(1) הפקודה יכולה למלא
רבים מהפערים.

בשנת 2008, זרם הבקשות לתרגום קרה למחבר לעתים קרובות מדי. זה היה
הגיע הזמן לבחון מחדש את הרעיון בן ה-25, ו-libexplain הוא התוצאה.

משתמש LA סִפְרִיָה


הממשק לספרייה מנסה להיות עקבי, היכן שניתן. נתחיל עם א
שימוש לדוגמה שטרור(3):
if (rename(old_path, new_path) < 0)
{
fprintf(stderr, "שנה שם %s %s: %s\n", old_path, new_path,
strerror(errno));
יציאה(1);
}
הרעיון מאחורי libexplain הוא לספק א שטרור(3) שווה ערך עבור כל אחד שיחת מערכת,
מותאם במיוחד לאותה קריאת מערכת, כך שתוכל לספק שגיאה מפורטת יותר
הודעה, המכילה הרבה מהמידע שאתה רואה תחת הכותרת "שגיאות" של הסעיף
2 ו3 איש דפים, בתוספת מידע על תנאים בפועל, טיעון ממשי
ערכים ומגבלות מערכת.

אל האני פָּשׁוּט מקרה
אל האני שטרור(3) החלפה:
if (rename(old_path, new_path) < 0)
{
fprintf(stderr, "%s\n", explain_rename(old_path, new_path));
יציאה(1);
}

אל האני ארנו מקרה
אפשר גם להעביר מפורש ארנו(3) ערך, אם תחילה עליך לעשות כמה
עיבוד שיפריע ארנו, כגון שחזור שגיאות:
if (rename(old_path, new_path < 0))
{
int old_errno = errno;
...קוד זֶה מפריע ארנו...
fprintf(stderr, "%s\n", explain_errno_rename(old_errno,
old_path, new_path));
יציאה(1);
}

אל האני רב חוטים ארונות
חלק מהיישומים הם מרובי פתילים, ולכן אינם מסוגלים לשתף את התוכן הפנימי של libexplain
בַּלָם. אתה יכול לספק חיץ משלך באמצעות
if (בטל קישור (שם נתיב))
{
הודעת char[3000];
explain_message_unlink(הודעה, גודל(הודעה), שם נתיב);
error_dialog(הוֹדָעָה);
return -1;
}
ולמען השלמות, שניהם ארנו(3) ובטוח לחוטים:
ssize_t nbytes = read(fd, data, sizeof(data));
if (nbytes < 0)
{
הודעת char[3000];
int old_errno = errno;
...שגיאה התאוששות...
explain_message_errno_read(הודעה, גודל(הוֹדָעָה),
old_errno, fd, data, sizeof(data));
error_dialog(הוֹדָעָה);
return -1;
}

אלה הם תחליפים עבור strerror_r(3), במערכות שיש להם את זה.

מִמְשָׁק סוכר
סט פונקציות שנוספו כפונקציות נוחות, כדי לחזר למתכנתים להשתמש ב-
ספריית libexplain, מסתבר שהיא פונקציות ה-libexplain הנפוצות ביותר של המחבר ב
תוכניות שורת הפקודה:
int fd = explain_creat_or_die(שם קובץ, 0666);
פונקציה זו מנסה ליצור קובץ חדש. אם זה לא יכול, הוא מדפיס הודעת שגיאה ו
יוצא עם EXIT_FAILURE. אם אין שגיאה, הוא מחזיר את מתאר הקובץ החדש.

פונקציה קשורה:
int fd = explain_creat_on_error(שם קובץ, 0666);
ידפיס את הודעת השגיאה במקרה של כשל, אך גם יחזיר את תוצאת השגיאה המקורית, ו
ארנו(3) אינו מופרע, גם כן.

תעשיות מה היא אַחֵר מערכת שיחות
באופן כללי, לכל קריאת מערכת יש קובץ כולל משלה
#לִכלוֹלשם.h>
שמגדיר אבות טיפוס של פונקציות עבור שש פונקציות:

· להסביר_שם,

· להסביר_שגיאה_שם,

· הסבר_הודעה_שם,

· להסביר_הודעה_שגיאה_שם,

· להסביר_שם_או_למות ו

· להסביר_שם_בשגיאה.

לכל אב טיפוס של פונקציה יש תיעוד של Doxygen, והתיעוד הזה is לֹא הופשט
כאשר קובצי include מותקנים.

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

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

חתול
כך יכולה להיראות תוכנית "חתול" היפותטית, עם דיווח מלא על שגיאות,
באמצעות libexplain.
#include
#לִכלוֹל
#לִכלוֹל
יש כולל אחד ל-libexplain, בתוספת החשודים הרגילים. (אם אתה רוצה להפחית את
עומס קדם-מעבד, אתה יכול להשתמש ב-<libexplain/ הספציפישם.h> כולל.)
חלל סטטי
process(FILE *fp)
{
ל (;;)
{
חיץ צ'אר [4096];
size_t n = explain_fread_or_die(buffer, 1, sizeof(buffer), fp);
אם (!n)
לשבור;
explain_fwrite_or_die(buffer, 1, n, stdout);
}
}
אל האני תהליך הפונקציה מעתיקה זרם קבצים לפלט הסטנדרטי. אם תתרחש שגיאה
עבור קריאה או כתיבה, זה מדווח (ושם הנתיב ייכלל ב-
error) והפקודה יוצאת עם EXIT_FAILURE. אנחנו אפילו לא דואגים לעקוב אחר
שמות נתיבים, או העברתם בערימת השיחות.
int
main(int argc, char **argv)
{
ל (;;)
{
int c = getopt(argc, argv, "o:");
if (c == EOF)
לשבור;
מתג (ג)
{
מקרה 'o':
explain_freopen_or_die(optarg, "w", stdout);
לשבור;
החלק המהנה בקוד זה הוא ש-libexplain יכול לדווח על שגיאות לְרַבּוֹת מה היא שם נתיב גם
אם אתה לא פתח מחדש במפורש את stdout כפי שנעשה כאן. אנחנו אפילו לא דואגים
מעקב אחר שם הקובץ.
ברירת מחדל:
fprintf(stderr, "שימוש: %ss [ -o ] ...\n",
argv[0]);
החזר EXIT_FAILURE;
}
}
if (optind == argc)
process(stdin);
אחר
{
while (optind < argc)
{
FILE *fp = explain_fopen_or_die(argv[optind]++, "r");
process(fp);
explain_fclose_or_die(fp);
}
}
הפלט הסטנדרטי ייסגר באופן מרומז, אך מאוחר מדי מכדי שניתן יהיה לקבל דוח שגיאה
הונפק, אז אנחנו עושים את זה כאן, למקרה שה-I/O המאוחסן לא כתב כלום עדיין, ו
יש שגיאת ENOSPC או משהו.
explain_fflush_or_die(stdout);
החזר EXIT_SUCCESS;
}
זה הכל. דיווח שגיאות מלא, קוד ברור.

רוסטי סולם of מִמְשָׁק אלוהים
לאלו מכם שלא מכירים את זה, "איך אני עושה את זה קשה לשימוש לרעה?" של ראסטי ראסל?
הדף הוא ספר חובה עבור מעצבי API.
http://ozlabs.org/~rusty/index.cgi/tech/2008‐03‐30.html

10. זה בלתי אפשרי ל לקבל לא בסדר.

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

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

ספריית libexplain נועדה להיות בטוחה בשרשור. סביר להניח שיותר שימוש בעולם האמיתי
לחשוף מקומות שאפשר לשפר את זה.

הבעיה הגדולה ביותר היא עם שמות הפונקציות עצמם. כי ל-C אין
name‐spaces, ספריית libexplain משתמשת תמיד בקידומת explain_ name. זה
דרך מסורתית ליצור מרחב פסאודו-שם על מנת למנוע התנגשויות סמלים.
עם זאת, זה מביא לכמה שמות שנשמעים לא טבעיים.

9. אל האני מַהְדֵר or קישורים לא לתת אתה לקבל it לא בסדר.

טעות נפוצה היא להשתמש ב-explain_open במקום בו נועדה explain_open_or_die.
למרבה המזל, המהדר יוציא לעתים קרובות שגיאת סוג בשלב זה (לְמָשָׁל לא יכול להקצות
const char * rvalue ל-int lvalue).

8. אל האני מַהְדֵר יצטרך להזהיר if אתה לקבל it לא בסדר.

אם נעשה שימוש ב-explain_rename כאשר נועדו explain_rename_or_die, זה יכול לגרום למשהו אחר
בעיות. ל-GCC יש תכונה שימושית של warn_unused_result, ואת ה-libexplain
הספרייה מצרף את זה לכל ההסבר_שם קריאות פונקציה כדי להפיק אזהרה כאשר אתה
לעשות את הטעות הזו. שלבו את זה עם gcc -שגיאה כדי לקדם את זה לרמה 9 טוב.

7. אל האני ברור להשתמש is (כנראה) מה היא לתקן אחד.

שמות הפונקציות נבחרו כדי להעביר את משמעותן, אבל זה לא תמיד
מוּצלָח. בזמן להסביר_שם_או_למות ולהסביר_שם_on_error הם די תיאוריים,
קשה יותר לפענח את גרסאות בטוחות החוטים הפחות בשימוש. אבות הטיפוס של הפונקציות עוזרים ל
מהדר לקראת הבנה, והערות Doxygen בקבצי הכותרת עוזרות למשתמש
לקראת הבנה.

6. אל האני שם אומר אתה אֵיך ל להשתמש זה.

חשוב במיוחד לקרוא להסביר_שם_or_die כמו "הסבר (שם או למות)".
לשימוש בקידומת רווח ההסבר_ שם עקבית יש כמה תופעות לוואי מצערות ב-
גם מחלקת ברורות.

סדר המילים בשמות מעיד גם על סדר הטיעונים. הטיעון
רשימות תמיד סוף עם אותם ארגומנטים שהועברו לקריאת המערכת; את כל of אוֹתָם. אם
_errno_ מופיע בשם, הארגומנט שלו תמיד קודם לארגומנטים של קריאת המערכת. אם
_הודעה_ מופיעה בשם, שני הארגומנטים שלו תמיד קודמים.

5. Do it תקין or it יצטרך לשבור at זמן ריצה.

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

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

4. עקבו משותף כנס ו אתה לקבל it ימין.

מכיוון ש-C אין מרחבי שמות, ספריית libexplain משתמשת תמיד בשם explain_
קידומת. זוהי הדרך המסורתית ליצור מרחב פסאודו-שם כדי להימנע
התנגשויות סמלים.

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

3. חומר עיוני מה היא תיעוד ו אתה לקבל it ימין.

ספריית libexplain שואפת לקבל תיעוד מלא של Doxygen עבור כל אחד ואחד
קריאת API ציבורית (וגם פנימית).

הודעה תוכן


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

כשעיין שוב בדוגמה הראשונה שלנו, הקוד ירצה את זה אם הוא משתמש ב-libexplain:
int fd = explain_open_or_die("משהו/דבר", O_RDONLY, 0);
ייכשל עם הודעת שגיאה כזו
open(pathname = "חלק/קובץ", דגלים = O_RDONLY) נכשל, אין קובץ או ספרייה כאלה
(2, ENOENT) כי אין ספרייה "כמה" בספרייה הנוכחית
זה מתפרק לשלושה חלקים
שיחת מערכת נִכשָׁל, שגיאת מערכת כי
הסבר

לפני כי
אפשר לראות את החלק של ההודעה לפני "כי" כטכני מדי עד לא
משתמשים טכניים, בעיקר כתוצאה מהדפסה מדויקת של המערכת קוראים לעצמה ב-
תחילת הודעת השגיאה. וזה נראה כמו שטרס(1) פלט, לחנון בונוס
נקודות.
open(pathname = "חלק/קובץ", דגלים = O_RDONLY) נכשל, אין קובץ או ספרייה כאלה
(2, ENOENT)
חלק זה של הודעת השגיאה חיוני למפתח כאשר הוא כותב את הקוד,
וחשוב באותה מידה למתחזק שצריך לקרוא דוחות באגים ולתקן באגים ב-
קוד. זה אומר בדיוק מה נכשל.

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

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

ייצוג שיחות המערכת נותן הקשר גם לשאר ההודעה. אם צריך
מתעוררת, ניתן להתייחס לטיעון קריאת המערכת הפוגע בשם בהסבר
אחרי "כי". בנוסף, כל המחרוזות מצוטטות במלואן וממחרוזות C בריחה, אז
שורות חדשות מוטבעות ותווים שאינם מודפסים לא יגרמו למסוף של המשתמש לצאת
מְבוּלגָן.

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

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

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

בינאום
רוב הודעות השגיאה בספריית libexplain הועברו לבינלאומיות. שם
עדיין אין לוקליזציות, אז אם אתה רוצה את ההסברים בשפת האם שלך,
בבקשה תרמו.

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

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

לאחר המוות
יש מקרים שבהם תוכנית עדיין לא השתמשה ב-libexplain, ואתה לא יכול להשתמש שטרס(1)
אוֹ. יש להסביר(1) פקודה הכלולה ב-libexplain שניתן להשתמש בה
לפענח הודעות שגיאה, אם מצב המערכת הבסיסית לא השתנה יותר מדי.
$ להסביר שינוי השם Foo /tmp/bar/baz -e ENOENT
rename(oldpath = "foo", newpath = "/tmp/bar/baz") נכשל, אין קובץ או ספרייה כאלה
(2, ENOENT) כי אין ספריית "סרגל" בנתיב החדש "/ Tmp"ספרייה
$
שים לב כיצד עמימות הנתיב נפתרת על ידי שימוש בשם הארגומנט של קריאת המערכת. שֶׁל
כמובן, אתה צריך לדעת את השגיאה ואת קריאת המערכת להסביר(1) להיות שימושי. בתור
מלבד זאת, זו אחת הדרכים שבהן משתמשת חבילת הבדיקות האוטומטיות של libexplain כדי לאמת זאת
libexplain עובד.

פִילוֹסוֹפִיָה
"ספר לי הכל, כולל דברים שלא ידעתי לחפש."

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

כאשר ניתן יהיה לספק מידע נוסף, libexplain יעשה זאת. ככל שהמשתמש פחות
צריך לאתר את עצמם, יותר טוב. משמעות הדבר היא ש-UID מלווים ב-
שם משתמש, GIDs מלווים בשם הקבוצה, PIDs מלווים בתהליך
שם, מתארי קבצים וזרמים מלווים בשם הנתיב, וכו '.

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

ספריית libexplain מנסה להשתמש בכמה שפחות ערימה, ובדרך כלל אף אחת. זה
כדי להימנע מלהטריד את מצב התהליך, עד כמה שניתן, למרות שלפעמים כן
בלתי נמנע.

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

ספריית libexplain אינה מפריעה למטפלי האותות של תהליך. זה עושה
קביעה אם מצביע יפגם באתגר, אך לא בלתי אפשרי.

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

ספריית libexplain מורכבת עם תמיכה בקבצים גדולים. אין גדול/קטן
סכִיזוֹפרֶנִיָה. כאשר זה משפיע על סוגי הארגומנטים ב-API, ותונפק שגיאה
אם הגדרות הקובץ הגדולות הנחוצות אינן.

FIXME: נדרשת עבודה כדי לוודא שמכסות מערכת הקבצים מטופלות בקוד. זֶה
חל על חלקם getrlimit(2) גם גבולות.

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

נתיב פתרון הבעיה


גרסה קצרה: ראה נתיב_רזולוציה(7).

גרסה ארוכה: רוב המשתמשים מעולם לא שמעו עליה נתיב_רזולוציה(7), ומשתמשים מתקדמים רבים
מעולם לא קראתי אותו. הנה גרסה מוערת:

שלב 1: הַתחָלָה of מה היא החלטה תהליך
אם שם הנתיב מתחיל באות הנטוי ("/"), ספריית חיפוש ההתחלה היא
ספריית השורש של תהליך ההתקשרות.

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

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

אם לתהליך אין הרשאת חיפוש בספריית החיפוש הנוכחית, הודעה EACCES
מוחזרת שגיאה ("הרשאה נדחתה").
open(pathname = "/home/archives/.ssh/private_key", flags = O_RDONLY) נכשל,
ההרשאה נדחתה (13, EACCES) מכיוון שלתהליך אין הרשאת חיפוש
אל ספריית הנתיב "/home/archives/.ssh", התהליך יעיל GID 1000
"pmiller" אינו תואם לבעל הספרייה 1001 "ארכיון" ולכן הבעלים
מצב ההרשאה "rwx" מתעלם, מצב ההרשאה האחרים הוא "---", וה-
התהליך אינו מיוחס (אין לו את יכולת DAC_READ_SEARCH)

אם הרכיב לא נמצא, מוחזרת שגיאת ENOENT ("אין קובץ או ספרייה כאלה").
unlink(pathname = "/home/microsoft/rubish") נכשל, אין קובץ או ספרייה כאלה (2,
ENOENT) כי אין ספריית "מיקרוסופט" בשם הנתיב "/ בית"ספרייה

יש גם תמיכה מסוימת למשתמשים כשהם מקלידים שגוי שמות נתיבים, ומציעים הצעות מתי
ENOENT מוחזר:
open(pathname = "/user/include/fcntl.h", flags = O_RDONLY) נכשל, אין קובץ כזה או
ספרייה (2, ENOENT) מכיוון שאין ספריית "משתמש" בשם הנתיב "/"
ספריה, האם התכוונת לספריית "usr" במקום זאת?

אם הרכיב נמצא, אך אינו ספרייה או קישור סמלי, ENOTDIR
מוחזרת שגיאה ("לא ספרייה").
open(pathname = "/home/pmiller/.netrc/lca", flags = O_RDONLY) נכשל, לא
ספרייה (20, ENOTDIR) בגלל הקובץ הרגיל ".netrc" בשם הנתיב
ספריית "/home/pmiller" נמצאת בשימוש כספרייה כאשר היא לא

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

אם הרכיב נמצא והוא קישור סמלי (סימבולי), קודם כל פותרים את הסימבולי הזה
קישור (עם ספריית הבדיקה הנוכחית בתור ספריית חיפוש מתחילה). על טעות, זה
שגיאה מוחזרת. אם התוצאה אינה ספרייה, מוחזרת שגיאת ENOTDIR.
unlink(pathname = "/tmp/dangling/rubbish") נכשל, אין קובץ או ספרייה כאלה (2,
ENOENT) כי הקישור הסמלי "המתנדנד" בשם הנתיב "/ Tmp"ספרייה
מתייחס ל"שום מקום" שאינו קיים
אם הרזולוציה של ה-Symlink מצליחה ומחזירה ספריה, אנו מגדירים את הנוכחי
חפש את ספריית הספרייה, ועבור לרכיב הבא. שימו לב שה-
תהליך הפתרון כאן כרוך ברקורסיה. על מנת להגן על הקרנל מפני מחסנית
הצפה, וגם כדי להגן מפני מניעת שירות, יש מגבלות על המקסימום
עומק רקורסיה, ועל המספר המרבי של קישורים סמליים שאחריהם. שגיאת ELOOP היא
מוחזר כאשר חריגה מהמקסימום ("יותר מדי רמות של קישורים סמליים").
open(pathname = "/tmp/dangling", flags = O_RDONLY) נכשל, יותר מדי רמות של
קישורים סמליים (40, ELOOP) מכיוון שנתקלה בלולאת קישור סמלית
שם נתיב, מתחיל ב-"/tmp/dangling"
אפשר גם לקבל שגיאת ELOOP או EMLINK אם יש יותר מדי סימלינקים, אבל לא
זוהתה לולאה.
open(pathname = "/tmp/rabbit‐hole", flags = O_RDONLY) נכשל, יותר מדי רמות של
קישורים סמליים (40, ELOOP) מכיוון שנתקלו בקישורים סמליים רבים מדי
שם נתיב (8)
שימו לב כיצד מודפסת גם המגבלה בפועל.

שלב 3: מה היא סופי כניסה
חיפוש הרכיב הסופי של שם הנתיב הולך בדיוק כמו של כל השאר
רכיבים, כמתואר בשלב הקודם, עם שני הבדלים:

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

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

(iii)
אפשר גם להיתקל בבעיה עם הרכיב האחרון אם זה קישור סמלי
ואין לעקוב אחריו. לדוגמה, שימוש ב- לפתוח(2) דגל O_NOFOLLOW:
open(pathname = "a‐symlink", flags = O_RDONLY | O_NOFOLLOW) נכשל, יותר מדי רמות של
קישורים סמליים (ELOOP) מכיוון שצוין O_NOFOLLOW אך שם הנתיב מתייחס ל-a
קישור סמלי

(iv)
זה נפוץ שמשתמשים עושים טעויות בעת הקלדת שמות נתיבים. ספריית libexplain
מנסה להציע הצעות כאשר ENOENT מוחזר, למשל:
open(pathname = "/usr/include/filecontrl.h", flags = O_RDONLY) נכשל, אין קובץ כזה או
ספרייה (2, ENOENT) מכיוון שאין קובץ רגיל "filecontrl.h" בשם הנתיב
"/ usr / include", האם התכוונת לקובץ הרגיל "fcntl.h" במקום זאת?

(v) ייתכן גם שהרכיב הסופי נדרש להיות משהו אחר מאשר א
קובץ רגיל:
readlink(שם נתיב = "רק-קובץ", data = 0x7F930A50, data_size = 4097) נכשל,
ארגומנט לא חוקי (22, EINVAL) מכיוון ששם הנתיב הוא קובץ רגיל, לא קישור סמלי

(vi)
FIXME: טיפול בסיבית "t".

גבולות
ישנן מספר מגבלות לגבי שמות נתיבים ושמות קבצים.

מגבלת אורך שם נתיב
יש אורך מקסימלי לשמות נתיבים. אם שם הנתיב (או ביניים כלשהו
שם הנתיב שהושג בעת פתרון קישורים סמליים) ארוך מדי, ENAMETOOLONG
מוחזרת שגיאה ("שם הקובץ ארוך מדי"). שימו לב כיצד מגבלת המערכת כלולה
בהודעת השגיאה.
open(pathname = "ארוך מאוד", flags = O_RDONLY) נכשלו, שם הקובץ ארוך מדי (36,
ENAMETOOLONG) מכיוון ששם הנתיב חורג מאורך הנתיב המרבי של המערכת (4096)

מגבלת אורך שם הקובץ
לכמה גרסאות Unix יש הגבלה על מספר הבתים בכל רכיב נתיב.
חלקם מתמודדים עם זה בשקט, וחלקם נותנים ENAMETOOLONG; ההסבר הליבי
שימושים בספרייה pathconf(3) _PC_NO_TRUNC כדי לדעת איזה. אם שגיאה זו מתרחשת, ה
ספריית libexplain תציין את המגבלה בהודעת השגיאה, המגבלה היא
הושג מ pathconf(3) _PC_NAME_MAX. שימו לב כיצד מגבלת המערכת כלולה
בהודעת השגיאה.
open(pathname = "למערכת 7/היו רק 14 תווים", flags = O_RDONLY) נכשל, קובץ
השם ארוך מדי (36, ENAMETOOLONG) כי הרכיב "היו רק 14 תווים" הוא
יותר ממגבלת המערכת (14)

שם נתיב ריק
ב-Unix המקורי, שם הנתיב הריק התייחס לספרייה הנוכחית.
כיום POSIX קובע שאסור לפתור שם נתיב ריק בהצלחה.
open(pathname = "", flags = O_RDONLY) נכשל, אין קובץ או ספרייה כאלה (2,
ENOENT) מכיוון ש-POSIX קובע שאסור לפתור שם נתיב ריק
בהצלחה

הרשאות
סיביות ההרשאה של קובץ מורכבות משלוש קבוצות של שלוש סיביות. הקבוצה הראשונה של
XNUMX משמש כאשר מזהה המשתמש האפקטיבי של תהליך ההתקשרות שווה לזהות הבעלים של
קוֹבֶץ. הקבוצה השנייה של שלוש משמשת כאשר מזהה הקבוצה של הקובץ שווה ל-
מזהה קבוצתי יעיל של תהליך ההתקשרות, או שהוא אחד מזהי הקבוצה המשלימים של
תהליך התקשרות. כאשר אף אחת מהן לא מתקיימת, משתמשים בקבוצה השלישית.
open(pathname = "/ etc / passwd", flags = O_WRONLY) נכשלו, הרשאה נדחתה (13,
EACCES) מכיוון שלתהליך אין הרשאת כתיבה ל-passwd הרגיל
קובץ בשם הנתיב "/וכו" מדריך, התהליך יעיל UID 1000 "pmiller"
לא תואם לבעל הקובץ הרגיל 0 "שורש" ולכן מצב הרשאת הבעלים "rw-"
הוא מתעלם, מצב ההרשאה של האחרים הוא "r--", והתהליך אינו מוגן
(אין לו את יכולת DAC_OVERRIDE)
ניתן מקום לא מבוטל להסבר זה, מכיוון שרוב המשתמשים אינם יודעים זאת
כך פועלת מערכת ההרשאות. בפרט: הבעלים, הקבוצה ואחרים
ההרשאות הן בלעדיות, הן אינן "או" יחד.

מוּזָר ו מעניין מערכת שיחות


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

ENOMEDIUM, לא בינוני מצא
פעולת העתקת התקליטור הייתה מקור הכותרת של מאמר זה.
$ dd if=/dev/cdrom of=fubar.iso
dd: פתיחת "/dev/cdrom": לא נמצא מדיום
$
המחבר תהה מדוע המחשב שלו אומר לו שאין דבר כזה מדיום
בינוני. לבד מהעובדה שמספרים עצומים של דוברי אנגלית שפת אם לא
אפילו מודע לכך ש"מדיה" הוא ריבוי, שלא לדבר על זה ש"מדיום" הוא היחיד שלו, המחרוזת
חזר על ידי שטרור(3) עבור ENOMEDIUM הוא כל כך קצר עד שהוא כמעט חופשי לחלוטין
תוכן.

מתי לפתוח(2) מחזיר ENOMEDIUM זה יהיה נחמד אם ספריית libexplain תוכל להרחיב את א
מעט על זה, בהתבסס על סוג הכונן. לדוגמה:
... כי אין דיסק בכונן התקליטונים
... כי אין דיסק בכונן התקליטורים
... כי אין קלטת בדיסק הטייפ
... כי אין מקל זיכרון בקורא הכרטיסים

וכך קרה...
open(pathname = "/dev/cdrom", flags = O_RDONLY) נכשל, לא נמצא מדיום (123,
ENOMEDIUM) מכיוון שלא נראה שיש דיסק בכונן התקליטורים
הטריק, שהכותב לא היה מודע לו קודם לכן, היה לפתוח את המכשיר באמצעות
דגל O_NONBLOCK, שיאפשר לך לפתוח כונן ללא מדיום בתוכו. אז אתה
בעיה ספציפית למכשיר ioctls(2) בקשות עד שתבינו מה זה לעזאזל. (לֹא
בטוח אם זה POSIX, אבל נראה שזה גם עובד ככה ב-BSD ו-Solaris, לפי
מה היא וודים(1) מקורות.)

שימו לב גם לשימושים השונים של "דיסק" ו"דיסק" בהקשר. מקורו של תקן התקליטורים
בצרפת, אבל לכל השאר יש "ק".

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

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

קשה יותר לבדוק מחרוזות C, כי במקום מצביע וגודל, אנחנו רק
יש מצביע. כדי לקבוע את הגודל נצטרך למצוא את ה-NUL, וזה יכול
segfault, catch‐22.

כדי לעקוף זאת, ספריית libexplain משתמשת ב- lstat(2) קריאה למערכת (עם ידוע
טיעון שני טוב) כדי לבדוק מחרוזות C עבור תקפות. החזרת כשל && errno == EFAULT
הוא "לא", וכל דבר אחר הוא "כן". זה כמובן מגביל מחרוזות ל-PATH_MAX
תווים, אבל זה בדרך כלל לא מהווה בעיה עבור ספריית libexplain, כי כן
כמעט תמיד המיתרים הארוכים ביותר שאכפת לו מהם.

EMFILE, גם רב לפתוח קבצים
שגיאה זו מתרחשת כאשר לתהליך כבר יש את המספר המרבי של מתארי קבצים פתוחים.
אם יש להדפיס את המגבלה בפועל, וספריית libexplain מנסה, אינך יכול לפתוח
קובץ פנימה / proc לקרוא מה זה.
open_max = sysconf(_SC_OPEN_MAX);
זה לא כל כך קשה, יש א sysconf(3) דרך השגת הגבול.

ENFILE, גם רב לפתוח קבצים in מערכת
שגיאה זו מתרחשת כאשר מגבלת המערכת על המספר הכולל של קבצים פתוחים הייתה
השיג. במקרה זה אין שימוש sysconf(3) דרך להשיג את הגבול.

אם חפירה עמוקה יותר, אפשר לגלות שבלינוקס יש א / proc ערך שיכולנו לקרוא אליו
להשיג את הערך הזה. מלכוד-22: נגמרו לנו מתארי הקבצים, אז אנחנו לא יכולים לפתוח קובץ אליו
לקרוא את הגבול.

בלינוקס יש קריאת מערכת להשיג אותו, אבל אין לו פונקציית [e]glibc wrapper, אז
אתה צריך לעשות הכל בזהירות רבה:
ארוך
explain_maxfile(void)
{
#ifdef __linux__
struct __sysctl_args args;
int32_t maxfile;
size_t maxfile_size = גודל(maxfile);
int name[] = { CTL_FS, FS_MAXFILE };
memset(&args, 0, sizeof(struct __sysctl_args));
args.name = שם;
args.nlen = 2;
args.oldval = &maxfile;
args.oldlenp = &maxfile_size;
if (syscall(SYS__sysctl, &args) >= 0)
החזר maxfile;
#endif
return -1;
}
זה מאפשר את ההגבלה להיכלל בהודעת השגיאה, כאשר היא זמינה.

EINVAL "לא חוקי טַעֲנָה" vs ENOSYS "פוּנקצִיָה לֹא מוטמע"
פעולות לא נתמכות (כגון סימלינק(2) במערכת קבצים FAT) אינם מדווחים
באופן עקבי משיחת מערכת אחת לאחרת. אפשר לקבל EINVAL או
ENOSYS חזרה.

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

הערות זֶה ארנו(3) is לֹא תמיד סט
יש מקרים שבהם יש צורך לקרוא את המקורות [e]glibc כדי לקבוע כיצד ו
כאשר שגיאות מוחזרות עבור שיחות מערכת מסוימות.

feof(3), קובץ(3)
לעתים קרובות מניחים שפונקציות אלו אינן יכולות להחזיר שגיאה. זה נכון רק אם
מה היא זרם הארגומנט תקף, אולם הם מסוגלים לזהות פסול
מַצבִּיעַ.

fpathconf(3), pathconf(3)
ערך ההחזר של fpathconf(2) ו pathconf(2) יכול להיות -1 באופן לגיטימי, כך זה
צריך לראות אם ארנו(3) נקבע במפורש.

ioctls(2)
ערך ההחזר של ioctls(2) יכול להיות באופן לגיטימי -1, אז יש צורך לראות אם
ארנו(3) נקבע במפורש.

readdir(3)
ערך ההחזר של readdir(3) הוא NULL הן עבור שגיאות והן עבור סוף הקובץ. זה
צריך לראות אם ארנו(3) נקבע במפורש.

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

strtod(3), סטרטול(3), strtold(3), strtoll(3), strtoul(3), strtoull(3)
פונקציות אלה מחזירות 0 בשגיאה, אבל זה גם ערך החזרה לגיטימי. זה
צריך לראות אם ארנו(3) נקבע במפורש.

ungetc(3)
בעוד שרק תו בודד של גיבוי מחויב על ידי תקן ANSI C, זה מתברר
החוצה ש-[e]glibc מאפשר יותר... אבל זה אומר שהוא יכול להיכשל עם ENOMEM. זה יכול
גם נכשל עם EBADF אם fp הוא מזויף. הכי קשה מכולם, אם עוברים שגיאה של EOF
החזרה מתרחשת, אך errno לא מוגדר.

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

ENOSPC, לא שטח עזבו on מכשיר
כאשר שגיאה זו מתייחסת לקובץ במערכת קבצים, ספריית libexplain מדפיסה את ה-mount
הנקודה של מערכת הקבצים עם הבעיה. זה יכול להפוך את מקור השגיאה להרבה
יותר ברור.
write(fildes = 1 "example", data = 0xbfff2340, data_size = 5) נכשל, לא נותר מקום
במכשיר (28, ENOSPC) מכיוון שמערכת הקבצים המכילה קבצים ("/ בית") אין לו
יותר מקום לנתונים
ככל שמתווספת עוד תמיכה במכשירים מיוחדים, הודעות שגיאה צפויות לכלול את המכשיר
השם והגודל האמיתי של המכשיר.

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

ככל שמתווספת עוד תמיכה במכשירים מיוחדים, הודעות שגיאה צפויות לכלול את המכשיר
שם וסוג.
open(pathname = "/dev/fd0", O_RDWR, 0666) נכשל, מערכת קבצים לקריאה בלבד (30, EROFS)
כי התקליטון כולל את הלשונית הגנת כתיבה

...כי תקליטור לא ניתן לכתיבה
...מכיוון שלכרטיס הזיכרון יש את הלשונית להגנת כתיבה
...מכיוון שלסרט המגנטי בגודל ½ אינץ' אין טבעת כתיבה

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

עם זאת, ישנן מגבלות: אתה יכול לשנות שם ספרייה רק ​​על גבי אחר
ספרייה אם ספריית היעד אינה ריקה.
rename(oldpath = "foo", newpath = "bar") נכשל, ספרייה לא ריקה (39,
ENOTEMPTY) מכיוון ש-newpath אינה ספרייה ריקה; כלומר, הוא מכיל ערכים
חוץ מ "." ו".."
אתה גם לא יכול לשנות שם ספרייה על גבי ספרייה שאינה כתובה.
rename(oldpath = "foo", newpath = "bar") נכשל, לא ספריה (20, ENOTDIR)
כי oldpath הוא ספרייה, אבל newpath הוא קובץ רגיל, לא ספרייה
גם לא מותר להיפך
rename(oldpath = "foo", newpath = "bar") נכשל, האם ספריה (21, EISDIR)
כי newpath הוא ספרייה, אבל oldpath הוא קובץ רגיל, לא ספרייה

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

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

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

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

הרפתקאות IN IOCTL תמיכה


אל האני ioctls(2) שיחת מערכת מספקת למחברי מנהלי התקנים דרך לתקשר איתם
מרחב משתמש שאינו מתאים ל- API הקיים של הליבה. לִרְאוֹת ioctl_list(2).

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

הרושם הראשוני הוא שאתה יכול לפענח ioctls(2) בקשות באמצעות מתג ענק
הַצהָרָה. מתברר שזה בלתי אפשרי כי מהר מאוד מגלים שכן
בלתי אפשרי לכלול את כל כותרות המערכת הנחוצות המגדירות את השונות ioctls(2)
בקשות, כי הם מתקשים לשחק יפה אחד עם השני.

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

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

ההטמעה של ioctls(2) תמיכה בספריית libexplain היא לקיים טבלה של
מצביע אל ioctls(2) מתארי בקשה. כל אחד מהתיאורים הללו כולל אופציונלי
מצביע לפונקציית ביעור.

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

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

אל האני ioctls(2) אב טיפוס נראה כך:
int ioctl(int fildes, int request, ...);
מה שאמור להפעיל את אזעקות הבטיחות שלך. פנימי ל-[e]glibc, זה הופך
במגוון צורות:
int __ioctl(int fildes, int request, long arg);
int __ioctl(int fildes, int request, void *arg);
וממשק ליבת Linux syscall מצפה
asmlinkage long sys_ioctl(לא חתום int fildes, בקשת int לא חתומה, לא חתום ארוך
arg);
השונות הקיצונית של הטיעון השלישי מהווה אתגר, כאשר ספריית ה-libexplain
מנסה להדפיס ייצוג של הטיעון השלישי הזה. עם זאת, פעם אחת את מספר הבקשה
היה ברור, לכל ערך בטבלת ioctl של ספריית libexplain יש
פונקציית print_data מותאמת אישית (OO נעשה באופן ידני).

הסברים
יש פחות בעיות לקבוע את ההסבר שיש להשתמש בו. פעם מספר הבקשה
לא ברור, לכל ערך בטבלת ioctl של ספריית libexplain יש התאמה אישית
פונקציית print_explanation (שוב, OO נעשה באופן ידני).

בניגוד לסעיף 2 וסעיף 3 קריאות מערכת, רובן ioctls(2) לבקשות אין שגיאות
מְתוֹעָד. זה אומר, כדי לתת תיאורי שגיאה טובים, יש צורך לקרוא את הקרנל
מקורות לגלות

· מה ארנו(3) ניתן להחזיר ערכים, וכן

· הסיבה לכל שגיאה.

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

EINVAL vs ENOTTY
המצב אפילו יותר גרוע עבור ioctls(2) בקשות מאשר לשיחות מערכת, עם EINVAL ו
ENOTTY שניהם משמשים כדי לציין כי an ioctls(2) הבקשה אינה הולמת בכך
הקשר, ומדי פעם ENOSYS, ENOTSUP ו-EOPNOTSUPP (נועדו לשמש עבור שקעים) כמו
נו. ישנן הערות במקורות ליבת לינוקס שמצביעות על פרוגרסיביות
ניקוי מתבצע. עבור כאוס נוסף, BSD מוסיף ENOIOCTL לבלבול.

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

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

אב הטיפוס של הפונקציה syscall שלמעלה ייכתב טוב יותר
long sys_ioctl(לא חתום int fildes, בקשת int לא חתומה, intptr_t arg);
הבעיה היא הדיסוננס הקוגניטיבי המושרה על ידי מכשיר ספציפי או ספציפי למערכת קבצים
ioctls(2) יישומים, כגון:
long vfs_ioctl(קובץ struct *filp, unsigned int cmd, unsigned long arg);
הרוב של ioctls(2) לבקשות יש למעשה ארגומנט שלישי int *arg. אבל שיש את זה
הוכרז ארוך מוביל לקוד שמתייחס לזה כאל ארוך *arg. זה לא מזיק ב-32 סיביות
(sizeof(long) == sizeof(int)) אבל מגעיל ב-64-bits (sizeof(long) != sizeof(int)).
בהתאם לאופי, אתה מקבל או לא מקבל את הערך שאתה מצפה לו, אבל אתה תמיד לקבל
שרבוט זיכרון או שרבוט מחסנית גם כן.

כותב את כל אלה בתור
int ioctl(int fildes, int request, ...);
int __ioctl(int fildes, int request, intptr_t arg);
long sys_ioctl(לא חתום int fildes, בקשת int לא חתומה, intptr_t arg);
long vfs_ioctl(קובץ struct *filp, int cmd לא חתום, intptr_t arg);
מדגיש שהמספר השלם הוא רק מספר שלם כדי לייצג כמות שהיא כמעט
תמיד סוג מצביע לא קשור.

סיכום


השתמש ב-libexplain, המשתמשים שלך יאהבו את זה.

זכויות יוצרים


libexplain גרסה 1.4
זכויות יוצרים (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014 פיטר מילר

השתמש ב-explain_lca2010 באינטרנט באמצעות שירותי onworks.net


שרתים ותחנות עבודה בחינם

הורד אפליקציות Windows & Linux

  • 1
    לְגִימָה אֲרוּכָּה
    לְגִימָה אֲרוּכָּה
    SWIG הוא כלי לפיתוח תוכנה
    שמחבר בין תוכניות הכתובות ב-C ו
    C++ עם מגוון של רמות גבוהות
    שפות תכנות. SWIG משמש עם
    שונה...
    הורד את SWIG
  • 2
    WooCommerce Nextjs React Theme
    WooCommerce Nextjs React Theme
    ערכת נושא WooCommerce של React, בנוי עם
    הבא JS, Webpack, Babel, Node ו
    אקספרס, באמצעות GraphQL ו-Apollo
    לָקוּחַ. WooCommerce Store ב-React(
    מכיל: מוצרים...
    הורד את WooCommerce Nextjs React Theme
  • 3
    archlabs_repo
    archlabs_repo
    ריפו חבילה עבור ArchLabs זהו
    יישום שניתן גם לאחזר
    החל מ-
    https://sourceforge.net/projects/archlabs-repo/.
    זה התארח ב-OnWorks ב...
    הורד את archlabs_repo
  • 4
    פרויקט זפיר
    פרויקט זפיר
    פרויקט זפיר הוא דור חדש
    מערכת הפעלה בזמן אמת (RTOS) כי
    תומך במספר חומרה
    ארכיטקטורות. הוא מבוסס על א
    ליבת טביעת רגל קטנה...
    הורד את Zephyr Project
  • 5
    SCons
    SCons
    SCons הוא כלי לבניית תוכנה
    זו אלטרנטיבה מעולה ל-
    כלי לבנות קלאסי "Make" כי
    כולנו מכירים ואוהבים. SCons הוא
    יישם א...
    הורד SCons
  • 6
    PSeInt
    PSeInt
    PSeInt הוא מתורגמן פסאודו-קוד עבור
    סטודנטים לתכנות דוברי ספרדית.
    מטרתו העיקרית היא להיות כלי ל
    ללמוד ולהבין את הבסיס
    קונספט...
    הורד את PSeInt
  • עוד »

פקודות לינוקס

Ad