Parsen des Linux-Quellcodes epoll

Parsen des Linux-Quellcodes epoll

1. Einleitung

Bei der Hochleistungs-Netzwerkprogrammierung unter Linux ist epoll unverzichtbar. Im Vergleich zu Select, Poll und anderen Systemaufrufen bietet Epoll unvergleichliche Vorteile, wenn eine große Anzahl von Dateideskriptoren überwacht werden muss und nur wenige davon aktiv sind. Epoll ermöglicht es dem Kernel, sich die interessierenden Deskriptoren zu merken und, wenn die entsprechenden Deskriptorereignisse bereit sind, diese bereiten Elemente zur Epoll-Bereitschaftsliste hinzuzufügen und den entsprechenden Epoll-Warteprozess zu aktivieren.

2. Einfaches Epoll-Beispiel

Das folgende Beispiel ist ein vom Autor in der Sprache C geschriebener Codeausschnitt von dbproxy. Aufgrund der Vielzahl der Details wurden einige Auslassungen vorgenommen.

int init_reactor(int listen_fd,int worker_count){
	......
	// Mehrere Epoll-FDs erstellen, um die Multi-Core-Funktionalität voll auszunutzen for(i=0;i<worker_count;i++){
		Reaktor->Arbeiter_fd = epoll_create(EPOLL_MAX_EVENTS);
	}
	/* epoll fügt listen_fd hinzu und akzeptiert */
	// Fügen Sie die Ereignisse nach dem Akzeptieren zum entsprechenden Epoll fd hinzu int client_fd = accept(listen_fd,(struct sockaddr *)&client_addr,&client_len)));
	// Registrieren Sie den Verbindungsdeskriptor beim entsprechenden Worker epoll_ctl(reactor->client_fd,EPOLL_CTL_ADD,epifd,&event);
}
//Arbeitsthread des Reaktors static void* rw_thread_func(void* arg){
	......

	für(;;){
		  // epoll_wait wartet auf Ereignisauslöser int retval = epoll_wait(epfd,events,EPOLL_MAX_EVENTS,500);
        wenn(retval > 0){
        	für(j=0; j < retval; j++){
        		// Lese-Ereignisse verarbeiten if(event & EPOLLIN){
                 führe eine Verbindung mit einem Computer aus, der eine Verbindung mit einem Computer herstellt.
                 weitermachen;
             }
             /* Andere Ereignisse verarbeiten */
        	}
        }
	}
	......
}

Der obige Code implementiert die Akzeptieren- und Lese-/Schreibverarbeitungsthreads tatsächlich in einem Reaktormodus, wie in der folgenden Abbildung gezeigt:

2.1, epoll_create

Das Unix-Konzept, dass alles eine Datei ist, spiegelt sich auch in epoll wider. Der Aufruf epoll_create gibt einen Dateideskriptor zurück, der im Stammverzeichnis von anon_inode_fs (anonymes Inode-Dateisystem) gemountet ist. Schauen wir uns den spezifischen Quellcode des Systemaufrufs epoll_create an:

SYSCALL_DEFINE1(epoll_create, int, Größe)
{
	wenn (Größe <= 0)
		Rückgabe -EINVAL;

	gibt sys_epoll_create1(0) zurück;
}

Wie aus dem obigen Quellcode ersichtlich ist, sind die Parameter von epoll_create grundsätzlich bedeutungslos. Der Kernel bestimmt einfach, ob er 0 ist, und ruft dann direkt sys_epoll_create1 auf. Da der Linux-Systemaufruf durch (SYSCALL_DEFINE1, SYSCALL_DEFINE2 ... SYSCALL_DEFINE6) definiert ist, ist der Quellcode, der sys_epoll_create1 entspricht, SYSCALL_DEFINE(epoll_create1).

(Hinweis: Aufgrund der begrenzten Anzahl von Registern begrenzt der Kernel (unter 80x86) Systemaufrufe auf maximal 6 Parameter. Laut ulk3 liegt dies an der Beschränkung auf 32-Bit-80x86-Register.)

Schauen wir uns als nächstes den Quellcode von epoll_create1 an:

