תוכנית TSR

בסביבת מערכת ההפעלה DOS, **תוכנית TSR** (ראשי תיבות באנגלית: Terminate and Stay Resident) היא סוג מסוים של תוכנית שנשארת בזיכרון לאחר סיומה, על מנת לחזור לפעולה מאוחר יותר. טכניקה זו אפשרה לעקוף במידה מסוימת את המגבלה המובנית של DOS, שמאפשרת הרצה של תוכנית אחת בכל פעם.

## רקע היסטורי
תוכניות TSR הופיעו בשנות ה-1980 המוקדמות ויועדו למחשבים תואמי PC שהריצו את מערכת ההפעלה DOS. DOS הייתה מערכת הפעלה שלא תמכה בריבוי משימות, ואפשרה רק לתוכנית אחת לרוץ בכל רגע נתון. עם זאת, ניתן היה לעקוף מגבלה זאת במידה מסוימת ולאפשר ביצוע משימות שונות ברקע, באמצעות תכנות ברמה נמוכה שניצל את מנגנון הפסיקה של מעבד ה-8086. DOS מצידה תמכה באפשרות להשאיר תוכנית שהסתיימה בזיכרון, ובכך לאפשר את הפעלתה מאוחר יותר באמצעות פסיקה.

באמצע שנות ה-1980 הופיע אחד השימושים הבולטים והמזוהים ביותר עם טכניקת ה-TSR: יישומי פופ-אפ, שהופעלו לרוב בלחיצת צירוף מקשים מסוים, ובאמצעות מימוש מנגנון של החלפת הקשר הצליחו לתת למשתמש חוויה של מעין ריבוי-משימות בסיסי ביותר. דוגמה מוקדמת ופופולרית במיוחד ליישום פופ-אפ הייתה Sidekick של בורלנד, שיצאה ב-1984 וכללה מחשבון ועזרי ניהול. באופן דומה, החלו להופיע תוכנות שירות שביצעו משימות שונות ברקע. PRINT.COM, פקודה חיצונית של DOS שסיפקה שירות spooling בסיסי למדפסת, היא דוגמה לכך. בשנים שלאחר מכן גבר השימוש בתוכנות TSR למטרות שונות, כגון תמיכה בהתקנים, שומרי מסך, אנטי-וירוסים ועוד. לשימוש בתוכנות אלה היה תפקיד חשוב בהרחבת אפשרויות השימוש ב-DOS מעבר למגבלותיה המובנות.

## סקירה טכנית
### סיום התוכנית מבלי לשחרר את הזיכרון
תוכנית רגילה מסיימת את פעולתה על ידי ביצוע קריאת מערכת שמשחררת את הזיכרון ומשאבים אחרים שהוקצו לה ומחזירה את השליטה לתוכנית שהפעילה אותה (בדרך כלל, מעטפת מערכת ההפעלה COMMAND.COM). תוכנית TSR, לעומת זאת, משתמשת בקריאת מערכת ייעודית, שמסיימת את פעולת התוכנית אך מותירה חלק מוגדר מהזיכרון מוקצה עבורה. קריאת המערכת מקבלת כפרמטר את גודל הזיכרון הנדרש, שמוקצה באופן רציף מהכתובת שבה נטענה התוכנית ועד לנקודה הנדרשת. קריאת המערכת המקורית (INT 27h - Terminate but Stay Resident) אפשרה להקצות לתוכנית עד 64K של זיכרון. החל מגרסה 2.0 של MS-DOS, הוצגה קריאת מערכת משופרת (INT 21h Function 31h - Keep Process) שהסירה את המגבלה הזו.

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

יירוט של פסיקת חומרה מאפשר את העברת השליטה לתוכנית באופן אסינכרוני בתגובה לאירוע מסוים: התוכנית יכולה ליירט, למשל, את פסיקת קוצב הזמן (IRQ0/INT 8), שמשוגרת 18.2 פעמים בשנייה, כדי להריץ קוד מסוים ברקע בתדירות זו. התוכנית יכולה גם ליירט את פסיקת בקר המקלדת (IRQ1/INT 9), המשוגרת עם כל לחיצת מקש והרפיה ממנו, כדי לבדוק אם נלחץ צירוף מקשים מסוים ולהגיב בהתאם.

בשונה מיירוט פסיקת חומרה, יירוט של פסיקת תוכנה מאפשר לתוכנית לסנן קריאות לשגרות שירות של DOS ושל BIOS ולשנות את ההתנהגות שלהן כרצונה, או אף לממש ממשק תכנות יישומים (API) משל עצמה. היו תוכנות TSR שהשתמשו בשיטה זו כדי לספק ממשק להתקני חומרה (מעין מנהלי התקנים), לדוגמה: MOUSE.COM, תוכנית שניהלה את העכבר וחשפה API סטנדרטי דרך INT 33h. תוכניות אחרות הרחיבו ממשק קיים, לדוגמה: VESA.COM, שהרחיבה את ממשק ה-BIOS של כרטיס המסך (INT 10h) כדי לאפשר שימוש ברזולוציות של Super VGA בכרטיסי מסך מוקדמים שתמכו בכך אך לא כללו הרחבת קושחה מתאימה. בנוסף, על ידי יירוט פסיקות של DOS ושל BIOS, תוכנת אנטי-וירוס יכולה לנטר ברקע קריאות מערכת בניסיון לזהות ולסכל דפוסי פעילות חשודים.

