Ce qui serait le premier de tous est apparu sur GitHub. À date, ce dernier semble ne plus être disponible sur la plateforme d’hébergement et de gestion de développement des logiciels (ou a tout simplement été renommé). Il a toutefois laissé filtrer un certain nombre d’informations importantes à propos de cet OS, notamment, sur les supputations relatives au possible lien de filiation avec Linux. On sait depuis lors que Fuchsia est basé sur Magenta, un microkernel qui tire ses fondations du projet Little Kernel – affectueusement baptisé LK par les férus de programmation – et donc, ne dérive pas du noyau Linux.
Google l’a réitéré dans la nouvelle documentation accessible sur sa propre plateforme Git en déclarant que « Fuchsia n’est pas Linux. » Magenta, devenu Zircon par le truchement d’un changement de dénomination, adopte bien l’approche microkernel tandis que Linux est un noyau monolithique. Sur la question des usages réservés à Fuchsia, le lien de filiation donne des pistes.
« LK (Little Kernel) est un infime système d’exploitation qui vise des applications au sein de petits appareils embarqués, des chargeurs d’amorce et autres environnements au sein desquels des primitives comme les threads, les mutexes et les timers sont requis, tout en gardant à l’esprit de conserver une empreinte mémoire légère », lit-on sur le dépôt du projet dont Fuchsia tire ses sources. Fuchsia est donc conçu pour s’adapter à différents types de systèmes de toutes tailles et formes (systèmes embarqués, ordiphones, ordinateurs…).
Voilà de façon brossée ce que cache le mystérieux OS de Google. Des détails supplémentaires feront surface dans les jours à venir puisque certaines sections de la documentation demeurent grisées. Toutefois, un aspect déjà bien visible dans la doc actuelle divise énormément la communauté : Google a fait le choix du langage C++ au détriment du C pour coder le microkernel qui anime le système d’exploitation.
Les discussions sur les fils reddit battent leur plein à ce sujet. Google a implémenté l’allocation dynamique de mémoire et la gestion des exceptions au sein du microkernel, toutes choses qui justifient le choix du langage C++. Illustration avec cette portion du code source de l’interface de gestion de threads.
Code cpp : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | // Copyright 2016 The Fuchsia Authors // Copyright (c) 2008-2015 Travis Geiselbrecht // // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT /** * @file * @brief Kernel threading * * This file is the core kernel threading interface. * * @defgroup thread Threads * @{ */ #include <kernel/thread.h> #include <arch/exception.h> #include <assert.h> #include <debug.h> #include <err.h> #include <inttypes.h> #include <kernel/atomic.h> #include <kernel/dpc.h> #include <kernel/mp.h> #include <kernel/percpu.h> #include <kernel/sched.h> #include <kernel/stats.h> #include <kernel/thread.h> #include <kernel/timer.h> #include <lib/counters.h> #include <lib/heap.h> #include <lib/ktrace.h> #include <list.h> #include <malloc.h> #include <object/c_user_thread.h> #include <platform.h> #include <printf.h> #include <string.h> #include <target.h> #include <vm/vm.h> #include <zircon/types.h> // kernel counters. TODO(cpu): remove LK-era counters // The counters below never decrease. // // counts the number of thread_t succesfully created. KCOUNTER(thread_create_count, "kernel.thread.create"); // counts the number of thread_t joined. Never decreases. KCOUNTER(thread_join_count, "kernel.thread.join"); // counts the number of calls to suspend() that succeeded. KCOUNTER(thread_suspend_count, "kernel.thread.suspend"); // counts the number of calls to resume() that succeeded. KCOUNTER(thread_resume_count, "kernel.thread.resume"); /* global thread list */ static struct list_node thread_list = LIST_INITIAL_VALUE(thread_list); /* master thread spinlock */ spin_lock_t thread_lock = SPIN_LOCK_INITIAL_VALUE; /* local routines */ static void thread_exit_locked(thread_t* current_thread, int retcode) __NO_RETURN; static void thread_do_suspend(void); static void init_thread_struct(thread_t* t, const char* name) { memset(t, 0, sizeof(thread_t)); t->magic = THREAD_MAGIC; strlcpy(t->name, name, sizeof(t->name)); wait_queue_init(&t->retcode_wait_queue); } static void initial_thread_func(void) TA_REQ(thread_lock) __NO_RETURN; static void initial_thread_func(void) { int ret; /* release the thread lock that was implicitly held across the reschedule */ spin_unlock(&thread_lock); arch_enable_ints(); thread_t* ct = get_current_thread(); ret = ct->entry(ct->arg); thread_exit(ret); } /** * @brief Create a new thread * * This function creates a new thread. The thread is initially suspended, so you * need to call thread_resume() to execute it. * * @param name Name of thread * @param entry Entry point of thread * @param arg Arbitrary argument passed to entry() * @param priority Execution priority for the thread. * @param stack_size Stack size for the thread. * @param alt_trampoline If not NULL, an alternate trampoline for the thread * to start on. * * Thread priority is an integer from 0 (lowest) to 31 (highest). Some standard * prioritys are defined in <kernel/thread.h>: * * HIGHEST_PRIORITY * DPC_PRIORITY * HIGH_PRIORITY * DEFAULT_PRIORITY * LOW_PRIORITY * IDLE_PRIORITY * LOWEST_PRIORITY * * Stack size is typically set to DEFAULT_STACK_SIZE * * @return Pointer to thread object, or NULL on failure. */ thread_t* thread_create_etc( thread_t* t, const char* name, thread_start_routine entry, void* arg, int priority, void* stack, void* unsafe_stack, size_t stack_size, thread_trampoline_routine alt_trampoline) { unsigned int flags = 0; if (!t) { <span style="font-family: monospace; padding: 2px; background: #ddd; display: inline-block">t = malloc(sizeof(thread_t)); // allocation dynamique </span> if (!t) return NULL; flags |= THREAD_FLAG_FREE_STRUCT; } init_thread_struct(t, name); t->entry = entry; t->arg = arg; t->state = THREAD_INITIAL; t->signals = 0; t->blocking_wait_queue = NULL; t->blocked_status = ZX_OK; t->interruptable = false; t->curr_cpu = INVALID_CPU; t->last_cpu = INVALID_CPU; t->cpu_affinity = CPU_MASK_ALL; t->retcode = 0; wait_queue_init(&t->retcode_wait_queue); sched_init_thread(t, priority); /* create the stack */ if (!stack) { if (THREAD_STACK_BOUNDS_CHECK) { stack_size += THREAD_STACK_PADDING_SIZE; flags |= THREAD_FLAG_DEBUG_STACK_BOUNDS_CHECK; } t->stack = malloc(stack_size); if (!t->stack) { if (flags & THREAD_FLAG_FREE_STRUCT) free(t); return NULL; } flags |= THREAD_FLAG_FREE_STACK; if (THREAD_STACK_BOUNDS_CHECK) { memset(t->stack, STACK_DEBUG_BYTE, THREAD_STACK_PADDING_SIZE); } } else { t->stack = stack; } #if __has_feature(safe_stack) if (!unsafe_stack) { DEBUG_ASSERT(!stack); DEBUG_ASSERT(flags & THREAD_FLAG_FREE_STACK); t->unsafe_stack = malloc(stack_size); if (!t->unsafe_stack) { free(t->stack); if (flags & THREAD_FLAG_FREE_STRUCT) free(t); return NULL; } if (THREAD_STACK_BOUNDS_CHECK) { memset(t->unsafe_stack, STACK_DEBUG_BYTE, THREAD_STACK_PADDING_SIZE); } } else { DEBUG_ASSERT(stack); t->unsafe_stack = unsafe_stack; } #else DEBUG_ASSERT(!unsafe_stack); #endif t->stack_size = stack_size; /* save whether or not we need to free the thread struct and/or stack */ t->flags = flags; if (likely(alt_trampoline == NULL)) { alt_trampoline = initial_thread_func; } /* set up the initial stack frame */ arch_thread_initialize(t, (vaddr_t)alt_trampoline); /* add it to the global thread list */ THREAD_LOCK(state); list_add_head(&thread_list, &t->thread_list_node); THREAD_UNLOCK(state); kcounter_add(thread_create_count, 1u); return t; } |
Pour les uns, ces décisions ne se justifient pas pour plusieurs raisons. Primo, elles ne respectent pas les canons de développement de ce type de noyau. « Je me serais attendu à ce qu’ils respectent les propriétés des microkernel connues depuis les années ’90 : pas d’allocation dynamique de mémoire au sein du noyau – toute la mémoire requise est allouée au démarrage », lit-on. Secundo, l’allocation dynamique de mémoire est susceptible de rendre le noyau du système d’exploitation perméable à des attaques par déni de service. Enfin, l’implémentation des exceptions est susceptible de le rendre plus lent et gourmand en ressource mémoire.
L’allocation dynamique de mémoire est cependant susceptible d’avoir des avantages dans le contexte du développement d’un microkernel pour une autre tranche d’intervenants reddit. « Il faut de la mémoire pour contenir les métadonnées relatives à un processus. Il en faut pour gérer les ressources liées aux autres services. Le passage de données entre ces services requiert également de la mémoire », lit-on en guise de réponse à la question sur l’utilité d’un gestionnaire de mémoire au sein d’un microkernel. Ce groupe d’intervenants reddit est également d’avis que Google a opté pour C++ en raison de la disponibilité de bibliothèques de type, ainsi que pour son typage plus poussé que le C, toutes choses qui peuvent également permettre une gestion efficiente des exceptions.
Sources
Google Git
Et vous ?
Quelles raisons selon vous devraient motiver un développeur à choisir le langage C++ plutôt que le C dans le cadre du développement d’un noyau de système d’exploitation ?
Quel commentaire faites-vous de la décision de Google de faire usage du langage C++ au détriment du C pour coder le noyau de ce système d’exploitation ?
L’implémentation de l’allocation dynamique de mémoire et des exceptions a-t-elle sa place au sein d’un microkernel ? Si oui, quelle serait la façon la plus efficiente de la mettre en œuvre ?
Avez-vous déjà eu à choisir entre les langages C et C++ dans le cadre du développement d'un noyau ? Partagez votre expérience
Voir aussi
Fuchsia : le nouvel OS mystérieux de Google se concrétise en images, il serait destiné aux téléphones et aux ordinateurs personnels modernes