SYSCALL_DEFINE1(epoll_create1, int, flags)
{
	// kzalloc(sizeof(*ep), GFP_KERNEL), nutzt Kernelspeicherplatz error = ep_alloc(&ep);
	// Den nicht verwendeten Dateideskriptor abrufen, d. h. den Steckplatz im Deskriptor-Array fd = get_unused_fd_flags(O_RDWR | (flags & O_CLOEXEC));
	// Ordnen Sie einen Inode im anonymen Inode-Dateisystem zu und holen Sie sich seine Dateistruktur // und file->f_op = &eventpoll_fops
	// und Datei->private_Daten = ep;
	Datei = anon_inode_getfile("[eventpoll]", &eventpoll_fops, ep,
				 O_RDWR | (Flags & O_CLOEXEC));
	//Datei in den entsprechenden Slot des Dateideskriptor-Arrays einfügen fd_install(fd,file);			 
	ep->file = Datei;
	fd zurückgeben;
}

Schließlich wird der von epoll_create generierte Dateideskriptor in der folgenden Abbildung dargestellt:

2.2, Eventpoll-Struktur

Alle epoll-Systemaufrufe basieren auf der Eventpoll-Struktur. Hier ist eine kurze Beschreibung ihrer Mitglieder:

/*
 * Diese Struktur wird in Datei->private_daten*/ gespeichert.
Struktur eventpoll {
	// Spin Lock. Verwenden Sie Spin Lock zum Sperren innerhalb des Kernels, damit mehrere Threads (Prozesse) gleichzeitig an dieser Struktur arbeiten können. // Hauptsächlich ready_list schützen.
	Spinlock_t-Sperre;
	// Dieses Mutex soll sicherstellen, dass der Dateideskriptor nicht entfernt wird, wenn die Ereignisschleife den entsprechenden Dateideskriptor verwendet. struct mutex mtx;
	// Die von epoll_wait verwendete Warteschlange bezieht sich auf den Prozess-Wakeup-wait_queue_head_t wq;
	// Die von file->poll verwendete Warteschlange bezieht sich auf den Prozess-Wakeup-wait_queue_head_t poll_wait;
	// Deskriptor-Warteschlange bereit struct list_head rdllist;
	// Organisieren Sie die Datei-Deskriptoren, denen aktuell Epoll folgt, durch eine Rot-Schwarz-Baumstruktur rb_root rbr;
	// Wenn Sie das Ready-Ereignis an den Benutzerbereich übertragen, verknüpfen Sie den Dateideskriptor des Ereignisses, das gleichzeitig auftritt, mit dieser verknüpften Liste struct epitem *ovflist;
	// Entsprechender Benutzer
	Struktur user_struct *user;
	//Entsprechender Dateideskriptor struct file *file;
	// Die folgenden beiden sind Optimierungen zur Schleifenerkennung: int visited;
	Struktur list_head besuchte_Liste_Link;
};

In diesem Artikel wird beschrieben, wie der Kernel das Ready-Ereignis an Epoll übergibt und den entsprechenden Prozess aufweckt. Daher konzentrieren wir uns hier hauptsächlich auf Mitglieder wie (wait_queue_head_t wq).

2.3, epoll_ctl (hinzufügen)

Sehen wir uns an, wie epoll_ctl (EPOLL_CTL_ADD) den entsprechenden Dateideskriptor in eventpoll einfügt.
Mithilfe von spin_lock und mutex können epoll_ctl-Aufrufe gleichzeitig in mehreren KSEs (Kernel Scheduling Entities, d. h. Prozessen/Threads) ausgeführt werden.

SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd,
		Struktur epoll_event __user *, Ereignis)
{
	/* Prüfen, ob epfd der Epoll-Deskriptor ist*/
	// Der Mutex dient hier dazu, gleichzeitige Aufrufe von epoll_ctl zu verhindern, d. h. die interne Datenstruktur zu schützen. // Sie wird nicht durch gleichzeitige Hinzufügungen, Änderungen und Löschungen zerstört. mutex_lock_nested(&ep->mtx, 0);
	Schalter (op) {
		Fall EPOLL_CTL_ADD:
			...
			// In den Rot-Schwarz-Baum einfügen error = ep_insert(ep, &epds, tfile, fd);
			...
			brechen;
		......
	}
	mutex_unlock(&ep->mtx);	
}

Der obige Vorgang ist in der folgenden Abbildung dargestellt:

