CRuby Code Reading (2): rb_newobj_of

Über dieses Thema

Dies ist ein Artikel, in dem ich die CRuby-Implementierung lesen werde. Dies ist das zweite Mal.

Dieses Mal wollte ich rb_intern lesen, das das Symbol definiert, aber nachdem ich einige Funktionen ausgegraben hatte Ich war verwirrt, weil gefälschter RString und echter RString im Zusammenhang mit RString (Zeichenfolge) ordnungsgemäß verwendet wurden. Wenn Sie sich den Peripheriecode ansehen, können Sie anscheinend lesen, dass Sie den Initialisierungsvorgang nach dem Sichern des Ruby-Objekts verstanden haben. Daher werde ich mich mit dem nächsten verbinden, indem ich "rb_newobj_of" lese, das ein neues Objekt zu sichern scheint.

Weil der Zweck darin besteht, den Anfangszustand des Objekts zu kennen Ich werde die detaillierte Geschichte von GC und Zuweisung in der Warteschleife lesen.

rb_newobj_of

rb_newobj_of ist in gc.c. definiert.

VALUE
rb_newobj_of(VALUE klass, VALUE flags)
{
    return newobj_of(klass, flags, 0, 0, 0);
}

Anscheinend ist die Entität wie "newobj_of". Wenn Sie auf der Seite, die Sie verwenden, nach dem Code suchen und ihn lesen, ist klass eine Konstante, die den Klassentyp ausdrückt. In "Flags" wurden verschiedene Flags, die sich auf das Objekt beziehen, in Form von Bit-Flags gesetzt.

Übrigens ist der Typ jedes Arguments und Rückgabewerts VALUE. Da es als Bit-Flag verwendet werden kann, besteht kein Zweifel daran, dass es sich um einen natürlichen Zahlentyp handelt. Aber um welche Art von Typ handelt es sich? Und 0 der drei Argumente des Aufrufs ist eine magische Zahl, aber was bedeutet das?

Ich las jeden und untersuchte ihn.

Typ VALUE

Was ist der VALUE-Typ?

Ich habe mit grep gesucht und typedef in include / ruby / ruby.h gefunden.

#if defined HAVE_UINTPTR_T && 0
typedef uintptr_t VALUE;
typedef uintptr_t ID;
# define SIGNED_VALUE intptr_t
# define SIZEOF_VALUE SIZEOF_UINTPTR_T
# undef PRI_VALUE_PREFIX
#elif SIZEOF_LONG == SIZEOF_VOIDP
typedef unsigned long VALUE;
typedef unsigned long ID;
# define SIGNED_VALUE long
# define SIZEOF_VALUE SIZEOF_LONG
# define PRI_VALUE_PREFIX "l"
#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
typedef unsigned LONG_LONG VALUE;
typedef unsigned LONG_LONG ID;
# define SIGNED_VALUE LONG_LONG
# define LONG_LONG_VALUE 1
# define SIZEOF_VALUE SIZEOF_LONG_LONG
# define PRI_VALUE_PREFIX PRI_LL_PREFIX
#else
# error ---->> ruby requires sizeof(void*) == sizeof(long) or sizeof(LONG_LONG) to be compiled. <<----
#endif

Es sieht aus wie ein Typ, der der Zeigergröße der Umgebung entspricht. Was bringt es, VALUE zu verwenden, anstatt einfach Zeiger oder natürliche Zahlentypen zu verwenden?

Wenn Sie sich die Verwendung von VALUE mit grep ansehen, können Sie Folgendes verstehen.

Wahrscheinlich ein Mechanismus zur Verbesserung der Speichereffizienz. Jetzt, wo ich weiß, dass es im Grunde ein Zeiger ist, ist das genug. Ich habe mich entschlossen, vorerst keine weiteren Untersuchungen zu VALUE durchzuführen.

newobj_of

Wie rb_newobj_of hat es eine Definition in gc.c.

static VALUE
newobj_of(VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3)
{
   :
<<Unterlassung>>
   :
   :
    return obj;
}

Der in rb_newobj_of angegebene Parameter 0 sollte in v1 bis v3 liegen.

Lesen wir aus dem Kopf.

