10.1 אסמבלי בתוך C תרגול
תרגול - אסמבלי בתוך C - inline assembly¶
תרגול 1 - קריאת TSC ומדידת ביצועים¶
- כתבו פונקציה
read_tsc()שמשתמשת ב-inline assembly עם הפקודהrdtscכדי לקרוא את מונה הזמן של המעבד. - השתמשו בפונקציה כדי למדוד כמה מחזורי שעון לוקחת כל אחת מהפעולות הבאות:
- חיבור שני מספרים (
a + b) - קריאה ל-
printf - לולאה שרצה 1000 פעמים ועושה חיבור
- הדפיסו את מספר המחזורים של כל פעולה.
רמז: הפקודה rdtsc שמה את 32 הביטים הנמוכים ב-eax ואת הגבוהים ב-edx. השתמשו ב-
"=a"ו-"=d"כדי לקרוא אותם.
תרגול 2 - זיהוי יצרן המעבד עם CPUID¶
- כתבו פונקציה
get_cpu_vendor(char *vendor)שמשתמשת ב-cpuidעם eax=0 כדי לקבל את מחרוזת היצרן. - כתבו פונקציה
get_cpu_brand(char *brand)שמשתמשת ב-cpuidעם eax=0x80000002, 0x80000003, ו-0x80000004 (שלוש קריאות) כדי לקבל את השם המלא של המעבד (48 תווים). - הדפיסו את שני הערכים.
רמז: בכל קריאה של cpuid עם function 0x8000000x, ארבעת הרגיסטרים eax, ebx, ecx, edx מחזירים 16 תווים (4 בכל רגיסטר). שלוש קריאות נותנות 48 תווים.
תרגול 3 - חיבור וחיסור עם inline asm¶
- כתבו פונקציה
int asm_add(int a, int b)שמבצעת חיבור באמצעות הפקודהaddlב-inline assembly. - כתבו פונקציה
int asm_sub(int a, int b)שמבצעת חיסור באמצעותsubl. - כתבו פונקציה
int asm_mul(int a, int b)שמבצעת כפל באמצעותimull. - בדקו שהפונקציות עובדות נכון עם מספרים חיוביים, שליליים, ואפס.
רמז: השתמשו ב-constraint
"=r"לפלט ו-"r"לקלט.
תרגול 4 - קריאת מערכת ישירה - write¶
- כתבו פונקציה
my_write(int fd, const char *buf, int len)שמבצעת את הsyscallwriteבאמצעות inline assembly, ללא שימוש בlibc. - הsyscall number של write הוא 1 (ב-x86_64).
- השתמשו בפונקציה כדי להדפיס הודעה ל-stdout (fd=1).
- השתמשו בפונקציה כדי לכתוב הודעה לקובץ (פתחו קובץ עם
openרגיל, ואז כתבו אליו עםmy_write).
רמז: ב-x86_64, הconvention הוא: מספר syscall ב-rax, ארגומנטים ב-rdi, rsi, rdx. הפקודה
syscallמבצעת את הקריאה. אל תשכחו clobbers על "rcx" ו-"r11".
תרגול 5 - compare-and-swap¶
- ממשו פונקציה
int cas(int *ptr, int expected, int desired)שמבצעת compare-and-swap אטומי באמצעות הפקודהlock cmpxchgl. - הפונקציה מחזירה 1 אם ההחלפה הצליחה, 0 אם לא.
- כתבו תוכנית שמדגימה את השימוש: נסו לעשות CAS עם הערך הנכון (צריך להצליח), ואז נסו שוב עם ערך לא נכון (צריך להיכשל).
- בונוס: השתמשו בCAS כדי לממש מונה אטומי - פונקציה
atomic_increment(int *counter)שמגדילה את המונה ב-1 באופן אטומי, באמצעות לולאת CAS (נסה, ואם נכשלת - קרא את הערך החדש ונסה שוב).