2.4, ep_insert

Epitem wird in ep_insert initialisiert, und dann wird der Schwerpunkt dieses Artikels, die Rückruffunktion, wenn das Ereignis bereit ist, initialisiert. Der Code lautet wie folgt:

statische int ep_insert(Struktur eventpoll *ep, Struktur epoll_event *event,
		     Strukturdatei *tfile, int fd)
{
	/* Epitem initialisieren */
	// &epq.pt->qproc = ep_ptable_queue_proc
	init_poll_funcptr(&epq.pt, ep_ptable_queue_proc);
	// Fügen Sie hier die Rückruffunktion ein revents = tfile->f_op->poll(tfile, &epq.pt);
	// Wenn ein Ereignis bereit ist, wird es am Anfang zur Bereitschaftsliste hinzugefügt
	// Zum Beispiel schreibbare Ereignisse // Rufen Sie außerdem nach der internen TCP-Bestätigung tcp_check_space auf und rufen Sie schließlich sock_def_write_space auf, um den entsprechenden Prozess unter epoll_wait zu wecken, if ((revents & event->events) && !ep_is_linked(&epi->rdllink)) {
		list_add_tail(&epi->rdllink, &ep->rdllist);
		// wake_up ep entspricht dem Prozess unter epoll_wait if (waitqueue_active(&ep->wq)){
			aufwachen_gesperrt(&ep->wq);
		}
		......
	}	
	//Epitem in den Rot-Schwarz-Baum einfügen ep_rbtree_insert(ep, epi);
	......
}

2.5. Implementierung von tfile->f_op->poll

Die auf der unteren Ebene des Kernels registrierte Rückruffunktion lautet tfile->f_op->poll(tfile, &epq.pt). Schauen wir uns den Initialisierungsprozess von fd=>file->f_op->poll für den entsprechenden Socket-Dateideskriptor an:

// Fügen Sie die Ereignisse nach dem Akzeptieren zum entsprechenden Epoll fd hinzu int client_fd = accept(listen_fd,(struct sockaddr *)&client_addr,&client_len)));
// Registrieren Sie den Verbindungsdeskriptor beim entsprechenden Worker epoll_ctl(reactor->client_fd,EPOLL_CTL_ADD,epifd,&event);

Wenn wir auf den obigen Benutzerbereichscode zurückblicken, wird fd, nämlich client_fd, von tcps listen_fd über accept aufgerufen. Sehen wir uns also den Schlüsselpfad der accept-Aufrufkette an:

akzeptieren

|->akzeptieren4

|->sock_attach_fd(newsock, neue Datei, Flags & O_NONBLOCK);

|->init_file(Datei,...,&socket_file_ops);

|->Datei->f_op = fop;

/* Datei->f_op = &socket_file_ops */

|->fd_install(newfd, newfile); // fd installieren

Dann sieht die durch Akzeptieren erhaltene Struktur von client_fd wie folgt aus:

