6.1 הקדמה לקרנל פתרון
פתרון תרגול - הקדמה לקרנל¶
1. מידע על הקרנל¶
גרסת הקרנל:
פלט לדוגמה:
תאריך הקימפול:
פלט לדוגמה:
ארכיטקטורת המעבד:
פלט לדוגמה:
זמן פעילות:
פלט לדוגמה:
המספר הראשון הוא מספר השניות שהמערכת רצה (כאן - 86400 שניות = יום אחד).
המספר השני הוא הזמן המצטבר שהמעבדים היו במצב idle.
אפשר גם בקיצור:
2. חקירת מודולים¶
ספירת מודולים:
שימו לב: השורה הראשונה היא כותרת, אז כמות המודולים היא התוצאה פחות 1.
מידע על מודול ספציפי:
פלט לדוגמה:
filename: /lib/modules/6.5.0-44-generic/kernel/drivers/net/ethernet/intel/e1000/e1000.ko
description: Intel(R) PRO/1000 Network Driver
license: GPL v2
version: 7.3.21-k8-NAPI
author: Intel Corporation
חיפוש מודולי רשת:
או אם יודעים את שם כרטיס הרשת:
3. חקירת sys¶
ליבות מעבד:
פלט לדוגמה (מערכת עם 4 ליבות):
התקני בלוק:
פלט לדוגמה:
ההבדל מ-/proc/partitions:
בsys רואים את ההתקנים ברמה עליונה, ובproc רואים גם את הpartitions שלהם (sda1, sda2 וכו') עם גודל בבלוקים.
גודל התקן:
הערך הוא בsectors של 512 בתים. כדי לקבל גיגהבייטים:
4. מודול קרנל עם PID וזמן¶
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/jiffies.h>
MODULE_LICENSE("GPL");
static unsigned long load_time;
static int __init mymod_init(void)
{
load_time = jiffies;
printk(KERN_INFO "mymod: loaded by process PID=%d (%s)\n",
current->pid, current->comm);
return 0;
}
static void __exit mymod_exit(void)
{
unsigned long elapsed = (jiffies - load_time) / HZ;
printk(KERN_INFO "mymod: unloaded after %lu seconds\n", elapsed);
}
module_init(mymod_init);
module_exit(mymod_exit);
הסבר:
- current הוא מאקרו שמחזיר מצביע ל-task_struct של התהליך הנוכחי. דרכו אפשר לגשת ל-pid (מזהה התהליך) ול-comm (שם התהליך)
- jiffies הוא מונה שעולה בכל tick של הטיימר. מחלקים ב-HZ (מספר הticks בשניה) כדי לקבל שניות
Makefile:
obj-m += mymod.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
הרצה:
make
sudo insmod mymod.ko
dmesg | tail -1
# mymod: loaded by process PID=1234 (insmod)
# ממתינים כמה שניות...
sudo rmmod mymod
dmesg | tail -1
# mymod: unloaded after 15 seconds
5. למה הקרנל ממופה בכל תהליך?¶
הקרנל ממופה בטבלאות הpaging של כל תהליך כדי שמעבר מיוזר מוד לקרנל מוד (בזמן syscall או פסיקה) יהיה מהיר ככל האפשר.
אם הקרנל היה ממופה בטבלת paging נפרדת, בכל syscall היינו צריכים:
1. לשמור את ערך הCR3 הנוכחי (של התהליך)
2. לטעון CR3 חדש (של הקרנל)
3. לבצע את הsyscall
4. לטעון בחזרה את הCR3 של התהליך
החלפת CR3 היא פעולה יקרה מאוד - היא גורמת לTLB flush מלא. הTLB (שלמדנו עליו בפרק 2.5) הוא cache של תרגומי כתובות, וכשמאפסים אותו, כל גישה לזכרון אחרי ההחלפה צריכה לעבור שוב את כל תהליך התרגום (page walk) דרך טבלאות הpaging.
בגלל שsyscall-ים קורים המון פעמים (אלפי פעמים בשניה בתוכנית טיפוסית), שתי החלפות CR3 בכל syscall היו מאטות את המערכת בצורה משמעותית.
לכן הפתרון הוא פשוט: ממפים את הקרנל בכל טבלת paging, ומסמנים את הדפים שלו כ-Ring 0 only. כך המעבר ל-Ring 0 לא דורש החלפת CR3 בכלל - הקוד של הקרנל כבר נגיש, פשוט עכשיו ההרשאה מאפשרת לגשת אליו.
הערה: במעבדים מודרניים יש מנגנון שנקרא PCID (Process Context ID) שמאפשר להחזיק כמה תרגומים בTLB במקביל ולהפחית את העלות של החלפת CR3. אבל הרעיון הבסיסי נשאר - עדיף להימנע מהחלפה מיותרת.