Einführung in PlantUML beim Lesen des Linux-Treibers

In diesem Artikel werde ich PlantUML kurz vorstellen, während ich die Linux-Treiberquelle lese.

Was ist Plant UML?

Offizielle Website: https://plantuml.com/

Da es viele Kommentarartikel zu PlantUML gibt, werde ich die Details weglassen, aber wie der Name schon sagt, ist es ein Werkzeug zum Schreiben von UML. Das Merkmal ist, dass es im Text beschrieben werden kann.

UML wird häufig für das Design verwendet, kann aber natürlich auch für die Bewegungsanalyse verwendet werden. Ist es beispielsweise beim Lesen der Treiberquelle nicht möglich, dass die Funktionsaufrufbeziehung zwischen Dateien kompliziert wird und nicht mit dem Verständnis Schritt halten kann? Nach langer Zeit vergesse ich sofort, was der Anrufer war. Es wäre schön zu klären, wer die Funktionen in welcher Reihenfolge aufruft, und Sequenzdiagramme sind in solchen Fällen nützlich.

Wenn ich mit PlantUML ein Sequenzdiagramm schreibe, sieht es so aus.

@startuml
Alice -> Bob: SYN
Alice <-- Bob: ACK
@enduml

Nachdem Sie das Obige mit einem Texteditor erstellt haben, speichern Sie es unter einem geeigneten Namen (Beispiel: hoge.puml) und generieren Sie ein Bild mit dem folgenden Befehl.

$ plantuml hoge.puml

Bei der Ausführung wird hoge.png in dem Verzeichnis generiert, in dem der Befehl ausgeführt wurde.

hoge.png

Die Syntax lautet Objektname Pfeil (wie '->' oder '->') Objektname: Nachricht Wenn Sie also den Namen der Quelldatei in den Objektnamen und die Funktion, die aufgerufen werden soll, in die Nachricht einfügen, können Sie die Beziehung zwischen Funktionsaufrufen zwischen Dateien anscheinend leicht ausdrücken. Weitere Informationen zur Syntax finden Sie auf der offiziellen Website.

https://plantuml.com/ja/sequence-diagram

Lesen Sie die Linux-Treiberquelle

Lassen Sie uns als Beispiel einen kurzen Blick auf den Linux v5.4 USB-Kameratreiber werfen. Es befindet sich unter drivers / media / usb / uvc.

$ ls drivers/media/uvc/
Kconfig  Makefile  uvc_ctrl.c  uvc_debugfs.c  uvc_driver.c
uvc_entity.c  uvc_isight.c  uvc_metadata.c  uvc_queue.c
uvc_status.c  uvc_v4l2.c  uvc_video.c  uvcvideo.h

Warum gibt es so viele Dateien?

Die Treiber der niedrigsten Ebene für Linux USB (Host-Controller und HUB-Steuerung) befinden sich unter drivers / usb / core. Andererseits ist der UVC-Treiber in Treiber / media / uvc / der Teil, der funktioniert, wenn die Klasse des angeschlossenen USB-Geräts UVC ist. Ich bin mir nicht sicher, wie die Beziehungen zwischen den Dateien hier aussehen, daher werde ich weiterlesen, um die Beziehungen zwischen diesen Dateien bis zu einem gewissen Grad zu verstehen. Da der Zweck dieser Zeit jedoch darin besteht, PlantUML ** einzuführen, werde ich es nicht im Detail lesen.

Zuallererst ist es ein Anfang. Da der UVC-Treiber ein Kameratreiber ist, sollte es sich um einen Mechanismus handeln, der das Linux Video Capture Framework (V4L2) verwendet, um die Kamera gemäß ioctl () für / dev / videoXXX aus dem Benutzerland zu betreiben. Beginnen Sie mit dieser Annahme zu lesen.

Erstens, wenn Sie grep, scheint es, dass uvc_driver.c, uvc_metadata.c und uvc_v4l2.c ioctl verarbeiten. Folgen wir als Beispiel dem Prozess von ioctl VIDIOC_S_FMT, mit dem das Datenformat festgelegt wird.

Wenn Sie einen ähnlichen Namen suchen, finden Sie ihn in uvc_metadata.c und uvc_ioctl.c.

$ grep -iH -n vidioc_s_fmt drivers/media/usb/uvc/*
drivers/media/usb/uvc/uvc_metadata.c:133:	.vidioc_s_fmt_meta_cap		= uvc_meta_v4l2_set_format,
drivers/media/usb/uvc/uvc_v4l2.c:472: * - VIDIOC_S_FMT
drivers/media/usb/uvc/uvc_v4l2.c:1474:	.vidioc_s_fmt_vid_cap = uvc_ioctl_s_fmt_vid_cap,
drivers/media/usb/uvc/uvc_v4l2.c:1475:	.vidioc_s_fmt_vid_out = uvc_ioctl_s_fmt_vid_out,

Beide Dateien setzen ihre Funktion im Funktionszeiger der Struktur v4l2_ioctl_ops.

ioctl VIDIOC_S_FMT ist eine API, die einen Zeiger auf eine v4l2_format-Struktur als Argument verwendet. Wenn Sie ioctl () aufrufen, lautet der erste Eintrag v4l_s_fmt () in drivers / media / v4l2-core / v4l2-ioctl.c. Wie Sie hier sehen können, wird diese API aufgerufen, indem der Typ angegeben wird, der im Typ der Struktur v4l2_format festgelegt werden soll. v4l2_s_fmt () ruft den in der Struktur v4l2_ioctl_ops festgelegten Funktionszeiger entsprechend dem Typ auf.

v4l2-ioctl.c


static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
				struct file *file, void *fh, void *arg)
{
...
	switch (p->type) {
	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
		if (unlikely(!ops->vidioc_s_fmt_vid_cap))
			break;
		CLEAR_AFTER_FIELD(p, fmt.pix);
		ret = ops->vidioc_s_fmt_vid_cap(file, fh, arg);
		/* just in case the driver zeroed it again */
		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
		if (vfd->vfl_type == VFL_TYPE_TOUCH)
			v4l_pix_format_touch(&p->fmt.pix);
		return ret;