תוכנית יכולה גם להשתמש ביירוט פסיקת תוכנה כדי לגרום ליישום יעד מסוים להריץ בעקיפין את הקוד שלה בהקשר ידוע (למשל, נקודה מסוימת בקוד היישום שבה מבוצעת קריאת מערכת כלשהי באמצעות הפקודה INT 21h), וכך להשתלב בצורה מבוקרת ומדויקת מאוד בקוד של יישום היעד בזמן הריצה ולשלוט בו באופן מלא. בשיטה זו עשו שימוש תוכנות TSR שביצעו מניפולציה על יישום בזמן הריצה, ללא שינוי בעותק הפיזי שלו השמור בדיסק, כדי לתקן בו פגמים, או כדי לשנות או להוסיף לו פונקציונליות. יצרן היישום המקורי יכול להטמיע טלאי תוכנה באמצעות תוכנית כזו כדי שלא להסתכן בהשחתה בלתי מכוונת של קובץ ההרצה, או במקרה שהטלאה ישירה של קובץ ההרצה איננה מעשית (למשל, קובץ שנדחס באמצעות תוכנת שירות כדוגמת EXEPACK); לחלופין, היא עשויה להיות מסופקת (באופן מורשה או בלתי מורשה) על ידי צד שלישי, לדוגמה: טלאי לפריצת הגנת תוכנה, או טריינר שנועד למשחק מסוים. למעשה, על אף שתוכנית מסוג זה חייבת לשכון בזיכרון בזמן שיישום היעד פועל, אין היא חייבת להיות ממומשת כ-TSR (כלומר, לעשות שימוש בקריאת המערכת Keep Process) - למימוש חלופי ראו להלן: "מימוש באמצעות הפעלת יישום משנה".

### אילוצי זיכרון
טכניקת ה-TSR זכתה אומנם לפופולריות רבה, אך היו לה כמה חסרונות בולטים. הן המשתמשים והן מפתחי התוכנה נאלצו להתמודד עם אילוצים שונים שנבעו, בין השאר, מהאופן שבו DOS, כמערכת הפעלה של real mode, מנהלת את זיכרון המחשב. הבעיה העיקרית הייתה המקום שתפסו תוכניות ה-TSR בזיכרון הראשי (מתחת למחסום ה-640K) על חשבונם של יישומים אחרים. מאחר שהזיכרון הראשי היה מוגבל מאוד, כל תוכנית TSR שנטענה לזיכרון הפחיתה את יתרת הזיכרון הפנוי והגבילה עוד יותר את האפשרות להשתמש ביישומים הצורכים נפח זיכרון גדול.

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

מתכנתי TSR השתמשו במגוון טכניקות ואלתורים כדי להתגבר על המגבלה ולצמצם ככל הניתן את צריכת הזיכרון של תוכניות ה-TSR. תוכניות אלה נוטות להיות קומפקטיות מאוד, ולעיתים קרובות ימומשו כקובץ COM. תוכנית TSR טיפוסית תחולק לשניים: חלק אתחול וחלק תושב (resident). חלק האתחול יכיל את כל הקוד שאיננו נחוץ לפעילות התוכנית כאשר היא מתפקדת ברקע, כלומר לאחר החזרת השליטה ל-DOS באמצעות Keep Process. כשחלק זה מסיים את תפקידו, אין בו עוד צורך. כדי לחסוך בזיכרון יכול המתכנת למקם את חלק האתחול בסוף התוכנית, אחרי הקוד והנתונים של החלק התושב, ולסמן את קצה החלק התושב כקצה התוכנית בעת הקריאה ל-Keep Process. בכך ישתחרר הזיכרון שהוקצה עבור חלק האתחול. החלק התושב ממשיך לשכון דרך קבע בזיכרון לאחר סיום התוכנית.

### אילוצים נוספים
המתכנתים התמודדו עם קשיים ואילוצים טכניים נוספים מלבד מגבלת הזיכרון. באופן כללי, טכניקת ה-TSR הייתה כרוכה בתכנות מורכב ברמה נמוכה (low-level), שדרש היכרות מעמיקה עם מערכת המחשב ושליטה בשפת סף. DOS היא מערכת הפעלה של real mode, ובניגוד למערכות הפעלה מודרניות איננה מספקת שום הגנות או ערבויות ליישומים הפועלים בה - לקוד שרץ ברגע מסוים יש שליטה מוחלטת על מערכת המחשב כולה, והשגיאה הפעוטה ביותר עלולה לגרום בקלות לקריסה של המערכת, ובמקרה הגרוע אף לאובדן נתונים או השחתתם. תכנות בטכניקת ה-TSR מועד במיוחד לתקלות כאלה מעצם טבעו. הטכניקה זכתה לתמיכה רשמית בסיסית בלבד מצד DOS, ויישום יעיל שלה חייב שימוש בקריאות מערכת שלא תועדו  בגרסאות מוקדמות של מערכת ההפעלה.

