C'est un article que je vais lire l'implémentation de CRuby. C'est la deuxième fois.
Cette fois, je voulais lire autour de rb_intern, qui définit le symbole, mais après avoir creusé quelques fonctions
J'étais confus car il y avait une utilisation correcte de la fausse RString et de la vraie RString liée à RString (string).
En regardant le code périphérique, il semble que vous puissiez lire que vous comprenez l'opération d'initialisation après avoir sécurisé l'objet Ruby.
Par conséquent, je vais me connecter au suivant en lisant rb_newobj_of
qui semble sécuriser un nouvel objet.
Parce que le but est de connaître l'état initial de l'objet Je vais lire l'histoire détaillée du GC et de l'attribution en attente.
rb_newobj_of
rb_newobj_of
est défini dans gc.c.
VALUE
rb_newobj_of(VALUE klass, VALUE flags)
{
return newobj_of(klass, flags, 0, 0, 0);
}
Apparemment, l'entité est comme newobj_of
.
Si vous recherchez le code du côté que vous utilisez et que vous le lisez, klass
est une constante qui exprime le type de classe.
Dans flags
, divers drapeaux liés à l'objet ont été mis sous la forme de bit flags.
À propos, le type de chaque argument et valeur de retour est VALUE. Puisqu'il peut être utilisé comme indicateur de bits, il ne fait aucun doute qu'il s'agit d'un type de nombre naturel, mais de quel type s'agit-il? Et 0 des trois arguments de l'appel est un nombre magique, mais qu'est-ce que cela signifie?
J'ai lu chacun d'eux et l'ai examiné.
Quel est le type VALUE?
J'ai cherché avec grep et j'ai trouvé typedef dans include / ruby / ruby.h.
#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
Il ressemble à un type qui correspond à la taille du pointeur de l'environnement. Quel est l'intérêt d'utiliser VALUE au lieu d'utiliser simplement des pointeurs ou des types de nombres naturels?
Si vous regardez l'utilisation de VALUE avec grep, vous pouvez comprendre ce qui suit.
--Basiquement utilisé comme pointeur vers un objet Ruby
fixnum_flag
(= 1 / nombre naturel).Probablement un mécanisme pour améliorer l'efficacité de la mémoire. Maintenant que je sais que c'est essentiellement un indicateur, ça suffit. J'ai décidé d'éviter d'autres recherches sur VALUE pour le moment.
newobj_of
Comme rb_newobj_of
, il a une définition dans gc.c.
static VALUE
newobj_of(VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3)
{
:
<<Omission>>
:
:
return obj;
}
Le paramètre 0 spécifié dans rb_newobj_of
doit être de v1 à v3.
Lisons dans la tête.
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();
}
}
}
:
<<Omission>>
Oh. «SÛREMENT». ʻUN LIKELY` est spécifié, donc Cette clause conditionnelle est fondamentalement un chemin qui ne se produit pas, alors sautons-la une fois. (Et pour autant que l'instruction conditionnelle soit vue, cela semble être l'opération pendant l'exécution du GC.)
ʻObjspace et ʻobj
sont sécurisés dans la déclaration de variable.
La macro pour rb_objspace
a été définie dans gc.c.
#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
Pour autant que vous puissiez voir, cela ressemble à une zone pour placer des objets, comme son nom l'indique. Pour le moment, le but est l'état initial, il semble donc qu'il n'est pas nécessaire de creuser profondément ici.
Passons au milieu de la fonction newobj_of
.
static VALUE
newobj_of(VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3)
{
<<Omission>>
:
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;
<<Omission>>
}
Vous pouvez le lire comme si vous aviez acquis freeobj (?) Avec la fonction heap_get_freeobj
dans ʻobjque vous avez réservée plus tôt. Après cela, il semble définir des
flags pour le résultat de la transmission de ʻobj
par la macro RBASIC
.
Lorsque FL_WB_PROTECTED
est grep,#define FL_WB_PROTECTED (((VALUE) 1) << 5)
Vous pouvez voir que c'est un bit flag qui utilise le 5ème bit.
Puisqu'il s'agit d'un produit logique négatif, seul le 5ème bit est ignoré même s'il est spécifié par l'argument flags
de newobj_of
.
Je me demande qui est le FL_WB_PROTECTED
, mais en tant que connaissance de la valeur initiale
Je pense qu'il suffit de savoir que la valeur ne tient pas au moment de l'initialisation.
C'est parce qu'il devrait y avoir un nom d'indicateur ou une forme d'accès au 5ème bit où vous l'utilisez.
(Au contraire, en lisant à partir de maintenant, vous serez en mesure de comprendre la signification de FL_WB_PROTECTED
.)
Alors je m'en fiche.
Qu'est-ce que la macro «RBASIC»? Ici, «RANY» est également utilisé d'une manière similaire. Surtout dans «RANY», les arguments «v1» à «v3» sont affectés. Quel genre d'effet cela a-t-il?
Que font «RBASIC_SET_CLASS_RAW» et «FL_SET»?
Et quelle heure est-il quand rb_safe_level ()
vaut 3 ou plus?
--Résumé des questions ici
values.v1
ou flags
?rb_safe_level ()> = 3
?Nous allons jeter un coup d'oeil.
La définition de heap_get_freeobj ()
est la suivante.
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, c'est «PROBABLEMENT». En utilisant «PROBABLE» comme indice, il semble que vous obtenez simplement un objet de la liste gratuite. Cela ne semble pas être lié à l'objectif, alors j'ai juste compris que j'étais capable de sécuriser la mémoire et je suis passé à l'étape suivante.
heap_get_freeobj réserve simplement l'objet de la liste libre
J'ai essayé de grep ce que «RBASIC» et «RANY» font à obj. En conséquence, les deux suivants ont été trouvés.
#define RBASIC(obj) (R_CAST(RBasic)(obj))
#define RANY(o) ((RVALUE*)(o))
Il y avait la définition suivante pour R_CAST
#define R_CAST(st) (struct st*)
En d'autres termes, les deux convertissent ʻobj` comme un pointeur vers une certaine structure. (Pour la macro «RBASIC», un pointeur vers la structure «RBasic». Pour RANY, le pointeur vers la structure «RVALUE».)
La structure RBasic et la structure RVALUE étaient les suivantes, respectivement.
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;
Ignorez GC_DEBUG
car il semble être des informations de débogage.
RBasic est la première partie de RVALUE. RVALUE semble être un organe communautaire intégré de toutes sortes. Quand je l'ai recherchée, la partie principale de la définition de structure de chaque type inclus dans RVALUE était essentiellement RBasic. À partir de là, on peut voir que RBasic semble être couramment utilisé comme en-tête de gestion d'objets.
Puisque RBasic a des flags
, qui est un en-tête commun pour tout l'objet,
L'affectation des indicateurs dans le code d'origine était l'attribution d'indicateurs à cet en-tête.
De plus, chaque type inclus dans «RVALUE» a été rendu petit et peut contenir jusqu'à 3 pointeurs (= 3 VALEURS).
Par conséquent, à partir de «v1» dans «valeurs» de «RVALUE» via «v3», En y accédant, il semble que les valeurs de tous les objets puissent être correctement initialisées.
Vous pouvez trouver ce qui suit par grep.
#define RBASIC_SET_CLASS_RAW(obj, cls) (((struct RBasicRaw *)((VALUE)(obj)))->klass = (cls))
Si vous grep également la définition de la structure RBasicRaw
struct RBasicRaw {
VALUE flags;
VALUE klass;
};
C'était comme ça. Il est très similaire à RBasic
, mais il semble y avoir une différence dans la propriété const de klass
lorsqu'on la compare.
Apparemment, toucher la valeur de klass
ne devrait pas être fait,
Utilisez RBASIC_SET_CLASS_RAW
partout où vous en avez besoin
Je pense qu'il est protégé pour pouvoir être exploité.
Au fait, la valeur de klass
était une constante qui semble indiquer le type de classe.
Par conséquent, après tout, RBASIC_SET_CLASS_RAW
peut être considéré comme une macro qui enregistre le type ʻobj spécifié comme
cls`.
rb_safe_level ()> = 3
?Enfin, lisons rb_safe_level ()
.
Vous pouvez le trouver au début de safe.c par grep.
int
rb_safe_level(void)
{
return GET_THREAD()->safe_level;
}
Et GET_THREAD
est une fonction en ligne si vous pensez que c'est une macro,
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;
}
Comme ça. Est-ce l'acquisition de l'objet thread actuel?
Alors, après avoir regardé jusqu'à ce point, j'ai réalisé: "Oh, n'est-ce pas le niveau de sécurité du modèle de sécurité?"
Modèle de sécurité
Chaque thread aura un safe_level
et sera maillé intuitivement.
J'ai décidé d'arrêter de creuser plus profondément ici et de prioriser si cette compréhension peut interpréter le code original.
Je republierai le milieu de newobj_of.
static VALUE
newobj_of(VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3)
{
<<Omission>>
:
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;
<<Omission>>
}
La signification de «FL_TAINT» est surprenante.
À propos, si vous l'avez lu avec la compréhension jusqu'à présent, cette partie est
Peut être lu comme. Je vois. J'ai l'impression d'avoir une sensation suffisante pour comprendre l'opération d'initialisation.
Donc si vous regardez le reste, vous trouverez des outils pour les assertions et le débogage, Le processus d'enregistrement auprès du garbage collector semblait être différent de quelque chose qui déterminait l'état initial de l'objet.
J'ai décidé que si je n'avais rien d'autre à faire, je pourrais comprendre l'état initial de l'objet.
L'état initial de l'objet sécurisé par rb_newobj_of est le suivant
--La partie d'en-tête de l'objet sera le drapeau avec FL_WB_PROTECTED
omis de la spécification des drapeaux et le klass comme spécifié.
--Cependant, si le niveau de sécurité est de 3 ou plus, FL_TAINT
sera également dans un état permanent.
--La partie du corps de l'objet est remise à 0
Cette fois, c'est devenu assez long. La prochaine fois, je veux lire la partie qui me semble facile et la raccourcir.
Le CRuby que vous lisez dans cet article est le ruby / ruby, branche tronc de github. La valeur de hachage de validation actuelle est c1b05c53b795fdb1137819bc2973d591af2712d0, Je lirai toujours la dernière version à l'avenir.