(Hinweis: Da es sich um einen TCP-Socket handelt, gilt hier sock->ops=inet_stream_ops. Nachdem wir nun die Implementierung von tfile->f_op->poll kennen, können wir sehen, wie dieser Poll die Rückruffunktion installiert.

2.6. Installation der Callback-Funktion

Der Aufrufpfad des Kernels lautet wie folgt:

sock_poll /*tfile->f_op->poll(tfile, &epq.pt)*/;

|->Socke->Ops->Umfrage

|->tcp_umfrage

/* Wichtig ist hier, sk_sleep für das Aufwecken von KSE (Prozess/Thread) zu erhalten*/

|->sock_poll_wait(Datei, sk->sk_sleep, warten);

|->Umfrage_warten

|->p->qproc(filp, Warteadresse, p);

/* p ist &epq.pt und &epq.pt->qproc = ep_ptable_queue_proc*/

|->ep_ptable_queue_proc(filp,Warteadresse,p);

Nach einem langen Umweg besteht die Installation unserer Rückruffunktion tatsächlich darin, ep_ptable_queue_proc in eventpoll.c aufzurufen und sk->sk_sleep als Kopf seiner Warteschlange zu übergeben. Der Quellcode lautet wie folgt:

statisches void ep_ptable_queue_proc(Struktur Datei *Datei, wait_queue_head_t *whead,
				 poll_table *pt)
{
	// Holen Sie sich das Epitem, das dem aktuellen client_fd entspricht
	Struktur epitem *epi = ep_item_from_epqueue(pt);
	// &pwq->wait->func=ep_poll_callback, wird zum Aufwecken des Callbacks verwendet // Beachten Sie, dass dies kein init_waitqueue_entry ist, d. h. der aktuelle KSE (aktueller, aktueller Prozess/Thread) wird nicht in // wait_queue geschrieben, da er nicht unbedingt vom aktuell installierten KSE aufgeweckt wird, sondern der KSE sein sollte, der epoll_wait aufweckt
	init_waitqueue_func_entry(&pwq->warten, ep_poll_callback);
	// Der Whead ist hier sk->sk_sleep, verknüpfe die aktuelle Warteschlange mit der Ruheliste, die dem Socket entspricht add_wait_queue(whead, &pwq->wait);	
}

Auf diese Weise wird die Struktur von client_fd weiter verbessert, wie in der folgenden Abbildung dargestellt:

In der Funktion ep_poll_callback wird das entsprechende epoll_wait aufgeweckt, das wir später beschreiben werden.

2.7, epoll_wait

epoll_wait ruft hauptsächlich ep_poll auf:

SYSCALL_DEFINE4(epoll_wait, int, epfd, Struktur epoll_event __user *, Ereignisse,
		int, MaxEvents, int, Timeout)
{
	/* Überprüfen Sie, ob epfd das von epoll_create erstellte fd ist */
	//ep_poll aufrufen
	Fehler = ep_poll(ep, Ereignisse, Max. Ereignisse, Timeout);
	...
}

Schauen wir uns als nächstes die Funktion ep_poll an:

statische int ep_poll(Struktur eventpoll *ep, Struktur epoll_event __user *events,
		   int maxevents, langes Timeout)
{
	......
wiederholen:
	// Spinlock abrufen
	spin_lock_irqsave(&ep->Sperre, Flags);
	// Schreibe die aktuelle Task-Struktur in die Warteschlange zum Aufwecken // wq_entry->func = default_wake_function;
	init_waitqueue_entry(&warten, aktuell);
	// WQ_FLAG_EXCLUSIVE, exklusives Aufwecken, Zusammenarbeit mit SO_REUSEPORT um das Accept-Panik-Problem zu lösen wait.flags |= WQ_FLAG_EXCLUSIVE;
	// Link zur Warteschlange von ep __add_wait_queue(&ep->wq, &wait);
	für (;;) {
		// Den aktuellen Prozessstatus auf unterbrechbar setzen set_current_state(TASK_INTERRUPTIBLE);
		//Überprüfen Sie, ob der aktuelle Thread ein zu verarbeitendes Signal hat. Wenn ja, geben Sie -EINTR zurück.
		wenn (signal_pending(current)) {
			res = -EINTR;
			brechen;
		}
		spin_unlock_irqrestore(&ep->Sperre, Flags);
		// Zeitplan planen, die CPU aufgeben
		jtimeout = Zeitplan_Timeout(jtimeout);
		spin_lock_irqsave(&ep->Sperre, Flags);
	}
	// Hier wird angezeigt, dass der Prozess aufgrund einer Zeitüberschreitung oder eines ausgelösten Ereignisses neu geplant wurde. __remove_wait_queue(&ep->wq, &wait);
	// Setzt den Prozessstatus auf „Läuft“
	setze_aktuellen_Zustand(AUFGABE_LÄUFT);
	......
	// Prüfen, ob Ereignisse verfügbar sind eavail = !list_empty(&ep->rdllist) || ep->ovflist != EP_UNACTIVE_PTR;
	......
	//Bereitige Ereignisse in den Benutzerbereich kopieren ep_send_events(ep, events, maxevents)
}

Die obige Logik wird in der folgenden Abbildung dargestellt:

2.8, ep_send_events

Die Funktion ep_send_events ruft hauptsächlich ep_scan_ready_list auf. Wie der Name schon sagt, ist ep_scan_ready_list die Scan-Ready-Liste:

statische int ep_scan_ready_list(Struktur eventpoll *ep,
			      int (*sproc)(Struktur eventpoll *,
					   Struktur list_head *, void *),
			      void *priv,
			      int Tiefe)
{
	...
	// Verknüpfe epfds rdlist mit txlist
	list_splice_init(&ep->rdlist, &txlist);
	...
	/* sproc = ep_send_events_proc */
	Fehler = (*sproc)(ep, &txlist, priv);
	...
	// Verarbeiten Sie ovflist, also das Ereignis, das im obigen Sproc-Prozess auftritt ...
}

Es ruft hauptsächlich ep_send_events_proc auf:

statische int ep_send_events_proc(Struktur eventpoll *ep, Struktur list_head *head,
			       void *priv)
{
	für (eventcnt = 0, uevent = esed->events;
	     !list_empty(head) && eventcnt < esed->maxevents;) {
	   // Durchlaufe die Bereitschaftsliste 
		epi = Liste_erster_Eintrag(Kopf, Struktur epitem, rdllink);
		list_del_init(&epi->rdlllink);
		// Readylist zeigt nur an, dass in der aktuellen Epi ein Ereignis vorliegt. Die spezifischen Ereignisinformationen müssen noch die Umfrage der entsprechenden Datei aufrufen
		// Die Umfrage hier ist tcp_poll, das die Maske und andere Informationen entsprechend den Informationen von TCP selbst festlegt und die Ereignismaske von Interesse festlegt, um zu wissen, ob das aktuelle Ereignis das Ereignis von Interesse für epoll_wait ist revents = epi->ffd.file->f_op->poll(epi->ffd.file, NULL) &
			epi->Ereignis.Ereignisse;
		wenn(Ereignisse){
			/* Das Ereignis in den Benutzerbereich setzen */
			/* ONESHOT-Logik verarbeiten */
			// Wenn es nicht durch die Flanke ausgelöst wird, fügen Sie das aktuelle Epi wieder zur verfügbaren Liste hinzu, damit die Umfrage beim nächsten Mal ausgelöst werden kann. Wenn die Ergebnisse der nächsten Umfrage nicht 0 sind, kann der Benutzerbereich sie immer noch wahrnehmen*/
			sonst wenn (!(epi->event.events & EPOLLET)){
				list_add_tail(&epi->rdllink, &ep->rdllist);
			}
			/* Wenn es durch die Flanke ausgelöst wird, wird es nicht wieder zur Liste der verfügbaren Ereignisse hinzugefügt. Daher wird das entsprechende Epi erst dann in die Liste der verfügbaren Ereignisse aufgenommen, wenn das nächste verfügbare Ereignis ausgelöst wird.*/
			Ereigniscnt++
		}
		/* Wenn epoll_wait nicht an dem abgefragten Revents-Ereignis interessiert ist (oder überhaupt kein Ereignis vorliegt), wird es nicht wieder zur Liste der verfügbaren Ereignisse hinzugefügt*/
		......
	}
	gibt eventcnt zurück;
}

Die Logik des obigen Codes ist wie folgt:

3. Der Prozess des Hinzufügens von Ereignissen zur Epoll-Bereitschaftswarteschlange (rdlist)

Nach der ausführlichen Beschreibung in den obigen Kapiteln können wir nun endlich erklären, wie sich TCP bei eintreffenden Daten in die Bereitschaftswarteschlange von Epoll einfügt.

3.1. Lesbare Ereignisse kommen an

Schauen wir uns zunächst das TCP-Datenpaket vom Netzwerkkartentreiber zur internen TCP-Protokollverarbeitungsaufrufkette des Kernels an:

Schritt 1:

Der Kernelpfad des ankommenden Netzwerkpakets, nachdem die Netzwerkkarte einen Interrupt initiiert hat, ruft netif_rx auf, um das Ereignis in die Warteschlange der CPU zu hängen, und ruft den Soft-Interrupt (soft_irq) auf. Anschließend ruft er net_rx_action über den Linux-Soft-Interrupt-Mechanismus auf, wie in der folgenden Abbildung dargestellt:

Hinweis: Das obige Bild stammt von PLKA (<<In-depth Linux Kernel Architecture>>)

Schritt 2:

Folgen Sie dann next_rx_action

nächste_RX_Aktion

|-Prozessrückstand

......

|->packet_type->func Hier betrachten wir ip_rcv

|->ipprot->handler Hier wird ipprot als tcp_protocol überladen

(Handler ist tcp_v4_rcv)

Schauen wir uns den entsprechenden tcp_v4_rcv an

tcp_v4_rcv

|->tcp_v4_do_rcv

|->TCP_Empfangsstatusprozess

|->TCP-Datenwarteschlange

|-> sk->sk_data_ready(sock_def_lesbar)

|->wake_up_interruptible_sync_poll(sk->sleep,...)

|->__aufwachen

|->__wake_up_common

|->aktuell->Funktion

/* Dies wurde von ep_insert als ep_poll_callback hinzugefügt und das exklusive Flag WQ_FLAG_EXCLUSIVE ist gesetzt*/

|->ep_poll_callback

Schauen wir uns auf diese Weise die Funktion ep_poll_callback an, die schließlich epoll_wait aufweckt:

statische int ep_poll_callback(wait_queue_t *warten, vorzeichenloser Modus, int sync, void *Schlüssel)
{
	// Holen Sie sich das Epitem, das dem Warten entspricht	
	Struktur epitem *epi = ep_item_from_wait(wait);
	// Eventpoll-Struktur entsprechend dem Epitem struct eventpoll *ep = epi->ep;
	// Holen Sie sich den Spinlock, um die Ready_List und andere Strukturen zu schützen spin_lock_irqsave(&ep->lock, flags);
	// Wenn das aktuelle Epi nicht mit der Liste der bereiten Ereignisse von EP verknüpft ist, dann verknüpfen Sie es. // Auf diese Weise werden die aktuell verfügbaren Ereignisse zur Liste der verfügbaren Ereignisse von Epoll hinzugefügt, wenn (!ep_is_linked(&epi->rdllink)).
		list_add_tail(&epi->rdllink, &ep->rdllist);
	// Wenn epoll_wait wartet, wecken Sie den epoll_wait-Prozess auf // Das entsprechende &ep->wq wird durch init_waitqueue_entry(&wait, current) generiert, wenn epoll_wait aufgerufen wird // Das aktuelle ist die Prozessinformation task_struct, die dem Aufruf von epoll_wait entspricht
	wenn (waitqueue_active(&ep->wq))
		aufwachen_gesperrt(&ep->wq);
}

Der obige Vorgang ist in der folgenden Abbildung dargestellt:

Schließlich ruft wake_up_locked __wake_up_common auf und ruft dann default_wake_function auf, das in init_waitqueue_entry registriert ist. Der Aufrufpfad lautet:

Aufwachen_gesperrt

|->__wake_up_common

|->Standard-Weckfunktion

|->try_wake_up (einen Thread aufwecken)

|->Aufgabe_aktivieren

|->enqueue_task wird ausgeführt

Schieben Sie den epoll_wait-Prozess in die ausführbare Warteschlange, warten Sie, bis der Kernel den Prozess neu plant, und nehmen Sie dann, nachdem der epoll_wait entsprechende Prozess erneut ausgeführt wurde, die Planung wieder auf und fahren Sie mit den folgenden ep_send_events fort (Ereignisse in den Benutzerbereich kopieren und zurückkehren).

Der Wake_up-Prozess ist in der folgenden Abbildung dargestellt:

3.2. Ankunft eines beschreibbaren Ereignisses

Der Ablauf bei schreibbaren Ereignissen ähnelt dem bei lesbaren Ereignissen:

Wenn epoll_ctl_add aufgerufen wird, wird zunächst einmal im Voraus eine Abfrage des entsprechenden Dateideskriptors aufgerufen. Wenn das Rückgabeereignis eine beschreibbare Maske enthält, wird wake_up_locked direkt aufgerufen, um den entsprechenden epoll_wait-Prozess aufzuwecken.

Wenn die Daten dann beim zugrunde liegenden Treiber von TCP ankommen, können sie eine Bestätigung enthalten, sodass einige der vom anderen Ende empfangenen Daten freigegeben werden können, wodurch ein Schreibereignis ausgelöst wird. Die Aufrufkette dieses Teils lautet:

tcp_input.c

tcp_v4_rcv

|-tcp_v4_do_rcv

|-tcp_rcv_state_process

|-tcp_data_snd_check

|->tcp_check_space

|->tcp_neuer_Bereich

|->sk->sk_Schreibbereich

/* Unter TCP ist es sk_stream_write_space*/

Schließlich weckt sk_stream_write_space in dieser Funktion den entsprechenden epoll_wait-Prozess

void sk_stream_write_space(Struktur sock *sk)
{
	// Das heißt, das Schreibereignis wird nur ausgelöst, wenn 1/3 des Schreibspeicherplatzes vorhanden ist, if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk) && sock) {
		: Clear_Bit (SOCK_NOSPACE, &sock->Flags);

		wenn (sk->sk_sleep und waitqueue_active(sk->sk_sleep))
			unterbrechungsfähige Aufwachumfrage(sk->sk_sleep, POLLOUT |
						POLLWRNORM | POLLWRBAND)
		......
	}
}

