In diesem Artikel
Zu Linux
Erklären Sie so detailliert wie möglich (damit der Leser es reproduzieren kann).
Wie auch immer, wir werden mit dem Aufbau der Umwelt beginnen. Dieses Mal werde ich mich mit dem Linux-Quellcode herumschlagen, aber am Ende brauche ich eine Umgebung, um den manipulierten Quellcode tatsächlich auszuführen. Selbst wenn Ihr Computer Mac oder Windows ist, können Sie natürlich nicht feststellen, ob der Quellcode des Hauptteils willkürlich geändert wird und der Vorgang merkwürdig wird. Führen Sie Linux auf einer virtuellen Maschine aus Zu.
Installieren Sie zunächst VirtualBox und Vagrant, um die Ausführung der virtuellen Maschine vorzubereiten. Im gesamten Artikel habe ich auf diese Website verwiesen, sofern nicht anders angegeben. Es gibt einige Änderungen, aber wenn Sie dieses Verfahren befolgen, gibt es kein Problem. Schließlich werden wir zwei Maschinen erstellen, eine, die den geänderten Quellcode widerspiegelt (im Folgenden als Debuggee bezeichnet), und die andere, die Debuggee debuggt (im Folgenden als Debugger bezeichnet). Es wird nachstehend ausführlich erläutert.
In diesem Teil verweise ich auf diese Site. Es ist eine englische Seite, aber Sie können sich den Teil von Methode 3 ansehen, oder wenn es ein Ärger ist, lesen Sie die Erklärung unten.
Fügen Sie zunächst den öffentlichen Schlüssel für VirtualBox mit dem folgenden Befehl hinzu.
~$ wget -q https://www.virtualbox.org/download/oracle_vbox_2016.asc -O- | sudo apt-key add -
Verwenden Sie als Nächstes den folgenden Befehl, um eine Verbindung zum VirtualBox-Repository herzustellen.
~$ sudo add-apt-repository "deb [arch=amd64] http://download.virtualbox.org/virtualbox/debian $(lsb_release -cs) contrib"
Sie können VirtualBox tatsächlich mit dem folgenden Befehl installieren.
~$ sudo apt update && sudo apt install virtualbox-6.0
Laden Sie zunächst die Paketdatei mit dem folgenden Befehl herunter. Beachten Sie zu diesem Zeitpunkt, dass der Befehl je nach Umgebung unterschiedlich ist. Da alle Mitglieder des Teams x86-Computer verwendeten, werden die folgenden Befehle entsprechend angepasst.
~$ wget https://releases.hashicorp.com/vagrant/2.2.6/vagrant_2.2.6_x86_64.deb
Installieren Sie Vagrant mit dem folgenden Befehl aus der zuvor heruntergeladenen Paketdatei.
~$ sudo dpkg -i vagrant_2.2.6_x86_64.deb
Wie Sie dem Befehl entnehmen können, ist die diesmal zu installierende Version 6.0 für VirtualBox und 2.2.6 für Vagrant. Wenn jedoch eine zu neu ist, wird sie von der anderen nicht unterstützt und ist normal. Es gibt Fälle, in denen es nicht funktioniert. Wenn Sie keinen bestimmten Grund haben, empfehlen wir Ihnen, diese Version zu verwenden.
Erstellen Sie schließlich eine virtuelle Maschine. Verwenden Sie dazu den folgenden Befehl, um ein Arbeitsverzeichnis zu erstellen und einzugeben.
~$ mkdir -p ~/Vagrant/ubuntu18
~$ cd Vagrant/ubuntu18
Vagrant initialisieren. Zu diesem Zeitpunkt wird eine Vagrant-Einstellungsdatei namens Vagrantfile generiert.
~/Vagrant/ubuntu18$ vagrant init ubuntu/bionic64
Ändern Sie diese Datei, installieren Sie jedoch zuvor die Plugins, die zur Erhöhung der Kapazität Ihrer virtuellen Maschine erforderlich sind.
~$ vagrant plugin install vagrant-disksize
Dies ermöglicht es, die Kapazität zu erhöhen, indem eine entsprechende Beschreibung in die Vagrant-Datei geschrieben wird. Wenn Sie dies nicht tun, wird möglicherweise eine Fehlermeldung angezeigt, da die Kapazität nach dem Starten der virtuellen Maschine nicht ausreicht. Seien Sie also vorsichtig.
Öffnen Sie die generierte Vagrant-Datei und ändern Sie sie wie folgt.
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/bionic64"
config.vm.define "debugger" do |c|
c.vm.provider "virtualbox" do |vb|
vb.customize ["modifyvm", :id, "--uart2", "0x2F8", "3"]
vb.customize ["modifyvm", :id, "--uartmode2", "server", "/tmp/vagrant-ttyS1"]
vb.memory = "8192"
end
end
config.vm.define "debuggee" do |c|
c.vm.provider "virtualbox" do |vb|
vb.customize ["modifyvm", :id, "--uart2", "0x2F8", "3"]
vb.customize ["modifyvm", :id, "--uartmode2", "client", "/tmp/vagrant-ttyS1"]
end
end
config.disksize.size = '100GB'
end
Starten Sie schließlich die virtuelle Maschine. Starten Sie die virtuelle Maschine mit dem folgenden Befehl.
~/Vagrant/ubuntu18$ vagrant up
Wenn sowohl Debugger als auch Debuggee problemlos gestartet werden, geben Sie den folgenden Befehl ein, um Debuggee im Terminal zu öffnen.
~/Vagrant/ubuntu18$ vagrant ssh debuggee
Fügen Sie mit dem folgenden Befehl Debuggee-Einstellungen hinzu.
debuggee:~$ sudo systemctl enable [email protected]
Starten Sie die virtuelle Maschine mit dem folgenden Befehl neu.
~/Vagrant/ubuntu18$ vagrant reload
Öffnen Sie nun den Debugger und prüfen Sie, ob eine serielle Kommunikation möglich ist.
debugger:~$ sudo screen /dev/ttyS1
<Drücken Sie ENTRY>
Ubuntu 18.04.3 LTS ubuntu-bionic ttyS1
ubuntu-bionic login:
Bis zu diesem Punkt können Sie die virtuelle Maschine starten, das Debuggen ist jedoch schwierig. Erstellen Sie den Kernel neu, um ihn mit kgdb kompatibel zu machen.
Erstellen Sie mit dem folgenden Befehl eine Kernel-Entwicklungsumgebung auf dem Debugger.
debugger:~$ sudo apt-get install git build-essential kernel-package fakeroot libncurses5-dev libssl-dev ccache bison flex gdb
Laden Sie dann den Kernel-Quellcode in das Home-Verzeichnis des Debuggers herunter. Dieses Mal werden wir Version 5.3.9 als Ziel haben.
debugger:~$ wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.3.9.tar.xz
Entpacken Sie die Download-Datei.
debugger:~$ tar Jxfv ./linux-5.3.9.tar.xz
Geben Sie das generierte Verzeichnis ein und legen Sie die Konfiguration fest.
debugger:~$ cd linux-5.3.9
debugger:~/linux-5.3.9$ cp /boot/config-`uname -r` .config
debugger:~/linux-5.3.9$ yes '' | make oldconfig
debugger:~/linux-5.3.9$ make menuconfig
Der Bildschirm mit den Konfigurationseinstellungen wird geöffnet. Suchen Sie daher nach den folgenden Elementen, aktivieren Sie die ersten fünf und deaktivieren Sie das letzte.
Kernel hacking -> [*]KGDB: kernel debugger
Kernel hacking -> KGDB: kernel debugger -> [*]KGDB: use kgdb over the serial console
Kernel hacking -> KGDB: kernel debugger -> [*]KGDB: internal test suite
Kernel hacking -> KGDB: kernel debugger -> [*]KGDB: Allow debugging with traps in notifiers
Kernel hacking -> KGDB: kernel debugger -> [*]KGDB_KDB: include kdb frontend for kgdb
Processor type and features -> [ ]Randomize the address of the kernel image (KASLR)
Erstellen Sie den Quellcode für den Kernel. Dies hängt vom verwendeten Computer ab, dauert jedoch einige Stunden. Machen Sie also eine Pause und lassen Sie den Computer Ihr Bestes geben.
debugger:~/linux-5.3.9$ make clean
debugger:~/linux-5.3.9$ make -j `getconf _NPROCESSORS_ONLN` deb-pkg
Geben Sie das vom Build generierte Paket für den Debuggee frei. Verwenden Sie die Tatsache, dass das durch den Pfad von / vagrant angegebene Verzeichnis als freigegebener Ordner festgelegt ist, und verschieben Sie das Paket einmal in diesen Ordner.
debugger:~/linux-5.3.9$ mv ../linux-headers-5.3.9_5.3.9-1_amd64.deb /vagrant
debugger:~/linux-5.3.9$ mv ../linux-libc-dev_5.3.9-1_amd64.deb /vagrant
debugger:~/linux-5.3.9$ mv ../linux-image-5.3.9_5.3.9-1_amd64.deb /vagrant
debugger:~/linux-5.3.9$ mv ../linux-image-5.3.9-dbg_5.3.9-1_amd64.deb /vagrant
Öffnen Sie den Debuggee und installieren Sie das zuvor freigegebene Paket.
debuggee:~$ sudo dpkg -i /vagrant/linux-headers-5.3.9_5.3.9-1_amd64.deb
debuggee:~$ sudo dpkg -i /vagrant/linux-libc-dev_5.3.9-1_amd64.deb
debuggee:~$ sudo dpkg -i /vagrant/linux-image-5.3.9_5.3.9-1_amd64.deb
debuggee:~$ sudo dpkg -i /vagrant/linux-image-5.3.9-dbg_5.3.9-1_amd64.deb
Sie können jetzt mit kdgb debuggen. Es wird empfohlen, die virtuelle Maschine neu zu starten, sobald ein neues Paket installiert ist. Dies liegt daran, dass kdgb möglicherweise nicht ordnungsgemäß funktioniert oder Probleme beim Betrieb des Kernels selbst auftreten können.
Wenn Sie die Remote-SSH-Erweiterung von VSCode installieren, können Sie die Dateien in der virtuellen Maschine mit VSCode bearbeiten. Wenn Sie also mit VSCode vertraut sind oder vim aus religiösen Gründen nicht verwenden können, versuchen Sie es. Du solltest es sehen.
Zu diesem Zeitpunkt ist die Umgebung zum Vornehmen von Änderungen am Linux-Quellcode und zum tatsächlichen Ausführen bereit. Lassen Sie uns nun tatsächlich mit dem Quellcode von hier spielen.
Wie Sie der Überschrift entnehmen können, besteht das Ziel darin, einen neuen Systemaufruf hinzuzufügen und ihn tatsächlich aufzurufen, um seine Funktion zu überprüfen. Ein Systemaufruf ist eine Funktion, mit der eine Funktion des Betriebssystems (des Kernels) in einem Programm aufgerufen wird. Dateieingabe / -ausgabe und Netzwerkkommunikation werden als Systemaufrufe implementiert. Dieses Mal werden jedoch zwei ganzzahlige Argumente hinzugefügt und addiert. [Diese Site](https://pr0gr4m.tistory.com/entry/Linux-Kernel-5-system-call-%EC%B6%94%EA%B0%80%ED%95% 98% EA% B8% B0? Category = 714120) ist koreanisch, aber es war sehr hilfreich. Das Verfahren wird unten beschrieben.
Suchen Sie zunächst im Linux-Quellcode nach der Datei, die durch * arch / x86 / entry / syscalls / syscall_64.tbl * angegeben ist. Ich denke, dass die in der aktuellen Version implementierten Systemaufrufe in einem Listenformat angeordnet sind.
Alles, was Sie tun müssen, ist, die Systemaufrufe, die Sie hinzufügen möchten, zu den freien Nummern hier hinzuzufügen. Wenn Sie die Schreibweise anderer bereits geschriebener Systemaufrufe kopieren, sieht dies folgendermaßen aus.
548 64 mycall __x64_sys_mycall
Der Systemaufruf mycall unter der Nummer 548 ist der Systemaufruf, den wir hinzufügen. Wenn nichts getan wird, funktioniert es natürlich nicht, weil keine Substanz nur auf dem Tisch liegt.
Als nächstes gehen wir zu der durch include / linux / syscalls.h angegebenen Datei. Es wird eine Erklärung über die Art des implementierten Systemaufrufs abgegeben.
Der Funktionsname hier entspricht dem Teil unter x64 in der vierten Spalte, die der obigen Liste hinzugefügt wurde. Daher können Sie den Typ von sys_mycall an einer geeigneten Position deklarieren.
asmlinkage long sys_mycall(int a,int b, int *to_user);
a und b sind zwei Variablen, die eine Addition durchführen, und das Argument to_user enthält die Informationen des Aufrufers des Systemaufrufs, die erforderlich sind, um das vom Kernel durchgeführte Berechnungsergebnis zu übergeben.
Nach der Deklaration ist es endlich Zeit, die Entität zu definieren. Erstellen Sie eine Datei mit dem Namen mycall.c (wie auch immer der Name lautet) in einem Verzeichnis mit dem Namen kernel und definieren Sie dort die Funktion.
#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <asm/processor.h>
#include <asm/uaccess.h>
SYSCALL_DEFINE3(mycall, int, a, int, b, int *, to_user)
{
int sum = 0;
printk("[Kernel Message] a = %d, b = %d\n", a, b);
sum = a + b;
put_user(sum, to_user);
return 21;
}
SYSCALL_DEFINE ist ein Makro, das beim Definieren eines Systemaufrufs verwendet wird und durch Hinzufügen der Anzahl der Argumente zum Zeitpunkt der Deklaration verwendet wird. Da es diesmal drei Argumente gab, wird es zu SYSCALL_DEFINE3. Das tatsächliche Argument ist eins mehr als diese Nummer, da die Systemaufrufnummer als erstes Argument angegeben wird. Wenn ein Systemaufruf tatsächlich aufgerufen wird, wird er mit dem Systemaufrufnamen mycall anstelle eines Makros aufgerufen. Im Fall von Argument 0 wird die asm-Verknüpfung wie im Fall der Deklaration verwendet.
Dies ist das Ende der Definition der Entität. Wenn Sie den Kernel korrekt erstellen, sollten Sie in der Lage sein, mycall zu verwenden.
Überprüfen Sie die Funktion von mycall. Dafür ist es notwendig, den Kernel erneut zu erstellen. Da mycall.c zum Zeitpunkt der Erstellung jedoch nicht wiedergegeben werden kann, fügen Sie dem Makefile ein wenig hinzu.
obj-y = fork.o exec_domain.o panic.o \
.................................................
mycall.o
Erstellen Sie es dann erneut, installieren Sie das Paket mit Debuggee, starten Sie es neu, und Sie können die neuen Systemaufrufe verwenden.
Lassen Sie uns den Testcode als Test ausführen.
Wenn dieser Code kompiliert und ausgeführt wird, lautet die Ausgabe wie folgt.
Das korrekte Berechnungsergebnis wird als Wert von sum ausgegeben, und 21, der Rückgabewert von mycall, wird als Wert von ret ausgegeben, und es ist ersichtlich, dass der Systemaufruf korrekt aufgerufen wird.
Versuchen Sie auch, die Kernel-Nachricht mit dem Befehl dmesg aufzurufen.
Die Werte von a und b werden am Ende als Kernelnachrichten ausgegeben, was darauf hinweist, dass die Werte korrekt an den Kernel übergeben werden können.
Die zufällige Planung zeigt, wie bei der Auswahl der nächsten Aufgabe aus der CPU zufällig unter den wartenden Aufgaben ausgewählt wird.
struct random_rq{
struct list_head task_list;
unsigned int random_nr_running;
int random_queued;
int random_throttled;
u64 random_time;
u64 random_runtime;
};
list_head ist eine Implementierung der verketteten Listendatenstruktur im Linux-Quellcode, die sich im (Linux-Quellcodeverzeichnis) /tools/include/linux/types.h befindet.
struct list_head {
struct list_head *next, *prev;
};
Im Allgemeinen wird bei Verwendung einer verketteten Liste der Zeiger der Struktur verwendet, um next und prev in der Struktur der Daten anzuzeigen. (Siehe Code unten)
struct linked_list{
int data; //Daten
struct linked_list *next, *prev; //Strukturzeiger
}
List_head implementiert jedoch eine verkettete Liste, indem die Variable list_head zur Datenstruktur hinzugefügt wird. (Siehe Code unten)
struct linked_list{
int data; //Daten
struct list_head list; //Verkettete Liste
}
Wenn Sie sich nun ansehen, wie list_head Elemente (Knoten) verbindet, wird die folgende Funktion verwendet. ((Es ist geschrieben in (Linux-Quellcode-Verzeichnis) /include/linux/list.h)
INIT_LIST_HEAD initialisiert list_head, indem das nächste und vorherige von list_head zu einem eigenen gemacht wird.
static inline void INIT_LIST_HEAD(struct list_head *list)
{
WRITE_ONCE(list->next, list);
list->prev = list;
}
In der Abbildung sieht es so aus, und es wird eine bidirektionale kreisförmige Liste erstellt.
list_add und list_add_tail sind Funktionen, die der verketteten Liste Elemente hinzufügen und den Zeiger des Kopfes empfangen, der der Kopf der Liste ist, und den Zeiger des Elements, das Sie als Argumente hinzufügen möchten.
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
if (!__list_add_valid(new, prev, next))
return;
next->prev = new;
new->next = next;
new->prev = prev;
WRITE_ONCE(prev->next, new);
}
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
Die Kernfunktion von list_add und list_add_tail ist __list_add, wodurch new in prev und next eingefügt wird. Der Unterschied zwischen list_add und list_add_tail besteht darin, ob neu in head-> next, das nächste Element von head and head, oder new in head-> prev, das Element vor head und head, eingefügt werden soll. Ist. Schließlich setzt list_add das Element an den Anfang der Liste und list_add_tail das Element an das Ende der Liste.
list_del und list_del_init sind Funktionen zum Entfernen von Elementen aus einer verketteten Liste.
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
WRITE_ONCE(prev->next, next);
}
static inline void __list_del_entry(struct list_head *entry)
{
if (!__list_del_entry_valid(entry))
return;
__list_del(entry->prev, entry->next);
}
static inline void list_del(struct list_head *entry)
{
__list_del_entry(entry);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
static inline void list_del_init(struct list_head *entry)
{
__list_del_entry(entry);
INIT_LIST_HEAD(entry);
}
Der Unterschied zwischen list_del und list_del_init besteht darin, was mit den nächsten und vorherigen Variablen der zu entfernenden Elemente zu tun ist. list_del macht die Daten zu einem bedeutungslosen Zeiger, list_del_init macht es selbst. (Beschreibung von LIST_POISON: https://lists.kernelnewbies.org/pipermail/kernelnewbies/2016-March/015879.html)
In random_rq war es erforderlich, list_head zu verwenden, um eine Liste von Aufgaben zu implementieren, die durch zufällige Planung behandelt werden sollen, und eine beliebige Zufallszahl n (kleinere Ganzzahl als die zu behandelnde Aufgabe) der Aufgabe auszuwählen. Daher habe ich list_for_each verwendet, ein Makro, das die Liste umgeht.
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
Es beginnt einfach mit dem nächsten Element am Anfang und wiederholt sich, bis es den Anfang erreicht.
Bisher habe ich alle Teile der verketteten Liste implementiert, aber da die verkettete Liste (list_head) keine Daten enthält, sind die Daten, die das n-te Element enthält, auch wenn ich mit list_for_each nach dem n-ten Element frage Ich weiß es nicht. Um diese Daten zu finden, verwendet Linux ein Makro namens list_entry. (Geschrieben in /include/linux/list.h)
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
Das Makro container_of sieht folgendermaßen aus: (Geschrieben in /include/linux/kernel.h)
#define container_of(ptr, type, member) ({ \
void *__mptr = (void *)(ptr); \
BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) && \
!__same_type(*(ptr), void), \
"pointer type mismatch in container_of()"); \
((type *)(__mptr - offsetof(type, member))); })
Der Offset des Makros sieht folgendermaßen aus:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
Die ausführliche Erklärung finden Sie unter https://stackoverflow.com/questions/5550404/list-entry-in-linux, aber kurz den Zeiger ptr von list_head, den Strukturtyp mit dem gewünschten Datentyp (oben). Nehmen Sie gemäß dem Beispiel struct linked_list den Variablennamen member von list_head (Liste gemäß dem obigen Beispiel) als Argument in der Struktur mit den gewünschten Daten und berechnen Sie die Anzahl der Bytes vom Anfang der gewünschten Struktur bis list_head ( Rolle von offsetof) Suchen Sie den Zeiger der Struktur mit den Daten, die Sie suchen möchten, indem Sie ihn aus dem Zeiger von list_head ziehen. Die Figur ist wie folgt.
Der Teil, der tatsächlich in der für den Scheduler erforderlichen Funktion implementiert ist, ist wie folgt. (Referenz: https://trepo.tuni.fi/bitstream/handle/10024/96864/GRADU-1428493916.pdf, S. 27 ~ S. 28)
enqueue_task
static void enqueue_task_random(struct rq *rq, struct task_struct *p, int flags){
enqueue_list(&rq->rd_rq,p,flags);
add_nr_running(rq,1);
rq->rd_rq.random_nr_running++;
}
Hier sieht die enqueue_list folgendermaßen aus:
void enqueue_list(struct random_rq *rd_rq,struct task_struct *p, int flags){
INIT_LIST_HEAD(&p->rd_list);
list_add_tail(&p->rd_list,&rd_rq->task_list);
}
dequeue_task
static void dequeue_task_random(struct rq *rq, struct task_struct *p, int flags){
dequeue_list(&rq->rd_rq,p,flags);
sub_nr_running(rq,1);
rq->rd_rq.random_nr_running--;
}
Hier sieht die dequeue_list folgendermaßen aus:
void dequeue_list(struct random_rq *rd_rq,struct task_struct *p, int flags){
list_del_init(&p->rd_list);
}
yield_task
static void yield_task_random(struct rq *rq){
struct task_struct *p = rq->curr; //TODO
dequeue_list(&rq->rd_rq,p,0);
enqueue_list(&rq->rd_rq,p,0);
}
Die Funktion yield_task kann implementiert werden, indem die aktuelle Aufgabe an das Ende der Liste verschoben wird. Nehmen Sie also die aktuelle Aufgabe p aus dequeue_list und setzen Sie sie aus enqueue_list wieder in die Liste ein.
pick_next_task
static struct task_struct * pick_next_task_random(struct rq *rq, struct task_struct *prev, struct rq_flags *rf){
put_prev_task(rq,prev);
struct task_struct *p;
struct random_rq *rd_rq= &rq->rd_rq;
p = pick_random_entity(rd_rq);
return p;
}
Die Implementierung der Funktion pick_random_entity ist wie folgt.
static struct task_struct* pick_random_entity(struct random_rq *random_rq){ //Pick one task randomly -> return p
struct task_struct *p;
unsigned long random_val;
unsigned long cnt;
struct list_head *ptr;
if(random_rq->random_nr_running){
random_val = 0UL;
get_random_bytes(&random_val,sizeof(unsigned long));
random_val = random_val%(random_rq->random_nr_running);
cnt = 0UL;
list_for_each(ptr,&random_rq->task_list){
if(cnt==random_val){
p = list_entry(ptr,struct task_struct,rd_list);
return p;
}
cnt++;
}
}
return NULL;
}
Verwenden Sie die Funktion get_random_bytes, um eine Zufallszahl in random_val einzufügen und durch die Anzahl der Aufgaben in der Zufallslaufwarteschlange zu dividieren, damit zu viel übrig bleibt, um die Zufallszahl n zu berechnen, die Sie finden möchten. Verwenden Sie dann list_for_each und list_entry, um die Aufgabe p zu finden.
put_prev_task
static void put_prev_task_random(struct rq *rq, struct task_struct *p){
enqueue_list(&rq->rd_rq,p,0);
}
put_prev_task soll die Aufgabe, die in der Ausführungswarteschlange funktioniert hat, am Ende der Ausführungswarteschlangenliste einfügen.
task_tick
static void task_tick_random(struct rq *rq, struct task_struct *p, int queued){
if(!queued){
resched_curr(rq);
}
return;
}
task_tick ist eine Funktion, die zum ersten Mal in einer bestimmten Zeit aufgerufen wird. Der Zufallsplaner muss die Wartezeit und die Endzeit der Aufgabe nicht berechnen, sodass nur eine Neuplanung durchgeführt wird.
Stellen Sie als Nächstes sicher, dass die oben implementierte Scheduler-Klasse tatsächlich verwendet wird.
Hier werden wir uns hauptsächlich mit Kernel / sched / core.c (im Folgenden als core.c abgekürzt) herumschlagen.
// kernel/sched/core.c
int sched_fork(...){
(Unterlassung)
if (dl_prio(p->prio))
return -EAGAIN;
else if (rt_prio(p->prio))
p->sched_class = &rt_sched_class;
else
p->sched_class = &fair_sched_class;
(Abkürzung)
}
static void __setscheduler(struct rq *rq, struct task_struct *p,
const struct sched_attr *attr, bool keep_boost)
(Unterlassung)
if (dl_prio(p->prio))
p->sched_class = &dl_sched_class;
else if (rt_prio(p->prio))
p->sched_class = &rt_sched_class;
else
p->sched_class = &fair_sched_class;
(Abkürzung)
}
Andere Scheduler-Klassen, um herauszufinden, wie die Scheduler-Klasse vom Kernel behandelt wird
Wenn Sie GNU global nach rt_sched_class, fair_sched_class usw. durchsuchen, wird der obige Teil in core.c getroffen.
Vielleicht könnten wir hier unserem Zufallsplaner etwas Verarbeitung hinzufügen.
Bei der Prüfung wird die Funktion {Scheduler-Name} _prio (rt_prio, dl_prio ...) in der Datei {Scheduler-Name} .h definiert.
Betrachtet man die Implementierung von dl_prio als Testversion, so scheint es, dass es die Priorität (p-> prio) von task_struct (process) erhält und 1 zurückgibt, wenn es innerhalb des Prioritätsbereichs liegt, für den der Scheduler verantwortlich ist, andernfalls gibt es 0 zurück.
Beispiel: Implementierung von dl_prio
// include/linux/sched/deadline.h
#define MAX_DL_PRIO 0
static inline int dl_prio(int prio)
{
if (unlikely(prio < MAX_DL_PRIO))
return 1;
return 0;
}
Anschließend wird eine Funktion namens rd_prio in der Header-Datei include / linux / sched / random_sched.h des Random Schedulers implementiert.
// include/linux/sched/random_sched.h
static inline int rd_prio(int prio)
{
if (121 <= prio && prio <= 139)
return 1;
return 0;
}
Hier wird 1 für Prozesse mit einem Prioritätsbereich von 121 bis 139 zurückgegeben.
Verwenden Sie diese Funktion, um core.c wie folgt neu zu schreiben.
// kernel/sched/core.c
int sched_fork(...){
(Unterlassung)
if (dl_prio(p->prio))
return -EAGAIN;
else if (rt_prio(p->prio))
p->sched_class = &rt_sched_class;
else if (rd_prio(p->prio)) // Added this line
p->sched_class = &random_sched_class;
else
p->sched_class = &fair_sched_class;
(Abkürzung)
}
static void __setscheduler(struct rq *rq, struct task_struct *p,
const struct sched_attr *attr, bool keep_boost)
(Unterlassung)
if (dl_prio(p->prio))
p->sched_class = &dl_sched_class;
else if (rt_prio(p->prio))
p->sched_class = &rt_sched_class;
else if (rd_prio(p->prio)) // Added this line
p->sched_class = &random_sched_class;
else
p->sched_class = &fair_sched_class;
(Abkürzung)
}
Die Aufgabe ist nun dem Prozess zugeordnet.
Sie müssen jedoch die neu erstellte Datei random_sched.h in core.c aufnehmen.
Wiederum nach der Implementierung anderer Scheduler, bei denen die include-Anweisungen in kernel / sched / sched.h organisiert sind.
#include <linux/sched/random_sched.h>Wurde hinzugefügt. Ader.c ist Kernel/sched/sched.Da h enthalten ist, ist dies der Kern.c zu zufällig_sched.Sie können jetzt die Funktionen in h verwenden.
#### Implementierung der Richtlinienfunktion
Ein Blick in die allgemeine Scheduler-Header-Datei kenel / sched / sched.h,
```c
static inline int idle_policy(int policy)
{
return policy == SCHED_IDLE;
}
static inline int fair_policy(int policy)
{
return policy == SCHED_NORMAL || policy == SCHED_BATCH;
}
static inline int rt_policy(int policy)
{(Weggelassen)}
static inline int dl_policy(int policy)
{(Weggelassen)}
static inline bool valid_policy(int policy)
{
return idle_policy(policy) || fair_policy(policy) ||
rt_policy(policy) || dl_policy(policy);
}
Ich habe festgestellt, dass die aufgerufene Funktion definiert ist. Sie können wahrscheinlich vermuten, dass Sie mit der Funktion valid_policy prüfen, ob der Scheduler richtig angegeben ist.
Daher folgt der Zufallsplaner wie folgt
// Added this function
static inline int random_policy(int policy)
{
return policy == SCHED_RANDOM;
}
static inline bool valid_policy(int policy)
{
return idle_policy(policy) || fair_policy(policy) ||
rt_policy(policy) || dl_policy(policy) || random_policy(policy); // Fixed this line
}
Ich habe eine Funktion namens random_policy implementiert und in valid_policy eingefügt.
Der hier angezeigte konstante Wert SCHED_RANDOM folgt anderen konstanten Werten wie SCHED_NORMAL und SCHED_IDLE in include / uapi / sched.h.
#define SCHED_NORMAL 0
#define SCHED_FIFO 1
#define SCHED_RR 2
#define SCHED_BATCH 3
/* SCHED_ISO: reserved but not implemented yet */
#define SCHED_IDLE 5
#define SCHED_DEADLINE 6
#define SCHED_RANDOM 7
Wurde definiert als.
Als erstes trat beim Booten von Linux der folgende Fehler auf, da auf dieselbe Weise wie in der Build-Umgebung von https://leavatail.hatenablog.com/entry/2019/11/04/235300 erstellt wurde. Im Vagrant-Ordner (/ Vagrant / ubuntu18) befindet sich ein Protokoll mit der Kernel-Nachricht (gedruckt von printk). (ubuntu-bionic-18.04-cloudimg-console.log) So konnte ich den Grund bestätigen, warum Linux nicht funktionieren konnte.
[ 28.333395] watchdog: BUG: soft lockup - CPU#2 stuck for 22s![kworker/2:1:88]
[ 28.333395] Modules linked in:
[ 28.333395] CPU: 2 PID: 88 Comm: kworker/2:1 Not tainted 5.3.9+ #1
[ 28.333395] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
[ 28.333395] Workqueue: events timer_update_keys
[ 28.333395] RIP: 0010:smp_call_function_many+0x239/0x270
[ 28.333395] Code: 08 89 c7 e8 e9 f7 90 00 3b 05 77 a8 70 01 0f 83 5c fe ff ff 48 63 c8 48 8b 13 48 03 14 cd 00 a9 3d 82 8b 4a 18 83 e1 01 74 0a <f3> 90 8b 4a 18 83 e1 01 75 f6 eb c7 0f 0b e9 0b fe ff ff 48 c7 c2
[ 28.333395] RSP: 0018:ffffc9000028bd08 EFLAGS: 00000202 ORIG_RAX: ffffffffffffff13
[ 28.333395] RAX: 0000000000000000 RBX: ffff88803eaab500 RCX: 0000000000000001
[ 28.333395] RDX: ffff88803ea30fe0 RSI: 0000000000000000 RDI: ffff88803e445a98
[ 28.333395] RBP: ffffc9000028bd40 R08: ffff88803eb40000 R09: ffff88803e403c00
[ 28.333395] R10: ffff88803e445a98 R11: 0000000000000000 R12: 0000000000000006
[ 28.333395] R13: 000000000002b4c0 R14: ffffffff810390a0 R15: 0000000000000000
[ 28.333395] FS: 0000000000000000(0000) GS:ffff88803ea80000(0000) knlGS:0000000000000000
[ 28.333395] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 28.333395] CR2: 0000000000000000 CR3: 000000000260a000 CR4: 00000000000406e0
[ 28.333395] Call Trace:
[ 28.333395] ? poke_int3_handler+0x70/0x70
[ 28.333395] on_each_cpu+0x2d/0x60
[ 28.333395] text_poke_bp_batch+0x8c/0x160
[ 28.333395] arch_jump_label_transform_apply+0x33/0x50
[ 28.333395] __jump_label_update+0x116/0x160
[ 28.333395] jump_label_update+0xb9/0xd0
[ 28.333395] static_key_enable_cpuslocked+0x5a/0x80
[ 28.333395] static_key_enable+0x1a/0x30
[ 28.333395] timers_update_migration+0x30/0x40
[ 28.333395] timer_update_keys+0x1a/0x40
[ 28.333395] process_one_work+0x1fd/0x3f0
[ 28.333395] worker_thread+0x34/0x410
[ 28.333395] kthread+0x121/0x140
[ 28.333395] ? process_one_work+0x3f0/0x3f0
[ 28.333395] ? kthread_park+0xb0/0xb0
[ 28.333395] ret_from_fork+0x35/0x40
Als ich hier RIP betrachtete, stellte ich fest, dass ein Fehler in der Funktion smp_call_function_many aufgetreten war. Als ich nach SMP suchte, stellte ich fest, dass derselbe Speicher von zwei oder mehr Prozessoren verwendet wurde, und dieser Teil wurde in zufälliger Planung implementiert. Ich habe dies nicht getan, also habe ich die SMP-Option entfernt und neu erstellt. (Referenz: https://en.wikipedia.org/wiki/Symmetric_multiprocessing)
Als ich SMP veröffentlichte, konnte ich mit ==> debuggee: Maschine gestartet und bereit! 'Verschieben. Bei
==> debuggee: Überprüfung auf Gastzugaben in VM ... `trat jedoch ein Fehler auf. Das Fehlerprotokoll sieht folgendermaßen aus:
[ 31.591660][ T1283] BUG: kernel NULL pointer dereference, address: 0000000000000000
[ 31.592313][ T1283] #PF: supervisor read access in kernel mode
[ 31.592667][ T1283] #PF: error_code(0x0000) - not-present page
[ 31.593014][ T1283] PGD 38cbf067 P4D 38cbf067 PUD 3d23e067 PMD 0
[ 31.593377][ T1283] Oops: 0000 [#1] NOPTI
[ 31.593615][ T1283] CPU: 0 PID: 1283 Comm: control Tainted: G OE 5.3.9+ #20
[ 31.594097][ T1283] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
[ 31.594640][ T1283] RIP: 0010:rb_erase+0x149/0x380
[ 31.594927][ T1283] Code: f6 c2 01 0f 84 c2 01 00 00 48 83 e2 fc 0f 84 ee 00 00 00 48 89 c1 48 89 d0 48 8b 50 08 48 39 ca 0f 85 71 ff ff ff 48 8b 50 10 <f6> 02 01 48 8b 4a 08 75 3a 48 89 c7 48 89 48 10 48 89 42 08 48 83
[ 31.596105][ T1283] RSP: 0018:ffffc90001df7988 EFLAGS: 00010046
[ 31.596454][ T1283] RAX: ffff88803deb0060 RBX: ffff888037a30000 RCX: 0000000000000000
[ 31.596909][ T1283] RDX: 0000000000000000 RSI: ffffffff8245ee90 RDI: ffff888037a30060
[ 31.597381][ T1283] RBP: ffffc90001df7988 R08: 0000000000000000 R09: ffffc90000343b88
[ 31.597846][ T1283] R10: 00000000faccb043 R11: 0000000078dc05ec R12: ffffffff8245ee40
[ 31.598322][ T1283] R13: 0000000000000009 R14: ffff888037a30060 R15: 0000000000000001
[ 31.598787][ T1283] FS: 00007fef954dc700(0000) GS:ffffffff82447000(0000) knlGS:0000000000000000
[ 31.599314][ T1283] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 31.599694][ T1283] CR2: 0000000000000000 CR3: 000000003de92000 CR4: 00000000000406f0
[ 31.600157][ T1283] Call Trace:
[ 31.600347][ T1283] dequeue_task_fair+0x9f/0x2a0
[ 31.600687][ T1283] deactivate_task+0x57/0xf0
[ 31.600957][ T1283] ? update_rq_clock+0x2c/0x80
[ 31.601235][ T1283] __schedule+0x344/0x5d0
[ 31.601559][ T1283] schedule+0x32/0xa0
[ 31.601823][ T1283] rtR0SemEventMultiLnxWait.isra.3+0x33b/0x370 [vboxguest]
[ 31.602298][ T1283] ? wait_woken+0x90/0x90
[ 31.602569][ T1283] VBoxGuest_RTSemEventMultiWaitEx+0xe/0x10 [vboxguest]
[ 31.603009][ T1283] VBoxGuest_RTSemEventMultiWaitNoResume+0x28/0x30 [vboxguest]
[ 31.603496][ T1283] vgdrvHgcmAsyncWaitCallbackWorker+0xda/0x210 [vboxguest]
[ 31.603925][ T1283] vgdrvHgcmAsyncWaitCallbackInterruptible+0x15/0x20 [vboxguest]
[ 31.604385][ T1283] VbglR0HGCMInternalCall+0x3ff/0x1180 [vboxguest]
[ 31.604764][ T1283] ? vgdrvHgcmAsyncWaitCallback+0x20/0x20 [vboxguest]
[ 31.605168][ T1283] ? prep_new_page+0x8e/0x130
[ 31.605435][ T1283] ? get_page_from_freelist+0x6db/0x1160
[ 31.605827][ T1283] ? page_counter_cancel+0x22/0x30
[ 31.606122][ T1283] ? page_counter_uncharge+0x22/0x40
[ 31.606426][ T1283] ? drain_stock.isra.49.constprop.76+0x33/0xb0
[ 31.606795][ T1283] ? try_charge+0x62e/0x760
[ 31.607062][ T1283] ? tomoyo_init_request_info+0x80/0x90
[ 31.607407][ T1283] vgdrvIoCtl_HGCMCallWrapper+0x127/0x2c0 [vboxguest]
[ 31.607856][ T1283] VGDrvCommonIoCtl+0x3ca/0x1a20 [vboxguest]
[ 31.608234][ T1283] ? __check_object_size+0xdd/0x1a0
[ 31.609064][ T1283] ? _copy_from_user+0x3d/0x60
[ 31.609829][ T1283] vgdrvLinuxIOCtl+0x113/0x290 [vboxguest]
[ 31.610671][ T1283] do_vfs_ioctl+0xa9/0x620
[ 31.611427][ T1283] ? tomoyo_file_ioctl+0x19/0x20
[ 31.612197][ T1283] ksys_ioctl+0x75/0x80
[ 31.612901][ T1283] __x64_sys_ioctl+0x1a/0x20
[ 31.613633][ T1283] do_syscall_64+0x59/0x130
[ 31.614355][ T1283] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 31.615173][ T1283] RIP: 0033:0x7fef965f56d7
[ 31.615877][ T1283] Code: b3 66 90 48 8b 05 b1 47 2d 00 64 c7 00 26 00 00 00 48 c7 c0 ff ff ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 b8 10 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 81 47 2d 00 f7 d8 64 89 01 48
[ 31.618044][ T1283] RSP: 002b:00007fef954dba18 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
[ 31.619237][ T1283] RAX: ffffffffffffffda RBX: 00007fef954dba60 RCX: 00007fef965f56d7
[ 31.620163][ T1283] RDX: 00007fef954dba60 RSI: 00000000c0485607 RDI: 0000000000000003
[ 31.621066][ T1283] RBP: 00007fef954dba20 R08: 0000000000000079 R09: 0000000000000000
[ 31.621966][ T1283] R10: 00007fef900008d0 R11: 0000000000000246 R12: 0000000000693410
[ 31.622844][ T1283] R13: 00007fef954dbdbc R14: 00007fef954dbdac R15: 00007fef954dcdac
[ 31.623731][ T1283] Modules linked in: nls_utf8 isofs snd_intel8x0 snd_ac97_codec ac97_bus input_leds snd_pcm snd_timer serio_raw snd soundcore vboxguest(OE) mac_hid sch_fq_codel ib_iser rdma_cm iw_cm ib_cm ib_core iscsi_tcp libiscsi_tcp libiscsi scsi_transport_iscsi ip_tables x_tables autofs4 btrfs zstd_compress raid10 raid456 async_raid6_recov async_memcpy async_pq async_xor async_tx xor raid6_pq libcrc32c raid1 raid0 multipath linear crct10dif_pclmul crc32_pclmul ghash_clmulni_intel aesni_intel aes_x86_64 crypto_simd cryptd glue_helper vboxvideo drm_vram_helper ttm drm_kms_helper syscopyarea psmouse sysfillrect sysimgblt mptspi mptscsih mptbase scsi_transport_spi i2c_piix4 fb_sys_fops e1000 drm pata_acpi video
[ 31.630278][ T1283] CR2: 0000000000000000
[ 31.631004][ T1283] ---[ end trace 7027dee837a160c7 ]---
Als ich jedoch direkt in die Oracle-VM gebootet habe, hat es gut funktioniert und ich habe dort den Testcode ausprobiert.
#include <assert.h>
#include <sched.h>
#include <sys/time.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
/*Die Zeit beginnt--Aufzeichnung der Ausführung auf proc bis zum Ende*/
typedef struct {
double begin;
double end;
int proc;
} rec_t;
/*Holen Sie sich die aktuelle Zeit*/
double cur_time() {
struct timeval tp[1];
gettimeofday(tp, 0);
return tp->tv_sec + tp->tv_usec * 1.0E-6;
}
void die(char * s) {
perror(s);
exit(1);
}
/*Laufen Sie T Sekunden lang weiter,Notieren Sie die Zeitzone, der anscheinend die CPU zugewiesen wurde*/
int run(double T, int n) {
pid_t pid = getpid();
struct sched_param param;
double limit = cur_time() + T;
rec_t * R = (rec_t *)calloc(n, sizeof(rec_t));
int i = 0;
R[i].begin = R[i].end = cur_time();
R[i].proc = sched_getcpu();
int ret_1 = syscall(145,pid);
printf("GET_SCHEDULER : %d\n",ret_1);
param.sched_priority = 139;
int ret_2 = syscall(144,pid,7,¶m);
if(ret_2==0){
printf("SET_SCHEDULER TO SCHED_RANDOM SUCCESS\n");
} else {
printf("SET_SCHEDULER TO SCHED_RANDOM FAILED\n");
}
ret_1 = syscall(145,pid);
printf("GET_SCHEDULER : %d\n",ret_1);
while (R[i].end < limit && i < n) {
double t = cur_time(); /*Holen Sie sich die aktuelle Zeit*/
int proc = sched_getcpu();
if (t - R[i].end < 1.0E-3 && proc == R[i].proc) {
/*Nicht viel anders als beim letzten Mal, als ich es gesehen habe(< 1ms) -> R[i].Ende erhöhen*/
R[i].end = t;
} else {
/*Mehr als 1 ms sind vergangen, seit ich es das letzte Mal gesehen habe->Geben Sie einen neuen Abschnitt ein*/
if (i + 1 >= n) break;
i++;
R[i].proc = proc;
R[i].begin = R[i].end = cur_time();
}
}
assert(i < n);
int j;
for (j = 0; j <= i; j++) {
printf("%d %f %f %d %f\n",
pid, R[j].begin, R[j].end, R[j].proc,
R[j].end - R[j].begin);
}
return 0;
}
int main(int argc, char ** argv) {
double T = (argc > 1 ? atof(argv[1]) : 10.0);
int n = (argc > 2 ? atoi(argv[2]) : 100000);
run(T, n);
return 0;
}
Stellen Sie die Zeit T (Sekunden) ein, die das Programm aus den vom Terminal übergebenen Argumenten ausführen soll, und verwenden Sie die Funktion sched_set_scheduler, damit der Scheduler einen zufälligen Scheduler verwendet. Stellen Sie hier vom Terminal aus die Priorität mit dem Befehl nice ein.
vagrant@ubuntu-bionic:~$ nice -15 ./test_2
Der Befehl nice ändert den Wert nice und der obige Befehl setzt den Wert nice auf 15. Das heißt, die Priorität wird 120 + 15 = 135 und gibt den Zufallsplaner ein.
Ich konnte bestätigen, dass sich die Aufgabe in der zufälligen Warteschlange befand, als ich sie ausführte, aber dann trat ein Fehler auf.
[ 107.960596][ C0] BUG: kernel NULL pointer dereference, address: 0000000000000058
[ 107.961544][ C0] #PF: supervisor read access in kernel mode
[ 107.962053][ C0] #PF: error_code(0x0000) - not-present page
[ 107.962552][ C0] PGD 3abb1067 P4D 3abb1067 PUD 3abb0067 PMD 0
[ 107.963078][ C0] Oops: 0000 [#1] NOPTI
[ 107.963432][ C0] CPU: 0 PID: 1515 Comm: lsb_release Tainted: G OE 5.3.9+ #20
[ 107.964161][ C0] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
[ 107.964929][ C0] RIP: 0010:task_tick_fair+0xcc/0x160
[ 107.965382][ C0] Code: 73 8b 0d 93 be 3a 01 48 39 ca 72 29 48 8b 0d 73 b5 3a 01 48 8d 51 e8 48 85 c9 b9 00 00 00 00 48 0f 44 d1 48 8b 8b a0 00 00 00 <48> 2b 4a 58 78 05 48 39 c8 72 12 0f 1f 44 00 00 48 83 c4 08 5b 41
[ 107.967048][ C0] RSP: 0000:ffffc90000003e78 EFLAGS: 00010046
[ 107.967563][ C0] RAX: 000000002d17f460 RBX: ffff88803abd8000 RCX: 000000076403fcc9
[ 107.968239][ C0] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 000b45fd181a81d0
[ 107.968913][ C0] RBP: ffffc90000003ea0 R08: 003ffff0b9e49c00 R09: 0000000000000400
[ 107.969584][ C0] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000
[ 107.970253][ C0] R13: ffff88803abd8048 R14: ffffffff810f4650 R15: 000000191c91658e
[ 107.970926][ C0] FS: 00007fd781841740(0000) GS:ffffffff82447000(0000) knlGS:0000000000000000
[ 107.971675][ C0] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 107.972231][ C0] CR2: 0000000000000058 CR3: 000000003bf5e000 CR4: 00000000000406f0
[ 107.972914][ C0] Call Trace:
[ 107.973188][ C0] <IRQ>
[ 107.973437][ C0] ? tick_sched_do_timer+0x60/0x60
[ 107.973867][ C0] scheduler_tick+0x44/0x60
[ 107.974245][ C0] update_process_times+0x45/0x60
[ 107.974666][ C0] tick_sched_handle+0x25/0x70
[ 107.975069][ C0] ? tick_sched_do_timer+0x52/0x60
[ 107.975505][ C0] tick_sched_timer+0x3b/0x80
[ 107.975909][ C0] __hrtimer_run_queues.constprop.24+0x10e/0x210
[ 107.976443][ C0] hrtimer_interrupt+0xd9/0x240
[ 107.976856][ C0] ? ksoftirqd_running+0x2f/0x40
[ 107.977281][ C0] smp_apic_timer_interrupt+0x68/0x100
[ 107.977743][ C0] apic_timer_interrupt+0xf/0x20
[ 107.978607][ C0] </IRQ>
[ 107.979290][ C0] RIP: 0033:0x56fd37
[ 107.980039][ C0] Code: ff 00 00 00 0f 8f 89 02 00 00 4c 8d 3c 07 48 81 fe 40 45 9d 00 0f 85 d3 03 00 00 4c 89 f5 4d 89 e2 4c 21 e5 48 0f be 5c 2a 28 <48> 83 fb ff 0f 84 3f 02 00 00 4c 8d 1c 5b 4f 8d 1c df 49 8b 73 08
[ 107.982571][ C0] RSP: 002b:00007fff64ad6810 EFLAGS: 00000202 ORIG_RAX: ffffffffffffff13
[ 107.983732][ C0] RAX: 0000000000000080 RBX: 0000000000000040 RCX: 00007fff64ad68b0
[ 107.984854][ C0] RDX: 000000000275fac0 RSI: 00000000009d4540 RDI: 000000000275fae8
[ 107.985985][ C0] RBP: 0000000000000075 R08: 0000000000000000 R09: 00007fd7817ccd80
[ 107.987119][ C0] R10: 9aa0515eaaf52cf5 R11: 00007fd781823130 R12: 9aa0515eaaf52cf5
[ 107.988255][ C0] R13: 00007fd7817d0db0 R14: 000000000000007f R15: 000000000275fb68
[ 107.989397][ C0] Modules linked in: nls_utf8 isofs snd_intel8x0 snd_ac97_codec ac97_bus input_leds snd_pcm serio_raw vboxguest(OE) snd_timer snd soundcore mac_hid sch_fq_codel ib_iser rdma_cm iw_cm ib_cm ib_core iscsi_tcp libiscsi_tcp libiscsi scsi_transport_iscsi ip_tables x_tables autofs4 btrfs zstd_compress raid10 raid456 async_raid6_recov async_memcpy async_pq async_xor async_tx xor raid6_pq libcrc32c raid1 raid0 multipath linear crct10dif_pclmul crc32_pclmul ghash_clmulni_intel aesni_intel aes_x86_64 crypto_simd cryptd glue_helper vboxvideo drm_vram_helper ttm drm_kms_helper syscopyarea psmouse sysfillrect sysimgblt mptspi mptscsih mptbase scsi_transport_spi i2c_piix4 fb_sys_fops e1000 drm pata_acpi video
[ 107.997916][ C0] CR2: 0000000000000058
[ 107.998821][ C0] ---[ end trace 717bffdc6fc42d15 ]---
Bei der Analyse dieses Fehlers trat der Fehler auf, weil die Funktion task_tick_fair auf den NULL-Zeiger zugegriffen hat. (Kernel-NULL-Zeiger-Dereferenzierung) Da die Funktion task_tick_fair Teil des CFS-Schedulers ist, dachte ich, dass ein Fehler aufgetreten ist, indem der Prioritätsbereich, der ursprünglich in den CFS-Scheduler eingegeben werden sollte, auf den Bereich des Zufalls-Schedulers gesetzt wurde.
Beim Umschreiben von Linux ist es sehr schwierig, das Ganze zu verstehen und zu schreiben. Es wird daher empfohlen, es zu implementieren, während Sie Tools wie GNU Global verwenden. Zu guter Letzt Wie war es ?
Recommended Posts