...

Wenn V4L2_BUF_TYPE_VIDEO_CAPTURE für den Typ angegeben ist, scheint es den Aufrufpfad der Funktion uvc_ioctl_s_fmt_vid_cap () einzugeben, die in .vidioc_s_fmt_vid_cap in uvc_v4l2.c festgelegt ist.

Also, wenn Sie dies in PlantUML ausdrücken

@startuml
participant "User Space" as user
participant "v4l2-ioctl.c" as v4l2
participant "uvc_v4l2.c" as uvc

user -> v4l2: ioctl(fd, V4L2_S_FMT, *v4l2_format)
alt v4l2_format->type == V4L2_BUF_TYPE_VIDEO_CAPTURE
	v4l2 -> uvc: uvc_ioctl_s_fmt_vid_cap()
end
@enduml

sample.png

Wie wäre es mit so etwas? Der Grund, warum "Teilnehmer ~ als ..." am Anfang hinzugefügt wird, ist, dass, wenn ein Bindestrich '-' im Objektnamen enthalten ist, dieser aus grammatikalischen Gründen in doppelte Anführungszeichen gesetzt werden muss, so dass es mühsam ist, jeden einzelnen zu schreiben, sodass ein Alias angegeben wird.

Wir werden den Funktionsaufrufen in Zukunft etwas tiefer folgen.

uvc_v4l2.c


static int uvc_ioctl_s_fmt_vid_cap(struct file *file, void *fh,
				   struct v4l2_format *fmt)
{
	struct uvc_fh *handle = fh;
	struct uvc_streaming *stream = handle->stream;
	int ret;

	ret = uvc_acquire_privileges(handle);
	if (ret < 0)
		return ret;

	return uvc_v4l2_set_format(stream, fmt);
}

Sowohl uvc_acquire_privileges () als auch uvc_v4l2_set_format (), die von hier aus aufgerufen werden, sind Funktionen von uvc_v4l2.c, aber uvc_v4l2_set_format () ruft danach uvc_v4l2_try_format () und Treiber / media / usb / uvc / uvc_video auf ) Wird genannt. Rufen Sie dann ubc_set_video_ctrl (), __uvc_query_ctrl () auf und rufen Sie schließlich die USB-Kernfunktion usb_control_msg () auf.

Wenn Sie detaillierter schreiben möchten, können Sie alle Funktionsaufrufe in der vorherigen Sequenz schreiben. Schreiben wir alle Funktionsaufrufe der Reihe nach.

@startuml
participant "User Space" as user
participant "v4l2-ioctl.c" as v4l2
participant "uvc_v4l2.c" as uvc
participant "uvc_video.c" as uvc_video
participant "usb/core/message.c " as usb_core

user -> v4l2: ioctl(fd, V4L2_S_FMT, *v4l2_format)
alt v4l2_format->type == V4L2_BUF_TYPE_VIDEO_CAPTURE
	v4l2 -> uvc: uvc_ioctl_s_fmt_vid_cap()
	uvc -> uvc: uvc_v4l2_try_format()
	uvc -> uvc_video:  uvc_probe_video()
	uvc_video -> uvc_video: uvc_set_video_ctrl()
	uvc_video -> uvc_video: __uvc_query_ctrl()
	uvc_video -> usb_core: usb_control_msg()
end
@enduml

sample2.png

Wenn Sie so weit gelesen haben, können Sie die grobe Struktur sehen, von dem Zeitpunkt, an dem ioctl () aus Userland aufgerufen wird, bis zu dem Zeitpunkt, an dem der Befehl auf das USB-Gerät übertragen wird. Darüber hinaus ist zu erwarten, dass uvc_v4l2.c für die Verarbeitung von v4l2 ioctl () und uvc_video.c für die Nachrichtenverarbeitung im Zusammenhang mit der USB-Kommunikation verantwortlich ist.

Zusammenfassung

In diesem Artikel habe ich PlantUML am Beispiel des in C geschriebenen Linux-Quellcodes vorgestellt.

Persönlich mag ich es, weil ich es intuitiv schreiben kann, als würde ich ein Memo auf einen Aufrufstapel schreiben. Ist es ein Engpass, der Java erfordert oder jedes Mal erstellt werden muss (dies kann mit Atom- oder VSCode-Plugins behoben werden, lesen Sie also andere Artikel)? Das Schreiben in Textform ist jedoch ein großer Vorteil.

Beispiele für die Verwendung von PlantUML im Design wurden an mehreren Stellen vorgestellt, aber ich habe nicht viele Beispiele für die Verwendung auf diese Weise für die Analyse gefunden, also habe ich es geschrieben. Bitte benutzen Sie es, wenn Sie sich hinsetzen und die Quelle lesen möchten.

Wenn Sie das Atom- oder VSCode-Plug-In verwenden, können Sie automatisch eine Vorschau erstellen, ohne jedes Mal den Build-Befehl zu drücken. Es gibt viele solcher Einführungsartikel, bitte suchen Sie nach ihnen.

Recommended Posts

Einführung in PlantUML beim Lesen des Linux-Treibers
Die Linux Watchdog-Treiber-API
Steuern Sie Linux-Trackpads