4. Deskriptor schließen (close fd)

Es ist zu beachten, dass wir beim Schließen des entsprechenden Dateideskriptors automatisch eventpoll_release aufrufen, um die entsprechende Datei aus dem zugehörigen epoll_fd zu löschen. Der Kernelschlüsselpfad lautet wie folgt:

schließen fd

|->filp_schließen

|->fput

|->__fput

|->Eventumfrage_Veröffentlichung

|->ep_entfernen

Daher müssen wir nach dem Schließen des entsprechenden Dateideskriptors den entsprechenden Deskriptor im entsprechenden Epoll nicht über epoll_ctl_del löschen.

V. Fazit

Epoll wird unter Linux häufig als hervorragender Mechanismus zur Ereignisauslösung verwendet. Der Quellcode ist relativ komplex. Dieser Artikel erklärt nur den Auslösemechanismus von Epoll-Lese- und Schreibereignissen.

Oben finden Sie den detaillierten Inhalt zum Parsen des Linux-Quellcodes Epoll. Weitere Informationen zum Linux-Quellcode Epoll finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM!

Das könnte Sie auch interessieren:
  • Linux IO-Multiplexing Epoll-Netzwerkprogrammierung
  • Detaillierte Erklärung des Linux-Epoll-Mechanismus
  • Linux-Kernel-Select/Poll, Epoll-Implementierung und Unterschiede
  • Python implementiert asynchronen Epoll-Code unter Linux
  • So installieren Sie MySQL unter Linux (Yum und Quellcode-Kompilierung)
  • Tutorial zur Installation von mysql5.6.20 aus dem Quellcode unter Linux
  • Teilen Sie die Schritte zum Kompilieren, Installieren und Konfigurieren eines SVN-Servers unter Linux
  • So installieren Sie MySQL 5.6 aus dem Quellcode unter SUSE Linux
  • Aufbau einer Linux+PHP+Apache+Oracle-Umgebung: Quellcode-Kompilierung und Installation von PHP unter CentOS