static VALUE
newobj_of(VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3)
{
    rb_objspace_t *objspace = &rb_objspace;
    VALUE obj;

    if (UNLIKELY(during_gc || ruby_gc_stressful)) {
	if (during_gc) {
	    dont_gc = 1;
	    during_gc = 0;
	    rb_bug("object allocation during garbage collection phase");
	}

	if (ruby_gc_stressful) {
	    if (!garbage_collect(objspace, FALSE, FALSE, FALSE, GPR_FLAG_NEWOBJ)) {
		rb_memerror();
	    }
	}
    }
   :
<<Unterlassung>>

Oh. Es ist "unwahrscheinlich". Da UNLIKELY angegeben ist, Diese Bedingungsklausel ist im Grunde ein Pfad, der nicht vorkommt. Lassen Sie uns ihn also einmal überspringen. (Und soweit die bedingte Anweisung gesehen wird, scheint es die Operation während der GC-Ausführung zu sein.)

objspace und obj sind in der Variablendeklaration reserviert. Das Makro für rb_objspace wurde in gc.c. definiert.

#if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
#define rb_objspace (*GET_VM()->objspace)
#else
static rb_objspace_t rb_objspace = {{GC_MALLOC_LIMIT_MIN}};
#endif

Soweit Sie sehen können, sieht es wie ein Bereich zum Platzieren von Objekten aus, wie der Name schon sagt. Der Zweck ist vorerst der Ausgangszustand, so dass es anscheinend nicht nötig ist, hier tief zu graben.

Kommen wir zur Mitte der Funktion newobj_of.

static VALUE
newobj_of(VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3)
{
<<Unterlassung>>
   :
    obj = heap_get_freeobj(objspace, heap_eden);

    if (RGENGC_CHECK_MODE > 0) assert(BUILTIN_TYPE(obj) == T_NONE);

    /* OBJSETUP */
    RBASIC(obj)->flags = flags & ~FL_WB_PROTECTED;
    RBASIC_SET_CLASS_RAW(obj, klass);
    if (rb_safe_level() >= 3) FL_SET((obj), FL_TAINT);
    RANY(obj)->as.values.v1 = v1;
    RANY(obj)->as.values.v2 = v2;
    RANY(obj)->as.values.v3 = v3;
<<Unterlassung>>
}

Sie können es lesen, als hätten Sie freeobj (?) Erworben. Mit der Funktion heap_get_freeobj im obj haben Sie zuvor reserviert. Danach scheint es "Flags" für das Ergebnis des Durchlaufens von "obj" durch das "RBASIC" -Makro zu setzen.

Wenn FL_WB_PROTECTED grep ist,#define FL_WB_PROTECTED (((VALUE) 1) << 5) Sie können sehen, dass es ein Bit-Flag ist, das das 5. Bit verwendet.

Da dies ein negatives logisches Produkt ist, wird nur das 5. Bit ignoriert, selbst wenn es durch das Argument "flags" von "newobj_of" angegeben wird.

Ich frage mich, wer der FL_WB_PROTECTED ist, aber als Wissen über den Anfangswert Ich denke, es reicht zu wissen, dass der Wert zum Zeitpunkt der Initialisierung nicht steht. Dies liegt daran, dass es einen Flaggennamen oder eine Form des Zugriffs auf das 5. Bit geben sollte, in dem Sie es verwenden. (Wenn Sie von nun an lesen, können Sie die Bedeutung von "FL_WB_PROTECTED" verstehen.)

Es ist mir also egal.

Was ist das RBASIC-Makro? In ähnlicher Weise wird auch "RANY" verwendet. Insbesondere in "RANY" werden die Argumente "v1" bis "v3" zugewiesen. Welche Auswirkung hat dies?

Was machen RBASIC_SET_CLASS_RAW und FL_SET? Und wie spät ist es, wenn rb_safe_level () 3 oder höher ist?

  1. Was macht heap_get_freeobj?
  2. Was machen RBASIC und RANY, um zu objen?
  1. Was macht RBASIC_SET_CLASS_RAW?
  2. Wie ist die Situation mit rb_safe_level ()> = 3?

Lass uns einen Blick darauf werfen.

  1. Was macht heap_get_freeobj?

Die Definition von "heap_get_freeobj ()" lautet wie folgt.

static inline VALUE
heap_get_freeobj(rb_objspace_t *objspace, rb_heap_t *heap)
{
    RVALUE *p = heap->freelist;

    while (1) {
	if (LIKELY(p != NULL)) {
	    heap->freelist = p->as.free.next;
	    return (VALUE)p;
	}
	else {
	    p = heap_get_freeobj_from_next_freepage(objspace, heap);
	}
    }
}

Oh, es ist wahrscheinlich. Wenn Sie "LIKELY" als Hinweis verwenden, scheinen Sie einfach ein Objekt aus der freien Liste zu erhalten. Es scheint nicht mit dem Zweck zu tun zu haben, also habe ich nur verstanden, dass ich in der Lage war, Speicher zu sichern, und bin zum nächsten Schritt übergegangen.

heap_get_freeobj reserviert einfach das Objekt aus der freien Liste

  1. Was machen RBASIC und RANY, um zu objen?

Ich habe versucht herauszufinden, was RBASIC und RANY tun, um zu objen. Als Ergebnis wurden die folgenden zwei gefunden.

#define RBASIC(obj)  (R_CAST(RBasic)(obj))
#define RANY(o) ((RVALUE*)(o))

Es gab die folgende Definition für "R_CAST"

#define R_CAST(st)   (struct st*)

Mit anderen Worten, beide betrachten "obj" als Zeiger auf eine bestimmte Struktur. (Für das Makro "RBASIC" ein Zeiger auf die Struktur "RBasic". Für RANY der Zeiger auf die Struktur "RVALUE".)

Die RBasic-Struktur und die RVALUE-Struktur waren wie folgt.

struct RBasic {
    VALUE flags;
    const VALUE klass;
}
typedef struct RVALUE {
    union {
	struct {
	    VALUE flags;		/* always 0 for freed obj */
	    struct RVALUE *next;
	} free;
	struct RBasic  basic;
	struct RObject object;
	struct RClass  klass;
	struct RFloat  flonum;
	struct RString string;
	struct RArray  array;
	struct RRegexp regexp;
	struct RHash   hash;
	struct RData   data;
	struct RTypedData   typeddata;
	struct RStruct rstruct;
	struct RBignum bignum;
	struct RFile   file;
	struct RNode   node;
	struct RMatch  match;
	struct RRational rational;
	struct RComplex complex;
	struct RSymbol symbol;
	struct {
	    struct RBasic basic;
	    VALUE v1;
	    VALUE v2;
	    VALUE v3;
	} values;
    } as;
#if GC_DEBUG
    const char *file;
    int line;
#endif
} RVALUE;

Ignorieren Sie "GC_DEBUG", da es sich anscheinend um Debug-Informationen handelt.

RBasic ist der erste Teil von RVALUE. RVALUE scheint ein eingebauter Gemeinschaftskörper aller Art zu sein. Als ich es nachgeschlagen habe, war der Hauptteil der Strukturdefinition jedes in RVALUE enthaltenen Typs im Grunde RBasic. Daraus ist ersichtlich, dass RBasic häufig als Objektverwaltungsheader verwendet wird.

Da RBasic "Flags" hat, was ein gemeinsamer Header für das gesamte Objekt ist, Die Zuordnung zu Flags im ursprünglichen Code war die Zuweisung von Flags zu diesem Header.

Außerdem wurde jeder in "RVALUE" enthaltene Typ klein gemacht und passt auf bis zu 3 Zeiger (= 3 WERTE).

Daher wird von "v1" in "Werten" von "RVALUE" über "v3" Durch den Zugriff darauf scheinen die Werte aller Objekte ordnungsgemäß initialisiert zu werden.

  1. Was macht RBASIC_SET_CLASS_RAW?

Sie können Folgendes von grep finden.

#define RBASIC_SET_CLASS_RAW(obj, cls) (((struct RBasicRaw *)((VALUE)(obj)))->klass = (cls))

Wenn Sie auch die Definition der RBasicRaw-Struktur kennen

struct RBasicRaw {
    VALUE flags;
    VALUE klass;
};

Es war so. Es ist sehr ähnlich zu "RBasic", aber es scheint einen Unterschied in der const-Eigenschaft von "klass" im Vergleich zu geben. Anscheinend sollte das Berühren des "klass" -Wertes grundsätzlich nicht erfolgen. Verwenden Sie "RBASIC_SET_CLASS_RAW", wo immer Sie es benötigen Ich denke, dass es geschützt ist, damit es betrieben werden kann.

Der Wert von "klass" war übrigens eine Konstante, die den Klassentyp anzugeben scheint. Daher kann RBASIC_SET_CLASS_RAW als Makro betrachtet werden, das den angegebenen Typ obj als cls aufzeichnet.

  1. Wie ist die Situation mit rb_safe_level ()> = 3?

Zum Schluss lesen wir rb_safe_level (). Sie finden es am Anfang von safe.c von grep.

int
rb_safe_level(void)
{
    return GET_THREAD()->safe_level;
}

Und GET_THREAD ist eine Inline-Funktion, wenn Sie denken, dass es sich um ein Makro handelt.

static inline rb_thread_t *
GET_THREAD(void)
{
    rb_thread_t *th = ruby_current_thread;
#if OPT_CALL_CFUNC_WITHOUT_FRAME
    if (UNLIKELY(th->passed_ci != 0)) {
	void vm_call_cfunc_push_frame(rb_thread_t *th);
	vm_call_cfunc_push_frame(th);
    }
#endif
    return th;
}

So was. Ist es die Erfassung des aktuellen Thread-Objekts?

Nachdem ich zu diesem Punkt aufgeschaut hatte, wurde mir klar: "Oh, ist das nicht die sichere Stufe des Sicherheitsmodells?" Sicherheitsmodell Jeder Thread hat ein "safe_level" und greift intuitiv ineinander.

Ich beschloss, hier nicht weiter tiefer zu graben und zu priorisieren, ob dieses Verständnis den ursprünglichen Code interpretieren kann.

Wieder mitten in newobj_of

Ich werde die Mitte von newobj_of neu veröffentlichen.

static VALUE
newobj_of(VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3)
{
<<Unterlassung>>
   :
    obj = heap_get_freeobj(objspace, heap_eden);

    if (RGENGC_CHECK_MODE > 0) assert(BUILTIN_TYPE(obj) == T_NONE);

    /* OBJSETUP */
    RBASIC(obj)->flags = flags & ~FL_WB_PROTECTED;
    RBASIC_SET_CLASS_RAW(obj, klass);
    if (rb_safe_level() >= 3) FL_SET((obj), FL_TAINT);
    RANY(obj)->as.values.v1 = v1;
    RANY(obj)->as.values.v2 = v2;
    RANY(obj)->as.values.v3 = v3;
<<Unterlassung>>
}

Die Bedeutung von "FL_TAINT" ist überraschend.

Übrigens, wenn Sie es mit dem bisherigen Verständnis lesen, ist dieser Teil

  1. Sichern Sie ein neues Objekt
  2. Setzen Sie die angegebenen Flags in der Kopfzeile des Objekts
  3. Ändern Sie die Klasse des Objekts in die angegebene Klasse
  4. Wenn die Sicherheitsstufe 3 oder höher ist, markieren Sie das Objekt für den Verschmutzungsstatus
  5. Initialisieren Sie den Körperteil (v1 bis v3) des Objekts auf den angegebenen Anfangswert.

Kann gelesen werden als. Das war's. Ich habe das Gefühl, ein ausreichendes Gefühl für das Verständnis der Initialisierungsoperation zu haben.

Wenn Sie sich also den Rest ansehen, finden Sie Tools für Assertions und Debugging. Der Prozess der Registrierung beim Garbage Collector schien sich von etwas zu unterscheiden, das den Anfangszustand des Objekts bestimmte.

Ich entschied, dass ich den Ausgangszustand des Objekts verstehen könnte, wenn ich nichts anderes zu tun hätte.

Fazit

Der Anfangszustand des durch rb_newobj_of gesicherten Objekts ist wie folgt

Über das nächste Mal

Diesmal ist es ziemlich lang geworden. Das nächste Mal möchte ich den Teil lesen, der einfach erscheint, und ihn verkürzen.

Andere Informationen

Der CRuby, den Sie in diesem Artikel lesen, ist der Rubin / Rubin-Stammzweig von Github. Der aktuelle Commit-Hash-Wert lautet c1b05c53b795fdb1137819bc2973d591af2712d0, Ich werde in Zukunft immer die neueste Version lesen.

Recommended Posts

CRuby Code Reading (2): rb_newobj_of
CRuby Code Reading (1): LIKELY / UNLIKELY
CRuby-Code lesen (3): Ausgabe der Ausführungszeile rb_bug
Technologie zum Lesen des Quellcodes (Cheet Sheet)
Rails 5 Code Reading Teil 1 ~ ActiveRecord neue Methode ~