כתיבת תוכניות TSR "אקטיביות" - תוכניות שהגיבו לפסיקות חומרה, ובפרט יישומי פופ-אפ - הייתה מאתגרת במיוחד. אחד הקשיים היה הצורך לממש את החלפת ההקשר ברמת היישום, כמעט ללא תמיכה של מערכת ההפעלה. המנגנון הבסיסי של יירוט פסיקות חומרה הוא מטבעו אסינכרוני - כלומר, התוכנית צפויה לקבל לידיה את השליטה בהקשר שרירותי, והדבר אף עלול להתרחש בזמן שמתבצעת פעילות קריטית אחרת, כגון כתיבה לדיסק. היבט זה חייב תשומת לב וזהירות רבה מצדו של המתכנת. בין השאר, היה עליו להתחשב בעובדה ש-DOS ו-BIOS לא תוכננו לריבוי כניסות (reentrancy). לדוגמה, אם התוכנה הפעילה הייתה פונה ל-DOS באמצעות קריאת פסיקה, למשל כדי לקרוא מידע מקובץ, ותוך כדי הטיפול בקריאה זו התקבל קלט מהמקלדת שגרם להפעלת תוכנית ה-TSR וזו מצידה הייתה פונה גם כן ל-DOS, למשל כדי להציג פלט על המסך, אזי שתי הקריאות ל-DOS היו מתנגשות ומחזירות תוצאות שגויות או גורמות למחיקת נתונים וקריסת התוכנה. כדי לפתור את הבעיה נאלצו מפתחי ה-TSR לנטר את כל הקריאות של תוכנות אחרות לפסיקות החשובות להם, ולשמור את הקריאות של עצמן עד לאחר שהפסיקות הקודמות תסתיימנה. אילוצים אלה הוסיפו למורכבות הקוד ולסרבולו. הדבר דרש לעיתים יירוט של פסיקות שונות (כגון INT 13h, ממשק BIOS לקלט/פלט של הדיסק) שלא כחלק מהפונקציונליות של התוכנית אלא רק לצורכי ניטור ובקרה, על ידי עטיפה של שגרת ה-BIOS המקורית ושימוש במשתנה גלובלי או "דגל" שתפקד כמעין מנעול או סמפור אד הוק: השגרה המיירטת לא עשתה הרבה יותר מלהרים את הדגל, לקרוא לשגרה המקורית ולהוריד את הדגל.

### מימוש באמצעות הפעלת יישום משנה
טכניקה קרובה מאוד ל-TSR, שלא עשתה שימוש בקריאת Keep Process, הייתה לממש את שגרות הטיפול בפסקה, כבתוכנית TSR רגילה, ולאחר מכן להריץ יישום משנה (child process). בזמן שיישום המשנה פועל, התוכנית מתפקדת ברקע כמו תוכנית TSR - כלומר, הקוד שלה נשמר בזיכרון ומופעל כשמתבצעת הפסיקה הרלוונטית. עם סגירת יישום המשנה חוזרת השליטה ליישום המפעיל (parent), שאחראי על השבתת הפעילות וביצוע יציאה מסודרת. שיטה זו התאימה במיוחד לתוכניות שתוכננו לביצוע מניפולציה על יישום מסוים בזמן הריצה, כפי שתואר לעיל. בנוסף, יש תוכניות שהשתמשו בשיטה זו כדי להריץ עותק נוסף של COMMAND.COM. מימוש זה יצר אפקט דומה מאוד, מנקודת המבט של המשתמש, לתוכנית TSR אמיתית וחסך מהמתכנת את הצורך לטפל בשלב הסרת התוכנית מהזיכרון: שלב זה בוצע באמצעות הפקודה EXIT של COMMAND.COM, שהחזירה את השליטה ליישום המפעיל, שבתורו סיים את התוכנית בדרך הרגילה. החיסרון העיקרי בדרך זו היה הצורך לטעון לזיכרון שני עותקים של COMMAND.COM, דבר שפגע ביכולת לטעון יישומים נוספים.

## לקריאה נוספת

* Tischer, Michael. *PC System Programming*. Abacus, 1990
* Duncan, Ray. *Advanced MS-DOS Programming: The Microsoft Guide for Assembly Language and C Programmers*. 2nd Ed. Microsoft Press, 1990


## קישורים חיצוניים
* פרק 18 בספר ''The Art of Assembly Language Programming'', מאת Randall Hyde

## הערות שוליים

קטגוריה:DOS