<<:  Mehrere Grundsätze für die Produktdesign-Referenz auf Websites

>>:  Detaillierte Erklärung der Vuex-Umgebung

Artikel empfehlen

Detaillierte Erklärung der CSS3-Textschatteneigenschaft Textschatten

Textschatten-Textschatten-Eigenschaftseffekte: 1....

Erste Erkundung gängiger Befehle für Docker-Anfänger

Bevor wir Docker offiziell verwenden, machen wir ...

Detaillierte Erläuterung der Nginx-Prozessverwaltungs- und Neuladeprinzipien

Prozessstrukturdiagramm Nginx ist eine Multiproze...

Datenbankabfrageoptimierung: Unterabfrageoptimierung

1. Fall Nehmen Sie alle Mitarbeiter, die nicht Fi...

Einige allgemeine erweiterte SQL-Anweisungen in MySQL

Erweiterte MySQL-SQL-Anweisungen benutze kgc; Tab...

Beispielcode für Nginx zur Erreichung dynamischer und statischer Trennung

1. Einfache Konfiguration der dynamischen und sta...

So konfigurieren Sie Bash-Umgebungsvariablen in Linux

Shell ist ein in der Programmiersprache C geschri...

So richten Sie Referer in Nginx ein, um Bilddiebstahl zu verhindern

Wenn die Bilder des Servers von anderen Websites ...

Verwenden von Shadowsocks zum Erstellen eines transparenten LAN-Gateways

Inhaltsverzeichnis Installieren und konfigurieren...