├── .clang-format ├── .dir-locals.el ├── .gitignore ├── 01-x86-arch.org ├── 02-qemu-simulator.org ├── 03-minimal-os.org ├── 11-os-overview.org ├── 12-from-asm-to-c.org ├── 12a-Q&A.org ├── 13-xv6-startup.org ├── 21-page-table.org ├── 22-mem-init.org ├── 23-kmem-pgtab.org ├── 23a-setup-env.org ├── 31-intro-process.org ├── 32-init-start.org ├── 33-context-switch.org ├── 34-enter-shell.org ├── 34a-Q&A.org ├── 34b-Q&A.org ├── 41-intro-interrupt.org ├── 42-exec-syscall.org ├── 43-hw-interrupt.org ├── 44-multi-core-boot.org ├── 45-disk-driver.org ├── 51-locking.org ├── 52-sleeplock.org ├── 53-scheduling.org ├── 54-proc-lifecycle.org ├── 55-pipe.org ├── 61-fs-overview.org ├── 62-buffer.org ├── 63-logging.org ├── 64-inode.org ├── 65-directory-path.org ├── 66-file-descriptor.org ├── 99-temp.org ├── LICENSE ├── Makefile ├── banner ├── bin └── sign.pl ├── img ├── bcache.dot ├── bcache.dot.pdf ├── bcache.dot.png ├── ds1.dot ├── ds1.dot.pdf ├── ds1.dot.png ├── ds2.dot ├── ds2.dot.pdf ├── ds2.dot.png ├── ds2.png ├── file-struct.dot ├── file-struct.dot.pdf ├── file-struct.dot.png ├── fs-arch.png ├── fs-phy.png ├── idequeue.dot ├── idequeue.dot.pdf ├── idequeue.dot.png ├── inode-relation.dot ├── inode-relation.dot.pdf ├── inode-relation.dot.png └── pay.jpg ├── lab ├── 01-register │ ├── .gdbinit │ ├── Makefile │ └── main.S ├── 02-stack │ ├── .gdbinit │ ├── Makefile │ └── main.S ├── 03-control-flow │ ├── .gdbinit │ ├── Makefile │ ├── adder.c │ ├── fcall.S │ ├── jump.S │ └── jump2.c ├── 04-qemu │ ├── 01-qemu-linux-0.11.sh │ ├── 02-qemu-xv6-nox.sh │ ├── 03-qemu-minix-initrd.sh │ ├── 04-qemu-linux-kernel.sh │ └── 05-qemu-cdrom-install.sh ├── 05-interrupt │ ├── Makefile │ ├── echo.S │ ├── echo2.c │ ├── greet.S │ ├── greet2.c │ └── hello.c ├── 06-barebone │ ├── .gdbinit │ ├── Makefile │ └── boot.S ├── 07-nasm-boot │ ├── .gdbinit │ ├── Makefile │ └── boot.s ├── 08-kernel-user-mode │ ├── Makefile │ ├── kernel.S │ └── user.S ├── 09-linux-syscall │ ├── Makefile │ ├── fork_gpt.c │ ├── forkme.c │ ├── loop.c │ └── printx.c ├── 10-real-to-protected │ ├── .gdbinit │ ├── Makefile │ ├── bochsrc.bxrc │ ├── bootmain.c │ ├── bootsect.s │ └── entry.s ├── 11-inline-asm │ ├── Makefile │ └── add.c ├── 12-linker-extern │ ├── Makefile │ ├── end-addr.c │ └── idle.c ├── 13-mp-boot │ ├── .gdbinit │ ├── Makefile │ ├── bochsrc.bxrc │ └── boot.s ├── 14-process-intro │ ├── Makefile │ └── hello.c ├── 15-locking │ ├── Makefile │ ├── atomic.c │ ├── mutex.c │ ├── no-lock.c │ └── single.c ├── 16-proc-sched │ ├── Makefile │ ├── lifecycle.c │ └── zombie.c └── Makefile ├── readme.org ├── scripts ├── build-bochs.sh └── build-qemu.sh └── version /.clang-format: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # 3 | # clang-format configuration file. Intended for clang-format >= 4. 4 | # 5 | # For more information, see: 6 | # 7 | # Documentation/process/clang-format.rst 8 | # https://clang.llvm.org/docs/ClangFormat.html 9 | # https://clang.llvm.org/docs/ClangFormatStyleOptions.html 10 | # 11 | --- 12 | AccessModifierOffset: -4 13 | AlignAfterOpenBracket: Align 14 | AlignConsecutiveAssignments: false 15 | AlignConsecutiveDeclarations: false 16 | #AlignEscapedNewlines: Left # Unknown to clang-format-4.0 17 | AlignOperands: true 18 | AlignTrailingComments: false 19 | AllowAllParametersOfDeclarationOnNextLine: false 20 | AllowShortBlocksOnASingleLine: false 21 | AllowShortCaseLabelsOnASingleLine: false 22 | AllowShortFunctionsOnASingleLine: None 23 | AllowShortIfStatementsOnASingleLine: false 24 | AllowShortLoopsOnASingleLine: false 25 | AlwaysBreakAfterDefinitionReturnType: None 26 | AlwaysBreakAfterReturnType: None 27 | AlwaysBreakBeforeMultilineStrings: false 28 | AlwaysBreakTemplateDeclarations: false 29 | BinPackArguments: true 30 | BinPackParameters: true 31 | BraceWrapping: 32 | AfterClass: false 33 | AfterControlStatement: false 34 | AfterEnum: false 35 | AfterFunction: true 36 | AfterNamespace: true 37 | AfterObjCDeclaration: false 38 | AfterStruct: false 39 | AfterUnion: false 40 | #AfterExternBlock: false # Unknown to clang-format-5.0 41 | BeforeCatch: false 42 | BeforeElse: false 43 | IndentBraces: false 44 | #SplitEmptyFunction: true # Unknown to clang-format-4.0 45 | #SplitEmptyRecord: true # Unknown to clang-format-4.0 46 | #SplitEmptyNamespace: true # Unknown to clang-format-4.0 47 | BreakBeforeBinaryOperators: None 48 | BreakBeforeBraces: Custom 49 | #BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0 50 | BreakBeforeTernaryOperators: false 51 | BreakConstructorInitializersBeforeComma: false 52 | #BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0 53 | BreakAfterJavaFieldAnnotations: false 54 | BreakStringLiterals: false 55 | ColumnLimit: 80 56 | CommentPragmas: '^ IWYU pragma:' 57 | #CompactNamespaces: false # Unknown to clang-format-4.0 58 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 59 | ConstructorInitializerIndentWidth: 8 60 | ContinuationIndentWidth: 8 61 | Cpp11BracedListStyle: false 62 | DerivePointerAlignment: false 63 | DisableFormat: false 64 | ExperimentalAutoDetectBinPacking: false 65 | #FixNamespaceComments: false # Unknown to clang-format-4.0 66 | 67 | # Taken from: 68 | # git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ \ 69 | # | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \ 70 | # | sort | uniq 71 | ForEachMacros: 72 | - 'apei_estatus_for_each_section' 73 | - 'ata_for_each_dev' 74 | - 'ata_for_each_link' 75 | - '__ata_qc_for_each' 76 | - 'ata_qc_for_each' 77 | - 'ata_qc_for_each_raw' 78 | - 'ata_qc_for_each_with_internal' 79 | - 'ax25_for_each' 80 | - 'ax25_uid_for_each' 81 | - '__bio_for_each_bvec' 82 | - 'bio_for_each_bvec' 83 | - 'bio_for_each_bvec_all' 84 | - 'bio_for_each_integrity_vec' 85 | - '__bio_for_each_segment' 86 | - 'bio_for_each_segment' 87 | - 'bio_for_each_segment_all' 88 | - 'bio_list_for_each' 89 | - 'bip_for_each_vec' 90 | - 'bitmap_for_each_clear_region' 91 | - 'bitmap_for_each_set_region' 92 | - 'blkg_for_each_descendant_post' 93 | - 'blkg_for_each_descendant_pre' 94 | - 'blk_queue_for_each_rl' 95 | - 'bond_for_each_slave' 96 | - 'bond_for_each_slave_rcu' 97 | - 'bpf_for_each_spilled_reg' 98 | - 'btree_for_each_safe128' 99 | - 'btree_for_each_safe32' 100 | - 'btree_for_each_safe64' 101 | - 'btree_for_each_safel' 102 | - 'card_for_each_dev' 103 | - 'cgroup_taskset_for_each' 104 | - 'cgroup_taskset_for_each_leader' 105 | - 'cpufreq_for_each_entry' 106 | - 'cpufreq_for_each_entry_idx' 107 | - 'cpufreq_for_each_valid_entry' 108 | - 'cpufreq_for_each_valid_entry_idx' 109 | - 'css_for_each_child' 110 | - 'css_for_each_descendant_post' 111 | - 'css_for_each_descendant_pre' 112 | - 'device_for_each_child_node' 113 | - 'displayid_iter_for_each' 114 | - 'dma_fence_chain_for_each' 115 | - 'do_for_each_ftrace_op' 116 | - 'drm_atomic_crtc_for_each_plane' 117 | - 'drm_atomic_crtc_state_for_each_plane' 118 | - 'drm_atomic_crtc_state_for_each_plane_state' 119 | - 'drm_atomic_for_each_plane_damage' 120 | - 'drm_client_for_each_connector_iter' 121 | - 'drm_client_for_each_modeset' 122 | - 'drm_connector_for_each_possible_encoder' 123 | - 'drm_for_each_bridge_in_chain' 124 | - 'drm_for_each_connector_iter' 125 | - 'drm_for_each_crtc' 126 | - 'drm_for_each_crtc_reverse' 127 | - 'drm_for_each_encoder' 128 | - 'drm_for_each_encoder_mask' 129 | - 'drm_for_each_fb' 130 | - 'drm_for_each_legacy_plane' 131 | - 'drm_for_each_plane' 132 | - 'drm_for_each_plane_mask' 133 | - 'drm_for_each_privobj' 134 | - 'drm_mm_for_each_hole' 135 | - 'drm_mm_for_each_node' 136 | - 'drm_mm_for_each_node_in_range' 137 | - 'drm_mm_for_each_node_safe' 138 | - 'flow_action_for_each' 139 | - 'for_each_acpi_dev_match' 140 | - 'for_each_active_dev_scope' 141 | - 'for_each_active_drhd_unit' 142 | - 'for_each_active_iommu' 143 | - 'for_each_aggr_pgid' 144 | - 'for_each_available_child_of_node' 145 | - 'for_each_bio' 146 | - 'for_each_board_func_rsrc' 147 | - 'for_each_bvec' 148 | - 'for_each_card_auxs' 149 | - 'for_each_card_auxs_safe' 150 | - 'for_each_card_components' 151 | - 'for_each_card_dapms' 152 | - 'for_each_card_pre_auxs' 153 | - 'for_each_card_prelinks' 154 | - 'for_each_card_rtds' 155 | - 'for_each_card_rtds_safe' 156 | - 'for_each_card_widgets' 157 | - 'for_each_card_widgets_safe' 158 | - 'for_each_cgroup_storage_type' 159 | - 'for_each_child_of_node' 160 | - 'for_each_clear_bit' 161 | - 'for_each_clear_bit_from' 162 | - 'for_each_cmsghdr' 163 | - 'for_each_compatible_node' 164 | - 'for_each_component_dais' 165 | - 'for_each_component_dais_safe' 166 | - 'for_each_comp_order' 167 | - 'for_each_console' 168 | - 'for_each_cpu' 169 | - 'for_each_cpu_and' 170 | - 'for_each_cpu_not' 171 | - 'for_each_cpu_wrap' 172 | - 'for_each_dapm_widgets' 173 | - 'for_each_dev_addr' 174 | - 'for_each_dev_scope' 175 | - 'for_each_dma_cap_mask' 176 | - 'for_each_dpcm_be' 177 | - 'for_each_dpcm_be_rollback' 178 | - 'for_each_dpcm_be_safe' 179 | - 'for_each_dpcm_fe' 180 | - 'for_each_drhd_unit' 181 | - 'for_each_dss_dev' 182 | - 'for_each_dtpm_table' 183 | - 'for_each_efi_memory_desc' 184 | - 'for_each_efi_memory_desc_in_map' 185 | - 'for_each_element' 186 | - 'for_each_element_extid' 187 | - 'for_each_element_id' 188 | - 'for_each_endpoint_of_node' 189 | - 'for_each_evictable_lru' 190 | - 'for_each_fib6_node_rt_rcu' 191 | - 'for_each_fib6_walker_rt' 192 | - 'for_each_free_mem_pfn_range_in_zone' 193 | - 'for_each_free_mem_pfn_range_in_zone_from' 194 | - 'for_each_free_mem_range' 195 | - 'for_each_free_mem_range_reverse' 196 | - 'for_each_func_rsrc' 197 | - 'for_each_hstate' 198 | - 'for_each_if' 199 | - 'for_each_iommu' 200 | - 'for_each_ip_tunnel_rcu' 201 | - 'for_each_irq_nr' 202 | - 'for_each_link_codecs' 203 | - 'for_each_link_cpus' 204 | - 'for_each_link_platforms' 205 | - 'for_each_lru' 206 | - 'for_each_matching_node' 207 | - 'for_each_matching_node_and_match' 208 | - 'for_each_member' 209 | - 'for_each_memcg_cache_index' 210 | - 'for_each_mem_pfn_range' 211 | - '__for_each_mem_range' 212 | - 'for_each_mem_range' 213 | - '__for_each_mem_range_rev' 214 | - 'for_each_mem_range_rev' 215 | - 'for_each_mem_region' 216 | - 'for_each_migratetype_order' 217 | - 'for_each_msi_entry' 218 | - 'for_each_msi_entry_safe' 219 | - 'for_each_msi_vector' 220 | - 'for_each_net' 221 | - 'for_each_net_continue_reverse' 222 | - 'for_each_netdev' 223 | - 'for_each_netdev_continue' 224 | - 'for_each_netdev_continue_rcu' 225 | - 'for_each_netdev_continue_reverse' 226 | - 'for_each_netdev_feature' 227 | - 'for_each_netdev_in_bond_rcu' 228 | - 'for_each_netdev_rcu' 229 | - 'for_each_netdev_reverse' 230 | - 'for_each_netdev_safe' 231 | - 'for_each_net_rcu' 232 | - 'for_each_new_connector_in_state' 233 | - 'for_each_new_crtc_in_state' 234 | - 'for_each_new_mst_mgr_in_state' 235 | - 'for_each_new_plane_in_state' 236 | - 'for_each_new_private_obj_in_state' 237 | - 'for_each_node' 238 | - 'for_each_node_by_name' 239 | - 'for_each_node_by_type' 240 | - 'for_each_node_mask' 241 | - 'for_each_node_state' 242 | - 'for_each_node_with_cpus' 243 | - 'for_each_node_with_property' 244 | - 'for_each_nonreserved_multicast_dest_pgid' 245 | - 'for_each_of_allnodes' 246 | - 'for_each_of_allnodes_from' 247 | - 'for_each_of_cpu_node' 248 | - 'for_each_of_pci_range' 249 | - 'for_each_old_connector_in_state' 250 | - 'for_each_old_crtc_in_state' 251 | - 'for_each_old_mst_mgr_in_state' 252 | - 'for_each_oldnew_connector_in_state' 253 | - 'for_each_oldnew_crtc_in_state' 254 | - 'for_each_oldnew_mst_mgr_in_state' 255 | - 'for_each_oldnew_plane_in_state' 256 | - 'for_each_oldnew_plane_in_state_reverse' 257 | - 'for_each_oldnew_private_obj_in_state' 258 | - 'for_each_old_plane_in_state' 259 | - 'for_each_old_private_obj_in_state' 260 | - 'for_each_online_cpu' 261 | - 'for_each_online_node' 262 | - 'for_each_online_pgdat' 263 | - 'for_each_pci_bridge' 264 | - 'for_each_pci_dev' 265 | - 'for_each_pci_msi_entry' 266 | - 'for_each_pcm_streams' 267 | - 'for_each_physmem_range' 268 | - 'for_each_populated_zone' 269 | - 'for_each_possible_cpu' 270 | - 'for_each_present_cpu' 271 | - 'for_each_prime_number' 272 | - 'for_each_prime_number_from' 273 | - 'for_each_process' 274 | - 'for_each_process_thread' 275 | - 'for_each_prop_codec_conf' 276 | - 'for_each_prop_dai_codec' 277 | - 'for_each_prop_dai_cpu' 278 | - 'for_each_prop_dlc_codecs' 279 | - 'for_each_prop_dlc_cpus' 280 | - 'for_each_prop_dlc_platforms' 281 | - 'for_each_property_of_node' 282 | - 'for_each_registered_fb' 283 | - 'for_each_requested_gpio' 284 | - 'for_each_requested_gpio_in_range' 285 | - 'for_each_reserved_mem_range' 286 | - 'for_each_reserved_mem_region' 287 | - 'for_each_rtd_codec_dais' 288 | - 'for_each_rtd_components' 289 | - 'for_each_rtd_cpu_dais' 290 | - 'for_each_rtd_dais' 291 | - 'for_each_set_bit' 292 | - 'for_each_set_bit_from' 293 | - 'for_each_set_clump8' 294 | - 'for_each_sg' 295 | - 'for_each_sg_dma_page' 296 | - 'for_each_sg_page' 297 | - 'for_each_sgtable_dma_page' 298 | - 'for_each_sgtable_dma_sg' 299 | - 'for_each_sgtable_page' 300 | - 'for_each_sgtable_sg' 301 | - 'for_each_sibling_event' 302 | - 'for_each_subelement' 303 | - 'for_each_subelement_extid' 304 | - 'for_each_subelement_id' 305 | - '__for_each_thread' 306 | - 'for_each_thread' 307 | - 'for_each_unicast_dest_pgid' 308 | - 'for_each_vsi' 309 | - 'for_each_wakeup_source' 310 | - 'for_each_zone' 311 | - 'for_each_zone_zonelist' 312 | - 'for_each_zone_zonelist_nodemask' 313 | - 'fwnode_for_each_available_child_node' 314 | - 'fwnode_for_each_child_node' 315 | - 'fwnode_graph_for_each_endpoint' 316 | - 'gadget_for_each_ep' 317 | - 'genradix_for_each' 318 | - 'genradix_for_each_from' 319 | - 'hash_for_each' 320 | - 'hash_for_each_possible' 321 | - 'hash_for_each_possible_rcu' 322 | - 'hash_for_each_possible_rcu_notrace' 323 | - 'hash_for_each_possible_safe' 324 | - 'hash_for_each_rcu' 325 | - 'hash_for_each_safe' 326 | - 'hctx_for_each_ctx' 327 | - 'hlist_bl_for_each_entry' 328 | - 'hlist_bl_for_each_entry_rcu' 329 | - 'hlist_bl_for_each_entry_safe' 330 | - 'hlist_for_each' 331 | - 'hlist_for_each_entry' 332 | - 'hlist_for_each_entry_continue' 333 | - 'hlist_for_each_entry_continue_rcu' 334 | - 'hlist_for_each_entry_continue_rcu_bh' 335 | - 'hlist_for_each_entry_from' 336 | - 'hlist_for_each_entry_from_rcu' 337 | - 'hlist_for_each_entry_rcu' 338 | - 'hlist_for_each_entry_rcu_bh' 339 | - 'hlist_for_each_entry_rcu_notrace' 340 | - 'hlist_for_each_entry_safe' 341 | - 'hlist_for_each_entry_srcu' 342 | - '__hlist_for_each_rcu' 343 | - 'hlist_for_each_safe' 344 | - 'hlist_nulls_for_each_entry' 345 | - 'hlist_nulls_for_each_entry_from' 346 | - 'hlist_nulls_for_each_entry_rcu' 347 | - 'hlist_nulls_for_each_entry_safe' 348 | - 'i3c_bus_for_each_i2cdev' 349 | - 'i3c_bus_for_each_i3cdev' 350 | - 'ide_host_for_each_port' 351 | - 'ide_port_for_each_dev' 352 | - 'ide_port_for_each_present_dev' 353 | - 'idr_for_each_entry' 354 | - 'idr_for_each_entry_continue' 355 | - 'idr_for_each_entry_continue_ul' 356 | - 'idr_for_each_entry_ul' 357 | - 'in_dev_for_each_ifa_rcu' 358 | - 'in_dev_for_each_ifa_rtnl' 359 | - 'inet_bind_bucket_for_each' 360 | - 'inet_lhash2_for_each_icsk_rcu' 361 | - 'key_for_each' 362 | - 'key_for_each_safe' 363 | - 'klp_for_each_func' 364 | - 'klp_for_each_func_safe' 365 | - 'klp_for_each_func_static' 366 | - 'klp_for_each_object' 367 | - 'klp_for_each_object_safe' 368 | - 'klp_for_each_object_static' 369 | - 'kunit_suite_for_each_test_case' 370 | - 'kvm_for_each_memslot' 371 | - 'kvm_for_each_vcpu' 372 | - 'list_for_each' 373 | - 'list_for_each_codec' 374 | - 'list_for_each_codec_safe' 375 | - 'list_for_each_continue' 376 | - 'list_for_each_entry' 377 | - 'list_for_each_entry_continue' 378 | - 'list_for_each_entry_continue_rcu' 379 | - 'list_for_each_entry_continue_reverse' 380 | - 'list_for_each_entry_from' 381 | - 'list_for_each_entry_from_rcu' 382 | - 'list_for_each_entry_from_reverse' 383 | - 'list_for_each_entry_lockless' 384 | - 'list_for_each_entry_rcu' 385 | - 'list_for_each_entry_reverse' 386 | - 'list_for_each_entry_safe' 387 | - 'list_for_each_entry_safe_continue' 388 | - 'list_for_each_entry_safe_from' 389 | - 'list_for_each_entry_safe_reverse' 390 | - 'list_for_each_entry_srcu' 391 | - 'list_for_each_prev' 392 | - 'list_for_each_prev_safe' 393 | - 'list_for_each_safe' 394 | - 'llist_for_each' 395 | - 'llist_for_each_entry' 396 | - 'llist_for_each_entry_safe' 397 | - 'llist_for_each_safe' 398 | - 'mci_for_each_dimm' 399 | - 'media_device_for_each_entity' 400 | - 'media_device_for_each_intf' 401 | - 'media_device_for_each_link' 402 | - 'media_device_for_each_pad' 403 | - 'nanddev_io_for_each_page' 404 | - 'netdev_for_each_lower_dev' 405 | - 'netdev_for_each_lower_private' 406 | - 'netdev_for_each_lower_private_rcu' 407 | - 'netdev_for_each_mc_addr' 408 | - 'netdev_for_each_uc_addr' 409 | - 'netdev_for_each_upper_dev_rcu' 410 | - 'netdev_hw_addr_list_for_each' 411 | - 'nft_rule_for_each_expr' 412 | - 'nla_for_each_attr' 413 | - 'nla_for_each_nested' 414 | - 'nlmsg_for_each_attr' 415 | - 'nlmsg_for_each_msg' 416 | - 'nr_neigh_for_each' 417 | - 'nr_neigh_for_each_safe' 418 | - 'nr_node_for_each' 419 | - 'nr_node_for_each_safe' 420 | - 'of_for_each_phandle' 421 | - 'of_property_for_each_string' 422 | - 'of_property_for_each_u32' 423 | - 'pci_bus_for_each_resource' 424 | - 'pcl_for_each_chunk' 425 | - 'pcl_for_each_segment' 426 | - 'pcm_for_each_format' 427 | - 'ping_portaddr_for_each_entry' 428 | - 'plist_for_each' 429 | - 'plist_for_each_continue' 430 | - 'plist_for_each_entry' 431 | - 'plist_for_each_entry_continue' 432 | - 'plist_for_each_entry_safe' 433 | - 'plist_for_each_safe' 434 | - 'pnp_for_each_card' 435 | - 'pnp_for_each_dev' 436 | - 'protocol_for_each_card' 437 | - 'protocol_for_each_dev' 438 | - 'queue_for_each_hw_ctx' 439 | - 'radix_tree_for_each_slot' 440 | - 'radix_tree_for_each_tagged' 441 | - 'rb_for_each' 442 | - 'rbtree_postorder_for_each_entry_safe' 443 | - 'rdma_for_each_block' 444 | - 'rdma_for_each_port' 445 | - 'rdma_umem_for_each_dma_block' 446 | - 'resource_list_for_each_entry' 447 | - 'resource_list_for_each_entry_safe' 448 | - 'rhl_for_each_entry_rcu' 449 | - 'rhl_for_each_rcu' 450 | - 'rht_for_each' 451 | - 'rht_for_each_entry' 452 | - 'rht_for_each_entry_from' 453 | - 'rht_for_each_entry_rcu' 454 | - 'rht_for_each_entry_rcu_from' 455 | - 'rht_for_each_entry_safe' 456 | - 'rht_for_each_from' 457 | - 'rht_for_each_rcu' 458 | - 'rht_for_each_rcu_from' 459 | - '__rq_for_each_bio' 460 | - 'rq_for_each_bvec' 461 | - 'rq_for_each_segment' 462 | - 'scsi_for_each_prot_sg' 463 | - 'scsi_for_each_sg' 464 | - 'sctp_for_each_hentry' 465 | - 'sctp_skb_for_each' 466 | - 'shdma_for_each_chan' 467 | - '__shost_for_each_device' 468 | - 'shost_for_each_device' 469 | - 'sk_for_each' 470 | - 'sk_for_each_bound' 471 | - 'sk_for_each_entry_offset_rcu' 472 | - 'sk_for_each_from' 473 | - 'sk_for_each_rcu' 474 | - 'sk_for_each_safe' 475 | - 'sk_nulls_for_each' 476 | - 'sk_nulls_for_each_from' 477 | - 'sk_nulls_for_each_rcu' 478 | - 'snd_array_for_each' 479 | - 'snd_pcm_group_for_each_entry' 480 | - 'snd_soc_dapm_widget_for_each_path' 481 | - 'snd_soc_dapm_widget_for_each_path_safe' 482 | - 'snd_soc_dapm_widget_for_each_sink_path' 483 | - 'snd_soc_dapm_widget_for_each_source_path' 484 | - 'tb_property_for_each' 485 | - 'tcf_exts_for_each_action' 486 | - 'udp_portaddr_for_each_entry' 487 | - 'udp_portaddr_for_each_entry_rcu' 488 | - 'usb_hub_for_each_child' 489 | - 'v4l2_device_for_each_subdev' 490 | - 'v4l2_m2m_for_each_dst_buf' 491 | - 'v4l2_m2m_for_each_dst_buf_safe' 492 | - 'v4l2_m2m_for_each_src_buf' 493 | - 'v4l2_m2m_for_each_src_buf_safe' 494 | - 'virtio_device_for_each_vq' 495 | - 'while_for_each_ftrace_op' 496 | - 'xa_for_each' 497 | - 'xa_for_each_marked' 498 | - 'xa_for_each_range' 499 | - 'xa_for_each_start' 500 | - 'xas_for_each' 501 | - 'xas_for_each_conflict' 502 | - 'xas_for_each_marked' 503 | - 'xbc_array_for_each_value' 504 | - 'xbc_for_each_key_value' 505 | - 'xbc_node_for_each_array_value' 506 | - 'xbc_node_for_each_child' 507 | - 'xbc_node_for_each_key_value' 508 | - 'zorro_for_each_dev' 509 | 510 | #IncludeBlocks: Preserve # Unknown to clang-format-5.0 511 | IncludeCategories: 512 | - Regex: '.*' 513 | Priority: 1 514 | IncludeIsMainRegex: '(Test)?$' 515 | IndentCaseLabels: false 516 | #IndentPPDirectives: None # Unknown to clang-format-5.0 517 | IndentWidth: 8 518 | IndentWrappedFunctionNames: false 519 | JavaScriptQuotes: Leave 520 | JavaScriptWrapImports: true 521 | KeepEmptyLinesAtTheStartOfBlocks: false 522 | MacroBlockBegin: '' 523 | MacroBlockEnd: '' 524 | MaxEmptyLinesToKeep: 1 525 | NamespaceIndentation: None 526 | #ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0 527 | ObjCBlockIndentWidth: 8 528 | ObjCSpaceAfterProperty: true 529 | ObjCSpaceBeforeProtocolList: true 530 | 531 | # Taken from git's rules 532 | #PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0 533 | PenaltyBreakBeforeFirstCallParameter: 30 534 | PenaltyBreakComment: 10 535 | PenaltyBreakFirstLessLess: 0 536 | PenaltyBreakString: 10 537 | PenaltyExcessCharacter: 100 538 | PenaltyReturnTypeOnItsOwnLine: 60 539 | 540 | PointerAlignment: Right 541 | ReflowComments: false 542 | SortIncludes: false 543 | #SortUsingDeclarations: false # Unknown to clang-format-4.0 544 | SpaceAfterCStyleCast: false 545 | SpaceAfterTemplateKeyword: true 546 | SpaceBeforeAssignmentOperators: true 547 | #SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0 548 | #SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0 549 | SpaceBeforeParens: ControlStatements 550 | #SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0 551 | SpaceInEmptyParentheses: false 552 | SpacesBeforeTrailingComments: 1 553 | SpacesInAngles: false 554 | SpacesInContainerLiterals: false 555 | SpacesInCStyleCastParentheses: false 556 | SpacesInParentheses: false 557 | SpacesInSquareBrackets: false 558 | Standard: Cpp03 559 | TabWidth: 8 560 | UseTab: Always 561 | ... 562 | -------------------------------------------------------------------------------- /.dir-locals.el: -------------------------------------------------------------------------------- 1 | ((c-mode . 2 | ((eval . (eglot-ensure)) 3 | (eval . (add-hook 'before-save-hook #'eglot-format-buffer nil t)))) 4 | (c++-mode . 5 | ((eval . (eglot-ensure)) 6 | (eval . (add-hook 'before-save-hook #'eglot-format-buffer nil t)))) 7 | (nil . ((eval . (add-hook 'before-save-hook #'whitespace-cleanup))))) 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .cache 2 | compile_commands.json 3 | bochsout.txt 4 | 5 | *.ini 6 | *.d 7 | *.o 8 | *.out 9 | .gdb_history 10 | *.log 11 | *.txt 12 | *.asm 13 | *.img 14 | *.bin 15 | *.run 16 | *.lst 17 | *.dis 18 | vm* -------------------------------------------------------------------------------- /01-x86-arch.org: -------------------------------------------------------------------------------- 1 | #+TITLE: x86 体系结构 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-01 Wed> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | 8 | 9 | * 寄存器及编译流程 10 | | 分类 | 英文全称 | 16 位 | 32 位 | 64 位 | 11 | |--------------+---------------------+-------+-------+-------| 12 | | 通用寄存器 | Accumulator | ax | eax | rax | 13 | | 通用寄存器 | Base | bx | ebx | rbx | 14 | | 通用寄存器 | Counter | cx | ecx | rcx | 15 | | 通用寄存器 | Data | dx | edx | rdx | 16 | | 指针寄存器 | Stack Pointer | sp | esp | rsp | 17 | | 指针寄存器 | Base Pointer | bp | ebp | rbp | 18 | | 变地址寄存器 | Source Index | si | esi | rsi | 19 | | 变地址寄存器 | Destination Index | di | edi | rdi | 20 | | 控制寄存器 | Instruction Pointer | ip | eip | rip | 21 | | 控制寄存器 | Flag | flag | eflag | eflag | 22 | | 段寄存器 | Code Segment | cs | cs | cs | 23 | | 段寄存器 | Data Segment | ds | ds | ds | 24 | | 段寄存器 | Stack Segment | ss | ss | ss | 25 | | 段寄存器 | Extra Segment | es | es | es | 26 | 27 | [[file:lab/01-register/main.S]] 28 | 29 | 操作示例 30 | #+BEGIN_EXAMPLE 31 | info reg 32 | info reg eax 33 | p $esp 34 | 35 | layout asm 36 | layout reg 37 | #+END_EXAMPLE 38 | 39 | ubuntu 分清 32 位和 64 位, 如果是 64 为系统需要安装 32 为支持库 40 | #+BEGIN_SRC sh 41 | apt install gcc-multilib 42 | #+END_SRC 43 | 44 | * 栈操作 push/pop 45 | 1. esp 指向栈的顶端 46 | 2. ebp 指向栈的基地址 47 | 3. push 操作时, esp = esp - 4, ebp 不变 (32b) 48 | 4. pop 操作时, esp = esp + 4, ebp 不变 49 | 50 | #+BEGIN_EXAMPLE 51 | origin: 52 | addr data 53 | 0x0008 | 1 | 54 | 0x0007 | 0 | <- ebp <-esp 55 | 0x0006 | 0 | 56 | 0x0005 | 2 | 57 | 0x0004 | 2 | 58 | 0x0003 | 2 | 59 | 60 | push: 操作后 61 | addr data 62 | 0x0008 | 1 | 63 | 0x0007 | 0 | <- ebp 64 | 0x0006 | 0 | 65 | 0x0005 | 2 | 66 | 0x0004 | 2 | 67 | 0x0003 | 2 | <-esp 68 | 69 | pop: 操作后 70 | addr data 71 | 0x0008 | 1 | 72 | 0x0007 | 0 | <- ebp <-esp 73 | 0x0006 | 0 | 74 | 0x0005 | 2 | 75 | 0x0004 | 2 | 76 | 0x0003 | 2 | 77 | #+END_EXAMPLE 78 | 79 | [[file:lab/02-stack/main.S]] 80 | 81 | .gdbinit 定义 hook, 设置 watch point 82 | #+BEGIN_EXAMPLE 83 | define hook-stepi 84 | if $ebp > 0 85 | # printf "ebp=%p, esp=%p\n", $ebp, $esp 86 | x/4x $ebp-16 87 | x/i $eip 88 | end 89 | end 90 | 91 | starti 92 | watch $ebp 93 | watch $esp 94 | #+END_EXAMPLE 95 | 96 | * 分支跳转 jmp 97 | 通常汇编的跳转通过 ~cmp~, ~je~, ~jmp~ 等指令配合实现,例如 98 | #+BEGIN_SRC asm 99 | jmp label 100 | #+END_SRC 101 | 102 | [[file:lab/03-control-flow/jump.S]] 103 | 104 | * 函数调用 call/ret 105 | 函数调用也属于简单的跳转指令, 例如 106 | - call 调用函数 107 | - ret 函数返回 108 | 109 | [[file:lab/03-control-flow/fcall.S]] 110 | 111 | 使用 gcc 编译的真实函数调用 112 | #+BEGIN_SRC sh 113 | gcc -m32 -O0 adder.c -o adder.out 114 | objdump -d adder.out > adder.asm 115 | #+END_SRC 116 | 117 | [[file:lab/03-control-flow/adder.c]] 118 | 119 | #+BEGIN_SRC asm 120 | 0000118d : 121 | 118d: 55 push %ebp 122 | 118e: 89 e5 mov %esp,%ebp 123 | 1190: 83 ec 10 sub $0x10,%esp 124 | 1193: e8 49 00 00 00 call 11e1 <__x86.get_pc_thunk.ax> 125 | 1198: 05 44 2e 00 00 add $0x2e44,%eax 126 | 119d: 8b 55 08 mov 0x8(%ebp),%edx 127 | 11a0: 8b 45 0c mov 0xc(%ebp),%eax 128 | 11a3: 01 d0 add %edx,%eax 129 | 11a5: 89 45 fc mov %eax,-0x4(%ebp) 130 | 11a8: 8b 45 fc mov -0x4(%ebp),%eax 131 | 11ab: c9 leave 132 | 11ac: c3 ret 133 | 134 | 000011ad
: 135 | 11ad: 55 push %ebp 136 | 11ae: 89 e5 mov %esp,%ebp 137 | 11b0: 83 ec 10 sub $0x10,%esp 138 | 11b3: e8 29 00 00 00 call 11e1 <__x86.get_pc_thunk.ax> 139 | 11b8: 05 24 2e 00 00 add $0x2e24,%eax 140 | 11bd: c7 45 f4 03 00 00 00 movl $0x3,-0xc(%ebp) 141 | 11c4: c7 45 f8 04 00 00 00 movl $0x4,-0x8(%ebp) 142 | 11cb: ff 75 f8 push -0x8(%ebp) 143 | 11ce: ff 75 f4 push -0xc(%ebp) 144 | 11d1: e8 b7 ff ff ff call 118d 145 | 11d6: 83 c4 08 add $0x8,%esp 146 | 11d9: 89 45 fc mov %eax,-0x4(%ebp) 147 | 11dc: 8b 45 fc mov -0x4(%ebp),%eax 148 | 11df: c9 leave 149 | 11e0: c3 ret 150 | #+END_SRC 151 | -------------------------------------------------------------------------------- /02-qemu-simulator.org: -------------------------------------------------------------------------------- 1 | #+TITLE: QEMU 模拟器 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-01 Wed> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | 8 | * 什么是模拟器 9 | 1. [[https://www.qemu.org/][qemu]] 10 | 2. [[https://bochs.sourceforge.io/][bochs]] 11 | 12 | 操作系统:直接通过指令对硬件进行控制 13 | #+BEGIN_EXAMPLE 14 | +============================================+ 15 | || Operation System (操作系统): Linux, xv6 || 16 | +============================================+ 17 | | | 18 | | Instruction(x86) | 19 | v v 20 | +============================================+ 21 | || Hardware(硬件): 硬盘,鼠标,显示器 || 22 | +============================================+ 23 | #+END_EXAMPLE 24 | 25 | 26 | 模拟器:直接通过指令对硬件进行控制 27 | #+BEGIN_EXAMPLE 28 | +============================================+ 29 | || Host OS: Ubuntu || 30 | || || 31 | || +--------------------------------+ || 32 | || | 调试操作系统: xv6-riscv | || 33 | || +--------------------------------+ || 34 | || | | || 35 | || | Instruction(riscv) | || 36 | || V v || 37 | || +--------------------------------+ || 38 | || | Simulator(模拟器): QEMU, Bochs | || 39 | || | 作为软件运行在宿主机器 | || 40 | || +--------------------------------+ || 41 | || | || 42 | || | 宿主操作系统指令(x86) || 43 | || | || 44 | || V || 45 | +============================================+ 46 | | | 47 | | Instruction(x86) | 48 | v v 49 | +============================================+ 50 | || Hardware(硬件): 硬盘,鼠标,显示器 || 51 | +============================================+ 52 | #+END_EXAMPLE 53 | 54 | * 操作系统启动过程 55 | 1. ~启动 BIOS~ Basic Input/Output System,基本输入输出系统。 56 | 1) =供电初始化= 按下电源开关后,电源就开始给主板和其他设备供电,CPU 会把所有寄 57 | 存器的值都设为默认值,即恢复初始状态。随后 CPU 会执行初始位置的指令,跳转到 58 | BIOS 启动代码位置,开始启动 BIOS。 59 | 2) =POST 硬件自检= BIOS 首先进行 POST(Power On Self Test,硬件自检),检查计算 60 | 机硬件是否满足运行的基本条件,并完成相应的初始化工作。如果发现了问题,主板会 61 | 发出不同的蜂鸣声表示不同的问题。 62 | 3) =搜索可引导的存储设备= BIOS 按照启动顺序开始搜索可引导的存储设备,找到后,会 63 | 将存储设备中的引导扇区读入物理内存,并执行引导扇区中的程序,开始系统引导。 64 | 4) BIOS 是按照顺序依次搜索可引导的存储设备的,这个顺序就叫启动顺序(Boot 65 | Sequence),可以在 BIOS 中进行查看和修改。 66 | 2. ~系统引导~ 67 | 1) =读取 MBR= 读取并执行硬盘的第一个扇区,即 MBR(Master Boot Record,主引导记 68 | 录,大小为 512 字节)。其作用为告诉计算机到硬盘的哪一个位置去找操作系统。MBR 69 | 的结构如下: 70 | + 第 1-446 字节:调用操作系统的机器码,可以被 CPU 直接执行 71 | + 第 447-510 字节:分区表(Partition Table),将硬盘分为了若干个分区 72 | + 第 511-512 字节:记录签名(0x55 和 0xAA),表示设备可以用于启动 73 | 2) =查找活动分区= 搜索 MBR 中的分区表,找到唯一的活动分区(Active Partition), 74 | 并将活动分区的第一个扇区中的引导扇区(称为 卷引导记录,Volume Boot Record, 75 | VBR)读入内存,就能找到操作系统在这个分区中的位置。然后,计算机就会开始加载 76 | 操作系统了。 77 | 3. ~启动系统~ 由于一台计算机中可以安装多个操作系统,所以启动系统时也会有不同的情 78 | 况。当有多个操作系统时,会运行启动管理器(Boot Loader),让用户选择启动操作系 79 | 统; 当只有一个操作系统时,就会直接从激活分区中启动操作系统(当然也可以选择将 80 | 控制权交 给启动管理器,boot loader 可以存在 MBR 中,也可以存在主分区的引导扇 81 | 区中)。 82 | 4. ~初始化系统~ 操作系统启动后,控制权就从 boot loader 转交给了操作系统,之后, 83 | 操作系统的内核首先被载入缓存,随后会运行初始化进程进行操作系统的初始化。 84 | 85 | 至此,整个计算机的启动过程就完成了。 86 | 87 | * 普通启动 88 | 存储设备 89 | 1. 软盘 -fda 90 | 2. 硬盘 -hda 91 | 3. 光盘介质 -cdrom 92 | #+BEGIN_SRC sh 93 | man qemu 94 | #+END_SRC 95 | 96 | 启动 linux 0.11 系统 97 | 1. 指定软盘 Image 98 | 2. 指定硬盘 hdc-0.11.img 99 | 3. -boot a 表示从软盘启动, man qemu ... /-boot 100 | #+BEGIN_SRC sh 101 | qemu-system-i386 -boot a -fda Image -hda hdc-0.11.img 102 | #+END_SRC 103 | 104 | 启动 xv6, 控制系统硬件参数 105 | 1. -smp CPU 数据 106 | 2. -m 内存大小 107 | 3. -nographic 表示不启动图形界面, 无图形界面的常用快捷键如下: 108 | + =ctrl-a x= 退出 qemu 109 | + =ctrl-a c= 切换串口 110 | + =ctrl-a h= 帮助 111 | #+BEGIN_SRC sh 112 | qemu-system-i386 -smp 2 -m 512 -nographic -hda xv6.img -hdb fs.img 113 | #+END_SRC 114 | 115 | 另外存储驱动也可以使用下面方法指定 116 | #+BEGIN_SRC sh 117 | qemu-system-i386 -smp 2 -m 512 -nographic \ 118 | -drive file=fs.img,index=1,media=disk,format=raw \ 119 | -drive file=xv6.img,index=0,media=disk,format=raw 120 | #+END_SRC 121 | 122 | * initrd 启动 123 | initrd 全称是 initial RAM disk ,它提供一种让核心可以简单使用 Ramdisk 的能力 124 | 1. 格式化一个 Ramdisk 125 | 2. 加载文件系统内容到 Ramdisk 126 | 3. 将 Ramdisk 作为根文件系统 127 | 128 | MINIX 3 的 initrd 启动 129 | #+BEGIN_SRC sh 130 | cd /data/os/minix/obj.i386/work 131 | qemu-system-i386 \ 132 | --enable-kvm \ 133 | -m 1G \ 134 | -kernel kernel \ 135 | -append "bootramdisk=1" \ 136 | -initrd "mod01_ds,mod02_rs,mod03_pm,mod04_sched,mod05_vfs,mod06_memory,mod07_tty,mod08_mib,mod09_vm,mod10_pfs,mod11_mfs,mod12_init" 137 | #+END_SRC 138 | 139 | Linux 内核启动方式 140 | #+BEGIN_SRC sh 141 | cd /data/os/x86_64/buildroot-2023.02.5/output/images 142 | 143 | qemu-system-x86_64 \ 144 | -M pc \ 145 | -nographic \ 146 | -kernel bzImage \ 147 | -drive file=rootfs.ext2,if=virtio,format=raw \ 148 | -append "rootwait root=/dev/vda console=tty1 console=ttyS0" \ 149 | -net nic,model=virtio -net user 150 | #+END_SRC 151 | 152 | * GDB 调试 XV6 153 | qemu 通过 gdb server 来支持 gdb 调试,启动时需要添加下面选项来开启调试 154 | 1. -S 启动时不要立即执行 CPU 指令,等待 c 命令 155 | 2. -s 开启 gdb 监听端口 1234, 这样 gdb 可以使用下面命令来链接到 qemu 156 | #+BEGIN_EXAMPLE 157 | (gdb) target remote :1234 158 | #+END_EXAMPLE 159 | 3. -s 也可以是 ~-gdb tcp::1234~ 的简写,也可以制定其他端口 160 | 161 | 以 xv6-public 为例,通过 make qemu-nox-gdb 可以开启调试 162 | #+BEGIN_SRC sh 163 | sed "s/localhost:1234/localhost:26000/" < .gdbinit.tmpl > .gdbinit 164 | qemu-system-i386 \ 165 | -nographic -drive file=fs.img,index=1,media=disk,format=raw \ 166 | -drive file=xv6.img,index=0,media=disk,format=raw -smp 2 -m 512 \ 167 | -S -gdb tcp::26000 168 | #+END_SRC 169 | 170 | * 模拟器的优缺点 171 | 1. 优点 172 | - 方便快捷,无需重启电脑 173 | - 可以作为 gdb server 来调试 174 | - 提供了丰富的调试命令, info regs, info tlb 175 | - 支持多架构 x86, riscv, arm, mips 等 176 | - 开源免费 177 | 2. 缺点 178 | - 模拟器性能会有一些损耗 179 | -------------------------------------------------------------------------------- /03-minimal-os.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 编写最小操作系统 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-01 Wed> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | 8 | * 中断,陷阱和系统调用 9 | 中断(Interrupt) 是指 CPU 停下当前的工作任务,去处理其他事情,处理完后回来继续执 10 | 行刚才的任务 11 | - 中断是硬件实现的基础功能 12 | - HelpPC Reference Library [[https://stanislavs.org/helppc/idx_interrupt.html][Interrupt Services]] 13 | - Interrupts [[https://wiki.osdev.org/Interrupts][osdev]] 14 | 15 | #+BEGIN_SRC asm 16 | int $0x80 # 中断号 17 | cli 18 | sti 19 | #+END_SRC 20 | 21 | BIOS 提供一下中断,[[https://stanislavs.org/helppc/idx_interrupt.html][link]] 22 | | 功能码 | 说明 | 23 | |--------+------------------------| 24 | | 0x13 | 读取磁盘数据 | 25 | | 0x14 | 异步通信: 串口输出字符 | 26 | | 0x16 | 读取键盘输入 | 27 | | 0x1a | 时钟中断 | 28 | 29 | 陷阱(Trap):是一种有意的,预先安排的异常事件 30 | 1. 一般是在编写程序时故意设下的陷阱指令,而后执行到陷阱指令后,CPU 将会调用特定 31 | 程序进行相应的处理,处理结束后返回到陷阱指令的下一条指令 32 | 2. 如系统调用,程序调试功能等, 最常见的就是 Linux 系统调用的实现,使用中断指令 33 | 34 | 系统调用(System Call) 指操作系统提供给应用的一组 API 函数, Linux 系统调用见 35 | 36 | 查看 32 位操作系统系统调用号 NR_syscall 文件见 37 | #+BEGIN_SRC sh :results output :exports both 38 | rg -n NR_write /usr/src/linux-headers-5.15.0-87-generic 39 | #+END_SRC 40 | 41 | #+RESULTS: 42 | : /usr/src/linux-headers-5.15.0-87-generic/arch/x86/include/generated/uapi/asm/unistd_32.h:8:#define __NR_write 4 43 | : /usr/src/linux-headers-5.15.0-87-generic/arch/x86/include/generated/uapi/asm/unistd_32.h:150:#define __NR_writev 146 44 | : /usr/src/linux-headers-5.15.0-87-generic/arch/x86/include/generated/uapi/asm/unistd_64.h:5:#define __NR_write 1 45 | : /usr/src/linux-headers-5.15.0-87-generic/arch/x86/include/generated/uapi/asm/unistd_64.h:24:#define __NR_writev 20 46 | : /usr/src/linux-headers-5.15.0-87-generic/arch/x86/include/generated/uapi/asm/unistd_x32.h:5:#define __NR_write (__X32_SYSCALL_BIT + 1) 47 | : /usr/src/linux-headers-5.15.0-87-generic/arch/x86/include/generated/uapi/asm/unistd_x32.h:321:#define __NR_writev (__X32_SYSCALL_BIT + 516) 48 | 49 | 50 | [[file:lab/05-interrupt/greet.S]] 51 | 52 | 通过 strace 查看系统调用过程 53 | #+BEGIN_SRC sh :results output :exports both 54 | strace ./lab/05-interrupt/greet.out 2>&1 1>/dev/null 55 | #+END_SRC 56 | 57 | #+RESULTS: 58 | : execve("./lab/05-interrupt/greet.out", ["./lab/05-interrupt/greet.out"], 0x7ffcb604f8b0 /* 56 vars */) = 0 59 | : [ Process PID=82486 runs in 32 bit mode. ] 60 | : write(1, "Hello from ASM!\n", 16) = 16 61 | : exit(0) = ? 62 | : +++ exited with 0 +++ 63 | 64 | * 系统引导及分段 65 | #+BEGIN_SRC sh :results output :exports both 66 | objdump -d ./lab/05-interrupt/greet2.run | grep section 67 | #+END_SRC 68 | 69 | #+RESULTS: 70 | : Disassembly of section .init: 71 | : Disassembly of section .plt: 72 | : Disassembly of section .plt.got: 73 | : Disassembly of section .text: 74 | : Disassembly of section .fini: 75 | 76 | 77 | 在 gas 汇编中对段的描述是 section, 例如 78 | #+BEGIN_SRC asm 79 | .section .text # 代码段, 可以简写 .text 80 | .section .data # 数据段, 可以简写 .data 81 | #+END_SRC 82 | 83 | AT&T 寻址方式编写格式 84 | #+BEGIN_QUOTE 85 | section:disp(base, index, scale) 86 | #+END_QUOTE 87 | 88 | #+BEGIN_SRC asm 89 | %es:msg # 寻址 es 段的 msg 90 | #+END_SRC 91 | 92 | 操作系统在对地址空间访问是通过分段实现 (segment) 93 | 1. 段使用物理上连续的一块空间 94 | 2. x86 系列分段的历史原因 95 | - 8086 是 16 位地址线 96 | - 当 CPU 升级到 20 位地址线是会出现问题 97 | - 为了兼容, 280 芯片启用来段寄存器 cs, ds, ss, es 寄存器 98 | - 380 芯片又添加了段寄存器 fs, gs 99 | - 最终导致了实模式下段寻址模式 100 | 101 | #+BEGIN_EXAMPLE 102 | 真实地址 = 段地址 << 4 + address 103 | ^ ^ 104 | | | 105 | 存储在段寄存器中 偏移 106 | #+END_EXAMPLE 107 | 108 | * 裸金属启动 109 | 回顾 BIOS 启动流程 110 | - 校验码 0x55aa => 0b01010101 0b10101010 111 | - 启动地址 0x7c00 112 | - fda.img => 0x7c00 113 | 114 | #+BEGIN_SRC sh :results output 115 | gdb -q -ex 'p/t 0x55aa' 116 | #+END_SRC 117 | 118 | #+RESULTS: 119 | : $1 = 101010110101010 120 | : (gdb) quit 121 | 122 | #+BEGIN_SRC sh :results output :exports both 123 | hexdump -n 512 ./lab/06-barebone/fda.img 124 | #+END_SRC 125 | 126 | #+RESULTS: 127 | : 0000000 01b8 bb00 0002 03b9 ba00 0004 feeb 0000 128 | : 0000010 0000 0000 0000 0000 0000 0000 0000 0000 129 | : * 130 | : 00001f0 0000 0000 0000 0000 0000 0000 0000 aa55 131 | : 0000200 132 | 133 | [[file:lab/06-barebone/boot.S]] 134 | 135 | 136 | 扩展: nasm 汇编器 137 | [[file:lab/07-nasm-boot/boot.s]] 138 | #+BEGIN_EXAMPLE 139 | (gdb) show disassembly-flavor 140 | att 141 | (gdb) set disassembly-flavor intel 142 | #+END_EXAMPLE 143 | -------------------------------------------------------------------------------- /11-os-overview.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 操作系统概览 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-01 Wed> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | 8 | 9 | * 为什么选 xv6 代码阅读? 10 | 第一点: 代码量适中,统计大约 6000 行左右 11 | 12 | xv6 代码统计 13 | #+BEGIN_SRC sh :results output :exports results :dir /data/gitana/study/os/xv6-public 14 | make clean > /dev/null 15 | cloc --quiet . 16 | #+END_SRC 17 | 18 | #+RESULTS: 19 | #+begin_example 20 | github.com/AlDanial/cloc v 1.90 T=0.06 s (1311.7 files/s, 159249.8 lines/s) 21 | ------------------------------------------------------------------------------- 22 | Language files blank comment code 23 | ------------------------------------------------------------------------------- 24 | C 46 1023 755 6455 25 | C/C++ Header 22 145 131 842 26 | make 1 36 33 210 27 | Assembly 7 51 117 201 28 | Perl 4 22 23 105 29 | Pascal 1 24 60 50 30 | Bourne Shell 3 7 3 23 31 | Lisp 1 0 0 4 32 | ------------------------------------------------------------------------------- 33 | SUM: 85 1308 1122 7890 34 | ------------------------------------------------------------------------------- 35 | #+end_example 36 | 37 | linux 0.11 代码统计 38 | #+BEGIN_SRC sh :results output :exports results :dir /data/gitana/study/os/linux-0.11 39 | cloc --quiet . 40 | #+END_SRC 41 | 42 | #+RESULTS: 43 | #+begin_example 44 | github.com/AlDanial/cloc v 1.90 T=0.09 s (1163.5 files/s, 161025.8 lines/s) 45 | ------------------------------------------------------------------------------- 46 | Language files blank comment code 47 | ------------------------------------------------------------------------------- 48 | C 51 782 1049 6747 49 | C/C++ Header 33 327 260 2154 50 | Assembly 8 165 312 1458 51 | make 8 74 52 460 52 | ------------------------------------------------------------------------------- 53 | SUM: 100 1348 1673 10819 54 | ------------------------------------------------------------------------------- 55 | #+end_example 56 | 57 | minix1 代码统计 58 | #+BEGIN_SRC sh :results output :exports results :dir /data/gitana/study/os/minix1/kernel 59 | cloc --quiet . 60 | #+END_SRC 61 | 62 | #+RESULTS: 63 | #+begin_example 64 | github.com/AlDanial/cloc v 1.90 T=0.05 s (451.0 files/s, 133013.8 lines/s) 65 | ------------------------------------------------------------------------------- 66 | Language files blank comment code 67 | ------------------------------------------------------------------------------- 68 | C 12 706 1245 3083 69 | Assembly 4 224 386 970 70 | make 2 30 6 149 71 | DOS Batch 2 1 0 139 72 | C/C++ Header 4 25 27 88 73 | ------------------------------------------------------------------------------- 74 | SUM: 24 986 1664 4429 75 | ------------------------------------------------------------------------------- 76 | #+end_example 77 | 78 | 第二点:代码比较干净 clean code 79 | - 用于教学 80 | - xv6 的原始代码设计的结构相对于 Linux 0.11 和 minix1 要好一些 81 | - 可以学到一些好的编程实践 82 | 83 | 第三点:代码比较现代 morden 84 | 1. 可以很轻松地在 macos 和 linux 发行版中编译调试 85 | 2. 作者值得信赖 86 | - 源自 Dennis Ritchie 和 Ken Thompson 的 Unix Version 6 87 | - Robert Morris 是 Cornell University 的学生,年仅 23 岁。蠕虫病毒发明者,通过互 88 | 联网传播直接侵犯到美国陆军弹道研究实验室 89 | - Frans Kaashoek 时 MIT 操作系统/分布式系统课程教授,系统研究领域 TOP 团队 90 | 91 | 和真实操作系统对比缺失部分 92 | 1. 没有网络部分的实现 net 93 | 2. 系统中有一些限制, 相比于真实操作系统要简化一些 94 | - [[file:../../study/os/xv6-public/param.h]] 95 | - round robin 96 | 97 | * 用户态/内核态 98 | #+BEGIN_SRC text 99 | ------------+================================================== 100 | | 101 | | vi cat echo 102 | User Mode | | sh | ls | 103 | | | | | | | 104 | | +---------+-----+-----+ 105 | | | 106 | | v 107 | ------------|=========== SYSCALL ============================== 108 | | | ^ 109 | | | +------------+ 110 | | v | 111 | Kernel Mode | [sys_fork/sys_exec] -> [exec/io_wait] 112 | | 113 | | [scheduler] 114 | ------------+================================================== 115 | #+END_SRC 116 | 117 | 1. RING 3 被叫做用户态,在应用程序中运行 118 | 2. RING 0 被叫做内核态,完全在操作系统内核中运行 119 | 3. 内核态有一些特权的命令 120 | - cli 关中断 [[file:lab/08-kernel-user-mode/kernel.S]] 121 | - sti 开中断 122 | - 但是用户态程序不行 [[file:lab/08-kernel-user-mode/user.S]] 123 | 124 | * 系统调用 125 | xv6 实现了如下的系统调用, 系统调用是操作系统提供给用户进程编写代码的 API 126 | 127 | | NO. | SYSCALL | Description | 128 | |-----+---------------------------+--------------------------------------------| 129 | | 1 | fork() | Create a process | 130 | | 2 | exit() | Terminate the current process | 131 | | 3 | wait() | Wait for a child process to exit | 132 | | 4 | kill(pid) | Terminate process pid | 133 | | 5 | getpid() | Return the current process’s pid | 134 | | 6 | sleep(n) | Sleep for n clock ticks | 135 | | 7 | exec(filename, *argv) | Load a file and execute it | 136 | | 8 | sbrk(n) | Grow process’s memory by n bytes | 137 | | 9 | open(filename, flags) | Open a file; the flags indicate read/write | 138 | | 10 | read(fd, buf, n) | Read n bytes from an open file into buf | 139 | | 11 | write(fd, buf, n) | Write n bytes to an open file | 140 | | 12 | close(fd) | Release open file fd | 141 | | 13 | dup(fd) | Duplicate fd | 142 | | 14 | pipe(p) | Create a pipe and return fd’s in p | 143 | | 15 | chdir(dirname) | Change the current directory | 144 | | 16 | mkdir(dirname) | Create a new directory | 145 | | 17 | mknod(name, major, minor) | Create a device file | 146 | | 18 | fstat(fd) | Return info about an open file | 147 | | 19 | link(f1, f2) | Create another name (f2) for the file f1 | 148 | | 20 | unlink(filename) | Remove a file | 149 | 150 | 151 | [[file:lab/09-linux-syscall/forkme.c]] 152 | 153 | * 地址空间 154 | 1. 操作系统的每个进程都有自己的地址空间 155 | 1) 这个空间只用进程内部是可见的,也称虚拟地址空间,不同进程的地址空间是隔离的 156 | - 如果不隔离会出现什么情况? 157 | 2) xv6 虚拟地址空间如下 158 | #+BEGIN_EXAMPLE 159 | 0xFFFFFFFF -> |-------------| - 160 | | | ^ 161 | | | | 162 | | Free Memory | 163 | | | Kernel Space 164 | |-------------| 165 | | Text & Data | 166 | | | | 167 | 0x80100000 -> |-------------| | 168 | | BIOS | v 169 | 0x80000000 -> |-------------| - 170 | (KERNBASE) | Heap | ^ 171 | | | | | 172 | | v | | 173 | | ... | | 174 | | | 175 | |-------------| User Space 176 | | ... | 177 | | ^ | 178 | | | | | 179 | | User stack | | 180 | |-------------| | 181 | | | | 182 | | User | | 183 | | Text & Data | v 184 | 0x0 -> |-------------| - 185 | #+END_EXAMPLE 186 | 3) xv6 虚拟地址空间 -> 物理地址空间 187 | #+BEGIN_EXAMPLE 188 | 0xFFFFFFFF -> |-------------| 189 | | Free Memory | 190 | | | 191 | |-------------| 192 | | Text & Data | 193 | 0x80100000 -> |-------------| -------+ 194 | | BIOS | | 195 | 0x80000000 -> |-------------| ----+ | 196 | (KERNBASE) | Heap | | | 197 | | | | | | 198 | | v | | | 199 | | ... | | | 200 | | | | | |-------------| <- Top Physical memory 201 | |-------------| | | | | 202 | | ... | | | | | 203 | | | | | | | 204 | | ^ | | | | | 205 | | | | | | |-------------| <- 4M (0x400000) 206 | | User stack | | | | Kernel | <- end (0x1154d0) 207 | |-------------| | | | Text & Data | 208 | | | | | | | 209 | | User | | +-----> |-------------| <- 0x100000 210 | | Text & Data | | | BIOS | 211 | 0x0 -> |-------------| +--------> |-------------| <- 0x0 212 | 213 | VIRTUAL PHYSICAL 214 | #+END_EXAMPLE 215 | 216 | 2. xv6 的地址管理: 分页/页表 217 | 1) xv6 为每个进程维护一个页表 218 | 2) 用以描述每个进程的用户地址空间,外加一个单独描述内核地址空间的页表 219 | 3) 虚拟地址访问物理内存和各种硬件资源 220 | - 虚拟地址 va (virtual address) 221 | - 物理地址 pa (physical address) 222 | - va -> pa 通过分页机制实现 223 | 4) 为什么需要搞虚拟地址或物理地址? 224 | 225 | * 进程管理 226 | 1. 并发 (Concurrency) 实现 227 | - 分时运行 228 | - 时间中断抢夺运行权利 229 | 230 | #+BEGIN_EXAMPLE 231 | core 1: 2GH/ 10ms 232 | t: a b 233 | t: 1 2 3 4 234 | ======================= 235 | sh x s x s 236 | vi s x s s 237 | cat s s s x 238 | #+END_EXAMPLE 239 | 240 | 2. 并行 (Parallelism) 实现 241 | - 程序真实同时运行 242 | - kernel 时间中断抢夺运行权利 243 | 244 | #+BEGIN_EXAMPLE 245 | core 1: 246 | 1 2 3 247 | ======================= 248 | sh x s x 249 | vi s x s 250 | cat s s s 251 | 252 | core 2: 253 | 1 2 3 254 | ======================= 255 | sh s s s 256 | vi x s s 257 | cat s x x 258 | #+END_EXAMPLE 259 | -------------------------------------------------------------------------------- /12-from-asm-to-c.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 从汇编到 C 语言 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-01 Wed> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | #+PROPERTY: header-args:sh :results output 8 | 9 | * 编译流程分析 10 | 1. 构建 MBR, bootsect.s 11 | 2. 构建内核 c 语言入口 kernel 12 | - entry.s 13 | - bootmain.c 14 | 15 | - 磁盘 fda.img 16 | - MBR bootsect.bin 17 | - entry.s 18 | - bootmain.c 19 | -> bootmain.bin 20 | 21 | * 读取磁盘,加载 kernel 文件 22 | 设置断点观察读取磁盘 | [[https://stanislavs.org/helppc/int_13-2.html][link]] 23 | 24 | 代码文件 [[file:lab/10-real-to-protected/bootsect.s]] 25 | 26 | #+BEGIN_EXAMPLE 27 | (gdb) 28 | [ 0:7c34] => 0x7c34: int $0x13 29 | 0x00007c34 in ?? () 30 | (gdb) x/4i $eip 31 | => 0x7c34: int $0x13 32 | 0x7c36: popa 33 | 0x7c37: ret 34 | 0x7c38: mov $0x10,%ax 35 | (gdb) b *0x7c36 36 | Breakpoint 2 at 0x7c36 37 | (gdb) c 38 | Continuing. 39 | [ 0:7c36] => 0x7c36: popa 40 | 41 | Breakpoint 2, 0x00007c36 in ?? () 42 | (gdb) x/40h 0x1000 43 | 0x1000: 0x37b9 0x0010 0xb800 0x8000 0x000b 0x48ba 0x0000 0x8800 44 | 0x1010: 0xc610 0x0140 0x830a 0x02c0 0xc183 0x0f01 0x11b6 0xfa80 45 | 0x1020: 0x750a 0xebec 0x66fe 0x6690 0x6690 0x6690 0x6690 0x9090 46 | 0x1030: 0xcbe8 0xffff 0xebff 0x48fe 0x6c65 0x6f6c 0x5720 0x726f 47 | 0x1040: 0x646c 0x000a 0x0014 0x0000 0x0000 0x0000 0x7a01 0x0052 48 | (gdb) 49 | #+END_EXAMPLE 50 | 51 | 查看磁盘镜像的数据 52 | #+BEGIN_SRC sh :results output :exports both 53 | hexdump -s 512 ./lab/10-real-to-protected/fda.img 54 | #+END_SRC 55 | 56 | #+RESULTS: 57 | : 0000200 37b9 0010 b800 8000 000b 48ba 0000 8800 58 | : 0000210 c610 0140 830a 02c0 c183 0f01 11b6 fa80 59 | : 0000220 750a ebec 66fe 6690 6690 6690 6690 9090 60 | : 0000230 cbe8 ffff ebff 48fe 6c65 6f6c 5720 726f 61 | : 0000240 646c 000a 0014 0000 0000 0000 7a01 0052 62 | : 0000250 7c01 0108 0c1b 0404 0188 0000 0010 0000 63 | : 0000260 001c 0000 ff9c ffff 0025 0000 0000 0000 64 | : 0000270 65 | 66 | * GDT 全局描述符表 67 | 1. GDT 主要保护以下信息, 总共 64 位, [[https://files.osdev.org/mirrors/geezer/os/pm.htm][link]] 68 | - base = low(16) + middle(8) + high(8) 69 | - limit = low(16) + high(4) 70 | - flags = (12) 71 | 2. GDT 的第一项必须全是 ~0x00~ 72 | 3. GDT 加载通过 ~lgdt~ 指令 73 | 74 | 下面是一个调试 gdt 内存的示例,使用 qemu 模拟器调试 75 | #+BEGIN_EXAMPLE 76 | (qemu) info registers 77 | ... 78 | GS =0000 00000000 0000ffff 00009300 79 | LDT=0000 00000000 0000ffff 00008200 80 | TR =0000 00000000 0000ffff 00008b00 81 | GDT= 00007c2b 00000017 82 | IDT= 00000000 000003ff 83 | CR0=00000010 CR2=00000000 CR3=00000000 CR4=00000000 84 | ... 85 | XMM06=0000000000000000 0000000000000000 XMM07=0000000000000000 0000000000000000 86 | (qemu) x/8x 0x7c2b 87 | 00007c2b: 0x00000000 0x00000000 0x0000ffff 0x00cf9a00 88 | 00007c3b: 0x0000ffff 0x00cf9200 0x7c2b0017 0x00000000 89 | (qemu) 90 | #+END_EXAMPLE 91 | 92 | 查询到 gdtr 后也可以使用 gdb 打印内存地址 93 | #+BEGIN_EXAMPLE 94 | (gdb) x/8x 0x7c2b 95 | 0x7c2b: 0x00000000 0x00000000 0x0000ffff 0x00cf9a00 96 | 0x7c3b: 0x0000ffff 0x00cf9200 0x7c2b0017 0x00000000 97 | #+END_EXAMPLE 98 | 99 | * 实模式(16)到保护模式(32)切换 100 | 1. =实模式= Real Mode 101 | - x86 处理器加点后都是先进入实模式 102 | - 实模式时直接访问所有地址空间 103 | - pc = cs:eip 104 | - pc = eip 105 | - 实模式只有 1 MB 的寻址空间,并且没有内存保护 106 | 2. =保护模式= Protected Mode 107 | - 保护模式提供了一些高级特性 108 | + memory protection 109 | + multitasking 110 | + enhanced memory addressing 111 | - 它支持更大的地址空间,并且多任务有不同的运行级别 (rings) 112 | 113 | 实模式到保护模式切换流程 114 | 1. 关闭中断 115 | - cli 116 | 2. 加载 gdt 117 | - 段寄存器变成段选择子 (selector) 118 | 3. 设置控制寄存器 ~cr0~ 119 | - 设置 PE 位 120 | 4. 通过远程跳转来 flush 流水线 121 | - 通过长跳转来设置 cs 寄存器, cs 变化成 0x8 122 | - 同时跳转到 .code32 位置 123 | 5. 更新所有的段寄存器 124 | - 更新数据段寄存器 ds, es, ss, fs, gs 125 | - 通用设置成 0x10 126 | 6. 更新系统栈 127 | - 初始化系统栈 ebp, esp 128 | 7. 跳转到 32 位指令出执行 129 | - 跳转到正式的启动代码 130 | - 启动代码也是 32 为 C 语言代码入口 131 | 132 | 实模式和保护模式寻址方式的变化 133 | 134 | 实模式 135 | #+BEGIN_EXAMPLE 136 | addr=seg:offset 137 | #+END_EXAMPLE 138 | 139 | 保护模式 140 | #+BEGIN_EXAMPLE 141 | linear_addr = logic_addr + gdt[selector].base 142 | #+END_EXAMPLE 143 | 1. 通过 gdtr 的地址找到 gdt 的地址 144 | 2. 通过选择子(selector=ds/es/ss/fs/gs), 找到 gdt 的偏移 145 | 3. 找到 gdt 项,选取 base 作为基地址 146 | 4. base + logic_addr 则为线性地址 147 | 148 | 寻址流程 149 | #+BEGIN_EXAMPLE 150 | Logic Address (input) 151 | 152 | selector(16) [Offset(32) ] {3} [+] --> Linear Address (output) {4} 153 | | 154 | | ^ 155 | | . {3} 156 | | . 157 | | @................@ 158 | | {2} . 159 | | |----------+--------------------------| 160 | | | Base(32) . | Limit(20) | Flag(12) | 161 | | |----------+---|-----------|----------| 162 | | | . | | | 163 | | | ... . | ... | ... | 164 | | | . | | | 165 | | 0x10 | 0 . | 0xffff | Data | 166 | `------------> 0x8 | 0 @@@ | 0xffff | Code | 167 | 0x0 | 0 | 0 | Null | 168 | gdtr {1} --------> |-------------------------------------| 169 | #+END_EXAMPLE 170 | 171 | * Bochs 调试 172 | 编译安装手册 [[https://bochs.sourceforge.io/doc/docbook/user/compiling.html][link]] 173 | 174 | 注意需要开启下面的选项: 175 | 1. --enable-gdb-stub 支持 gdb 调试 176 | 2. --enable-debugger 开启自带的调试器 177 | 3. --enable-debugger-gui 开启图形调试界面 178 | 179 | 需要注意的是 --enable-debugger 和 --enable-gdb-stub 不能同时开启 180 | 181 | #+BEGIN_EXAMPLE 182 | bochs -q -f bochsrc.bxrc 183 | ======================================================================== 184 | Bochs x86 Emulator 2.7 185 | Built from SVN snapshot on August 1, 2021 186 | Timestamp: Sun Aug 1 10:07:00 CEST 2021 187 | ======================================================================== 188 | 00000000000i[ ] BXSHARE not set. using compile time default '/opt/bochs-2.7-native/share/bochs' 189 | 00000000000i[ ] reading configuration from bochsrc.bxrc 190 | 00000000000e[ ] bochsrc.bxrc:968: wrong value for parameter 'mode' 191 | 00000000000e[PCSPK ] bochsrc.bxrc:968: unknown parameter for speaker ignored. 192 | 00000000000e[ ] bochsrc.bxrc:968: unknown parameter 'volume' 193 | 00000000000e[PCSPK ] bochsrc.bxrc:968: unknown parameter for speaker ignored. 194 | 00000000000i[ ] installing x module as the Bochs GUI 195 | 00000000000i[ ] using log file bochsout.txt 196 | Next at t=0 197 | (0) [0x0000fffffff0] f000:fff0 (unk. ctxt): jmpf 0xf000:e05b ; ea5be000f0 198 | b 0x7c00 199 | c 200 | (0) Breakpoint 1, 0x0000000000007c00 in ?? () 201 | Next at t=4435772 202 | (0) [0x000000007c00] 0000:7c00 (unk. ctxt): xor ax, ax ; 31c0 203 | u/10 204 | 0000000000007c00: ( ): xor ax, ax ; 31c0 205 | 0000000000007c02: ( ): mov ds, ax ; 8ed8 206 | 0000000000007c04: ( ): mov ss, ax ; 8ed0 207 | 0000000000007c06: ( ): mov es, ax ; 8ec0 208 | 0000000000007c08: ( ): mov fs, ax ; 8ee0 209 | 0000000000007c0a: ( ): mov gs, ax ; 8ee8 210 | 0000000000007c0c: ( ): call .+21 (0x00007c24) ; e81500 211 | 0000000000007c0f: ( ): cli ; fa 212 | 0000000000007c10: ( ): lgdt ds:0x7c6b ; 0f01166b7c 213 | 0000000000007c15: ( ): mov eax, cr0 ; 0f20c0 214 | s 215 | Next at t=4435773 216 | (0) [0x000000007c02] 0000:7c02 (unk. ctxt): mov ds, ax ; 8ed8 217 | 218 | Next at t=4435774 219 | (0) [0x000000007c04] 0000:7c04 (unk. ctxt): mov ss, ax ; 8ed0 220 | 221 | Next at t=4435775 222 | (0) [0x000000007c06] 0000:7c06 (unk. ctxt): mov es, ax ; 8ec0 223 | 224 | Next at t=4435776 225 | (0) [0x000000007c08] 0000:7c08 (unk. ctxt): mov fs, ax ; 8ee0 226 | 227 | Next at t=4435777 228 | (0) [0x000000007c0a] 0000:7c0a (unk. ctxt): mov gs, ax ; 8ee8 229 | 230 | Next at t=4435778 231 | (0) [0x000000007c0c] 0000:7c0c (unk. ctxt): call .+21 (0x00007c24) ; e81500 232 | 233 | Next at t=4435779 234 | (0) [0x000000007c24] 0000:7c24 (unk. ctxt): pusha ; 60 235 | 236 | Next at t=4435780 237 | (0) [0x000000007c25] 0000:7c25 (unk. ctxt): mov ah, 0x02 ; b402 238 | 239 | Next at t=4435781 240 | (0) [0x000000007c27] 0000:7c27 (unk. ctxt): mov al, 0x08 ; b008 241 | 242 | Next at t=4435782 243 | (0) [0x000000007c29] 0000:7c29 (unk. ctxt): mov ch, 0x00 ; b500 244 | u/10 245 | 0000000000007c29: ( ): mov ch, 0x00 ; b500 246 | 0000000000007c2b: ( ): mov cl, 0x02 ; b102 247 | 0000000000007c2d: ( ): mov dh, 0x00 ; b600 248 | 0000000000007c2f: ( ): mov dl, 0x00 ; b200 249 | 0000000000007c31: ( ): mov bx, 0x1000 ; bb0010 250 | 0000000000007c34: ( ): int 0x13 ; cd13 251 | 0000000000007c36: ( ): popa ; 61 252 | 0000000000007c37: ( ): ret ; c3 253 | 0000000000007c38: ( ): mov eax, 0xd88e0010 ; 66b810008ed8 254 | 0000000000007c3e: ( ): mov ss, ax ; 8ed0 255 | b 0x7c36 256 | x/32h 0x1000 257 | [bochs]: 258 | 0x0000000000001000 : 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 259 | 0x0000000000001010 : 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 260 | 0x0000000000001020 : 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 261 | 0x0000000000001030 : 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 262 | c 263 | (0) Breakpoint 2, 0x0000000000007c36 in ?? () 264 | Next at t=8881120 265 | (0) [0x000000007c36] 0000:7c36 (unk. ctxt): popa ; 61 266 | x/32h 0x1000 267 | [bochs]: 268 | 0x0000000000001000 : 0xe0e8 0x0000 0xeb00 0x55fe 0xe589 0x558b 0xec08 0xc35d 269 | 0x0000000000001010 : 0x8955 0x0fe5 0x55b7 0x0f08 0x45b6 0xee0c 0xc35d 0x8955 270 | 0x0000000000001020 : 0x53e5 0xd4bb 0x0003 0xb800 0x000e 0x0000 0xda89 0xbaee 271 | 0x0000000000001030 : 0x03d5 0x0000 0x89ec 0xb8c1 0x000f 0x0000 0xda89 0x0fee 272 | 273 | #+END_EXAMPLE 274 | 275 | 查看寄存器, reg, sreg, creg 276 | #+BEGIN_EXAMPLE 277 | sreg <= 段寄存器 278 | es:0x0000, dh=0x00009300, dl=0x0000ffff, valid=1 279 | Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed 280 | cs:0x0000, dh=0x00009300, dl=0x0000ffff, valid=1 281 | Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed 282 | ss:0x0000, dh=0x00009300, dl=0x0000ffff, valid=7 283 | Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed 284 | ds:0x0000, dh=0x00009300, dl=0x0000ffff, valid=1 285 | Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed 286 | fs:0x0000, dh=0x00009300, dl=0x0000ffff, valid=1 287 | Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed 288 | gs:0x0000, dh=0x00009300, dl=0x0000ffff, valid=1 289 | Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed 290 | ldtr:0x0000, dh=0x00008200, dl=0x0000ffff, valid=1 291 | tr:0x0000, dh=0x00008b00, dl=0x0000ffff, valid=1 292 | gdtr:base=0x00000000000f9ad7, limit=0x30 293 | idtr:base=0x0000000000000000, limit=0x3ff 294 | reg <= 通用寄存器 295 | rax: 00000000_60000008 296 | rbx: 00000000_00001000 297 | rcx: 00000000_00090002 298 | rdx: 00000000_00000000 299 | rsp: 00000000_0000ffc4 300 | rbp: 00000000_00000000 301 | rsi: 00000000_000e0000 302 | rdi: 00000000_0000070c 303 | r8 : 00000000_00000000 304 | r9 : 00000000_00000000 305 | r10: 00000000_00000000 306 | r11: 00000000_00000000 307 | r12: 00000000_00000000 308 | r13: 00000000_00000000 309 | r14: 00000000_00000000 310 | r15: 00000000_00000000 311 | rip: 00000000_00007c36 312 | eflags 0x00000046: id vip vif ac vm rf nt IOPL=0 of df if tf sf ZF af PF cf 313 | creg <= 控制寄存器 314 | CR0=0x60000010: pg CD NW ac wp ne ET ts em mp pe 315 | CR2=page fault laddr=0x0000000000000000 316 | CR3=0x000000000000 317 | PCD=page-level cache disable=0 318 | PWT=page-level write-through=0 319 | CR4=0x00000000: pks cet pke smap smep keylock osxsave pcid fsgsbase smx vmx la57 umip osxmmexcpt osfxsr pce pge mce pae pse de tsd pvi vme 320 | CR8: 0x0 321 | EFER=0x00000000: ffxsr nxe lma lme sce 322 | XCR0=0x00000001: cet_s cet_u pkru hi_zmm zmm_hi256 opmask bndcfg bndregs ymm sse FPU 323 | 324 | #+END_EXAMPLE 325 | 326 | 配置图形调试器 327 | #+BEGIN_SRC conf 328 | # gui_debug 开启图形调试器, 需要编译时添加 --enable-debugger-gui 选项 329 | display_library: x, options="gui_debug" 330 | #+END_SRC 331 | 332 | * 总结 333 | 1. 通过磁盘中断 int 13 加载 bootmain.bin 334 | 2. GDT 加载 335 | 3. 16 => 32 汇编 336 | 4. 32 => start_kernel C 语言函数 337 | 5. 0xb8000 显存 338 | 6. in/out 指令获取光标位置 339 | -------------------------------------------------------------------------------- /12a-Q&A.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 答疑一 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-18 Sat> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | 8 | 9 | * 视频(编号 11) 中的地址空间看起来有问题? 10 | 有粉丝对 0x0 ~ 0x80000000 的地址空间存在疑问,认为 0x0 以上是外设程序? 11 | #+BEGIN_EXAMPLE 12 | 0x80000000 -> |-------------| - 13 | (KERNBASE) | Heap | ^ 14 | | | | | 15 | | v | | 16 | | ... | | 17 | | | 18 | |-------------| User Space 19 | | ... | 20 | | ^ | 21 | | | | | 22 | | User stack | | 23 | |-------------| | 24 | | | | 25 | | User | | 26 | | Text & Data | v 27 | 0x0 -> |-------------| - 28 | #+END_EXAMPLE 29 | 30 | 我这边理解可能架构没有对齐问题 31 | - 因为 ta 参考的是 riscv 架构,和 i386 可能存在差异 32 | - 如果 BIOS 代码中断向量和一些 BIOS 处理流程我这边可以粗略地认为是用户代码 33 | - 实际的用户程序通过加载到内存精确地说肯定不是从 0x0 开始,应该可以使用下面命令 34 | 验证一下 35 | #+BEGIN_SRC sh :results output :exports both 36 | readelf -l ./lab/09-linux-syscall/forkme.out | grep Entry 37 | #+END_SRC 38 | 39 | #+RESULTS: 40 | : Entry point 0x1120 41 | 42 | * 为什么视频编码看起来不是连续的? 43 | 视频顺序采用三段式来编码,具体含义如下 44 | #+BEGIN_EXAMPLE 45 | xyz-描述 46 | ^`` 47 | | ` `- 番外号, z := null|a|b|c 48 | | | 49 | | +--- 节编号, y := 1|2|3|... 50 | | 51 | +----- 章编号, z := 0|1|2|... 52 | #+END_EXAMPLE 53 | 54 | 最终结果是视频会根据字典序来排布 55 | -------------------------------------------------------------------------------- /13-xv6-startup.org: -------------------------------------------------------------------------------- 1 | #+TITLE: xv6 启动流程分析 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-10-30 Mon> 5 | #+STARTUP: overview num indent 6 | #+PROPERTY: header-args:sh :results output :dir ../../study/os/xv6-public 7 | 8 | * xv6.img 构建过程 9 | 首先 xv6-public 是通过 make 编译系统构建,构建流程如下 10 | #+BEGIN_SRC sh :exports both 11 | make -nB | sed 's/gcc.*-c/gcc -c/' 12 | #+END_SRC 13 | 14 | #+RESULTS: 15 | #+begin_example 16 | gcc -c bootmain.c 17 | gcc -c bootasm.S 18 | ld -m elf_i386 -N -e start -Ttext 0x7C00 -o bootblock.o bootasm.o bootmain.o 19 | objdump -S bootblock.o > bootblock.asm 20 | objcopy -S -O binary -j .text bootblock.o bootblock 21 | ./sign.pl bootblock 22 | gcc -c -o bio.o bio.c 23 | gcc -c -o console.o console.c 24 | gcc -c -o exec.o exec.c 25 | gcc -c -o file.o file.c 26 | gcc -c -o fs.o fs.c 27 | gcc -c -o ide.o ide.c 28 | gcc -c -o ioapic.o ioapic.c 29 | gcc -c -o kalloc.o kalloc.c 30 | gcc -c -o kbd.o kbd.c 31 | gcc -c -o lapic.o lapic.c 32 | gcc -c -o log.o log.c 33 | gcc -c -o main.o main.c 34 | gcc -c -o mp.o mp.c 35 | gcc -c -o picirq.o picirq.c 36 | gcc -c -o pipe.o pipe.c 37 | gcc -c -o proc.o proc.c 38 | gcc -c -o sleeplock.o sleeplock.c 39 | gcc -c -o spinlock.o spinlock.c 40 | gcc -c -o string.o string.c 41 | gcc -c -o swtch.o swtch.S 42 | gcc -c -o syscall.o syscall.c 43 | gcc -c -o sysfile.o sysfile.c 44 | gcc -c -o sysproc.o sysproc.c 45 | gcc -c -o trapasm.o trapasm.S 46 | gcc -c -o trap.o trap.c 47 | gcc -c -o uart.o uart.c 48 | ./vectors.pl > vectors.S 49 | gcc -c -o vectors.o vectors.S 50 | gcc -c -o vm.o vm.c 51 | gcc -c -o entry.o entry.S 52 | gcc -c entryother.S 53 | ld -m elf_i386 -N -e start -Ttext 0x7000 -o bootblockother.o entryother.o 54 | objcopy -S -O binary -j .text bootblockother.o entryother 55 | objdump -S bootblockother.o > entryother.asm 56 | gcc -c initcode.S 57 | ld -m elf_i386 -N -e start -Ttext 0 -o initcode.out initcode.o 58 | objcopy -S -O binary initcode.out initcode 59 | objdump -S initcode.o > initcode.asm 60 | ld -m elf_i386 -T kernel.ld -o kernel entry.o bio.o console.o exec.o file.o fs.o ide.o ioapic.o kalloc.o kbd.o lapic.o log.o main.o mp.o picirq.o pipe.o proc.o sleeplock.o spinlock.o string.o swtch.o syscall.o sysfile.o sysproc.o trapasm.o trap.o uart.o vectors.o vm.o -b binary initcode entryother 61 | objdump -S kernel > kernel.asm 62 | objdump -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$/d' > kernel.sym 63 | dd if=/dev/zero of=xv6.img count=10000 64 | dd if=bootblock of=xv6.img conv=notrunc 65 | dd if=kernel of=xv6.img seek=1 conv=notrunc 66 | #+end_example 67 | 68 | 启动编译阶段: 69 | 1. bootasm.S, bootmain.c 构建 bootblock 70 | - 产出汇编文件 bootblock.asm 71 | - 产出 MBR 文件 bootblock 72 | 2. 编译入口文件, entry.S, entryother.S, initcode.S 73 | - entry.o entry 函数, 启动入口函数 (paging off) 74 | - entryother 75 | - initcode start 函数 76 | 3. 编译内核文件, bio.c, console.c ... swtch.S, trap.c 77 | - c 文件和汇编单个编译,混合链接 78 | - 产出 kernel.asm 79 | - 产出 kernel 内核镜像文件 80 | - kernel 起始地址 0x80100000, 见 kernel.ld 81 | 4. 合成启动镜像 bootblock, kernel 82 | - 产出 xv6.img 83 | - xv6.img 512 bootblock 84 | - xv6.img >512 kernel 85 | - 4096 86 | 87 | * bootloader 阶段 88 | 系统启动文件 89 | [[file:../../study/os/xv6-public/bootasm.S]] 90 | 91 | 读取磁盘函数 readsect 92 | [[file:../../study/os/xv6-public/bootmain.c]] 93 | 94 | 磁盘读取发展 IO 操作读取硬盘的三种方式: 95 | 1. CHS 方式 :小于 8G (8064MB) 96 | 2. LBA28 方式:小于 137GB 97 | 3. LBA48 方式:小于 144,000,000 GB 98 | 99 | LBA 方式访问使用了 data 寄存器,LBA 寄存器(总共 3 个),device 寄存器,command 100 | 寄存器来完成的。 101 | 102 | LBA28 和 LBA48 方式:LBA28 方式使用 28 位来描述一个扇区地址,最大支持 128GB 的硬 103 | 磁盘容量。 104 | 105 | xv6 使用 LBA28 方式读取硬盘中的数据 106 | 1. ~0x1F0~ data 寄存器 107 | - 已经读取或写入的数据,大小为两个字节(16 位数据) 每次读取 1 个 word 108 | - 反复循环,直到读完所有数据 109 | 2. ~0x1F1~ features 寄存器 110 | - 读取时的错误信息 写入时的额外参数 111 | 3. ~0x1F2~ sector count 寄存器 112 | - 指定读取或写入的扇区数 113 | 4. ~0x1F3~ LBA low 寄存器 114 | - lba 地址的低 8 位 115 | 5. ~0x1F4~ LBA mid 寄存器 116 | - lba 地址的中 8 位 117 | 6. ~0x1F5~ LBA high 寄存器 118 | - lba 地址的高 8 位 119 | 7. ~0x1F6~ device 寄存器 120 | - lba 地址的前 4 位(占用 device 寄存器的低 4 位) 121 | - 主盘值为 0(占用 device 寄存器的第 5 位) 122 | - 第 6 位值为 1 LBA 模式为 1, 123 | - CHS 模式为 0(占用 device 寄存器的第 7 位) 第 8 位值为 1 124 | 8. ~0x1F7~ command 寄存器 125 | - 读取,写入的命令,返回磁盘状态 1 126 | - 读取扇区:0x20 写入扇区:0x30 磁盘识别:0xEC 127 | 128 | - grub2 129 | - uboot 130 | 131 | 132 | 内联汇编形式 133 | #+BEGIN_QUOTE 134 | asm [volatile] ("Instruction list":Output:Input:Clobber/Modify) 135 | #+END_QUOTE 136 | 137 | 从 port 端口读取一个 byte,返回这个读取的值。 138 | 使用内联汇编,指定把 data 放在%al 存储,port 放在%dx 存储,然后调用 139 | inb %dx, %al 140 | #+BEGIN_SRC c 141 | static inline uchar 142 | inb(ushort port) 143 | { 144 | uchar data; 145 | 146 | asm volatile("in %1,%0" : "=a" (data) : "d" (port)); // =a, al 寄存器, d 表示 dx 寄存器 147 | return data; 148 | } 149 | #+END_SRC 150 | 151 | 152 | 读取端口的字节到给定地址 153 | #+BEGIN_SRC c 154 | static inline void 155 | insl(int port, void *addr, int cnt) // 从端口 port 读 4*cnt 个字节到地址 addr 156 | { 157 | asm volatile("cld; rep insl" : // 清零 DF 位,重复指令 insl 158 | "=D" (addr), "=c" (cnt) : // addr 目的地址绑定寄存器 edi,cnt 循环次数绑定 ecx 159 | "d" (port), "0" (addr), "1" (cnt) : // port 端口绑定 dx,addr,cnt 同上 160 | "memory", "cc"); // 改变了内存,改变了eflags寄存器 161 | } 162 | #+END_SRC 163 | 164 | 重复执行 insl 指令来读取多个字节的数据到目的地址, 165 | - 具体操作顺序是先读目的地址和循环次数,insl 每次执行后再更新 addr,cnt 的值,所 166 | 以 addr 和 cnt 是又读又写的 167 | - Output 和 Input 部分都存在 addr 和 cnt。期间改变了内存和 eflags 寄存器,所以 168 | 内联汇编的最后一部分申明 "cc" 和 "memory" 169 | 170 | * kernel 执行阶段 171 | 1. entry.S 配置起始地址 0x10000c 172 | 2. 进行初始化操作(先忽略) 173 | 3. 跳转到 main 方法 174 | 175 | 查看 ELF 头 176 | #+BEGIN_SRC sh :exports both 177 | readelf -h kernel 178 | #+END_SRC 179 | 180 | #+RESULTS: 181 | #+begin_example 182 | ELF Header: 183 | Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 184 | Class: ELF32 185 | Data: 2's complement, little endian 186 | Version: 1 (current) 187 | OS/ABI: UNIX - System V 188 | ABI Version: 0 189 | Type: EXEC (Executable file) 190 | Machine: Intel 80386 191 | Version: 0x1 192 | Entry point address: 0x10000c 193 | Start of program headers: 52 (bytes into file) 194 | Start of section headers: 134731012 (bytes into file) 195 | Flags: 0x0 196 | Size of this header: 52 (bytes) 197 | Size of program headers: 32 (bytes) 198 | Number of program headers: 3 199 | Size of section headers: 40 (bytes) 200 | Number of section headers: 16 201 | Section header string table index: 15 202 | #+end_example 203 | 204 | 参考程序段 205 | #+BEGIN_SRC sh :exports both 206 | readelf -l kernel 207 | #+END_SRC 208 | 209 | #+RESULTS: 210 | #+begin_example 211 | 212 | Elf file type is EXEC (Executable file) 213 | Entry point 0x10000c 214 | There are 3 program headers, starting at offset 52 215 | 216 | Program Headers: 217 | Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align 218 | LOAD 0x001000 0x80100000 0x00100000 0x07b6b 0x07b6b R E 0x1000 219 | LOAD 0x009000 0x80108000 0x00108000 0x804a5c6 0x8055590 RW 0x1000 220 | GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x10 221 | 222 | Section to Segment mapping: 223 | Segment Sections... 224 | 00 .text .rodata 225 | 01 .data .bss 226 | 02 227 | #+end_example 228 | 229 | * 总结 230 | 1. bootasm.S -> bootmain.c:bootmain() 231 | 2. bootmain 232 | - 加载 kernel 到内存 233 | - 解析 elf 头 234 | - 跳转 elf->entry() 235 | 3. entry.S 236 | - 初始化 237 | - 跳转 main.c:main() 238 | -------------------------------------------------------------------------------- /21-page-table.org: -------------------------------------------------------------------------------- 1 | #+TITLE: x86 体系结构分页机制 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-06 Mon> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | 8 | 9 | * 分页机制 10 | 1. 内核和用户的代码都是 virtual address 11 | 2. 页目录和页表中包含 1024 个目录项 PTE (page table entry) 12 | - PPN (physical page number), 20 位的物理页码 13 | - Flags, 12 位 14 | 3. 第一步从 cr3 寄存器中获取页目录 page directory 15 | 4. va.dir 索引 page directory 获取到页表 page table 16 | 5. page directory / page table 包含 1024 个 PTE 17 | - 这段连续内存大小位 1024 * 4 byte = 4KB 18 | - 这 4KB 又叫页 page 19 | 6. va.table 索引 page table 获取到物理地址到 PPN 20 | #+BEGIN_EXAMPLE 21 | +--------------------------------------------+ 22 | | | 23 | Virtual Address(32) | Physical Address(32) v 24 | +----------------------------------+ +----------------------------------+ 25 | | Dir(10) | Table(10) | Offset(12) | | PPN (20) | Offset(12) | 26 | +----------------------------------+ +----------------------------------+ 27 | | | ^ 28 | | | | 29 | | | | 30 | | | 1023 |----+---------------| 31 | | | | | | | 32 | | | | PPN(20) | Flag(12) | 33 | | | | | | 34 | | | | 0x81 | | 35 | | +-----------------------> 2 | 0x43 | | 36 | | 1 | 0 | | 37 | | +---------------------> 0 |--------------------| 38 | | | PAGE TABLE 39 | | | 40 | | | 41 | | | 1023 |------------| 42 | | 1023 |----+---------------| | | | 43 | | | | | | | PPN | Flag | 44 | | | PPN(20) | Flag(12) | | | | 45 | | | | | 2 | | | 46 | | | | | 1 | 0 | | 47 | `--> 2 | 0x23 ---+----------+----------------------> 0 |------------| 48 | 1 | 0x10 | | 49 | cr3 ---> 0 |--------------------| 50 | PAGE DIRECTORY 51 | #+END_EXAMPLE 52 | 53 | * x86 的寻址模式 54 | 1. 虚拟地址/逻辑地址 logic address 55 | - 编译器产生的地址 56 | 2. 线性地址 linear address 57 | - CPU 加载程序后,会为这个程序分配内存,通过分段产生的地址 58 | - 段基址 + 逻辑地址 = 线性地址 59 | 3. 物理地址 physical address 60 | - 硬件真实的内存地址 61 | - 如果没有开启分页, pa = la 62 | - 开启分页后,la 经过硬件转换才能得到 pa 63 | 64 | #+BEGIN_EXAMPLE 65 | [ CPU ] 66 | | | 67 | | | (Logic Address) 68 | | | 69 | v v 70 | [Segment Translation] (Selector/Offset/gdtr) 71 | | | 72 | | | (Linear Address) 73 | | | 74 | v v 75 | [ Page Translation ] (Paging/cr3/PD/PT) 76 | | | 77 | | | (Physical Address) 78 | | | 79 | v v 80 | [ Memory ] 81 | #+END_EXAMPLE 82 | 83 | * 分页代码讲解 84 | [[file:../../study/os/xv6-public/mmu.h]] 85 | 86 | #+BEGIN_SRC c 87 | // A virtual address 'la' has a three-part structure as follows: 88 | // 89 | // +--------10------+-------10-------+---------12----------+ 90 | // | Page Directory | Page Table | Offset within Page | 91 | // | Index | Index | | 92 | // +----------------+----------------+---------------------+ 93 | // \--- PDX(va) --/ \--- PTX(va) --/ 94 | 95 | // page directory index 96 | #define PDX(va) (((uint)(va) >> PDXSHIFT) & 0x3FF) 97 | 98 | // page table index 99 | #define PTX(va) (((uint)(va) >> PTXSHIFT) & 0x3FF) 100 | #+END_SRC 101 | 102 | qemu 调试过程 103 | #+BEGIN_EXAMPLE 104 | (qemu) info mem 105 | 0000000080000000-0000000080100000 0000000000100000 -rw 106 | 0000000080100000-0000000080108000 0000000000008000 -r- 107 | 0000000080108000-000000008e000000 000000000def8000 -rw 108 | 00000000fe000000-0000000100000000 0000000002000000 -rw 109 | (qemu) xp/8x 0x3ff000 + 0x200*4 110 | 00000000003ff800: 0x003fe027 0x003fd007 0x003fc007 0x003fb007 111 | 00000000003ff810: 0x003fa007 0x003f9007 0x003f8007 0x003f7007 112 | (qemu) xp/8x 0x3fe000 + 0x103*4 113 | 00000000003fe40c: 0x00103001 0x00104001 0x00105001 0x00106021 114 | 00000000003fe41c: 0x00107001 0x00108003 0x00109003 0x0010a003 115 | (qemu) xp/8x 0x103086 116 | 0000000000103086: 0x000185e8 0xf760e800 0xabe8ffff 0xe8000038 117 | 0000000000103096: 0x00000376 0xfff331e8 0xd9bce8ff 0x27e8ffff 118 | (qemu) 119 | #+END_EXAMPLE 120 | 121 | gdb 调试过程 122 | #+BEGIN_EXAMPLE 123 | (gdb) n 124 | => 0x80103086 : call 0x80103210 125 | 22 mpinit(); // detect other processors 126 | (gdb) source custom.gdb 127 | (gdb) idx 0x80103086 128 | VA(0x80103086): PDX=0x200 PTX=0x103 OFFSET=0x86 129 | (gdb) p/x $cr3 130 | $1 = 0x3ff000 131 | (gdb) ppn 0x003fe027 132 | PTE(0x3fe027): PPN=0x3fe000 FLAGS=0x27 133 | (gdb) ppn 0x00103001 134 | PTE(0x103001): PPN=0x103000 FLAGS=0x1 135 | (gdb) p/x 0x103000 | 0x86 136 | $2 = 0x103086 137 | (gdb) x/8x 0x80103086 138 | 0x80103086 : 0x000185e8 0xf760e800 0xabe8ffff 0xe8000038 139 | 0x80103096 : 0x00000376 0xfff331e8 0xd9bce8ff 0x27e8ffff 140 | (gdb) 141 | #+END_EXAMPLE 142 | -------------------------------------------------------------------------------- /22-mem-init.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 开启分页及 freelist 初始化 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-07 Tue> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | 8 | 9 | * entry.S 阶段分页机制分析 10 | xv6 在 entry.S 中开启分页 11 | 12 | 启用分页机制需要完成下面三步 13 | 1. 准备好页目录表和页表 14 | 2. 将页表地址写入控制寄存器 cr3 15 | 3. 将寄存器 cr0 的 PG 位置 1 16 | 17 | [[file:../../study/os/xv6-public/entry.S]] 18 | 19 | entry 阶段使用 CR4_PSE 的 4M 大页映射,这里的前提是做以下操作 20 | 1. 需要 Pentium 以上的 CPU 21 | 2. CR4 中的 PSE 位(第 4 位)为 1 22 | 3. 页目录项的 PS 位(第 7 位)为 1 23 | 24 | 通过 qemu 验证开启分页后 25 | #+BEGIN_EXAMPLE 26 | (qemu) help info tlb 27 | info tlb -- show virtual to physical memory mappings 28 | (qemu) help info mem 29 | info mem -- show the active virtual memory mappings 30 | (qemu) info tlb 31 | 0000000000000000: 0000000000000000 --P-A---W 32 | 0000000080000000: 0000000000000000 --P-----W 33 | (qemu) info mem 34 | 0000000000000000-0000000000400000 0000000000400000 -rw 35 | 0000000080000000-0000000080400000 0000000000400000 -rw 36 | (qemu) 37 | #+END_EXAMPLE 38 | 39 | gdb 调试分页 40 | #+BEGIN_EXAMPLE 41 | (gdb) b main 42 | Breakpoint 2 at 0x80103060: file main.c, line 20. 43 | (gdb) c 44 | Continuing. 45 | => 0x80103060
: lea 0x4(%esp),%ecx 46 | 47 | Thread 1 hit Breakpoint 2, main () at main.c:20 48 | 20 kinit1(end, P2V(4*1024*1024)); // phys page allocator 49 | (gdb) info reg cr3 50 | cr3 0x109000 [ PDBR=265 PCID=0 ] 51 | (gdb) x/8x 0x109000 52 | 0x109000: 0x000000a3 0x00000000 0x00000000 0x00000000 53 | 0x109010: 0x00000000 0x00000000 0x00000000 0x00000000 54 | (gdb) p *(int *)0x109000 55 | $1 = 163 56 | (gdb) p/x *(int *)0x109000 57 | $2 = 0xa3 58 | (gdb) p/t *(int *)0x109000 59 | $3 = 10100011 60 | (gdb) 61 | #+END_EXAMPLE 62 | 63 | * kmem.freelist 空闲页面初始化 64 | 1. end 查看手册,获取 ~extern char end[];~ 细节 65 | #+BEGIN_SRC sh 66 | man 3 end 67 | #+END_SRC 68 | 2. kinit1 初始化内存 69 | - kmem.freelist 初始化链表 70 | - 头插法 71 | - kfree 初始化页面 72 | #+BEGIN_EXAMPLE 73 | (gdb) x/8x 0x803fc000 74 | 0x803fc000: 0x803fb000 0x01010101 0x01010101 0x01010101 75 | 0x803fc010: 0x01010101 0x01010101 0x01010101 0x01010101 76 | #+END_EXAMPLE 77 | 3. memset 初始化块内存 78 | - 内联汇编设置内存值 79 | -------------------------------------------------------------------------------- /23-kmem-pgtab.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 内核页表初始化 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-08 Wed> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | 8 | * 上个视频 9 | 1. entry.S 开启分页 CR0_PG 10 | 2. main():kinit1 -> freelist 11 | #+BEGIN_EXAMPLE 12 | (qemu) info tlb 13 | 0000000000000000: 0000000000000000 --P-A---W 14 | 0000000080000000: 0000000000000000 --PDA---W 15 | (qemu) info mem 16 | 0000000000000000-0000000000400000 0000000000400000 -rw 17 | 0000000080000000-0000000080400000 0000000000400000 -rw 18 | (qemu) 19 | #+END_EXAMPLE 20 | 21 | #+BEGIN_EXAMPLE 22 | (gdb) xv-freelist 23 | #1: 0x803ff000 -> use 24 | #2: 0x803fe000 -> use 25 | #3: 0x803fd000 26 | #4: 0x803fc000 27 | #5: 0x803fb000 28 | #6: 0x803fa000 29 | #7: 0x803f9000 30 | #8: 0x803f8000 31 | #9: 0x803f7000 32 | total=746 size=746.00M 782237696 33 | #+END_EXAMPLE 34 | 35 | #+BEGIN_EXAMPLE 36 | (gdb) p/x kmap 37 | $6 = { 38 | {virt = 0x80000000, phys_start = 0x0, phys_end = 0x100000, perm = 0x2}, 39 | {virt = 0x80100000, phys_start = 0x100000, phys_end = 0x108000, perm = 0x0}, 40 | {virt = 0x80108000, phys_start = 0x108000, phys_end = 0xe000000, perm = 0x2}, 41 | {virt = 0xfe000000, phys_start = 0xfe000000, phys_end = 0x0, perm = 0x2} 42 | } 43 | #+END_EXAMPLE 44 | 45 | * kvmalloc 初始化页表 46 | kvmalloc 初始化内存页表,核心调研 setupkvm 初始化内核分页表 47 | - kalloc 申请 4K 物理页 48 | 1) pgdir 页目录 49 | - memset 设置内存值 50 | - mappages 初始化页表项, 初始化 kmap 51 | 1. walkpgdir 创建页表项 52 | 2. 设置权限 53 | #+BEGIN_EXAMPLE 54 | (gdb) p pgdir 55 | $10 = (pde_t *) 0x803ff000 56 | (gdb) x/8x 0x803ff000 57 | 0x803ff000: 0x803fe000 0x01010101 0x01010101 0x01010101 58 | 0x803ff010: 0x01010101 0x01010101 0x01010101 0x01010101 59 | (gdb) set print array on 60 | (gdb) p/x kmap 61 | $4 = { 62 | {virt = 0x80000000, phys_start = 0x0, phys_end = 0x100000, perm = 0x2}, 63 | {virt = 0x80100000, phys_start = 0x100000, phys_end = 0x108000, perm = 0x0}, 64 | {virt = 0x80108000, phys_start = 0x108000, phys_end = 0xe000000, perm = 0x2}, 65 | {virt = 0xfe000000, phys_start = 0xfe000000, phys_end = 0x0, perm = 0x2} 66 | } 67 | (gdb) 68 | #+END_EXAMPLE 69 | 70 | * 物理内存分布 71 | 1. 0x0 ~ 0x100000: BIOS 地址空间 (total 1MB) 72 | - 0x7c00 ~ 0x7e00 MBR 73 | - 0xa0000 ~ 0x100000 device memory region 74 | 2. EXTMEM(0x100000) ~ end: BIOS extended memory start 75 | - kernel text+data 76 | - end 0x1154d0 in main.c 77 | 4. end ~ PHYSTOP(0xe000000): Free Memory 78 | - end ~ 4M kinit1 初始化 79 | - PHYSTOP 物理内存终点, kmap 定义, kvmalloc 中初始化 80 | 5. DEVSPACE(0xfe000000) ~ 4G: Memory-mapped 32-bit I/O Devices 81 | - DEVSPACE memory mapped direct 82 | - devices such as ioapic 83 | -------------------------------------------------------------------------------- /23a-setup-env.org: -------------------------------------------------------------------------------- 1 | #+TITLE: xv6 调试环境配置 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-09 Thu> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | 8 | * 疑问 9 | 1. 如何构建调试环境 10 | - Morden 11 | - linux 12 | - macos 13 | 2. 如何学习操作系统 14 | - 资料 15 | - xv6-book 16 | - manual 17 | - debuging 18 | 3. 如何学习操作系统概念 19 | - 《操作系统概念》 20 | - 操作系统 21 | - Linux 22 | - LKD linux 内核设计与实现 23 | linux kernel developement 24 | - ULK 25 | understand linux kernel 26 | 27 | * 下载源码 28 | 下载 xv6 的 x86 版本的源码 29 | #+BEGIN_SRC sh 30 | git clone git://github.com/mit-pdos/xv6-public.git 31 | #+END_SRC 32 | 33 | 安装 32 位的库 34 | #+BEGIN_SRC sh 35 | sudo apt-get install gcc-multilib build-essential gdb 36 | #+END_SRC 37 | 38 | 安装 qemu 39 | #+BEGIN_SRC sh 40 | apt install qemu-system-i386 41 | #+END_SRC 42 | 43 | * 编译调试 44 | 启动 qemu 并开启 gdb server 45 | #+BEGIN_SRC sh 46 | make qemu-nox-gdb 47 | #+END_SRC 48 | 49 | gdb 链接 50 | #+BEGIN_SRC sh 51 | gdb 52 | #+END_SRC 53 | 54 | * 编译 QEMU 55 | #+BEGIN_SRC sh 56 | git clone https://github.com/mit-pdos/6.828-qemu.git qemu 57 | # built 6.828 QEMU on Debian/Ubuntu 16.04 after installing the following packages: 58 | sudo apt-get install -y libsdl1.2-dev libtool-bin libglib2.0-dev libz-dev libpixman-1-dev 59 | # Linux 60 | ./configure --disable-kvm --disable-werror --prefix=/opt/qemu-6.828 --target-list="i386-softmmu x86_64-softmmu" --python=/usr/bin/python2 61 | # OS X 62 | # ./configure --disable-kvm --disable-werror --disable-sdl [--prefix=PFX] [--target-list="i386-softmmu x86_64-softmmu"] 63 | make && make install 64 | #+END_SRC 65 | -------------------------------------------------------------------------------- /31-intro-process.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 进程创建 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-13 Mon> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | #+PROPERTY: header-args:sh :results output :dir ../../study/os/xv6-public 8 | 9 | 10 | * 究竟什么是进程 11 | 进程是一个抽象概念,它是操作系统对计算资源的抽象 12 | 13 | 1. 编译时程序可以假设它独占一台机器,编译器在编译时就可以产生固定的地址 14 | #+BEGIN_SRC sh 15 | cc hello.c 16 | #+END_SRC 17 | 2. 操作系统需要管理所有用户提交的程序,所以抽象出进程的概念 18 | #+BEGIN_SRC sh 19 | top 20 | #+END_SRC 21 | 3. 编译好的程序 (a.out) 执行代码相当于与把控制权交给操作系统 22 | - 操作系统会给 a.out 分配虚拟地址空间,通过分页机制使得进程彼此隔离 23 | - a.out 静态的 / 文件 24 | - process 动态的 / 运行的 CPU 25 | - 同一个 a.out 在不同时刻运行产生的实体(也就是进程),拥有自己的资源 26 | - CPU 时间片 27 | - 地址空间 (分页) 28 | - 相关属性 (pid), 在 xv6 中的 ~struct proc~ 结构体 29 | - pid 30 | - parent id: ppid ~fork()~ 31 | - 打开的文件 32 | - 进程在内核态和用户态上下文切换时需要保存一些状态 33 | - ~p->kstack~ 内核栈 34 | - ~p->state~ 进程状态 35 | #+BEGIN_SRC c 36 | enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; 37 | #+END_SRC 38 | 39 | [[file:../../study/os/xv6-public/proc.h::struct proc {]] 40 | 41 | * 创建第一个进程 42 | 1. userinit 创建第一个进程 43 | - [[file:../../study/os/xv6-public/main.c::userinit(); // first user process]] 44 | - 加载 initcode.S 并启动 45 | 2. allocproc() 分配进程数据结构体 proc 46 | - ptable 记录所有进程表 47 | - 查找到 proc 结构体, 设置 p->state = EMBRYO 48 | - 分配内核栈 p->kstack, 页面大小 KSTACKSIZE 49 | #+BEGIN_EXAMPLE 50 | | | 51 | 0x8e000000 |-------------| TOP STACK (total KSTACKSIZE(4K)) 52 | | esp | 53 | | ... | 54 | | eip | 55 | | ... | 56 | | edi | 57 | 0x8dffffb4 |-------------| <- p->tf 58 | | trapret | (0x80105852) 59 | |-------------| 60 | | eip | <= fork() return address 61 | | | 62 | | ... | 63 | | edi | 64 | 0x8dffff9c |-------------| <- p->context 65 | | | 66 | | | 67 | | (empty) | 68 | | | 69 | | | 70 | 0x8dfff000 |-------------| <- p->kstack 71 | | | 72 | #+END_EXAMPLE 73 | 3. setupkvm 初始化页表 74 | - p->pgdir 0x8dffe000 75 | 4. inituvm 初始化, 加载用户代码 76 | - 地址范围 0 ~ PGSIZE 77 | - memmove 复制数据 78 | #+BEGIN_SRC sh :results output :exports both 79 | make -nB | grep -i initcode 80 | #+END_SRC 81 | 82 | #+RESULTS: 83 | : gcc -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer -fno-stack-protector -fno-pie -no-pie -nostdinc -I. -c initcode.S 84 | : ld -m elf_i386 -N -e start -Ttext 0 -o initcode.out initcode.o 85 | : objcopy -S -O binary initcode.out initcode 86 | : objdump -S initcode.o > initcode.asm 87 | : ld -m elf_i386 -T kernel.ld -o kernel entry.o bio.o console.o exec.o file.o fs.o ide.o ioapic.o kalloc.o kbd.o lapic.o log.o main.o mp.o picirq.o pipe.o proc.o sleeplock.o spinlock.o string.o swtch.o syscall.o sysfile.o sysproc.o trapasm.o trap.o uart.o vectors.o vm.o -b binary initcode entryother 88 | 5. 设置 p->state = RUNNABLE 89 | 6. ptable->proc[] 数组插入 RUNNABLE proc 结构体 90 | -------------------------------------------------------------------------------- /32-init-start.org: -------------------------------------------------------------------------------- 1 | #+TITLE: scheduler 启动及 switchuvm 切换用户态地址空间 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-14 Tue> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | #+PROPERTY: header-args:sh :results output :dir ../../study/os/xv6-public 8 | 9 | 10 | * 唤醒首个进程 11 | mpmain 完成初始化并启动 scheduler 12 | 1. idtinit 加载 IDT (中断描述符表) 13 | #+BEGIN_SRC c 14 | struct gatedesc idt[256]; 15 | #+END_SRC 16 | 2. xchg 设置 CPU 启动 17 | 3. scheduler() 执行调度 18 | 19 | * scheduler 进程调度 20 | scheduler 目的是调度进程 p 到 cpu 上执行 21 | 22 | 1. scheduler 不返回,是一个死循环 23 | 2. 查找 RUNNABLE 进程,等待调度 24 | - 当前 mycpu() 25 | - 轮训 ptable.proc ,调度算法 RR (Round ribbon) 26 | - 当前可执行进程 p 27 | 3. switchuvm 用户态执行 28 | 4. 设置 p->state = RUNNING 29 | 5. swtch(...) 执行程序 30 | 6. switchkvm 执行内核态 31 | 32 | * switchuvm 用户态地址空间 33 | 1. 关中断 pushcli 34 | 2. 切换 TSS 段 (Task Status Segment) 35 | #+BEGIN_EXAMPLE 36 | TR 0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy 37 | #+END_EXAMPLE 38 | 39 | - TSS Descriptor 提供硬件级的进程切换机制 40 | - TSS 是一段内存区域,存放进程相关的执行环境信息 41 | - 初始化的 TSS 是由用户提供,进程切换时的保存信息由 processor 执行 42 | - TSS selector => TR 寄存器,其结构与 segment registers 是完全一致的 43 | - ltr 指令 44 | 1) 使用提供的 selector 在 GDT / LDT 里索引查找 TSS descriptor 45 | 2) 查找结果加载到 TR 寄存器里 46 | #+BEGIN_EXAMPLE 47 | (qemu) info registers 48 | TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy 49 | (qemu) info registers 50 | TR =0028 801117a8 00000067 00408900 DPL=0 TSS32-avl 51 | #+END_EXAMPLE 52 | 3. 切换页表 53 | - lcr3 加载 p->pgdir 的页目录 54 | 4. 开中断 popcli, 让中断控制器调度进程运行 55 | -------------------------------------------------------------------------------- /33-context-switch.org: -------------------------------------------------------------------------------- 1 | #+TITLE: swtch上下文切换及内核态执行 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-14 Tue> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | #+PROPERTY: header-args:sh :results output :dir ../../study/os/xv6-public 8 | 9 | 10 | * 上个视频 11 | 对于多任务 OS 来说,TSS segment 是必不可少的,系统至少需要一个 TSS segment,但是 12 | 现在的 OS 系统不使用 TSS 机制来进行任务的切换。 13 | 14 | 情景提示: 15 | 1. TSS 存在的唯一理由是:需要提供 0 ~ 2 权限级别的 stack pointer,当发生 stack 16 | 切换时,必须使用 TSS 提供的相应的 stack pointer 17 | 2. 若提供空的 TSS segment,或者可以考虑以直接传递 stack pointer 的方式实现 stack 18 | 切换,即便是这样设计 processor 要读取 TSS segment 这一工作是必不可少的 19 | 20 | * swtch 切换进程上下文 21 | 切换 kernel 上下文到当前进程 22 | #+BEGIN_SRC c 23 | void swtch(struct context** old, struct context* new); 24 | swtch(&(c->scheduler), p->context); 25 | #+END_SRC 26 | 27 | 1. 传值 old, new 28 | 2. 保存旧的寄存器 struct context 29 | 3. 上下文切换 30 | 4. 恢复新的寄存器 31 | 32 | [[file:../../study/os/xv6-public/swtch.S::swtch:]] 33 | 34 | #+BEGIN_EXAMPLE 35 | swtch(&(c->scheduler), p->context); 36 | (gdb) p/x &(c->scheduler) 37 | $1 = 0x801117a4 38 | (gdb) p/x p->context 39 | $2 = 0x8dffff9c 40 | (gdb) p/x $eip 41 | $3 = 0x80103c92 42 | (gdb) info reg eax 43 | eax 0x801117a4 -2146363484 44 | (gdb) info reg edx 45 | edx 0x8dffff9c -1912602724 46 | (gdb) 47 | 48 | 1. Initial 49 | eax = 0x801117a4 old &(c->scheduler) 50 | edx = 0x8dffff9c new p->context 51 | 52 | | ??? | 53 | | ??? | 54 | | eip(0x80103c97) | <- esp 55 | | | 56 | 57 | 2. Save 58 | | ??? | 59 | | ??? | 60 | | eip(0x80103c97) | 61 | | ebp | 62 | | ebx | 63 | | esi | 64 | | edi | <- esp (0x8011544c) 65 | 66 | 3.1 Switch stack: set eax 67 | | ??? | 68 | | ??? | 69 | | eip(0x80103c97) | 70 | | ebp | 71 | | ebx | 72 | | esi | 73 | | edi | <- esp (0x8011544c) 74 | 75 | # *(c->scheduler) = 0x8011544c 76 | 77 | 3.2 Switch stack: set esp <= edx 78 | | ??????????????? | 79 | | ??????????????? | 80 | | ??????????????? | 81 | | ??????????????? | 82 | | ??????????????? | 83 | | ??????????????? | 84 | | ??????????????? | <- esp (0x8dffff9c) 85 | 86 | # *(c->scheduler) = 0x8011544c 87 | 88 | 3.3 Switch stack: 脑补 p->context 89 | | ??????????????? | 90 | | ??????????????? | 91 | | eip | 92 | | ebp | 93 | | ebx | 94 | | esi | 95 | | edi | <- esp (0x8dffff9c) 96 | 97 | # *(c->scheduler) = 0x8011544c 98 | 99 | 4. Pop 100 | | ??????????????? | 101 | | ??????????????? | 102 | | eip (0x80103880)| <- esp (0x8dffffac) 103 | | ebp | 104 | | ebx | 105 | | esi | 106 | | edi | 107 | 108 | # *(c->scheduler) = 0x8011544c 109 | # 0x80103880 110 | 111 | 5. ret 112 | => forkret 113 | #+END_EXAMPLE 114 | 115 | * forkret 初始化工作 116 | 调用 swtch 前的堆栈 117 | #+BEGIN_EXAMPLE 118 | (gdb) b swtch 119 | Breakpoint 1 at 0x8010487b: file swtch.S, line 11. 120 | (gdb) c 121 | Continuing. 122 | The target architecture is set to "i386". 123 | => 0x8010487b : mov 0x4(%esp),%eax 124 | 125 | Thread 1 hit Breakpoint 1, swtch () at swtch.S:11 126 | 11 movl 4(%esp), %eax 127 | (gdb) bt 128 | #0 swtch () at swtch.S:11 129 | #1 0x80103c97 in scheduler () at proc.c:343 130 | #2 0x8010303f in mpmain () at main.c:57 131 | #3 0x8010318c in main () at main.c:37 132 | (gdb) si 133 | => 0x8010487f : mov 0x8(%esp),%edx 134 | 12 movl 8(%esp), %edx 135 | (gdb) 136 | #+END_EXAMPLE 137 | 138 | - swtch.S 返回 ret 时的,跳转到 forkret 139 | - allocproc 时设置的返回值 $eip 140 | #+BEGIN_EXAMPLE 141 | (gdb) si 142 | => 0x80103880 : push %ebp 143 | forkret () at proc.c:398 144 | 398 release(&ptable.lock); 145 | (gdb) bt 146 | #0 forkret () at proc.c:398 147 | (gdb) 148 | #+END_EXAMPLE 149 | 150 | * trapret 陷阱返回 151 | trapret 时 forkret 返回的 pc, 在 trapret 中调用 iret 将控制权交给内核 152 | #+BEGIN_EXAMPLE 153 | (gdb) b forkret 154 | Breakpoint 1 at 0x80103880: forkret. (2 locations) 155 | (gdb) c 156 | Continuing. 157 | The target architecture is set to "i386". 158 | => 0x80103880 : push %ebp 159 | 160 | Thread 1 hit Breakpoint 1, forkret () at proc.c:398 161 | 398 release(&ptable.lock); 162 | (gdb) n 163 | => 0x80103890 : mov 0x8010a000,%eax 164 | 400 if (first) { 165 | (gdb) si 166 | => 0x80103895 : add $0x10,%esp 167 | 0x80103895 400 if (first) { 168 | (gdb) 169 | => 0x80103898 : test %eax,%eax 170 | 0x80103898 400 if (first) { 171 | (gdb) 172 | => 0x8010389a : jne 0x801038a0 173 | 0x8010389a 400 if (first) { 174 | (gdb) 175 | => 0x801038a0 : movl $0x0,0x8010a000 176 | 177 | Thread 1 hit Breakpoint 1, forkret () at proc.c:404 178 | 404 first = 0; 179 | (gdb) 180 | => 0x801038aa : sub $0xc,%esp 181 | 405 iinit(ROOTDEV); 182 | (gdb) n 183 | => 0x801038b4 : movl $0x1,(%esp) 184 | 406 initlog(ROOTDEV); 185 | (gdb) n 186 | => 0x80105852 : popa 187 | trapret () at trapasm.S:26 188 | 26 popal 189 | (gdb) bt 190 | #0 trapret () at trapasm.S:26 191 | (gdb) 192 | 193 | #+END_EXAMPLE 194 | 195 | 用于唤醒是通过中断,所以返回 trap 使得最终控制器交给操作系统 196 | #+BEGIN_SRC nasm 197 | iret 198 | ;; 依次取出 199 | pop eip 200 | pop cs 201 | popf 202 | pop esp 203 | pop ss 204 | #+END_SRC 205 | 206 | #+BEGIN_EXAMPLE 207 | (gdb) x/4x $esp 208 | 0x8dffffec: 0x00000000 0x0000001b 0x00000200 0x00001000 209 | (gdb) set print pretty 210 | (gdb) p/x *(struct trapframe *)0x8dffffb4 211 | $4 = { 212 | edi = 0x0, 213 | esi = 0x0, 214 | ebp = 0x0, 215 | oesp = 0x0, 216 | ebx = 0x0, 217 | edx = 0x0, 218 | ecx = 0x0, 219 | eax = 0x0, 220 | gs = 0x0, 221 | padding1 = 0x0, 222 | fs = 0x0, 223 | padding2 = 0x0, 224 | es = 0x23, 225 | padding3 = 0x0, 226 | ds = 0x23, 227 | padding4 = 0x0, 228 | trapno = 0x0, 229 | err = 0x0, 230 | eip = 0x0, 231 | cs = 0x1b, 232 | padding5 = 0x0, 233 | eflags = 0x200, 234 | esp = 0x1000, 235 | ss = 0x23, 236 | padding6 = 0x0 237 | } 238 | (gdb) 239 | #+END_EXAMPLE 240 | 241 | * initcode 执行地址空间观察 242 | 虚拟地址数据 243 | #+BEGIN_EXAMPLE 244 | (gdb) xv-v2p 0 245 | Prepare: VA=0x00000000 PDX=0x00000000 PTX=0x00000000 OFFSET=0x00000000 246 | Stage 1: CR3=0x0dffe000 PDX=0x00000000 ADDR1=0x0dffe000 PDE=0x0dfbc027 247 | Stage 2: PPN=0x0dfbc000 PTX=0x00000000 ADDR2=0x0dfbc000 PTE=0x0dfbd067 248 | Final : PPN2=0x0dfbd000 OFFSET=0x00000000 PA=0x0dfbd000 249 | Summary: VA=0x00000000 -> PA=0x0dfbd000 250 | (gdb) x/16h 0 251 | 0x0: 0x2468 0x0000 0x6800 0x001c 0x0000 0x006a 0x07b8 0x0000 252 | 0x10: 0xcd00 0xb840 0x0002 0x0000 0x40cd 0xf7eb 0x692f 0x696e 253 | (gdb) 254 | #+END_EXAMPLE 255 | 256 | 物理地址数据 257 | #+BEGIN_EXAMPLE 258 | (qemu) info tlb 259 | 0000000000000000: 000000000dfbd000 -------UW 260 | 0000000080000000: 0000000000000000 --------W 261 | 0000000080001000: 0000000000001000 --------W 262 | 0000000080002000: 0000000000002000 --------W 263 | ... 264 | (qemu) info mem 265 | 0000000000000000-0000000000001000 0000000000001000 urw 266 | 0000000080000000-0000000080100000 0000000000100000 -rw 267 | 0000000080100000-0000000080108000 0000000000008000 -r- 268 | 0000000080108000-000000008e000000 000000000def8000 -rw 269 | 00000000fe000000-0000000100000000 0000000002000000 -rw 270 | (qemu) xp/16h 0x0dfbd000 271 | 000000000dfbd000: 0x2468 0x0000 0x6800 0x001c 0x0000 0x006a 0x07b8 0x0000 272 | 000000000dfbd010: 0xcd00 0xb840 0x0002 0x0000 0x40cd 0xf7eb 0x692f 0x696e 273 | (qemu) 274 | #+END_EXAMPLE 275 | 276 | 磁盘镜像数据 277 | #+BEGIN_EXAMPLE 278 | xv6-public $ hexdump -n 32 initcode 279 | 0000000 2468 0000 6800 001c 0000 006a 07b8 0000 280 | 0000010 cd00 b840 0002 0000 40cd f7eb 692f 696e 281 | 0000020 282 | xv6-public $ 283 | #+END_EXAMPLE 284 | 285 | * 总结 286 | 1. swtch => p->context 287 | 2. ret => forkret 288 | - allocproc 289 | 3. forkret => trapret 290 | 4. trapret iret => eip(0x0) 291 | - user space 292 | - 开始执行 initcode.S 293 | -------------------------------------------------------------------------------- /34-enter-shell.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 从 initcode 到 shell 启动 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-15 Wed> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | #+PROPERTY: header-args:sh :results output :dir ../../study/os/xv6-public 8 | 9 | 10 | * 从 initcode.S 到 init.c 11 | [[file:../../study/os/xv6-public/initcode.S]] 12 | 13 | 1. 调用 exec 系统调用加载 sh 启动用户 shell 14 | 2. 通过系统调用 exec() 执行 /init 文件 15 | 16 | #+BEGIN_SRC sh :results output :exports both 17 | make -nB fs.img | sed 's/gcc.*-c/gcc -c/' | grep init 18 | #+END_SRC 19 | 20 | #+RESULTS: 21 | : gcc -c -o init.o init.c 22 | : ld -m elf_i386 -N -e main -Ttext 0 -o _init init.o ulib.o usys.o printf.o umalloc.o 23 | : objdump -S _init > init.asm 24 | : objdump -t _init | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$/d' > init.sym 25 | : ./mkfs fs.img README _cat _echo _forktest _grep _init _kill _ln _ls _mkdir _rm _sh _stressfs _usertests _wc _zombie 26 | 27 | * init.c 到 sh.c 28 | [[file:../../study/os/xv6-public/init.c]] 29 | 30 | init.c 运行 shell 31 | 1. 打开 console 文件 32 | 2. 设置 stdout, stderr 33 | 3. 循环创建 shell 34 | - fork 子进程启动 sh 35 | - 父进程等待 36 | - 如果子进程退出,则重新拉起 sh 37 | 38 | * sh.c 执行,系统启动 39 | 调用 sh.c 实现用户 shell 40 | 1. 检测 console 文件打开 41 | 2. 死循环执行命令 42 | - 获取命令 getcmd 43 | - cd 命令的特殊处理 44 | - fork1 创建子进程 45 | - 父进程循环 46 | - 子进程解析命令并运行命令 47 | 48 | [[file:../../study/os/xv6-public/sh.c]] 49 | 50 | * 总结 51 | 1. initcode (kernel) 52 | 2. int 0x40 (exec) => init.c 53 | 3. init.c (exec) => sh.c 54 | - open 55 | 4. sh.c (shell) 56 | 1. 检测 console 文件打开 57 | 2. 死循环执行命令 58 | - 获取命令 getcmd 59 | - cd 命令的特殊处理 60 | - fork1 创建子进程 61 | - 父进程循环 62 | - 子进程解析命令并运行命令 63 | 64 | #+BEGIN_EXAMPLE 65 | (gdb) xv-ps 66 | ptable.proc[0]: pid=1 state=2 name=init 67 | ptable.proc[1]: pid=2 state=2 name=sh 68 | ptable.proc[2]: pid=4 state=2 name=sh 69 | ptable.proc[3]: pid=5 state=4 name=wc 70 | 71 | init(1) 72 | | 73 | --- sh(2) 74 | | 75 | |--- sh(4) 76 | | 77 | --- wc(5) 78 | #+END_EXAMPLE 79 | -------------------------------------------------------------------------------- /34a-Q&A.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 答疑二 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-18 Sat> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | 8 | 9 | * 是否预计讲 xv6-riscv 架构 10 | 1. 暂时没有计划讲 11 | 2. 体系结构比较多, 建议先掌握一个, 用的时候再查手册 12 | - riscv 13 | - mips 14 | - arm 15 | 3. riscv 建议 MIT 16 | 17 | * 视频 33 中 forkret 如何跳转到 trapret 18 | 答案: ret 的语义, x86 手册 19 | 详细调试 20 | #+BEGIN_EXAMPLE 21 | (gdb) x/8i $pc 22 | => 0x80103890 : mov 0x8010a000,%eax 23 | 0x80103895 : add $0x10,%esp 24 | 0x80103898 : test %eax,%eax 25 | 0x8010389a : jne 0x801038a0 26 | 0x8010389c : leave 27 | 0x8010389d : ret 28 | 0x8010389e : xchg %ax,%ax 29 | 0x801038a0 : movl $0x0,0x8010a000 30 | (gdb) n 31 | => 0x801038a0 : movl $0x0,0x8010a000 32 | 394 forkret(void) 33 | (gdb) lay asm 34 | (gdb) 35 | (gdb) x/x $esp 36 | 0x8dffffb0: 0x80105852 37 | (gdb) f 38 | #0 0x801038c4 in forkret () at proc.c:410 39 | 410 } 40 | (gdb) x/i $pc 41 | => 0x801038c4 : ret 42 | (gdb) si 43 | => 0x80105852 : popa 44 | trapret () at trapasm.S:26 45 | 26 popal 46 | (gdb) p/x $pc 47 | $1 = 0x80105852 48 | (gdb) bt 49 | #0 trapret () at trapasm.S:26 50 | (gdb) 51 | #0 trapret () at trapasm.S:26 52 | (gdb) 53 | #+END_EXAMPLE 54 | 55 | * 视频 34 中 如何调试用户进程 56 | - init.c 57 | - sh.c 58 | #+BEGIN_EXAMPLE 59 | kernel 60 | #+END_EXAMPLE 61 | 62 | 就是手工切换符号文件 63 | 64 | init.c 65 | #+BEGIN_EXAMPLE 66 | (gdb) symbol-file _init 67 | #+END_EXAMPLE 68 | 69 | sh.c 70 | #+BEGIN_EXAMPLE 71 | (gdb) symbol-file _sh 72 | #+END_EXAMPLE 73 | -------------------------------------------------------------------------------- /34b-Q&A.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 答疑三 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-21 Tue> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | 8 | 9 | * 如何系统地学习 makefile 和 ld 链接脚本 10 | 1. GNU project by RMS(author) 11 | 2. make https://www.gnu.org/software/make/ 12 | 3. binutils https://sourceware.org/binutils/docs/ 13 | 14 | * 视频 33 中 swtch 中 bt 输出变化是为什么 15 | x86 ebp 16 | 17 | #+BEGIN_EXAMPLE 18 | | ... ... | 19 | | return addr | 20 | ebp -> | prev ebp | 21 | | ... ... | 22 | esp -> | ... ... | 23 | #+END_EXAMPLE 24 | 25 | frame 0: 26 | scheduler ebp=0x80115488 27 | #+BEGIN_EXAMPLE 28 | | ... ... | 29 | | 0x8010303f | 30 | ebp -> | 0x801154a8 | 0x80115488 31 | | ... ... | 32 | esp -> | ... ... | 33 | #+END_EXAMPLE 34 | 35 | frame 1: 36 | mpmain ebp=0x80115488 37 | #+BEGIN_EXAMPLE 38 | | ... ... | 39 | | 0x8010318c | 40 | ebp -> | 0x801154c8 | 0x801154a8 41 | | ... ... | 42 | esp -> | ... ... | 43 | #+END_EXAMPLE 44 | 45 | frame 2: 46 | main ebp=0x80115488 47 | #+BEGIN_EXAMPLE 48 | | ... ... | 49 | | 0x00000000 | 50 | ebp -> | 0x00007bf8 | 0x801154c8 51 | | ... ... | 52 | esp -> | ... ... | 53 | #+END_EXAMPLE 54 | 55 | #+BEGIN_EXAMPLE 56 | (gdb) backtrace 57 | #0 scheduler () at proc.c:324 58 | #1 0x8010303f in mpmain () at main.c:57 59 | #2 0x8010318c in main () at main.c:37 60 | #+END_EXAMPLE 61 | 62 | 违反 x86 call convention 63 | #+BEGIN_EXAMPLE 64 | (gdb) bt 65 | #0 swtch () at swtch.S:25 66 | #1 0x00000000 in ?? () 67 | Backtrace stopped: previous frame inner to this frame (corrupt stack?) 68 | (gdb) p/x $esp 69 | $3 = 0x8dffff9c 70 | (gdb) p/x $ebp 71 | $4 = 0x80115488 72 | #+END_EXAMPLE 73 | 74 | * 视频 42 中 initcode 系统调用 exec 如何切换到 _init 地址空间 75 | 1. initcode 到 _init 的切换流程 76 | 2. 切换到 0x0 77 | #+BEGIN_EXAMPLE 78 | (gdb) bt 79 | #0 exec (path=0x1c "", argv=0x8dfffed0) at exec.c:104 80 | #1 0x801055aa in sys_exec () at sysfile.c:418 81 | #2 0x80104a89 in syscall () at syscall.c:139 82 | #3 0x80105aad in trap (tf=0x8dffffb4) at trap.c:42 83 | #4 0x8010584f in alltraps () at trapasm.S:20 84 | #+END_EXAMPLE 85 | 86 | alltraps 里面的 通过汇编执行下一条指令来 87 | #+BEGIN_SRC asm 88 | pushl %esp 89 | call trap 90 | addl $4, %esp 91 | 92 | # Return falls through to trapret... 93 | .globl trapret 94 | trapret: 95 | popal 96 | popl %gs 97 | popl %fs 98 | popl %es 99 | popl %ds 100 | addl $0x8, %esp # trapno and errcode 101 | iret 102 | #+END_SRC 103 | 104 | esp => 0x00000000 105 | next PC = 0 106 | #+BEGIN_EXAMPLE 107 | (gdb) x/4x $esp 108 | 0x8dffffec: 0x00000000 0x0000001b 0x00000202 0x00002fe4 109 | (gdb) 110 | #+END_EXAMPLE 111 | -------------------------------------------------------------------------------- /41-intro-interrupt.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 中断初始化及调用流程 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-16 Thu> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | #+PROPERTY: header-args:sh :results output :dir ../../study/os/xv6-public 8 | 9 | 10 | * tvinit 初始中断向量 IDT 11 | 1. 设置了 IDT 表中的 256 个表项 12 | - 中断 i 的处理向量为 vectors[i] 13 | - 0x40 设置成系统调用 T_SYSCALL 14 | 2. xv6 中断编号 [[file:../../study/os/xv6-public/traps.h]] 15 | - 0-31 软件中断: divide error、 page fault 16 | - 32-63 硬件中断 17 | - 64 系统调用: syscall 18 | 3. syscall 特殊处理 19 | - 设置 trap 位为 1 指定 syscall 时陷阱门,即处理系统调用时可以同时处理其它中断 20 | - 设置 syscall 调用权限 DPL_USER,阻止用户程序产生其它中断 21 | 1) 例如用户不能产生设备中断 22 | 2) 如果用户产生不合法的中断,会抛出 general protection fault, 即 int 13 23 | 4. 特权等级切换 24 | - 用户态切换内核态,内核不能使用用户的堆栈,可能存在恶意攻击 25 | - 内核态切换用户态,从 TSS 中恢复用户堆栈,见 switchuvm 26 | 5. 当 trap 发生时,处理器会做下面一些事 27 | - 用户态时,它会从任务段描述符中加载 esp 和 ss,把老的 ss 和 esp 压入新的栈中 28 | - 内核态时,上面的事件就不会发生。处理器接下来会把 eflags,cs,eip 压栈 29 | - 另外,对于某些 trap 来说,处理器会压入一个 errno 30 | #+BEGIN_EXAMPLE 31 | (gdb) si 32 | => 0x80105859 : add $0x8,%esp 33 | 31 addl $0x8, %esp # trapno and errcode 34 | (gdb) xv-ctx 35 | ctx: cs=0x8 eip=0x80105859 ss=0x10 esp=0x8dffffe4 eflags=0x282 36 | (gdb) si 37 | => 0x8010585c : iret 38 | 32 iret 39 | (gdb) xv-ctx 40 | ctx: cs=0x8 eip=0x8010585c ss=0x10 esp=0x8dffffec eflags=0x282 41 | (gdb) si 42 | => 0x0: push $0x24 43 | 0x00000000 in ?? () 44 | (gdb) xv-ctx 45 | ctx: cs=0x1b eip=0x0 ss=0x23 esp=0x1000 eflags=0x202 46 | (gdb) si 47 | => 0x5: push $0x1c 48 | 0x00000005 in ?? () 49 | (gdb) 50 | => 0xa: push $0x0 51 | 0x0000000a in ?? () 52 | (gdb) 53 | => 0xc: mov $0x7,%eax 54 | 0x0000000c in ?? () 55 | (gdb) 56 | => 0x11: int $0x40 57 | 0x00000011 in ?? () 58 | (gdb) xv-ctx 59 | ctx: cs=0x1b eip=0x11 ss=0x23 esp=0xff4 eflags=0x202 60 | (gdb) si 61 | => 0x80105f57 : push $0x0 62 | 319 pushl $0 63 | (gdb) xv-ctx 64 | ctx: cs=0x8 eip=0x80105f57 ss=0x10 esp=0x8dffffec eflags=0x202 65 | (gdb) x/8x 0x8dffffec 66 | 0x8dffffec: 0x00000013 0x0000001b 0x00000202 0x00000ff4 67 | 0x8dfffffc: 0x00000023 Cannot access memory at address 0x8e000000 68 | (gdb) 69 | #+END_EXAMPLE 70 | 71 | * int 指令 72 | 在 x86 中,中断处理程序的入口在中断描述符表(IDT)中被定义。这个表有 256 个表项, 73 | 每一个都提供了相应的 cs 和 eip 74 | 75 | 程序进行一个系统调用,它需要调用 int n 指令,这里 n 就是 IDT 的索引 76 | #+BEGIN_SRC asm 77 | int 0x40 78 | #+END_SRC 79 | 80 | ~int~ 指令进行下面一些步骤: 81 | 1. 从 IDT 中获得第 n 个描述符,n 就是 int 的参数 82 | 2. 检查 cs 的域 CPL <= DPL,DPL 是描述符中记录的特权级 83 | 3. 如果目标段选择符的 PL < CPL,就在 CPU 内部的寄存器中保存 esp 和 ss 的值 84 | 4. 从一个 TSS 任务段描述符中加载 ss 和 esp, 记做 esp0 ~cpus[0].ts.esp0~ 85 | 5. push ss 86 | 6. push esp 87 | 7. push eflags 88 | 8. push cs 89 | 9. push eip 90 | 10. 清除 eflags 的一些位 91 | 11. 设置 cs 和 eip 为描述符中的值 92 | 93 | Before int 94 | #+BEGIN_EXAMPLE 95 | ctx: cs=0x1b eip=0x11 ss=0x23 esp=0xff4 eflags=0x202 96 | #+END_EXAMPLE 97 | 98 | After int 99 | #+BEGIN_EXAMPLE 100 | (gdb) x/i $pc 101 | => 0x11: int $0x40 102 | (gdb) si 103 | => 0x80105f57 : push $0x0 104 | 319 pushl $0 105 | (gdb) x/8x $esp 106 | 0x8dffffec: 0x00000013 0x0000001b 0x00000202 0x00000ff4 107 | 0x8dfffffc: 0x00000023 Cannot access memory at address 0x8e000000 108 | (gdb) xv-ctx 109 | ctx: cs=0x8 eip=0x80105f57 ss=0x10 esp=0x8dffffec eflags=0x202 110 | (gdb) 111 | #+END_EXAMPLE 112 | 113 | Stack layout 114 | #+BEGIN_EXAMPLE 115 | | ?????? | <- esp0(0x8e000000) ss(0x10) from TSS 116 | Present on / | ss 0x00000023 | 117 | privilege change \ | esp 0x00000ff4 | 118 | | eflags 0x00000202 | 119 | | cs 0x0000001b | 120 | | eip 0x00000013 | <- esp(0x8dffffec) 121 | | | 122 | #+END_EXAMPLE 123 | 124 | 操作系统可以使用 ~iret~ 指令来从一个 ~int~ 指令中返回 125 | - 它从栈中弹出 ~int~ 指令保存的值 126 | - 然后通过恢复保存的 eip 的值来继续用户程序的执行 127 | 128 | * int 指令到 trap 流程分析 129 | 1. int 0x40 => 跳转到中断处理向量 130 | - vector64 131 | #+BEGIN_SRC asm 132 | vector64: 133 | pushl $0 134 | pushl $64 135 | jmp alltraps 136 | #+END_SRC 137 | - vector64 跳转 alltraps 138 | 2. alltraps 是所有 trap 的入口 139 | - 设置 trapframe 140 | - 修改寄存器 141 | - 返回内核态, ~call trap~ 142 | 3. trap 中判断系统调用 syscall() 143 | 144 | * 总结 145 | 1. 初始化 IDT, tvinit 256 vectors.S 146 | 2. 加载 IDTR, main.c -> mpinit -> idtinita 147 | 3. int 语义 148 | - int 0x40 149 | - vector64 150 | - alltraps 准备 trampframe 151 | - trap(tf) 152 | - tf.trapno => syscall() 153 | -------------------------------------------------------------------------------- /42-exec-syscall.org: -------------------------------------------------------------------------------- 1 | #+TITLE: exec 系统调用流程分析 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-17 Fri> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | #+PROPERTY: header-args:sh :results output :dir ../../study/os/xv6-public 8 | 9 | * syscall 到 exec 调用 10 | 1. syscall() 处理系统调用 11 | - 获取当前执行进程 12 | - 从系统调用表 syscalls 中调用系统调用函数 sys_exec 13 | 2. sys_exec 14 | - 检查, 并从栈上获取参数 15 | - 命令行路径 path 16 | - 获取 argv 17 | - 调用 exec 18 | 3. exec 19 | #+BEGIN_EXAMPLE 20 | (gdb) p path 21 | $3 = 0x1c "/init" 22 | (gdb) p argv 23 | $4 = {0x1c "/init", 0x0 } 24 | #+END_EXAMPLE 25 | 26 | * exec() 系统调用 27 | #+BEGIN_SRC c 28 | void exec(init, argv) 29 | #+END_SRC 30 | 31 | 1. 创建用户地址空间 32 | 2. 从磁盘加载 path 文件, 到内存中 33 | - 打开文件, namei 34 | - 申请页目录 setupkvm 35 | - 读取 ELF 头, readi 36 | #+BEGIN_SRC sh :results output :exports both 37 | readelf -h _init 38 | #+END_SRC 39 | 40 | #+RESULTS: 41 | #+begin_example 42 | ELF Header: 43 | Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 44 | Class: ELF32 45 | Data: 2's complement, little endian 46 | Version: 1 (current) 47 | OS/ABI: UNIX - System V 48 | ABI Version: 0 49 | Type: EXEC (Executable file) 50 | Machine: Intel 80386 51 | Version: 0x1 52 | Entry point address: 0x0 53 | Start of program headers: 52 (bytes into file) 54 | Start of section headers: 14252 (bytes into file) 55 | Flags: 0x0 56 | Size of this header: 52 (bytes) 57 | Size of program headers: 32 (bytes) 58 | Number of program headers: 2 59 | Size of section headers: 40 (bytes) 60 | Number of section headers: 18 61 | Section header string table index: 17 62 | #+end_example 63 | - 解析代码段, allocuvm/loaduvm/readi 64 | #+BEGIN_SRC sh :results output :exports both 65 | readelf -l _init 66 | #+END_SRC 67 | 68 | #+RESULTS: 69 | #+begin_example 70 | 71 | Elf file type is EXEC (Executable file) 72 | Entry point 0x0 73 | There are 2 program headers, starting at offset 52 74 | 75 | Program Headers: 76 | Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align 77 | LOAD 0x000080 0x00000000 0x00000000 0x00b2c 0x00b38 RWE 0x10 78 | GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x10 79 | 80 | Section to Segment mapping: 81 | Segment Sections... 82 | 00 .text .rodata .eh_frame .data .bss 83 | 01 84 | #+end_example 85 | 3. 初始化用户堆栈 ustack 86 | - 设置参数 args, copyout 87 | - ustack 前 3 个分别是: fake pc, argc, argv pointer 88 | 4. 提交用户态,跳转用户态, switchuvm 89 | - elf.entry 90 | - pgdir 91 | - eip/esp 92 | 93 | * _init 堆栈调试 94 | 当前内核态 95 | #+BEGIN_EXAMPLE 96 | pgdir = cr3 = 0xdffe000 97 | ustack[] , len=3+32+1, addr=0x8dfffe00 98 | #+END_EXAMPLE 99 | 100 | _init 起始堆栈 dump 101 | #+BEGIN_EXAMPLE 102 | (gdb) xv-ps 103 | ptable.proc[0]: pid=1 state=4 name=init 104 | (gdb) x/8x $esp 105 | 0x2fe4: 0xffffffff 0x00000001 0x00002ff0 0x00002ff8 106 | 0x2ff4: 0x00000000 0x696e692f 0x00000074 Cannot access memory at address 0x3000 107 | #+END_EXAMPLE 108 | 109 | 目标 _init 虚拟地址空间 110 | #+BEGIN_EXAMPLE 111 | pgdir = 0x8dfbb000 112 | 113 | 0x3000 |--------------| <- sp 114 | | \0 \0 \0 t | 115 | | i n i / | <- 0x2ff8 116 | | \0 | 117 | | *argv1 | 118 | | **argv | 119 | | argc (1) | 120 | | fake pc | <- sp 0x2fe4 121 | | | 122 | | | 123 | | | 124 | | | 125 | 0x2000 |--------------| 126 | | xxxxxxxxxxxx | 127 | | xxxxxxxxxxxx | 128 | | xxxxxxxxxxxx | 129 | | xxxxxxxxxxxx | 130 | | xxxxxxxxxxxx | 131 | | xxxxxxxxxxxx | 132 | 0x1000 |--------------| 133 | | | 134 | | | 135 | | | 136 | 0x0b38 |--------------| <- end 137 | | _init | 138 | | Text & Data | 139 | 0x0 |--------------| <- entry 140 | #+END_EXAMPLE 141 | -------------------------------------------------------------------------------- /43-hw-interrupt.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 中断及设备初始化 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-20 Mon> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | #+PROPERTY: header-args:sh :results output :dir ../../study/os/xv6-public 8 | 9 | 10 | * 硬件中断 11 | 1. 主板上的设备会产生中断, 常见的中断有 12 | - 用户键盘输入 13 | - 读写磁盘存储 14 | - 时钟中断 15 | 2. xv6 需要设置中断处理程序,来保证操作系统可以正常运行 16 | - 中断处理流程和系统调用过程类似 17 | - 大体流程是:主板硬件产生一个信号,我们对设备编程产生中断,然后 CPU 接受中断 18 | 3. 可以理解中断是来自硬件的 19 | 20 | * PIC 中断 21 | 1. PIC 早期单核中断处理单元 22 | - 处理程序位于 picirq.c 23 | - 早期单个 PIC 最多支持 8 个中断,受限与设备引脚数量 24 | - 主 PIC: IRQ0 ~ IRQ7 25 | - 从 PIC: IRQ8 ~ IRQ16 26 | - inb/outb 指令配置主 PIC 产出 IRQ0 ~ IRQ7 27 | - xv6 直接关闭 PIC 28 | 2. 多核处理中断分为两个部分 29 | - lapic 30 | - ioapic 31 | 3. Local APIC 32 | - 本地 APIC, 位于单个 CPU 核心 33 | - lapicinit 34 | - 内存映射方式操作,而不是 inb/outb 35 | 4. IO APIC 36 | - I/O 系统中断 37 | - 内存映射方式操作,而不是 inb/outb 38 | - ioapicinit 39 | 40 | #+BEGIN_EXAMPLE 41 | (qemu) info lapic 42 | dumping local APIC state for CPU 0 43 | 44 | LVT0 0x00010000 active-hi edge masked Fixed (vec 0) 45 | LVT1 0x00010000 active-hi edge masked Fixed (vec 0) 46 | LVTPC 0x00010000 active-hi edge masked Fixed (vec 0) 47 | LVTERR 0x00000033 active-hi edge Fixed (vec 51) 48 | LVTTHMR 0x00010000 active-hi edge masked Fixed (vec 0) 49 | LVTT 0x00020020 active-hi edge periodic Fixed (vec 32) 50 | Timer DCR=0xb (divide by 1) initial_count = 10000000 current_count = 7172266 51 | SPIV 0x0000013f APIC enabled, focus=off, spurious vec 63 52 | ICR 0x00088500 physical level de-assert all-self 53 | ICR2 0x00000000 54 | ESR 0x00000000 55 | ISR (none) 56 | IRR 32 57 | 58 | APR 0x00 TPR 0x00 DFR 0x0f LDR 0x00 PPR 0x00 59 | (qemu) info pic 60 | pic1: irr=40 imr=ff isr=00 hprio=0 irq_base=70 rr_sel=0 elcr=0c fnm=0 61 | pic0: irr=15 imr=ff isr=00 hprio=0 irq_base=08 rr_sel=0 elcr=00 fnm=0 62 | ioapic0: ver=0x20 id=0x00 sel=0x00 63 | pin 0 0x0000000000010000 dest=0 vec=0 active-hi edge masked fixed physical 64 | pin 1 0x0000000000010000 dest=0 vec=0 active-hi edge masked fixed physical 65 | pin 2 0x0000000000010000 dest=0 vec=0 active-hi edge masked fixed physical 66 | pin 3 0x0000000000010000 dest=0 vec=0 active-hi edge masked fixed physical 67 | pin 4 0x0000000000010000 dest=0 vec=0 active-hi edge masked fixed physical 68 | pin 5 0x0000000000010000 dest=0 vec=0 active-hi edge masked fixed physical 69 | pin 6 0x0000000000010000 dest=0 vec=0 active-hi edge masked fixed physical 70 | pin 7 0x0000000000010000 dest=0 vec=0 active-hi edge masked fixed physical 71 | pin 8 0x0000000000010000 dest=0 vec=0 active-hi edge masked fixed physical 72 | pin 9 0x0000000000010000 dest=0 vec=0 active-hi edge masked fixed physical 73 | pin 10 0x0000000000010000 dest=0 vec=0 active-hi edge masked fixed physical 74 | pin 11 0x0000000000010000 dest=0 vec=0 active-hi edge masked fixed physical 75 | pin 12 0x0000000000010000 dest=0 vec=0 active-hi edge masked fixed physical 76 | pin 13 0x0000000000010000 dest=0 vec=0 active-hi edge masked fixed physical 77 | pin 14 0x0000000000010000 dest=0 vec=0 active-hi edge masked fixed physical 78 | pin 15 0x0000000000010000 dest=0 vec=0 active-hi edge masked fixed physical 79 | pin 16 0x0000000000010000 dest=0 vec=0 active-hi edge masked fixed physical 80 | pin 17 0x0000000000010000 dest=0 vec=0 active-hi edge masked fixed physical 81 | pin 18 0x0000000000010000 dest=0 vec=0 active-hi edge masked fixed physical 82 | pin 19 0x0000000000010000 dest=0 vec=0 active-hi edge masked fixed physical 83 | pin 20 0x0000000000010000 dest=0 vec=0 active-hi edge masked fixed physical 84 | pin 21 0x0000000000010000 dest=0 vec=0 active-hi edge masked fixed physical 85 | pin 22 0x0000000000010000 dest=0 vec=0 active-hi edge masked fixed physical 86 | pin 23 0x0000000000010000 dest=0 vec=0 active-hi edge masked fixed physical 87 | IRR (none) 88 | Remote IRR (none) 89 | (qemu) 90 | #+END_EXAMPLE 91 | 92 | * 中断初始化 93 | 1. Local APIC 初始化 lapicinit 94 | 2. 时钟中断 [[file:../../study/os/xv6-public/lapic.c::lapicw(TICR, 10000000);]] 95 | 3. PIC 关闭 [[file:../../study/os/xv6-public/picirq.c::picinit(void)]] 96 | 4. IO APIC 初始化 ioapicinit 97 | 5. console 初始化 consoleinit 98 | 6. 键盘中断 [[file:../../study/os/xv6-public/console.c::ioapicenable(IRQ_KBD, 0);]] 99 | 7. UART 串口通信协议 uartinit 100 | - COM1 设置: baud, data bits, stop bit, parity 101 | - 开启 COM1 102 | 8. IDE 磁盘中断 103 | -ideinit enable IRQ_IDE 104 | - SCSI/SATA 105 | -------------------------------------------------------------------------------- /44-multi-core-boot.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 多处理器启动逻辑 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-20 Mon> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | #+PROPERTY: header-args:sh :results output :dir ../../study/os/xv6-public 8 | 9 | * 多处理器启动流程 10 | 1. BSP BootStrap Processor 11 | - 主启动 core 12 | 2. AP Application Processor 13 | - 从启动 core 14 | 3. BSP 作为主启动首先启动 15 | - 跳转实模式 16 | - 初始化 17 | - 读取 AP 代码 jmp 语句 18 | + 0x8000 19 | 4. BSP 设置好基础环境后,再拉起 AP 20 | - 开启 APIC 21 | - INIT => APs 22 | + 初始化 core 23 | - STARTUP => APs 24 | + AP 入口点 0x8000 25 | - Print apicid RED 26 | 5. AP 设置各自的环境,最后达到所有处理器都启动 27 | - AP => 0x8000 28 | - Print apicid(12) => 2 GREEN 29 | 30 | * mpinit 信息收集 31 | mp.h 配置头文件 [[file:../../study/os/xv6-public/mp.h]] 32 | 1. volatile uint *lapic 33 | 34 | 参考 Intel64 手册 volume 3 - chapter 11 ADVANCED PROGRAMMABLE INTERRUPT CONTROLLER (APIC) 35 | 36 | 1. CPU 需要路由中断到处理查询(handler) 37 | 2. APIC 的目的是把中断传递给多个 CPU. 分成 LAPIC 和 IOAPIC 38 | 3. LAPIC 和 IOAPIC 的寄存器都是一块内存地址, 对应实现文件中有定义 39 | - [[file:../../study/os/xv6-public/ioapic.c]] 0xfec00000 40 | - [[file:../../study/os/xv6-public/lapic.c]] 0xfee00000 41 | 4. 现代计算机它通常由两个部分组成 42 | - LAPIC : Local APIC 本地高级可编程中断控制器 43 | 1) 每个 CPU 都有 LAPIC 44 | 2) LAPIC 里面有一些寄存器, 定时器 45 | 3) LAPIC 还有 2 条 IRQ 线 LINT0 和 LINT1 46 | - IOAPIC : I/O 高级可编程中断控制器 47 | - IOAPIC 通常在所谓的南桥, 负责接收外部 IO 设备发送来的中断 48 | - 例如:键盘字符输入 49 | 5. xv6 只处理 APIC 和 IOAPIC, 而忽略 PIC 50 | 51 | LAPIC 初始化 52 | #+BEGIN_SRC c 53 | volatile uint *lapic; // Initialized in mp.c 54 | #+END_SRC 55 | 56 | * 中断流程 57 | 1. 一个 CPU 给其他 CPU 发送中断的时候, 就在自己的 ICR 中, 放中断向量和目标 LAPIC 58 | ID, 然后通过总线发送到对应 LAPIC 59 | 2. 目标 LAPIC 根据自己的 LVT(Local Vector Table) 来对不同的中断进行处理. 60 | 3. 处理完了写 EOI 表示处理完了 61 | -------------------------------------------------------------------------------- /45-disk-driver.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 磁盘驱动 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-21 Tue> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | #+PROPERTY: header-args:sh :results output :dir ../../study/os/xv6-public 8 | 9 | * 说明 10 | 1. driver hardware 11 | 2. disk/memory/mouse/keyboard 12 | 13 | * 思路 14 | 1. idequeue 缓冲队列 15 | - 磁盘慢, r/w 发送请求 16 | 2. 响应, 中断响应函数 17 | 3. struct buf 表示一块数据 18 | 1) prev/next 双向链表 19 | 2) qnext 请求缓冲队列 20 | 3) data 21 | + BSIZE 512 22 | + xv6 1 data = 1 sector, 1 data = N sector 23 | 4) sector 扇区 24 | + SECTOR_SIZE 512 25 | 4. iderw 26 | - idestart 发送磁盘请求 r/w 27 | - r/w 28 | - flag dirty write 29 | - otherwise read 30 | 5. ideintr 31 | - 中断响应 insl 0x1f0 32 | 6. read 33 | - idestart read_cmd 34 | - ideintr insl 0x1f0 35 | 7. write 36 | - idestart write_cmd && outsl 0x1f0 37 | - ideintr b->flags &= ~B_DIRTY 38 | 39 | 查看 IDE 控制器 40 | #+BEGIN_EXAMPLE 41 | (qemu) info pci 42 | Bus 0, device 0, function 0: 43 | Host bridge: PCI device 8086:1237 44 | PCI subsystem 1af4:1100 45 | id "" 46 | Bus 0, device 1, function 0: 47 | ISA bridge: PCI device 8086:7000 48 | PCI subsystem 1af4:1100 49 | id "" 50 | Bus 0, device 1, function 1: 51 | IDE controller: PCI device 8086:7010 52 | PCI subsystem 1af4:1100 53 | BAR4: I/O at 0xc040 [0xc04f]. 54 | id "" 55 | Bus 0, device 1, function 3: 56 | Bridge: PCI device 8086:7113 57 | PCI subsystem 1af4:1100 58 | IRQ 9, pin A 59 | id "" 60 | .... 61 | #+END_EXAMPLE 62 | 63 | * 磁盘设计思路 iderw 64 | 1. 磁盘的访问相对于 CPU 的指令计算速度来说非常慢 65 | 2. ide 驱动维护一个 idequeue 的缓存队列 66 | 3. 通过 iderw 来同步 idequeue 的队列到磁盘中 67 | 4. iderw 将缓冲区 b 送到队列的末尾 68 | - 如果这个缓冲区在队首,iderw 通过 idestart 将它送到磁盘上 69 | - 否则,一个缓冲区被开始处理当且仅当它前面的缓冲区被处理完 70 | 71 | * 实际情况 72 | 1. 想要完美的支持所有的设备需要投入大量的工作 73 | 2. 这是因为各种各样的设备有各种各样的特性 74 | 3. 设备和驱动之间的协议有时会很复杂 75 | 4. 在很多操作系统当中,各种驱动合起来的代码数量要比系统内核的数量更多 76 | -------------------------------------------------------------------------------- /51-locking.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 同步与锁 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-23 Thu> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | #+PROPERTY: header-args:sh :results output :dir ../../study/os/xv6-public 8 | 9 | 10 | * 数据竞争 11 | count 自增 12 | 1. 无锁 13 | 2. 有锁 14 | 15 | * 汇编指令执行简述 16 | #+BEGIN_SRC asm 17 | add $0x1,%edx 18 | inc $edx 19 | #+END_SRC 20 | 21 | 组成原理 / 体系结构 22 | 23 | x86 汇编 CPU 执行流程分析 24 | 1. Fetch Instruction 取指令 25 | 2. Decode Instruction 解码 26 | 3. Memory Access 访问内存 27 | 4. Execute 执行 28 | 5. Write Back 回写 29 | 30 | 1. 每个操作 一个指令周期 31 | 2. 流水线 pipeline 32 | 33 | count = 4 34 | 35 | t3: count=4 36 | t4: count=5 CPU1 写入 37 | t5: count=5 CPU2 写入 38 | t6: count=5 39 | ... 40 | #+BEGIN_EXAMPLE 41 | t: 0 1 2 3 4 5 6 42 | ----------------------------- 43 | CPU1: |F | | | | | | | 44 | | |D | | | | | | 45 | | | |M/4| | | | | 46 | | | | |E/5| | | | 47 | | | | | |W/5| | | 48 | | | | | | | | | 49 | ----------------------------- 50 | CPU2: | |F | | | | | | 51 | | | |D | | | | | 52 | | | | |M/4| | | | 53 | | | | | |E/5| | | 54 | | | | | | |W/5| | 55 | | | | | | | | | 56 | ----------------------------- 57 | #+END_EXAMPLE 58 | 59 | - 指令不是原子的 60 | - lock 在 x86 原子 61 | - atomic instruction 62 | #+BEGIN_EXAMPLE 63 | lock addl $0x1,0x0(%eax) 64 | #+END_EXAMPLE 65 | 66 | lock 67 | #+BEGIN_EXAMPLE 68 | t: 0 1 2 3 4 5 6 69 | ----------------------------- 70 | CPU1: |F | | | | | | | 71 | | |D | | | | | | 72 | | | |M/4| | | | | 73 | | | | |E/5| | | | 74 | | | | | |W/5| | | 75 | | | | | | | | | 76 | ----------------------------- 77 | CPU2: | | | | | |F | | | | | | 78 | | | | | | | |D | | | | | 79 | | | | | | | | |M/4| | | | 80 | | | | | | | | | |E/5| | | 81 | | | | | | | | | | |W/5| | 82 | | | | | | | | | | | | | 83 | ----------------------------- 84 | #+END_EXAMPLE 85 | 86 | * Spin Lock 自旋锁 87 | [[file:../../study/os/xv6-public/spinlock.h]] 88 | #+BEGIN_SRC c 89 | // Mutual exclusion lock. 90 | struct spinlock { 91 | uint locked; // Is the lock held? 92 | 93 | // For debugging: 94 | char *name; // Name of lock. 95 | struct cpu *cpu; // The cpu holding the lock. 96 | uint pcs[10]; // The call stack (an array of program counters) 97 | // that locked the lock. 98 | }; 99 | #+END_SRC 100 | 101 | #+BEGIN_SRC sh :results output :exports both 102 | rg -n initlock $PWD | sort 103 | #+END_SRC 104 | 105 | #+RESULTS: 106 | #+begin_example 107 | /data/gitana/study/os/xv6-public/bio.c:43: initlock(&bcache.lock, "bcache"); 108 | /data/gitana/study/os/xv6-public/console.c:289: initlock(&cons.lock, "console"); 109 | /data/gitana/study/os/xv6-public/defs.h:130:void initlock(struct spinlock*, char*); 110 | /data/gitana/study/os/xv6-public/file.c:22: initlock(&ftable.lock, "ftable"); 111 | /data/gitana/study/os/xv6-public/fs.c:176: initlock(&icache.lock, "icache"); 112 | /data/gitana/study/os/xv6-public/ide.c:55: initlock(&idelock, "ide"); 113 | /data/gitana/study/os/xv6-public/kalloc.c:34: initlock(&kmem.lock, "kmem"); 114 | /data/gitana/study/os/xv6-public/log.c:60: initlock(&log.lock, "log"); 115 | /data/gitana/study/os/xv6-public/pipe.c:37: initlock(&p->lock, "pipe"); 116 | /data/gitana/study/os/xv6-public/proc.c:26: initlock(&ptable.lock, "ptable"); 117 | /data/gitana/study/os/xv6-public/sleeplock.c:16: initlock(&lk->lk, "sleep lock"); 118 | /data/gitana/study/os/xv6-public/spinlock.c:13:initlock(struct spinlock *lk, char *name) 119 | /data/gitana/study/os/xv6-public/trap.c:26: initlock(&tickslock, "time"); 120 | #+end_example 121 | 122 | 1. acquire 获取锁 123 | - CAS: Compare And Swap 124 | - xchg: ~lock; xchgl %0, %1~ 125 | - pushcli() 关中断 126 | 2. release 释放锁 127 | - popcli() 开中断 128 | -------------------------------------------------------------------------------- /52-sleeplock.org: -------------------------------------------------------------------------------- 1 | #+TITLE: sleeplock 与死锁 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-26 Sun> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | #+PROPERTY: header-args:sh :results output :dir ../../study/os/xv6-public 8 | 9 | * Spin Lock 回顾 10 | 1. acquire 11 | 2. release 12 | 3. 并发编程 13 | - mutex 14 | - atomic 15 | 16 | * Sleep Lock 睡眠锁 17 | 1. 磁盘 IDE 读写 18 | - 慢 19 | - 依赖 interrupt 20 | 2. disk 竞争 21 | - spinlock ??? 22 | + while loop 23 | 3. sleeplock 睡眠锁 24 | - spinlock 内涵 25 | - locked 记录是否上锁 26 | 27 | [[file:../../study/os/xv6-public/sleeplock.h]] 28 | 29 | #+BEGIN_SRC sh :results output :exports both 30 | rg -n initsleeplock $PWD | sort 31 | #+END_SRC 32 | 33 | #+RESULTS: 34 | : /data/gitana/study/os/xv6-public/bio.c:51: initsleeplock(&b->lock, "buffer"); 35 | : /data/gitana/study/os/xv6-public/defs.h:139:void initsleeplock(struct sleeplock*, char*); 36 | : /data/gitana/study/os/xv6-public/fs.c:178: initsleeplock(&icache.inode[i].lock, "inode"); 37 | : /data/gitana/study/os/xv6-public/sleeplock.c:14:initsleeplock(struct sleeplock *lk, char *name) 38 | 39 | * Sleep Lock 死锁场景 40 | 1. 死锁 deadlock 41 | 2. 经典案例 42 | - 生产者/消费者 43 | - 哲学家 44 | #+BEGIN_EXAMPLE 45 | S: Sleep Lock 46 | X: Spin Lock 47 | a: acquire 48 | r: release 49 | 50 | 分析流程: 51 | TIME 52 | --------------------------- Long Time ---------------------------> 53 | T1:(S)| (S/a) | (S/r) ... (S/a) | 54 | (X)| | ... (X/a) | 55 | | | ... x | 56 | | | ... x | 57 | | | ... x | 58 | | | ... x | 59 | T2:(S)| | ... (S/a) | 60 | (X)| |(X/a) ... x | 61 | | | ... x | 62 | | | ... x | 63 | | | ... x | 64 | | | ... x | 65 | 66 | T1: hold S, wait X 67 | T2: hold X, wait S 68 | #+END_EXAMPLE 69 | 70 | * Sleep Lock 实现 71 | 1. 获取锁 acquiresleep 72 | 2. 释放锁 releasesleep 73 | 3. acquiresleep/releasesleep 74 | - 包裹 spinlock: acquire/release 75 | -------------------------------------------------------------------------------- /53-scheduling.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 调度、睡眠与唤醒 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-23 Thu> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | #+PROPERTY: header-args:sh :results output :dir ../../study/os/xv6-public 8 | 9 | * 多路复用 Multiplexing 10 | 场景: 11 | 当一个进程等待磁盘请求时, xv6 使之进入睡眠状态, 12 | 然后调度执行另一个进程。 13 | 另外,当一个进程耗尽了它在处理器上运行的时间片(10w 指令)后, 14 | xv6 使用时钟中断强制它停止运行,这样调度器才能调度运行其他进程 15 | 16 | 1. 简单地使用时钟中断处理程序来驱动上下文切换 17 | - p1 时间长, timer, yield 18 | 2. 可能出现多个 CPU 同时切换进程的情况 19 | - 那么我们必须使用一个带锁的方案来避免竞争 20 | 3. 进程退出时必须释放其占用内存与资源, 21 | - 但由于它本身在使用自己的资源(譬如其内核栈) 22 | - 所以不能由该进程本身释放其占有的所有资源 23 | - 父进程 24 | - parent 25 | - child -> init 26 | 27 | * sched()/scheduler 28 | sched() 主要目的是要将进程上下文切换到 mycpu()->scheduler 29 | 1. 通常一段程序代码的执行被称为 thread 30 | - 如果在内核态执行,为 kernel thread 31 | - 否则在用户态执行,为 user thread 32 | 2. sched() 调用的条件是: 33 | - 持有 ptable.lock 34 | - 进程释放其他持有的锁 35 | - 并且修改了 p->state 状态 36 | - 调用 sched() 来调度进程 37 | 3. 传递 intena 位,因为 intena 是 thread 的属性,而非 CPU 的 38 | 39 | * yield 40 | [[file:../../study/os/xv6-public/proc.c::yield(void)]] 41 | 42 | 1. yield 获取 ptable.lock 43 | 2. 然后通过调用 sched() 主动释放 CPU 44 | 45 | * 调度中锁循环 46 | 1. 在遍历 ptable 是先获取 ptable.lock 47 | 2. 如果没有可调度的进程 (RUNNABLE), 则 Round Robin 结束后释放锁 48 | 3. 否则出现上下文切换 swtch 49 | - 这时会在 forkret 中释放 ptable.lock 锁 50 | - 后续如果进程调用 ~sched()~, 根据之前的推导则又持有锁 51 | + 通常在 ~yield()~ 中获取 52 | + 也有在 ~sleep()~ 中获取, 通过 spinlock 转换成 ptable.lock 53 | - 如此反复,不会出现死锁 54 | 55 | ptable.lock 56 | k => u: scheduler (acquire) = swtch => forkret (release) 57 | u => k: yield (acquire) => sched(swtch) => (release) 58 | swtch 59 | 60 | * Sleep & Wakeup 61 | sleep 和 wakeup 是一对系统调用,其工作方式如下 62 | 1. sleep 设置 p->state = SLEEPING, 然后调用 sched 释放 CPU 63 | - 让进程在任意的 chan 上休眠,称之为等待队列(wait channel) 64 | + chan 一般就是资源的内核地址 65 | - disk I/O 66 | - console 67 | + 用于唤醒时查找到对于 chan 上的进程, Multiplexing 68 | - sleep 需要设置一个等待的锁 lk 69 | + 锁交换 lk <-> ptable.lock 70 | + 如果 lk == ptable.lock 跳过锁交换过程 71 | - sleep 让调用进程休眠,释放所占 CPU 72 | 2. wakeup(chan) 则唤醒在 chan 上休眠的所有进程 73 | - 让他们的 sleep 调用返回 74 | - 如果没有进程在 chan 上等待唤醒,wakeup 就什么也不做 75 | -------------------------------------------------------------------------------- /54-proc-lifecycle.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 进程生命周期 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-24 Fri> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | #+PROPERTY: header-args:sh :results output :dir ../../study/os/xv6-public 8 | 9 | 10 | * init 进程 11 | 1. init 第一个进程 12 | - initproc pid=1 13 | 14 | * fork -> exec -> exit 15 | 进程正常退出 16 | 1. fork() 17 | - 申请 sturct proc 18 | - 复制父进程数据 19 | + pgdir 20 | + tf 21 | + p->parent 22 | - p->tf->eax = 0, 子进程返回 0 23 | 2. exec() 替换地址空间 sh(11) => ls(11) 24 | - 参考之前的讲解 25 | 3. exit() 26 | - ls 有子进程 a, b, c 27 | + a, b, c 的父进程设置成 init 28 | + p->parent = initproc 29 | - curpoc->state = ZOMBIE 30 | - sched() 让位 CPU 调度 31 | 32 | * wait 33 | ~wait()~ 等待子进程 34 | - ZOMBIE 35 | + 标记子进程退出了 36 | + 告知父进程可以进行回收资源 37 | - 回收子进程的 kstack 38 | - 内核页面 vm 39 | 40 | * kill 41 | 杀死进程 42 | 1. kill() p->killed = 1 43 | 2. trap() 检测 mycpu()->killed 标识, 然后调用 exit() 44 | -------------------------------------------------------------------------------- /55-pipe.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 管道 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-24 Fri> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | #+PROPERTY: header-args:sh :results output :dir ../../study/os/xv6-public 8 | 9 | 10 | * pipe 概念介绍 11 | #+BEGIN_SRC sh 12 | ls | wc 13 | #+END_SRC 14 | 15 | #+RESULTS: 16 | : 185 185 1497 17 | 18 | ls(9) 19 | - stdin 20 | - stdout xxx 21 | wc(10) 22 | - stdin yyy 23 | - stdout 24 | 25 | 26 | * struct pipe 27 | [[file:../../study/os/xv6-public/pipe.c::struct spinlock lock;]] 28 | 29 | 管道结构体数据结构定义 30 | 1. p->lock 锁 31 | 2. 读写的文件描述符: readopen, writeopen 32 | 3. 读写字节数: nread, nwrite 33 | 4. data 数据缓冲器, 循环缓冲 34 | - 空 nwrite == nread 35 | - 满 nwrite == nread+PIPESIZE 36 | 37 | * 管道读写 38 | 1. pipewrite 39 | - 获取 p->lock 40 | - 尝试将 addr 的数据写入 p->data 41 | - 唤醒读进程 42 | 2. piperead 43 | - 获取 p->lock 44 | - 尝试读取数据 p->data, 写入 addr 处 45 | - 唤醒写进程 46 | 3. pipe 实现中使用不同的读写 chan , 47 | - 读 chan 是 p->nread 48 | - 写 chan 是 p->nwrite 49 | - 目的是提高效率 50 | - 防止 for 循环中造成读写进程等待同一个 chan 的争抢 51 | 52 | 53 | pipe 读写调试过程: 54 | #+BEGIN_EXAMPLE 55 | E1: w nr=0 nw=1 56 | E2: w nr=0 nw=1 57 | ... 58 | E6: w nr=0 nw=6 59 | E7: r nr=0 nw=6 60 | E8: w nr=6 nw=7 61 | E9: r nr=6 nw=8 62 | ... 63 | #+END_EXAMPLE 64 | -------------------------------------------------------------------------------- /61-fs-overview.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 文件系统概览 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-28 Tue> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | #+PROPERTY: header-args:sh :results output :dir ../../study/os/xv6-public 8 | 9 | 10 | * 文件系统 11 | 1. 解决 on-disk 存储结构 12 | - 树形结构 13 | - 文件/目录 14 | 2. crash recovery 崩溃恢复 15 | - 突然断电后重启后依然可以正常工作 16 | - xv6 17 | 3. 多进程同时读写 18 | - 协调并发操作问题 19 | 4. 解决读写磁盘速度慢的问题 20 | - 通过 in-memory cache 来提高系统的读写效率 21 | 22 | #+BEGIN_SRC sh 23 | lsblk -f 24 | #+END_SRC 25 | 26 | #+RESULTS: 27 | #+begin_example 28 | NAME FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS 29 | sda 30 | ├─sda1 31 | ├─sda2 ext4 1.0 e956e553-25fe-4a85-97f8-f084e5eb25d0 1.5G 13% /boot 32 | └─sda3 LVM2_member LVM2 001 J7BATi-xwv8-jjeE-D3gl-LWLr-1RIc-WheXOt 33 | ├─ubuntu--vg-ubuntu--lv ext4 1.0 86b5ef6e-95f5-48c4-b7d0-7ccc32dad828 37.9G 56% / 34 | └─ubuntu--vg-data--lv ext4 1.0 1a1d35fe-cfc5-4ae6-a0bf-b7cdbef6d4e2 46.8G 83% /data 35 | sdb 36 | └─sdb1 ext4 1.0 14dadaca-04d2-4f1f-9565-b40e1437df32 27.4G 2% /mnt/lfs 37 | sdc 38 | sr0 39 | #+end_example 40 | 41 | * xv6 文件系统逻辑结构 42 | #+BEGIN_SRC ditaa :exports results :file ./img/fs-arch.png :cmdline -s 2 43 | +-----------------+ 44 | | File descriptor | 45 | +-----------------+ 46 | | Pathname | 47 | +-----------------+ 48 | | Directory | 49 | +-----------------+ 50 | | inode | 51 | +-----------------+ 52 | | Logging | 53 | +-----------------+ 54 | | Buffer cache | 55 | +-----------------+ 56 | | Disk | 57 | +-----------------+ 58 | #+END_SRC 59 | 60 | #+RESULTS: 61 | [[file:./img/fs-arch.png]] 62 | 63 | 1. xv6-fs 七层逻辑结构 64 | - File descriptor 65 | - Pathname 66 | - Directory 67 | - inode 68 | - Logging 69 | - Buffer cache 70 | - Disk 71 | 2. 读代码/研究 72 | - 自底而上 / 选这个 73 | - 自顶而下 74 | 3. Disk 读写磁盘, 直接操作磁盘, ide.c 75 | 4. Buffer Cache 管理 buf 结构 76 | - 与磁盘同步数据 77 | - 确保多进程同时只能有一个进程进行操作 78 | 5. Logging 允许多个 block 同时操作 79 | - 多个 block 同时操作形成 transaction 80 | 6. inode 提供文件操作, 每个 inode 有唯一编号 inum 81 | - 一个 inode 保护多个 block 82 | - 这样的 inode 可表示一个文件 83 | - unix 84 | 7. Directory 实现目录结构 dirent 85 | - 它可以表示一个目录 86 | - 本质上是 inode 序列 87 | - name 记录目录名称 88 | 8. Pathname 表示一个文件路径 ~/etc/my.cnf~ 89 | 9. File descriptor 是文件描述符 90 | - read(fd,...) 91 | + stdin(0), stdout(1), stderr(2) 92 | - 标记操作系统的资源: pipe/device/files 等 93 | 94 | * xv6 文件系统物理结构 95 | 96 | #+BEGIN_SRC ditaa :exports results :file ./img/fs-phy.png :cmdline -s 2 97 | +------+-------+----------+-----------+--------+----------+ 98 | | boot | super | log ... | inode ... | bitmap | data ... | 99 | +------+-------+----------+-----------+--------+----------+ 100 | 0 1 2 101 | #+END_SRC 102 | 103 | #+RESULTS: 104 | [[file:./img/fs-phy.png]] 105 | 106 | 107 | 1. boot 没有使用,一般用于装启动引导 108 | - 扇区 sector 512/ boot / MBR 109 | 2. super 时 superblock 记录文件的元信息 110 | 3. 接着是第 2 个扇区, 记录了若干 log 111 | - nlog 112 | 4. 然后 inode, bitmap 113 | 5. data 114 | -------------------------------------------------------------------------------- /62-buffer.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Disk 和 Buffer 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-28 Tue> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | #+PROPERTY: header-args:sh :results output :dir ../../study/os/xv6-public 8 | 9 | * mkfs 10 | [[file:../../study/os/xv6-public/mkfs.c]] 11 | 12 | #+BEGIN_SRC sh :exports both 13 | make fs.img | grep nmeta 14 | #+END_SRC 15 | 16 | #+RESULTS: 17 | : nmeta 59 (boot, super, log blocks 30 inode blocks 26, bitmap blocks 1) blocks 941 total 1000 18 | 19 | * Disk 层回顾 20 | 1. ide.c 21 | 22 | [[file:img/idequeue.png]] 23 | 24 | * Buffer Cache 25 | 26 | 1. 同步对磁盘的访问 27 | - 目的是对于每一个块,同一时间只有一份拷贝放在内存中 28 | - 并且只有一个内核线程使用这份拷贝 29 | 2. 缓存常用的块以提升性能 30 | - bcache 管理 31 | - LRU 最近访问缓存 32 | 33 | [[file:img/bcache.png]] 34 | 35 | * bio.c 代码分析 36 | 1. bget 获取一个 (dev,blockno) 对于的 buf 37 | - 如果 cached, return b 38 | - 否则 return 一个未使用的 buf 39 | 2. bread 40 | - 做检查 b->flags & B_VALID) == 0 41 | - 调用 iderw 42 | 3. bwrite 43 | - 置 B_DIRTY 44 | - 调用 iderw 写入数据 45 | 4. brelse 46 | - 获取锁 bcache.lock 47 | - refcnt-- 48 | - 将当前的 block 移到 LRU 的前面 49 | -------------------------------------------------------------------------------- /63-logging.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Log 层 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-11-28 Tue> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | #+PROPERTY: header-args:sh :results output :dir ../../study/os/xv6-public 8 | 9 | * Log 10 | 1. 简单的日志系统来解决文件操作过程当中崩溃所导致的问题 11 | - 一个系统调用 read/write 并不直接导致对磁盘上文件系统的写操作 12 | - xv6 会把一个对磁盘写操作的描述包装成一个日志写在磁盘中 13 | - 最后写入一个特殊的提交记录到磁盘上 14 | - 所有写成果后日志就会删除 15 | 2. 为什么日志可以保证 xv6 崩溃恢复 16 | - 情况一: 如果崩溃发生在操作提交之前 17 | + 那么磁盘上的日志文件就不会被标记为已完成 18 | + 恢复系统的代码就会忽视它 19 | - 情况二: 如果崩溃发生在操作提交之后 20 | + 恢复程序会重演所有的写操作 21 | + 可能会重复之前已经进行了的对磁盘文件系统的写操作 22 | - 写提交日志块对 xv6 的 logging 层来说是原子操作,所以只可能出现上述两种情况 23 | 24 | #+BEGIN_SRC ditaa 25 | +------+-------+-----------------+-----------+--------+----------------------------+ 26 | | boot | super | log1, log2 ... | inode ... | bitmap | data, data1, ... data2 ... | 27 | +------+-------+-----------------+-----------+--------+----------------------------+ 28 | 0 1 2 29 | #+END_SRC 30 | 31 | 情景一: write: N 32 | #+BEGIN_SRC ditaa 33 | +------+-------+-----------------+-----------+--------+----------------------------------+ 34 | | boot | super | log1, log2 ... | inode ... | bitmap | data, data1(y), ... data2(n) ... | 35 | +------+-------+-----------------+-----------+--------+----------------------------------+ 36 | 0 1 2 37 | #+END_SRC 38 | 39 | 情景二: write: Y 40 | #+BEGIN_SRC ditaa 41 | +------+-------+-----------------+-----------+--------+----------------------------------+ 42 | | boot | super | log1, log2 ... | inode ... | bitmap | data, data1(y), ... data2(y) ... | 43 | +------+-------+-----------------+-----------+--------+----------------------------------+ 44 | 0 1 2 45 | #+END_SRC 46 | 47 | * 代码分析 48 | 1. 调用 log 操作的序列 begin_op/end_op 49 | - begin_op() 开启事务 50 | - end_op() 提交事务 51 | #+BEGIN_SRC c 52 | begin_op(); 53 | // ... 54 | end_op(); 55 | #+END_SRC 56 | 2. outstanding 支持多个系统调用同时提交 group commit 57 | 3. 日志结构体 struct log 58 | - 包含多个 block/ log header 59 | 4. log 的物理结构 60 | #+BEGIN_EXAMPLE 61 | // header block, containing block #s for block A, B, C, ... 62 | // block A 63 | // block B 64 | // block C 65 | // ... 66 | #+END_EXAMPLE 67 | 5. recover_from_log 完成崩溃恢复 68 | - redo 重做 69 | 6. commit 提交 70 | - 写 log 71 | - write_head 写入 log header 72 | - install_trans: 回写 log 到 data 73 | - log.lh.n = 0/write_head => 清理 log header 74 | 75 | #+BEGIN_SRC ditaa 76 | +------+-------+-------------- 77 | | boot | super | log header 78 | +------+-------+-------------- 79 | 0 0x200 0x400 80 | #+END_SRC 81 | 82 | #+BEGIN_SRC ditaa 83 | +------+-------+-----------------+-----------+--------+----------------------------------------------+ 84 | | boot | super | log1, log2, log3| inode ... | bitmap | data, data1(y), ... data2(y) ... data3(y)... | 85 | +------+-------+-----------------+-----------+--------+----------------------------------------------+ 86 | 0 1 2 87 | #+END_SRC 88 | -------------------------------------------------------------------------------- /64-inode.org: -------------------------------------------------------------------------------- 1 | #+TITLE: inode 层 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-12-03 Sun> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | #+PROPERTY: header-args:sh :results output :dir ../../study/os/xv6-public 8 | 9 | 10 | * inode 数据结构 11 | 12 | [[file:img/inode-relation.dot.png]] 13 | 14 | inode 例子 15 | #+BEGIN_EXAMPLE 16 | /tmp $ mkdir test 17 | /tmp $ cd test 18 | /tmp/test $ ls 19 | /tmp/test $ touch aaa 20 | /tmp/test $ ll -i 21 | total 0 22 | 3933261 -rw-rw-r-- 1 mes mes 0 Dec 6 15:44 aaa 23 | /tmp/test $ touch bbb 24 | /tmp/test $ ll -i 25 | total 0 26 | 3933261 -rw-rw-r-- 1 mes mes 0 Dec 6 15:44 aaa 27 | 3933263 -rw-rw-r-- 1 mes mes 0 Dec 6 15:44 bbb 28 | /tmp/test $ ln aaa ccc 29 | /tmp/test $ ls -i 30 | 3933261 aaa 3933263 bbb 3933261 ccc 31 | /tmp/test $ ll -i 32 | total 0 33 | 3933261 -rw-rw-r-- 2 mes mes 0 Dec 6 15:44 aaa 34 | 3933263 -rw-rw-r-- 1 mes mes 0 Dec 6 15:44 bbb 35 | 3933261 -rw-rw-r-- 2 mes mes 0 Dec 6 15:44 ccc 36 | /tmp/test $ stat ccc 37 | File: ccc 38 | Size: 0 Blocks: 0 IO Block: 4096 regular empty file 39 | Device: fd00h/64768d Inode: 3933261 Links: 2 40 | Access: (0664/-rw-rw-r--) Uid: ( 1000/ mes) Gid: ( 1000/ mes) 41 | Access: 2023-12-06 15:44:02.257552590 +0800 42 | Modify: 2023-12-06 15:44:02.257552590 +0800 43 | Change: 2023-12-06 15:44:23.713551331 +0800 44 | Birth: 2023-12-06 15:44:02.257552590 +0800 45 | /tmp/test $ 46 | #+END_EXAMPLE 47 | 48 | 1. dinode 49 | - type: file/directory/device 50 | - nlink link 数量 51 | - size 文件大小 52 | - addrs 数据块地址 53 | - direct 6kB (NDIRECT*BSIZE) 54 | - indirect 64kB (NINDIRECT*BSIZE) 55 | 2. inode 56 | - inum 57 | - ref 内存中引用 inode 计数 58 | 59 | * block 分配器 60 | 1. 文件和目录的内容都存放在磁盘块 block 中 61 | 2. block 都是从空闲池中获取的数据 62 | - 磁盘中有一个 block 空闲位图 freebitmap 63 | - 每一位表示一个 block 是否空闲 64 | 3. sb.size 表示文件系统块总数 65 | 4. BSIZE 66 | - 通常 block 要比 buf 大 67 | - 但是 xv6 中, 1 block = 1 buf = BSIZE = 512 68 | 5. balloc 分配一个新的磁盘块 block 69 | 6. bfree 释放一个磁盘块 70 | 71 | * inode 内存操作 72 | 1. inode 描述一个匿名的文件 73 | 2. 分配 74 | - ialloc() 分配 inode 75 | + 创建文件时调用 76 | - iput() 当 ref/link count 为零时, 清理 inode 77 | 3. iget()/iput() 78 | - ip->ref 表示 inode 的引用计数 79 | - iget() 创建 inode, 或者增加 ref 80 | - iput() 清理 inode 81 | 4. 合法性 ip->valid 82 | - ilock() 从磁盘读取 inode 数据, 并设置 ip->valid=1 83 | - iput() 当 ip->ref=0 时, 清理 ip->valid 84 | 5. bmap 85 | 86 | * inode 读写 87 | 1. readi 读取 inode 数据 88 | - iget() 获取 inode 89 | - ilock() 读取数据 90 | 2. writei 写入 inode 数据 91 | -------------------------------------------------------------------------------- /65-directory-path.org: -------------------------------------------------------------------------------- 1 | #+TITLE: directory 层和 pathname 层 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-12-08 Fri> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | #+PROPERTY: header-args:sh :results output :dir ../../study/os/xv6-public 8 | 9 | 10 | * directory 层 11 | 1. dirent 结构体 directory entry 12 | - inum 查找 inode 13 | - name 目录名 14 | 2. dirlookup 在 dp 中查找 name 对应的 inode 15 | - 如果存在, 返回 inode 16 | - 同时写入偏移 poff 17 | 3. dirlink 往 dp 中写入新的 inode 数据 18 | - inum 19 | - name 20 | 21 | * path 层 22 | 1. path 目录结构 23 | - /aa/bb/a 24 | - char * 25 | 2. namei 通过 path 查询 inode 26 | 3. nameiparent 查询上级目录 27 | 4. namex 28 | - 如果 / 开头, 跳转到根目录, 否则从当前目录开始搜索 29 | - dirlookup 递归查找是否是当前目录 30 | -------------------------------------------------------------------------------- /66-file-descriptor.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 文件描述符层 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-12-08 Fri> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | #+PROPERTY: header-args:sh :results output :dir ../../study/os/xv6-public 8 | 9 | 10 | * fd 文件描述符 11 | 1. file 数据结构 12 | - type 实现 unix 的一切皆文件 13 | 1) FD_NONE 14 | 2) FD_PIPE 管道 15 | 3) FD_INODE 16 | - T_DIR 目录 17 | - T_FILE 文件 18 | - T_DEV 设备 19 | - ref 结构体的引用计数 20 | - readable 读权限 21 | - writable 写权限 22 | - off 文件偏移 23 | 2. 全局文件表 ftable 24 | - NFILE 100 // open files per system 25 | 26 | * 文件操作 27 | [[file:../../study/os/xv6-public/file.c]] 28 | 29 | 1. filealloc/filedup/fileclose 30 | - filealloc 分配一个 ref=0 的 file struct 31 | - filedup 增加 ref 32 | - fileclose 减少 ref, 释放资源 33 | 2. fileread 读取文件内容 34 | - readable 35 | - ilock 读取 ip 的数据 36 | - readi 读取 inode 数据 37 | - iunlock 释放 ip 资源 38 | 3. filewrite 写入文件内容 39 | - writable 40 | - begin_op 41 | - ilock 读取 ip 的数据 42 | - writei 写入 inode 数据 43 | - iunlock 释放 ip 资源 44 | - end_op 45 | 46 | * 文件系统调用 sysfile.c 47 | [[file:../../study/os/xv6-public/sysfile.c]] 48 | 49 | 1. sys_link 50 | -------------------------------------------------------------------------------- /99-temp.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Temp 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-12-06 Wed> 5 | #+STARTUP: overview num indent 6 | #+OPTIONS: ^:nil 7 | 8 | * test 9 | #+BEGIN_SRC ditaa :file ./img/ditaa-dinode-struct.png :cmdline -E -s 1.5 10 | dinode 11 | +---------------+ 12 | | type | 13 | +---------------+ 14 | | major | 15 | +---------------+ 16 | | minor | 17 | +---------------+ 18 | | nlink | 19 | +---------------+ 20 | | size | 21 | +---------------+ +------+ 22 | | addr1 |---------------------->| data | 23 | +---------------+ +------+ 24 | | ... | | | 25 | +---------------+ +------+ 26 | | addr12 |---------------------->| data | 27 | +---------------+ +------+ 28 | | indirect | | | 29 | +---------------+ | | 30 | | indirect data | | 31 | +-----------> +---------+ +------+ 32 | | addr1 |------->| data | 33 | |---------| +------+ 34 | | ... | | | 35 | |---------| +------+ 36 | | addr128 |------->| data | 37 | +---------+ +------+ 38 | #+END_SRC 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | IMG_DIR := img 2 | 3 | DOTS := $(shell find $(IMG_DIR) -name *.dot) 4 | PDFS := $(DOTS:%=%.pdf) 5 | PNGS := $(DOTS:%=%.png) 6 | 7 | 8 | all: build images 9 | 10 | images: $(PDFS) $(PNGS) 11 | 12 | 13 | %.dot.pdf: %.dot 14 | dot -Tpdf $< -o $@ 15 | 16 | %.dot.png: %.dot 17 | dot -Tpng $< -o $@ 18 | 19 | build: 20 | make -C lab 21 | 22 | clean: 23 | make -C lab clean 24 | 25 | init: 26 | bear -- make -C lab 27 | 28 | publish: 29 | git push github master 30 | git push gitee master 31 | git push github --tags 32 | git push gitee --tags 33 | 34 | .PHONY: all build images clean init publish 35 | -------------------------------------------------------------------------------- /banner: -------------------------------------------------------------------------------- 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 | 您免费的点赞时 UP 主更新的动力, 都看到这儿了, 不妨来个三连吧 31 | . -------------------------------------------------------------------------------- /bin/sign.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | open(SIG, $ARGV[0]) || die "open $ARGV[0]: $!"; 4 | 5 | $n = sysread(SIG, $buf, 1000); 6 | 7 | if($n > 510){ 8 | print STDERR "boot block too large: $n bytes (max 510)\n"; 9 | exit 1; 10 | } 11 | 12 | print STDERR "boot block is $n bytes (max 510)\n"; 13 | 14 | $buf .= "\0" x (510-$n); 15 | $buf .= "\x55\xAA"; 16 | 17 | open(SIG, ">$ARGV[0]") || die "open >$ARGV[0]: $!"; 18 | print SIG $buf; 19 | close SIG; 20 | -------------------------------------------------------------------------------- /img/bcache.dot: -------------------------------------------------------------------------------- 1 | digraph bcache { 2 | // rankdir=LR; 3 | node [shape=record]; 4 | 5 | subgraph cluster_bcache { 6 | label=<bcache>; style=filled; color=white; 7 | bcache[label="lock|buf[30]|head|{*next|

*prev}"]; 8 | } 9 | // cluster_bcache -> cluster_bufs; 10 | 11 | 12 | subgraph cluster_bufs { 13 | label=<buf[30]>; style=filled; color=white; 14 | buf1[label="buf[29] |*qnext|dev|blockno|*next|

*prev"]; 15 | buf2[label="buf[28] |*qnext|dev|blockno|*next|

*prev"]; 16 | buf3[label="buf[...]|*qnext|dev|blockno|*next|

*prev"]; 17 | buf9[label="buf[0] |*qnext|dev|blockno|*next|

*prev"]; 18 | } 19 | 20 | // {rank=same buf1 buf2 buf9} 21 | 22 | buf1:n -> buf2:a [color=sienna style=solid]; 23 | buf2:n -> buf3:a [color=sienna style=solid]; 24 | buf3:n -> buf9:a [color=sienna style=solid]; 25 | buf9:n -> bcache:h [color=sienna style=solid]; 26 | bcache:n -> buf1:ne [color=sienna style=solid]; 27 | 28 | buf9:p -> buf3 [color=black style=dashed]; 29 | buf3:p -> buf2 [color=black style=dashed]; 30 | buf2:p -> buf1 [color=black style=dashed]; 31 | buf1:p -> bcache:h [color=black style=dashed]; 32 | bcache:p -> buf9:se [color=black style=dashed]; 33 | } 34 | -------------------------------------------------------------------------------- /img/bcache.dot.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jeanhwea/xv6-course/c2771ab75f2a1f5314f636f9b2056f7048fd4cfc/img/bcache.dot.pdf -------------------------------------------------------------------------------- /img/bcache.dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jeanhwea/xv6-course/c2771ab75f2a1f5314f636f9b2056f7048fd4cfc/img/bcache.dot.png -------------------------------------------------------------------------------- /img/ds1.dot: -------------------------------------------------------------------------------- 1 | digraph structs { 2 | rankdir = LR; 3 | node [shape=record, style=filled]; 4 | 5 | key [label="Key", shape=box, fillcolor="#8ec5cc"] 6 | string [label="is string", width=2, fillcolor="#f5c18e"]; 7 | hashtable [label="{key1|key2}|{value1|value2}", width=2, fillcolor="#9dd69f"] 8 | linklist [label="{ C | B | B | A}", width=2, fillcolor="#c4f8f5"] 9 | set [label="{C | B | D | A}", width=2, fillcolor="#fac4f5"] 10 | sortset [label="{C\n1 | B\n2.6 | D\n500 | A\n500}",width=2, fillcolor="#fac4d5"]; 11 | 12 | key:e -> string:f0 [minlen=2]; 13 | key:e -> hashtable:w [minlen=2]; 14 | key:e -> linklist:f0 [minlen=2]; 15 | key:e -> set:w [minlen=2]; 16 | key:e -> sortset:w [minlen=2]; 17 | } 18 | -------------------------------------------------------------------------------- /img/ds1.dot.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jeanhwea/xv6-course/c2771ab75f2a1f5314f636f9b2056f7048fd4cfc/img/ds1.dot.pdf -------------------------------------------------------------------------------- /img/ds1.dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jeanhwea/xv6-course/c2771ab75f2a1f5314f636f9b2056f7048fd4cfc/img/ds1.dot.png -------------------------------------------------------------------------------- /img/ds2.dot: -------------------------------------------------------------------------------- 1 | digraph ds2_struct { 2 | rankdir = LR; 3 | node [shape=record]; 4 | 5 | wt [ 6 | shape=plain; 7 | label=< 8 | 9 | 18 |
WorkTask
10 | 11 | 12 | 13 | 14 | 15 | 16 |
uint dev
uint inum
int ref
struct sleeplock lock
int valid
17 |
> 19 | ]; 20 | } 21 | -------------------------------------------------------------------------------- /img/ds2.dot.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jeanhwea/xv6-course/c2771ab75f2a1f5314f636f9b2056f7048fd4cfc/img/ds2.dot.pdf -------------------------------------------------------------------------------- /img/ds2.dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jeanhwea/xv6-course/c2771ab75f2a1f5314f636f9b2056f7048fd4cfc/img/ds2.dot.png -------------------------------------------------------------------------------- /img/ds2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jeanhwea/xv6-course/c2771ab75f2a1f5314f636f9b2056f7048fd4cfc/img/ds2.png -------------------------------------------------------------------------------- /img/file-struct.dot: -------------------------------------------------------------------------------- 1 | digraph file_struct { 2 | rankdir=LR; 3 | node [shape=record]; 4 | 5 | subgraph cluster_file { 6 | label=<file>; style=filled; color=white; 7 | file [label="enum type|int ref|char readable|char writable|

struct pipe *pipe|struct inode *ip|uint off"]; 8 | } 9 | subgraph cluster_pipe { 10 | label =<pipe>; style=filled; color=white; 11 | pipe [label="struct sleeplock lock|char data[]|uint nread|uint nwrite|int readopen|int writeopen"]; 12 | } 13 | subgraph cluster_inode { 14 | label =<inode>; style=filled; color=lightgray; 15 | inode [label="inode struct|uint dev|uint inum|int ref|struct sleeplock lock|int valid|short type|short major|short minor|short nlink|uint size|uint addrs"]; 16 | dinode [label="dinode struct|short type|short major|short minor|short nlink|uint size|uint addrs"]; 17 | inode -> dinode [style=invis]; 18 | } 19 | 20 | file:p -> pipe; 21 | file:i -> inode; 22 | } 23 | -------------------------------------------------------------------------------- /img/file-struct.dot.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jeanhwea/xv6-course/c2771ab75f2a1f5314f636f9b2056f7048fd4cfc/img/file-struct.dot.pdf -------------------------------------------------------------------------------- /img/file-struct.dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jeanhwea/xv6-course/c2771ab75f2a1f5314f636f9b2056f7048fd4cfc/img/file-struct.dot.png -------------------------------------------------------------------------------- /img/fs-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jeanhwea/xv6-course/c2771ab75f2a1f5314f636f9b2056f7048fd4cfc/img/fs-arch.png -------------------------------------------------------------------------------- /img/fs-phy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jeanhwea/xv6-course/c2771ab75f2a1f5314f636f9b2056f7048fd4cfc/img/fs-phy.png -------------------------------------------------------------------------------- /img/idequeue.dot: -------------------------------------------------------------------------------- 1 | digraph idequeue { 2 | // rankdir=LR; 3 | node [shape=record]; 4 | 5 | subgraph cluster_queue { 6 | label=<ide>; style=filled; color=lightgray; 7 | idelock[label="idelock"]; 8 | idequeue[label="idequeue"]; 9 | } 10 | // bcache:b -> cluster_bufs; 11 | 12 | subgraph cluster_bufs { 13 | label=<buf[30]>; style=filled; color=lightgray; 14 | buf1[label="buf[0] |*qnext |dev|blockno|*prev|*next"]; 15 | buf2[label="buf[1] |*qnext |dev|blockno|*prev|*next"]; 16 | buf3[label="buf[...]|*qnext |dev|blockno|*prev|*next"]; 17 | buf9[label="buf[29] |*qnext(NULL)|dev|blockno|*prev|*next"]; 18 | buf1->buf2->buf3->buf9 [style=invis]; 19 | // rank=same {buf1 buf2 buf3 buf9} 20 | } 21 | 22 | idequeue:h -> buf2:a [color=red]; 23 | buf2:n -> buf9:a [color=red]; 24 | 25 | // subgraph cluster_bcache { 26 | // label=<bcache>; style=filled; color=lightgray; 27 | // bcache[label="lock|buf[30]|head|{

*prev|*next}"]; 28 | // } 29 | } 30 | -------------------------------------------------------------------------------- /img/idequeue.dot.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jeanhwea/xv6-course/c2771ab75f2a1f5314f636f9b2056f7048fd4cfc/img/idequeue.dot.pdf -------------------------------------------------------------------------------- /img/idequeue.dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jeanhwea/xv6-course/c2771ab75f2a1f5314f636f9b2056f7048fd4cfc/img/idequeue.dot.png -------------------------------------------------------------------------------- /img/inode-relation.dot: -------------------------------------------------------------------------------- 1 | digraph inode_relation { 2 | rankdir=LR; 3 | node [shape=record]; 4 | 5 | subgraph cluster_inode { 6 | label =<inode struct (in memory)>; style=filled; color=white; 7 | inode [label="uint dev|uint inum|int ref|struct sleeplock lock|int valid|short type|short major|short minor|short nlink|uint size|uint addrs"]; 8 | } 9 | inode:f0 -> dinode:f0 [label=copy arrowhead=inv color=blue]; 10 | inode:f9 -> dinode:f9 [label=copy arrowhead=inv color=blue]; 11 | subgraph cluster_dinode { 12 | label =<dinode struct (on disk)>; style=filled; color=white; 13 | dinode [label="short type|short major|short minor|short nlink|uint size|{uint addrs|addr1|...|addr12|indirect}"]; 14 | } 15 | subgraph cluster_indirect { 16 | label =<indirect addrs>; style=filled; color=white; 17 | indirect [label="addr1|...|addrX|...|addr128"]; 18 | } 19 | subgraph cluster_data { 20 | label =<data in disk>; style=filled; color=lightgray; 21 | data2 [label="data #1"]; 22 | data1 [label="data #3"]; 23 | data4 [label="data #4"]; 24 | idata1 [label="data #2"]; 25 | data7 [label="data #7"]; 26 | idataX [label="data #8"]; 27 | idata2 [label="data #5"]; 28 | } 29 | 30 | dinode:d1 -> data1; 31 | dinode:d2 -> data2; 32 | dinode:ind -> indirect; 33 | indirect:i1 -> idata1; 34 | indirect:ix -> idataX; 35 | indirect:i2 -> idata2; 36 | 37 | } 38 | -------------------------------------------------------------------------------- /img/inode-relation.dot.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jeanhwea/xv6-course/c2771ab75f2a1f5314f636f9b2056f7048fd4cfc/img/inode-relation.dot.pdf -------------------------------------------------------------------------------- /img/inode-relation.dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jeanhwea/xv6-course/c2771ab75f2a1f5314f636f9b2056f7048fd4cfc/img/inode-relation.dot.png -------------------------------------------------------------------------------- /img/pay.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jeanhwea/xv6-course/c2771ab75f2a1f5314f636f9b2056f7048fd4cfc/img/pay.jpg -------------------------------------------------------------------------------- /lab/01-register/.gdbinit: -------------------------------------------------------------------------------- 1 | set history save on 2 | set history size 9999 3 | set logging file gdb.log 4 | set logging enabled 5 | -------------------------------------------------------------------------------- /lab/01-register/Makefile: -------------------------------------------------------------------------------- 1 | all: a.out 2 | 3 | main.o: main.S 4 | gcc -m32 -c main.S 5 | 6 | a.out: main.o 7 | ld -m elf_i386 main.o 8 | 9 | clean: 10 | -rm -f *.o *.out *.log 11 | -------------------------------------------------------------------------------- /lab/01-register/main.S: -------------------------------------------------------------------------------- 1 | .global _start 2 | 3 | .text 4 | _start: 5 | mov $1, %eax 6 | mov $2, %ebx 7 | add %ebx, %eax 8 | 9 | loop: 10 | jmp loop 11 | -------------------------------------------------------------------------------- /lab/02-stack/.gdbinit: -------------------------------------------------------------------------------- 1 | define hook-stepi 2 | if $ebp > 0 3 | # printf "ebp=%p, esp=%p\n", $ebp, $esp 4 | x/4x $ebp-16 5 | x/i $eip 6 | end 7 | end 8 | 9 | starti 10 | watch $ebp 11 | watch $esp 12 | 13 | # layout asm 14 | # layout reg 15 | 16 | set disassemble-next-line on 17 | # set logging file gdb.log 18 | # set logging enabled 19 | -------------------------------------------------------------------------------- /lab/02-stack/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | LD = ld 3 | CCFLAG = -m32 4 | LDFLAG = -m elf_i386 5 | GDB = gdb 6 | 7 | all: a.out 8 | 9 | start: a.out 10 | -rm -f gdb.log 11 | $(GDB) ./a.out 12 | 13 | main.o: main.S 14 | $(CC) $(CCFLAG) -c main.S 15 | 16 | a.out: main.o 17 | $(LD) $(LDFLAG) main.o 18 | 19 | clean: 20 | -rm -f *.o *.out 21 | -------------------------------------------------------------------------------- /lab/02-stack/main.S: -------------------------------------------------------------------------------- 1 | .global _start 2 | 3 | .text 4 | _start: 5 | push %ebp 6 | mov %esp, %ebp 7 | 8 | push $0xa 9 | push $0xb 10 | push $0xc 11 | pop %eax 12 | 13 | push $4 14 | pop %eax 15 | pop %eax 16 | 17 | loop: 18 | jmp loop 19 | -------------------------------------------------------------------------------- /lab/03-control-flow/.gdbinit: -------------------------------------------------------------------------------- 1 | set disassemble-next-line on 2 | lay asm 3 | lay reg 4 | starti 5 | -------------------------------------------------------------------------------- /lab/03-control-flow/Makefile: -------------------------------------------------------------------------------- 1 | all: adder.asm jump.out fcall.out 2 | 3 | adder.asm: adder.out 4 | objdump -d $< > $@ 5 | 6 | adder.out: adder.c 7 | gcc -m32 adder.c -o adder.out 8 | 9 | jump.out: jump.S 10 | gcc -m32 -c jump.S 11 | ld -m elf_i386 jump.o -o jump.out 12 | 13 | fcall.out: fcall.S 14 | gcc -m32 -c fcall.S 15 | ld -m elf_i386 fcall.o -o fcall.out 16 | 17 | clean: 18 | -rm -f *.out *.asm *.o 19 | -------------------------------------------------------------------------------- /lab/03-control-flow/adder.c: -------------------------------------------------------------------------------- 1 | int add(int a, int b) { 2 | int c = a + b; 3 | return c; 4 | } 5 | 6 | int main(int argc, char *argv[]) 7 | { 8 | int a, b, c; 9 | a = 7; 10 | b = 4; 11 | c = add(a, b); 12 | return c; 13 | } 14 | -------------------------------------------------------------------------------- /lab/03-control-flow/fcall.S: -------------------------------------------------------------------------------- 1 | .global _start 2 | 3 | .text 4 | _start: 5 | mov $3, %eax 6 | mov $4, %ebx 7 | 8 | call add 9 | 10 | mov $1, %eax 11 | int $0x80 12 | 13 | # func add(%eax, %ebx) => %ebx 14 | add: 15 | add %eax, %ebx 16 | ret 17 | -------------------------------------------------------------------------------- /lab/03-control-flow/jump.S: -------------------------------------------------------------------------------- 1 | .global _start 2 | 3 | .text 4 | _start: 5 | mov $4, %eax 6 | 7 | cmp $4, %eax 8 | je eax_eq_four 9 | jmp eax_ne_four 10 | jmp endif 11 | 12 | eax_eq_four: 13 | xor $1, %ebx 14 | jmp endif 15 | eax_ne_four: 16 | mov $2, %ebx 17 | jmp endif 18 | 19 | endif: 20 | mov $1, %eax # eax 为中断号 21 | int $0x80 # ebx 为返回值 22 | -------------------------------------------------------------------------------- /lab/03-control-flow/jump2.c: -------------------------------------------------------------------------------- 1 | int main() 2 | { 3 | int a = 2; 4 | if (a == 4) { 5 | return 1; 6 | } else { 7 | return 2; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lab/04-qemu/01-qemu-linux-0.11.sh: -------------------------------------------------------------------------------- 1 | cd ~/code/gitana/os/linux-0.11-in-ubuntu 2 | 3 | qemu-system-i386 -boot a -fda Image -hda hdc-0.11.img 4 | -------------------------------------------------------------------------------- /lab/04-qemu/02-qemu-xv6-nox.sh: -------------------------------------------------------------------------------- 1 | cd ~/code/gitana/os/xv6-public 2 | 3 | qemu-system-i386 -smp 2 -m 512 -nographic -hda xv6.img -hdb fs.img 4 | -------------------------------------------------------------------------------- /lab/04-qemu/03-qemu-minix-initrd.sh: -------------------------------------------------------------------------------- 1 | cd /data/os/minix/obj.i386/work 2 | 3 | qemu-system-i386 \ 4 | --enable-kvm \ 5 | -m 1G \ 6 | -kernel kernel \ 7 | -append "bootramdisk=1" \ 8 | -initrd "mod01_ds,mod02_rs,mod03_pm,mod04_sched,mod05_vfs,mod06_memory,mod07_tty,mod08_mib,mod09_vm,mod10_pfs,mod11_mfs,mod12_init" 9 | -------------------------------------------------------------------------------- /lab/04-qemu/04-qemu-linux-kernel.sh: -------------------------------------------------------------------------------- 1 | cd /data/os/x86_64/buildroot-2023.02.5/output/images 2 | 3 | qemu-system-x86_64 \ 4 | -M pc \ 5 | -nographic \ 6 | -kernel bzImage \ 7 | -drive file=rootfs.ext2,if=virtio,format=raw \ 8 | -append "rootwait root=/dev/vda console=tty1 console=ttyS0" \ 9 | -net nic,model=virtio -net user 10 | -------------------------------------------------------------------------------- /lab/04-qemu/05-qemu-cdrom-install.sh: -------------------------------------------------------------------------------- 1 | cd ~/vm/disks 2 | 3 | if [ ! -f minix_R3.3.0-588a35b.iso ]; then 4 | cat << EOF 5 | Please download images first 6 | 7 | wget -c https://jaist.dl.sourceforge.net/project/archiveos/m/minix/minix_R3.3.0-588a35b.iso.bz2 8 | bzip2 -d minix_R3.3.0-588a35b.iso.bz2 9 | 10 | EOF 11 | exit 1 12 | fi 13 | 14 | 15 | qemu-system-x86_64 \ 16 | -smp 4 -m 4G \ 17 | -enable-kvm \ 18 | -hda hd001.img \ 19 | -cdrom minix_R3.3.0-588a35b.iso 20 | -------------------------------------------------------------------------------- /lab/05-interrupt/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | AS = as 3 | LD = ld 4 | CCFLAG = -m32 5 | ASFLAG = -32 6 | LDFLAG = -m elf_i386 7 | GDB = gdb 8 | OUTS = $(patsubst %.S,%.out,$(wildcard *.S)) 9 | RUNS = $(patsubst %.c,%.run,$(wildcard *.c)) 10 | 11 | all: $(OUTS) $(RUNS) 12 | 13 | 14 | %.o: %.S 15 | $(AS) $(ASFLAG) -c $< -o $@ 16 | 17 | %.out: %.o 18 | $(LD) $(LDFLAG) $< -o $@ 19 | 20 | %.run: %.c 21 | $(CC) $(CCFLAG) $< -o $@ 22 | 23 | 24 | # 屏蔽自动删除 .o 文件的规则 25 | .PRECIOUS: %.o 26 | 27 | .PHONY: clean 28 | clean: 29 | -rm -f *.o *.out *.run *.bin *.asm 30 | -------------------------------------------------------------------------------- /lab/05-interrupt/echo.S: -------------------------------------------------------------------------------- 1 | .global _start 2 | 3 | .text 4 | _start: 5 | # sys_read(unsigned int fd, const char *buf, size_t count) 6 | mov $len, %edx # count 字符串长度 7 | mov $msg, %ecx # buf 字符串缓冲区 8 | mov $0, %ebx # fd 输出文件描述符 9 | mov $3, %eax # Linux 系统调用号 (sys_read) 10 | int $0x80 # syscall 中断 11 | 12 | # sys_write(unsigned int fd, const char *buf, size_t count) 13 | mov %eax, %edx # count 字符串长度 14 | mov $msg, %ecx # buf 字符串缓冲区 15 | mov $1, %ebx # fd 输出文件描述符 16 | mov $4, %eax # Linux 系统调用号 (sys_write) 17 | int $0x80 # syscall 中断 18 | 19 | # sys_exit(int code) 20 | mov $0, %ebx # 程序退出码 code 21 | mov $1, %eax # Linux 系统调用号 (sys_exit) 22 | int $0x80 # syscall 中断 23 | 24 | 25 | .data 26 | msg: 27 | .ascii "????????" # 字符串缓存 28 | len = . - msg # 当前位置减去 msg 地址 = 字符串 msg 的长度 29 | -------------------------------------------------------------------------------- /lab/05-interrupt/echo2.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | char msg[] = "12345678\n"; 4 | 5 | int main() 6 | { 7 | ssize_t n = read(0, msg, 8); 8 | write(1, msg, n); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /lab/05-interrupt/greet.S: -------------------------------------------------------------------------------- 1 | .global _start 2 | 3 | .text 4 | _start: 5 | # sys_write(unsigned int fd, const char *buf, size_t count) 6 | mov $len, %edx # count 字符串长度 7 | mov $msg, %ecx # buf 字符串缓冲区 8 | mov $1, %ebx # fd 输出文件描述符 9 | mov $4, %eax # Linux 系统调用号 (sys_write) 10 | int $0x80 # syscall 中断 11 | 12 | # sys_exit(int code) 13 | mov $0, %ebx # 程序退出码 code 14 | mov $1, %eax # Linux 系统调用号 (sys_exit) 15 | int $0x80 # syscall 中断 16 | 17 | 18 | .data 19 | msg: 20 | .ascii "Hello from ASM!\n" # 字符串缓存 21 | len = . - msg # 当前位置减去 msg 地址 = 字符串 msg 的长度 22 | -------------------------------------------------------------------------------- /lab/05-interrupt/greet2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | char msg[] = "Hello from C\n"; 5 | 6 | int main() 7 | { 8 | write(1, msg, 13); 9 | exit(0); 10 | } 11 | -------------------------------------------------------------------------------- /lab/05-interrupt/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main (int argc, char *argv[]) 4 | { 5 | printf("Hello World!\n"); 6 | // printf = glibc => syscall(write) 7 | // -> int $0x80 write(4) 8 | // -> (linux kernel) 9 | // -> BIOS 10 | // -> int 0x16/0x13 11 | // foreach "Hello world" 12 | // single char 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /lab/06-barebone/.gdbinit: -------------------------------------------------------------------------------- 1 | define hook-quit 2 | kill 3 | end 4 | 5 | set architecture i8086 6 | target remote :1234 7 | 8 | set disassemble-next-line on 9 | b *0x7c00 10 | c 11 | # lay asm 12 | # lay reg 13 | -------------------------------------------------------------------------------- /lab/06-barebone/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | AS = as 3 | LD = ld 4 | OD = objdump 5 | OC = objcopy 6 | CCFLAG = -m32 7 | ASFLAG = -32 8 | LDFLAG = -m elf_i386 -N -e start -Ttext 0x7c00 9 | ODFLAG = -S -m i8086 10 | OCFLAG = -S -O binary -j .text 11 | GDB = gdb 12 | QEMU = qemu-system-i386 13 | QMFLAG = -nographic -fda fda.img 14 | OBJS = $(patsubst %.S,%.out,$(wildcard *.S)) 15 | SIGN = ../../bin/sign.pl 16 | 17 | # all: $(OBJS) 18 | 19 | all: fda.img 20 | 21 | 22 | %.o: %.S 23 | $(CC) $(CCFLAG) -c -o $@ $< 24 | 25 | %.o: %.c 26 | $(CC) $(CCFLAG) -c -o $@ $< 27 | 28 | %.out: %.o 29 | $(LD) $(LDFLAG) $< -o $@ 30 | 31 | boot.bin: boot.out 32 | $(OD) $(ODFLAG) $< > boot.asm 33 | $(OC) $(OCFLAG) $< boot.bin 34 | 35 | fda.img: boot.bin 36 | $(SIGN) boot.bin 37 | dd if=/dev/zero of=fda.img bs=512 count=1 38 | dd if=boot.bin of=fda.img conv=notrunc 39 | 40 | qemu: fda.img 41 | $(QEMU) $(QMFLAG) 42 | 43 | qemu-debug: fda.img 44 | $(QEMU) $(QMFLAG) -s -S 45 | 46 | .PRECIOUS: %.o 47 | 48 | .PHONY: clean qemu 49 | clean: 50 | -rm -f *.o *.d *.out *.img *.bin *.asm 51 | -------------------------------------------------------------------------------- /lab/06-barebone/boot.S: -------------------------------------------------------------------------------- 1 | .code16 2 | 3 | .global start 4 | start: 5 | mov $1, %ax 6 | mov $2, %bx 7 | mov $3, %cx 8 | mov $4, %dx 9 | 10 | spin: 11 | jmp spin 12 | -------------------------------------------------------------------------------- /lab/07-nasm-boot/.gdbinit: -------------------------------------------------------------------------------- 1 | define hook-quit 2 | kill 3 | end 4 | 5 | target remote :1234 6 | 7 | set disassemble-next-line on 8 | set disassembly-flavor intel 9 | show disassembly-flavor intel 10 | 11 | set architecture i8086 12 | 13 | lay asm 14 | lay reg 15 | b *0x7c00 16 | c 17 | -------------------------------------------------------------------------------- /lab/07-nasm-boot/Makefile: -------------------------------------------------------------------------------- 1 | QEMU = qemu-system-i386 2 | 3 | all: boot.bin 4 | 5 | qemu: boot.bin 6 | $(QEMU) -nographic -fda boot.bin 7 | 8 | qemu-debug: boot.bin 9 | $(QEMU) -s -S -nographic -fda boot.bin 10 | 11 | boot.bin: boot.s 12 | nasm -f bin -o boot.bin boot.s 13 | 14 | .PHONY: clean qemu 15 | clean: 16 | -rm -f *.o *.d *.out *.img *.bin *.asm 17 | -------------------------------------------------------------------------------- /lab/07-nasm-boot/boot.s: -------------------------------------------------------------------------------- 1 | [org 0x7c00] ; 设置起始地址, BIOS 默认跳转地址为 0x7c00 2 | [bits 16] ; 设置 16 位汇编 3 | 4 | start: 5 | mov si, msg ; 读取 msg 地址到 SI 6 | call puts 7 | hlt 8 | 9 | puts: 10 | lodsb ; 从 si 中读取 1 byte 到 al, 然后 si++ 11 | cmp al, 0 ; al 是否为 0 12 | je done ; if al == 0, 函数返回 13 | mov ah, 0x0e ; 设置中断为输出一个字符 14 | int 0x10 ; 中断向屏幕输出一个字符 15 | jmp puts ; 循环读取下一个字符 16 | done: 17 | ret 18 | 19 | 20 | msg: 21 | db 0x0d, 0x0a, 22 | db 'Hello from Kernel!', 0x0d, 0x0a 23 | db 0x0d, 0x0a, 24 | db 0 ; '\0' 25 | 26 | times 510-($-$$) db 0 ; 填充多余的 510 个字节为零值 27 | dw 0xaa55 ; BIOS 结束校验码 28 | -------------------------------------------------------------------------------- /lab/08-kernel-user-mode/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | AS = as 3 | LD = ld 4 | OD = objdump 5 | OC = objcopy 6 | CCFLAG = -m32 7 | ASFLAG = -32 8 | LDFLAG = -m elf_i386 -N -e start -Ttext 0x7c00 9 | ODFLAG = -S -m i8086 10 | OCFLAG = -S -O binary -j .text 11 | GDB = gdb 12 | QEMU = qemu-system-i386 13 | QMFLAG = -nographic -fda fda.img 14 | OBJS = $(patsubst %.S,%.out,$(wildcard *.S)) 15 | SIGN = ../../bin/sign.pl 16 | 17 | all: fda.img user.run 18 | 19 | %.o: %.S 20 | $(CC) $(CCFLAG) -c -o $@ $< 21 | 22 | %.o: %.c 23 | $(CC) $(CCFLAG) -c -o $@ $< 24 | 25 | %.out: %.o 26 | $(LD) $(LDFLAG) $< -o $@ 27 | 28 | user.run: user.S 29 | $(CC) -c user.S 30 | $(OD) -d user.o > user.asm 31 | $(LD) -o user.run user.o 32 | 33 | kernel.bin: kernel.out 34 | $(OD) $(ODFLAG) $< > kernel.asm 35 | $(OC) $(OCFLAG) $< kernel.bin 36 | 37 | fda.img: kernel.bin 38 | $(SIGN) kernel.bin 39 | dd if=/dev/zero of=fda.img bs=512 count=1 40 | dd if=kernel.bin of=fda.img conv=notrunc 41 | 42 | qemu: fda.img 43 | $(QEMU) $(QMFLAG) 44 | 45 | qemu-debug: fda.img 46 | $(QEMU) $(QMFLAG) -s -S 47 | 48 | .PRECIOUS: %.o 49 | 50 | .PHONY: clean qemu 51 | clean: 52 | -rm -f *.o *.out *.run *.bin *.asm *.img 53 | -------------------------------------------------------------------------------- /lab/08-kernel-user-mode/kernel.S: -------------------------------------------------------------------------------- 1 | .code16 2 | 3 | .global start 4 | start: 5 | cli 6 | mov $1, %ax 7 | sti 8 | 9 | spin: 10 | jmp spin 11 | -------------------------------------------------------------------------------- /lab/08-kernel-user-mode/user.S: -------------------------------------------------------------------------------- 1 | .global _start 2 | 3 | .text 4 | _start: 5 | cli 6 | 7 | xor %ebx, %ebx 8 | mov $1, %eax 9 | int $0x80 10 | 11 | loop: 12 | jmp loop 13 | -------------------------------------------------------------------------------- /lab/09-linux-syscall/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | AS = as 3 | LD = ld 4 | OD = objdump 5 | OC = objcopy 6 | CCFLAG = -g 7 | ASFLAG = 8 | LDFLAG = 9 | ODFLAG = -S 10 | OCFLAG = -S 11 | GDB = gdb 12 | OBJS = $(patsubst %.c,%.out,$(wildcard *.c)) 13 | 14 | all: $(OBJS) 15 | 16 | %.o: %.c 17 | $(CC) $(CCFLAG) -c -o $@ $< 18 | 19 | %.out: %.o 20 | $(CC) $(LDFLAG) $< -o $@ 21 | 22 | 23 | .PRECIOUS: %.o 24 | 25 | .PHONY: clean 26 | clean: 27 | -rm -f *.o *.out *.run *.bin *.asm 28 | -------------------------------------------------------------------------------- /lab/09-linux-syscall/fork_gpt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | pid_t child_pid; 7 | 8 | // Create a new child process 9 | child_pid = fork(); 10 | 11 | if (child_pid == -1) { 12 | // Fork failed 13 | perror("Fork failed"); 14 | return 1; 15 | } 16 | 17 | if (child_pid == 0) { 18 | // This code is executed by the child process 19 | printf("Child process: PID = %d, Parent PID = %d\n", getpid(), 20 | getppid()); 21 | } else { 22 | // This code is executed by the parent process 23 | printf("Parent process: PID = %d, Child PID = %d\n", getpid(), 24 | child_pid); 25 | } 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /lab/09-linux-syscall/forkme.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | int pid = fork(); 10 | int wstatus = -1; 11 | if (pid > 0) { 12 | printf("parent: child %d\n", pid); 13 | pid = wait(&wstatus); 14 | printf("parent: child %d exit, wstatus = %d\n", pid, wstatus); 15 | } else if (pid == 0) { 16 | printf("child: starting\n"); 17 | sleep(1); 18 | printf("child: exiting\n"); 19 | exit(0); 20 | } else { 21 | printf("fork error\n"); 22 | } 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /lab/09-linux-syscall/loop.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | while (1) { 6 | printf("This is an endless loop.\n"); 7 | } 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /lab/09-linux-syscall/printx.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | int num = fork(); 8 | if (num > 0) { 9 | printf("aaa\n"); 10 | } else { 11 | printf("bbb\n"); 12 | } 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /lab/10-real-to-protected/.gdbinit: -------------------------------------------------------------------------------- 1 | set $lastcs = -1 2 | 3 | define hook-stop 4 | # There doesn't seem to be a good way to detect if we're in 16- or 5 | # 32-bit mode, but in 32-bit mode we always run with CS == 8 in the 6 | # kernel and CS == 35 in user space 7 | if $cs == 8 || $cs == 35 8 | if $lastcs != 8 && $lastcs != 35 9 | set architecture i386 10 | end 11 | x/i $pc 12 | else 13 | if $lastcs == -1 || $lastcs == 8 || $lastcs == 35 14 | set architecture i8086 15 | end 16 | # Translate the segment:offset into a physical address 17 | printf "[%4x:%4x] ", $cs, $eip 18 | x/i $cs*16+$eip 19 | end 20 | set $lastcs = $cs 21 | end 22 | 23 | # define hook-quit 24 | # kill 25 | # end 26 | 27 | file vmKernel 28 | 29 | target remote :1234 30 | 31 | 32 | break *0x7c00 33 | break start_kernel 34 | # layout asm 35 | # layout reg 36 | continue 37 | -------------------------------------------------------------------------------- /lab/10-real-to-protected/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | AS = nasm 3 | LD = ld 4 | OD = objdump 5 | CCFLAG = -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -fno-omit-frame-pointer -fno-stack-protector -fno-pie -no-pie -O -nostdinc -I. 6 | LDFLAG = -m elf_i386 -N -e start_kernel -Ttext 0x1000 7 | ODFLAG = -D -m i386 -b binary 8 | QEMU = qemu-system-i386 9 | BOCHS = bochs 10 | 11 | all: fda.img 12 | 13 | qemu: fda.img 14 | $(QEMU) -boot a -fda fda.img 15 | 16 | qemu-nox: fda.img 17 | $(QEMU) -nographic -boot a -fda fda.img 18 | 19 | qemu-debug: fda.img 20 | $(QEMU) -s -S -boot a -fda fda.img >/dev/null 2>&1 & 21 | 22 | bochs: fda.img 23 | $(BOCHS) -q -f bochsrc.bxrc 24 | 25 | bootsect.bin: bootsect.s 26 | $(AS) -f bin -o bootsect.bin -l bootsect.lst bootsect.s 27 | ndisasm -b 16 bootsect.bin > bootsect.dis 28 | 29 | entry.o: entry.s 30 | $(AS) -f elf -g -o entry.o entry.s 31 | 32 | bootmain.o: bootmain.c 33 | $(CC) $(CCFLAG) -c -g -o bootmain.o bootmain.c 34 | 35 | bootmain.bin: entry.o bootmain.o 36 | $(LD) $(LDFLAG) -g -o vmKernel $^ 37 | $(LD) $(LDFLAG) -o bootmain.bin --oformat binary $^ 38 | $(OD) $(ODFLAG) bootmain.bin > bootmain.asm 39 | 40 | fda.img: bootsect.bin bootmain.bin 41 | cat bootsect.bin bootmain.bin > fda.img 42 | 43 | .PHONY: clean qemu 44 | clean: 45 | -rm -f *.o *.d *.out *.img *.bin *.asm *.dis *.lst vm* *.ini bochsout.txt 46 | -------------------------------------------------------------------------------- /lab/10-real-to-protected/bootmain.c: -------------------------------------------------------------------------------- 1 | // 屏幕显示 2 | #define VGA_ADDR ((char *)0xb8000) 3 | #define CTRL_REG 0x3d4 4 | #define DATA_REG 0x3d5 5 | #define MAX_ROWS 25 6 | #define MAX_COLS 80 7 | 8 | // 颜色 9 | #define RED_ON_BLACK 0x0c 10 | #define GREEN_ON_BLACK 0x0a 11 | 12 | unsigned char port_in(unsigned short port) 13 | { 14 | unsigned char result; 15 | __asm__("in %%dx, %%al" : "=a"(result) : "d"(port)); 16 | return result; 17 | } 18 | 19 | void port_out(unsigned short port, unsigned char data) 20 | { 21 | __asm__("out %%al, %%dx" : : "a"(data), "d"(port)); 22 | } 23 | 24 | int get_cursor() 25 | { 26 | port_out(CTRL_REG, 14); 27 | unsigned char high = port_in(DATA_REG); 28 | port_out(CTRL_REG, 15); 29 | unsigned char low = port_in(DATA_REG); 30 | int offset = (int)(high << 8) + (int)low; 31 | return offset * 2; 32 | } 33 | 34 | void set_cursor(int offset) 35 | { 36 | offset /= 2; 37 | unsigned char high = (unsigned char)(offset >> 8); 38 | unsigned char low = (unsigned char)(offset & 0xff); 39 | port_out(CTRL_REG, 14); 40 | port_out(DATA_REG, high); 41 | port_out(CTRL_REG, 15); 42 | port_out(DATA_REG, low); 43 | } 44 | 45 | int get_offset(int col, int row) 46 | { 47 | return 2 * (row * MAX_ROWS + col); 48 | } 49 | 50 | void clear_screen() 51 | { 52 | char *video = VGA_ADDR; 53 | for (int col = 0; col < MAX_COLS; ++col) { 54 | for (int row = 0; row < MAX_ROWS; ++row) { 55 | video[get_offset(col, row)] = ' '; 56 | } 57 | } 58 | } 59 | 60 | void print_char(char ch) 61 | { 62 | char *video = VGA_ADDR; 63 | int curr = get_cursor(); 64 | video += curr; 65 | *video = ch; 66 | video++; 67 | *video = RED_ON_BLACK; 68 | } 69 | 70 | void start_kernel() 71 | { 72 | char *video = VGA_ADDR; 73 | 74 | // print_char('X'); 75 | 76 | // clear_screen(); 77 | // set_cursor(get_offset(11, 0)); 78 | 79 | char *msg = "Hello World\n"; 80 | for (char *p = msg; *p != '\n'; ++p) { 81 | *video = *p; 82 | video++; 83 | *video = GREEN_ON_BLACK; 84 | video++; 85 | } 86 | 87 | while (1) { 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /lab/10-real-to-protected/bootsect.s: -------------------------------------------------------------------------------- 1 | [org 0x7c00] ; 设置起始地址, BIOS 默认跳转地址为 0x7c00 2 | KERNEL_BASE equ 0x1000 ; 内核加载到内存的起始地址 3 | 4 | ;; 实模式 5 | [bits 16] 6 | start: 7 | xor ax, ax 8 | mov ds, ax 9 | mov ss, ax 10 | mov es, ax 11 | mov fs, ax 12 | mov gs, ax 13 | 14 | call load_disk 15 | 16 | cli ; 1. 关中断 17 | lgdt [desc] ; 2. 加载 GDT / GDTr 18 | mov eax, cr0 19 | or eax, 0x1 ; 3. 设置 cr0 20 | mov cr0, eax 21 | jmp CODE_SEG:start2 ; 4. 长跳转到 32 汇编入口 22 | 23 | 24 | ;; 参考手册 https://stanislavs.org/helppc/int_13-2.html 25 | ;; ES:BX = pointer to buffer 26 | load_disk: 27 | pusha 28 | mov ah, 0x02 ; function code: 2 = read disk 29 | mov al, 8 ; nsector 30 | mov ch, 0 ; cylinder 31 | mov cl, 2 ; sector 32 | mov dh, 0 ; header 33 | mov dl, 0 ; 0 = flappy, 1 = flappy1, 0x80 = hdd 34 | mov bx, KERNEL_BASE ; 35 | int 0x13 ; BIOS 中断 36 | popa 37 | ret 38 | 39 | ;; 保护模式 40 | [bits 32] 41 | start2: 42 | mov ax, DATA_SEG ; 5. 更新所有段寄存器 43 | mov ds, ax 44 | mov ss, ax 45 | mov es, ax 46 | mov fs, ax 47 | mov gs, ax 48 | mov ebp, 0x90000 ; 6. 更新系统栈 49 | mov esp, ebp 50 | call KERNEL_BASE ; 7. 跳转到 C 语言入口代码 51 | 52 | hlt 53 | 54 | 55 | ;; 用于校验,前 8 字节要求置零 56 | gdt_begin: 57 | dd 0x0 58 | dd 0x0 59 | 60 | ;; 代码段 base = 0x00000000, length = 0xfffff 61 | gdt_code: 62 | dw 0xffff ; segment length, bits 0-15 | limit_low(16) 63 | dw 0x0 ; segment base, bits 0-15 | base_low(16) 64 | db 0x0 ; segment base, bits 16-23 | base_middle(8) 65 | db 10011010b ; flags (8 bits) | flags1(8) 66 | db 11001111b ; flags (4 bits) + segment length, bits 16-19 | limit_high(4), flags2(4) 67 | db 0x0 ; segment base, bits 24-31 | base_high(8) 68 | 69 | ;; 数据段 70 | gdt_data: 71 | dw 0xffff 72 | dw 0x0 73 | db 0x0 74 | db 10010010b 75 | db 11001111b 76 | db 0x0 77 | 78 | gdt_end: 79 | 80 | desc: 81 | dw gdt_end - gdt_begin - 1 ; size (16 bit), always one less of its true size 82 | dd gdt_begin ; address (32 bit) 83 | 84 | ; define some constants for later use 85 | CODE_SEG equ gdt_code - gdt_begin 86 | DATA_SEG equ gdt_data - gdt_begin 87 | 88 | ;; BIOS 结束校验码 89 | times 510-($-$$) db 0 90 | dw 0xaa55 91 | -------------------------------------------------------------------------------- /lab/10-real-to-protected/entry.s: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | [extern start_kernel] 3 | call start_kernel 4 | jmp $ 5 | -------------------------------------------------------------------------------- /lab/11-inline-asm/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | AS = as 3 | LD = ld 4 | OD = objdump 5 | OC = objcopy 6 | CCFLAG = -m32 -O0 7 | ASFLAG = 8 | LDFLAG = -m32 -O0 9 | ODFLAG = -S 10 | OCFLAG = -S 11 | GDB = gdb 12 | OBJS = $(patsubst %.c,%.out,$(wildcard *.c)) 13 | ASMS = $(patsubst %.c,%.asm,$(wildcard *.c)) 14 | 15 | all: $(OBJS) $(ASMS) 16 | 17 | %.o: %.c 18 | $(CC) $(CCFLAG) -c -o $@ $< 19 | 20 | %.asm: %.o 21 | $(OD) $(ODFLAG) -d $< > $@ 22 | 23 | %.out: %.o 24 | $(CC) $(LDFLAG) $< -o $@ 25 | 26 | 27 | .PRECIOUS: %.o 28 | 29 | .PHONY: clean 30 | clean: 31 | -rm -f *.o *.out *.run *.bin *.asm 32 | -------------------------------------------------------------------------------- /lab/11-inline-asm/add.c: -------------------------------------------------------------------------------- 1 | #include 2 | int add(int a, int b) 3 | { 4 | int res; 5 | asm("add %1, %2;" : "=r"(res) : "r"(a), "0"(b)); 6 | return res; 7 | } 8 | 9 | int main() 10 | { 11 | printf("8+4 == %d\n", add(8, 4)); // prints 8+4 == 12 12 | } 13 | -------------------------------------------------------------------------------- /lab/12-linker-extern/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | AS = as 3 | LD = ld 4 | OD = objdump 5 | OC = objcopy 6 | CCFLAG = -m32 -O0 -g 7 | ASFLAG = 8 | LDFLAG = -m32 -O0 9 | ODFLAG = -S 10 | OCFLAG = -S 11 | GDB = gdb 12 | OBJS = $(patsubst %.c,%.out,$(wildcard *.c)) 13 | ASMS = $(patsubst %.c,%.asm,$(wildcard *.c)) 14 | 15 | all: $(OBJS) $(ASMS) 16 | 17 | %.o: %.c 18 | $(CC) $(CCFLAG) -c -o $@ $< 19 | 20 | %.asm: %.o 21 | $(OD) $(ODFLAG) -d $< > $@ 22 | 23 | %.out: %.o 24 | $(CC) $(LDFLAG) $< -o $@ 25 | 26 | 27 | .PRECIOUS: %.o 28 | 29 | .PHONY: clean 30 | clean: 31 | -rm -f *.o *.out *.run *.bin *.asm 32 | -------------------------------------------------------------------------------- /lab/12-linker-extern/end-addr.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // The symbols must have some type, or "gcc -Wall" complains 5 | extern char etext, edata, end; 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | printf("First address past:\n"); 10 | printf(" program text (etext) %10p\n", &etext); 11 | printf(" initialized data (edata) %10p\n", &edata); 12 | printf(" uninitialized data (end) %10p\n", &end); 13 | 14 | exit(EXIT_SUCCESS); 15 | } 16 | -------------------------------------------------------------------------------- /lab/12-linker-extern/idle.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int n = 100; 5 | int nums[100] = {}; 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | printf("pid = %d\n", getpid()); 10 | for (int i = 0; i < n; ++i) { 11 | nums[i] = 0; 12 | } 13 | sleep(5 * 60); 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /lab/13-mp-boot/.gdbinit: -------------------------------------------------------------------------------- 1 | # define hook-quit 2 | # kill 3 | # end 4 | 5 | set disassemble-next-line on 6 | set disassembly-flavor intel 7 | show disassembly-flavor intel 8 | set architecture i8086 9 | 10 | target remote :1234 11 | lay asm 12 | lay reg 13 | b *0x7c00 14 | c 15 | -------------------------------------------------------------------------------- /lab/13-mp-boot/Makefile: -------------------------------------------------------------------------------- 1 | QEMU = qemu-system-i386 2 | BOCHS = bochs 3 | N = 4 4 | 5 | all: boot.bin 6 | 7 | qemu: boot.bin 8 | $(QEMU) -smp $(N) -m 1M -boot a -fda boot.bin 9 | 10 | qemu-debug: boot.bin 11 | $(QEMU) -s -S -smp 1 -m 1M -boot a -fda boot.bin 12 | 13 | bochs: boot.bin 14 | $(BOCHS) -q -f bochsrc.bxrc 15 | 16 | boot.bin: boot.s 17 | nasm -f bin -l boot.lst -o boot.bin boot.s 18 | 19 | .PHONY: clean qemu 20 | clean: 21 | -rm -f *.o *.d *.out *.img *.bin *.asm 22 | -------------------------------------------------------------------------------- /lab/13-mp-boot/boot.s: -------------------------------------------------------------------------------- 1 | [org 0x7c00] 2 | ;; COMMON 3 | VGA equ 0x000b8a00 ; VGA Address 4 | PT_AP_ENTRY equ 0x8000 ; AP Entry Address 5 | PT_STACK equ 0x9000 ; Stack Pointer 6 | 7 | ;; APIC 8 | APIC_ID equ 0xfee00020 ; Local APIC ID Register 9 | APIC_SVR equ 0xfee000f0 ; Spurious interrupt Vector Register 10 | APIC_ICR equ 0xfee00300 ; Interrupt Command Register 11 | 12 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 13 | ;; BSP: Bootstrap Processor 14 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 15 | [bits 16] 16 | bsp_start: 17 | cli 18 | xor ax, ax 19 | mov ds, ax 20 | lgdt [desc] 21 | mov eax, cr0 22 | or eax, 0x1 23 | mov cr0, eax 24 | jmp CODE_SR:bsp_start32 25 | 26 | [bits 32] 27 | bsp_start32: 28 | mov ax, DATA_SR 29 | mov ds, ax 30 | mov ebp, PT_STACK 31 | mov esp, ebp 32 | 33 | ;; Load AP's Jump Code to PT_AP_ENTRY 34 | mov esi, ap_start 35 | mov edi, PT_AP_ENTRY 36 | mov ecx, ap_entry - ap_start 37 | cld 38 | rep movsb 39 | 40 | ;; Step 1 - Enable APIC 41 | mov eax, [APIC_SVR] 42 | or eax, 0x100 ; APIC software enable 43 | mov [APIC_SVR], eax 44 | 45 | ;; Step 2 - Send INIT to other APs, bit(9-10) 101=INIT 46 | mov eax, 0x000c4500 47 | mov [APIC_ICR], eax 48 | 49 | ;; Step 3 - Send STARTUP to other APs, bit(9-10) 110=STARTUP, bit(0-7) vector 50 | mov eax, 0x000c4600 | (PT_AP_ENTRY >> 12) 51 | mov [APIC_ICR], eax 52 | 53 | ;; Read APIC ID 54 | mov ebx, [APIC_ID] ; wait for write finish, by reading 55 | shr ebx, 24 56 | mov dl, 0xcf ; RED 57 | call print_lsb_digit 58 | 59 | hlt 60 | 61 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 62 | ;; AP: Application Processor 63 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 64 | [bits 16] 65 | ap_start: 66 | jmp 0x0000:ap_entry 67 | 68 | ap_entry: 69 | cli 70 | xor ax, ax 71 | mov ds, ax 72 | mov ss, ax 73 | mov es, ax 74 | 75 | lgdt [desc] 76 | mov eax, cr0 77 | or eax, 0x1 78 | mov cr0, eax 79 | jmp CODE_SR:s32_ap 80 | 81 | [bits 32] 82 | s32_ap: 83 | ;; Init Core 84 | mov ax, DATA_SR 85 | mov ds, ax 86 | mov ebp, PT_STACK 87 | mov esp, ebp 88 | 89 | ;; Read APIC ID 90 | mov ebx, [APIC_ID] 91 | shr ebx, 24 92 | mov dl, 0xaf ; GREEN 93 | call print_lsb_digit 94 | 95 | hlt 96 | 97 | ;; print lsb digit in ebx, bl:digit, bh:color 98 | print_lsb_digit: 99 | pusha 100 | ; xor eax, eax 101 | mov edi, VGA 102 | mov eax, ebx 103 | mov cl, 10 104 | div cl 105 | add ah, '0' 106 | mov [edi+2*ebx], ah ; char 107 | mov byte [edi+2*ebx+1], dl ; color 108 | popa 109 | ret 110 | 111 | ;; setup gdt 112 | gdt_begin: 113 | dd 0, 0 ; dummy 114 | 115 | gdt_code: 116 | dd 0x0000ffff 117 | dd 0x00cf9a00 118 | 119 | gdt_data: 120 | dd 0x0000ffff 121 | dd 0x00cf9200 122 | 123 | gdt_end: 124 | 125 | desc: 126 | dw gdt_end - gdt_begin - 1 127 | dd gdt_begin 128 | 129 | CODE_SR equ gdt_code - gdt_begin 130 | DATA_SR equ gdt_data - gdt_begin 131 | 132 | times 510-($-$$) db 0 133 | dw 0xaa55 134 | -------------------------------------------------------------------------------- /lab/14-process-intro/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | AS = as 3 | LD = ld 4 | OD = objdump 5 | OC = objcopy 6 | CCFLAG = -g 7 | ASFLAG = 8 | LDFLAG = 9 | ODFLAG = -S 10 | OCFLAG = -S 11 | GDB = gdb 12 | OBJS = $(patsubst %.c,%.out,$(wildcard *.c)) 13 | 14 | all: $(OBJS) 15 | 16 | %.o: %.c 17 | $(CC) $(CCFLAG) -c -o $@ $< 18 | 19 | %.out: %.o 20 | $(CC) $(LDFLAG) $< -o $@ 21 | 22 | 23 | .PRECIOUS: %.o 24 | 25 | .PHONY: clean 26 | clean: 27 | -rm -f *.o *.out *.run *.bin *.asm 28 | -------------------------------------------------------------------------------- /lab/14-process-intro/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | int i = 0; 7 | while (1) { 8 | i++; 9 | // printf("pid = %d\n", getpid()); 10 | // sleep(1); 11 | } 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /lab/15-locking/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | AS = as 3 | LD = ld 4 | OD = objdump 5 | OC = objcopy 6 | CCFLAG = -m32 -O0 -pthread -g 7 | ASFLAG = 8 | LDFLAG = -m32 -O0 -pthread 9 | ODFLAG = -S 10 | OCFLAG = -S 11 | GDB = gdb 12 | OBJS = $(patsubst %.c,%.out,$(wildcard *.c)) 13 | ASMS = $(patsubst %.c,%.asm,$(wildcard *.c)) 14 | 15 | all: $(OBJS) $(ASMS) 16 | 17 | %.o: %.c 18 | $(CC) $(CCFLAG) -c -o $@ $< 19 | 20 | %.asm: %.o 21 | $(OD) $(ODFLAG) -d $< > $@ 22 | 23 | %.out: %.o 24 | $(CC) $(LDFLAG) $< -o $@ 25 | 26 | 27 | .PRECIOUS: %.o 28 | 29 | .PHONY: clean 30 | clean: 31 | -rm -f *.o *.out *.run *.bin *.asm 32 | -------------------------------------------------------------------------------- /lab/15-locking/atomic.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define NUM_THREADS 5 11 | #define NUM_INCRESE 100000 12 | 13 | atomic_int count = ATOMIC_VAR_INIT(0); 14 | 15 | void *run(void *t) 16 | { 17 | for (int i = 0; i < NUM_INCRESE; ++i) { 18 | atomic_fetch_add(&count, 1); 19 | } 20 | pthread_exit(NULL); 21 | } 22 | 23 | int main() 24 | { 25 | pthread_t threads[NUM_THREADS]; 26 | int rc; 27 | int t; 28 | void *result; 29 | 30 | // create threads 31 | for (t = 0; t < NUM_THREADS; t++) { 32 | rc = pthread_create(&threads[t], NULL, run, (void *)t); 33 | assert(rc == 0); 34 | } 35 | 36 | // join threads 37 | for (t = 0; t < NUM_THREADS; t++) { 38 | rc = pthread_join(threads[t], &result); 39 | assert(rc == 0); 40 | } 41 | 42 | // print result 43 | printf("Main: count=%d\n", atomic_load(&count)); 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /lab/15-locking/mutex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define NUM_THREADS 5 11 | #define NUM_INCRESE 100000 12 | 13 | int count = 0; 14 | pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 15 | 16 | void *run(void *t) 17 | { 18 | for (int i = 0; i < NUM_INCRESE; ++i) { 19 | pthread_mutex_lock(&mutex); 20 | count++; 21 | pthread_mutex_unlock(&mutex); 22 | } 23 | pthread_exit(NULL); 24 | } 25 | 26 | int main() 27 | { 28 | pthread_t threads[NUM_THREADS]; 29 | int rc; 30 | int t; 31 | void *result; 32 | 33 | // create threads 34 | for (t = 0; t < NUM_THREADS; t++) { 35 | rc = pthread_create(&threads[t], NULL, run, (void *)t); 36 | assert(rc == 0); 37 | } 38 | 39 | // join threads 40 | for (t = 0; t < NUM_THREADS; t++) { 41 | rc = pthread_join(threads[t], &result); 42 | assert(rc == 0); 43 | } 44 | 45 | // print result 46 | printf("Main: count=%d\n", count); 47 | 48 | pthread_mutex_destroy(&mutex); 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /lab/15-locking/no-lock.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define NUM_THREADS 5 11 | #define NUM_INCRESE 100000 12 | 13 | int count = 0; 14 | 15 | void *run(void *t) 16 | { 17 | for (int i = 0; i < NUM_INCRESE; ++i) { 18 | count++; 19 | } 20 | pthread_exit(NULL); 21 | } 22 | 23 | int main() 24 | { 25 | pthread_t threads[NUM_THREADS]; 26 | int rc; 27 | int t; 28 | void *result; 29 | 30 | // create threads 31 | for (t = 0; t < NUM_THREADS; t++) { 32 | rc = pthread_create(&threads[t], NULL, run, (void *)t); 33 | assert(rc == 0); 34 | } 35 | 36 | // join threads 37 | for (t = 0; t < NUM_THREADS; t++) { 38 | rc = pthread_join(threads[t], &result); 39 | assert(rc == 0); 40 | } 41 | 42 | // print result 43 | printf("Main: count=%d\n", count); 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /lab/15-locking/single.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define NUM_THREADS 5 11 | #define NUM_INCRESE 100000 12 | 13 | int count = 0; 14 | 15 | void *run(void *t) 16 | { 17 | for (int i = 0; i < NUM_INCRESE; ++i) { 18 | asm volatile("add $1, %0" : "=r"(count) : "0"(count)); 19 | } 20 | pthread_exit(NULL); 21 | } 22 | 23 | int main() 24 | { 25 | pthread_t threads[NUM_THREADS]; 26 | int rc; 27 | int t; 28 | void *result; 29 | 30 | // create threads 31 | for (t = 0; t < NUM_THREADS; t++) { 32 | rc = pthread_create(&threads[t], NULL, run, (void *)t); 33 | assert(rc == 0); 34 | } 35 | 36 | // join threads 37 | for (t = 0; t < NUM_THREADS; t++) { 38 | rc = pthread_join(threads[t], &result); 39 | assert(rc == 0); 40 | } 41 | 42 | // print result 43 | printf("Main: count=%d\n", count); 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /lab/16-proc-sched/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | AS = as 3 | LD = ld 4 | OD = objdump 5 | OC = objcopy 6 | CCFLAG = -m32 -O0 -pthread -g 7 | ASFLAG = 8 | LDFLAG = -m32 -O0 -pthread 9 | ODFLAG = -S 10 | OCFLAG = -S 11 | GDB = gdb 12 | OBJS = $(patsubst %.c,%.out,$(wildcard *.c)) 13 | ASMS = $(patsubst %.c,%.asm,$(wildcard *.c)) 14 | 15 | all: $(OBJS) $(ASMS) 16 | 17 | %.o: %.c 18 | $(CC) $(CCFLAG) -c -o $@ $< 19 | 20 | %.asm: %.o 21 | $(OD) $(ODFLAG) -d $< > $@ 22 | 23 | %.out: %.o 24 | $(CC) $(LDFLAG) $< -o $@ 25 | 26 | 27 | .PRECIOUS: %.o 28 | 29 | .PHONY: clean 30 | clean: 31 | -rm -f *.o *.out *.run *.bin *.asm 32 | -------------------------------------------------------------------------------- /lab/16-proc-sched/lifecycle.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | int pid = fork(); 10 | if (pid < 0) { 11 | printf("fork() failed\n"); 12 | return 0; 13 | } 14 | 15 | if (pid == 0) { 16 | char *argv[] = { "ls", "-l", NULL }; 17 | char *envs[] = { NULL }; 18 | execve("/bin/ls", argv, envs); 19 | exit(0); 20 | } else { 21 | wait(NULL); 22 | } 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /lab/16-proc-sched/zombie.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | int pid = fork(); 10 | if (pid < 0) { 11 | printf("fork() failed\n"); 12 | return 0; 13 | } 14 | 15 | if (pid == 0) { 16 | sleep(10); 17 | printf("child exiting\n"); 18 | exit(0); 19 | } else { 20 | sleep(30); 21 | wait(NULL); 22 | printf("parent exiting\n"); 23 | } 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /lab/Makefile: -------------------------------------------------------------------------------- 1 | SUBDIRS = $(patsubst %/,%,$(shell ls -d */)) 2 | 3 | 4 | all: 5 | -for d in $(SUBDIRS); do ( \ 6 | [ -f $$d/Makefile ] && $(MAKE) -C $$d \ 7 | ); done 8 | 9 | clean: 10 | -for d in $(SUBDIRS); do ( \ 11 | [ -f $$d/Makefile ] && $(MAKE) -C $$d clean \ 12 | ); done 13 | -------------------------------------------------------------------------------- /readme.org: -------------------------------------------------------------------------------- 1 | #+TITLE: XV6 操作系统实践 2 | #+AUTHOR: Jinghui Hu 3 | #+EMAIL: hujinghui@buaa.edu.cn 4 | #+DATE: <2023-10-24 Tue> 5 | #+STARTUP: overview num indent 6 | 7 | #+BEGIN_QUOTE 8 | 如果觉得讲解得好,请喝一杯咖啡也是极好的 9 | #+END_QUOTE 10 | 11 | [[file:img/pay.jpg]] 12 | 13 | - Github [[https://github.com/Jeanhwea/xv6-course][link]] 14 | - Gitee 镜像 [[https://gitee.com/jeanhwea/course-xv6][link]] 15 | 16 | * 捐赠 17 | 好心的捐赠大佬可以备注一下: *xv6+昵称* 18 | 我会在下面会记录一下,感谢! 19 | 20 | | date | name | 21 | |------------------+------| 22 | | <2023-12-27 Wed> | V*i | 23 | | <2024-05-28 Tue> | M*N | 24 | 25 | * 简介 26 | XV6 是 MIT 开发的一个教学用的完整的类 Unix 操作系统,并且在 MIT 的操作系统课程 27 | 6.828 中使用 28 | 1. 它源自 Dennis Ritchie 和 Ken Thompson 的 Unix Version 6 (v6) 29 | 2. 通过阅读并理解 XV6 的代码,可以清楚地了解操作系统中众多核心的概念 (1w) 30 | 3. 目前 MIT XV6 操作系统支持 x86 和 riscv 两种体系结构 31 | 32 | * 主题 33 | - 序言 34 | 1) [[file:01-x86-arch.org][x86 体系结构]] | [[https://www.bilibili.com/video/BV1cw411z7Ro][视频 01]] 35 | 2) [[file:02-qemu-simulator.org][QEMU 模拟器]] | [[https://www.bilibili.com/video/BV1me411R7MN][视频 02]] 36 | 3) [[file:03-minimal-os.org][编写最小操作系统]] | [[https://www.bilibili.com/video/BV1Fe411975E][视频 03]] 37 | - 走进操作系统 38 | 1) [[file:11-os-overview.org][操作系统概览]] | [[https://www.bilibili.com/video/BV1vu4y1h7mR/][视频 11]] 39 | 2) [[file:12-from-asm-to-c.org][从汇编到 C 语言]] | [[https://www.bilibili.com/video/BV1hM411Q7eb/][视频 12]] | [[https://www.bilibili.com/video/BV1a94y1G7HV/][视频 12a]] 40 | 3) [[file:13-xv6-startup.org][xv6 启动流程分析]] | [[https://www.bilibili.com/video/BV1az4y1A7zU/][视频 13]] 41 | - 内存管理 42 | 1) [[file:21-page-table.org][x86 体系结构分页机制]] | [[https://www.bilibili.com/video/BV1CC4y1778j/][视频 21]] 43 | 2) [[file:22-mem-init.org][开启分页及 freelist 初始化]] | [[https://www.bilibili.com/video/BV1bQ4y1n7iE/][视频 22]] 44 | 3) [[file:23-kmem-pgtab.org][内核页表初始化]] | [[https://www.bilibili.com/video/BV1Ew411x77A/][视频 23]] | [[https://www.bilibili.com/video/BV1ng4y19751/][视频 23a]] 45 | - 进程管理 46 | 1) [[file:31-intro-process.org][进程创建]] | [[https://www.bilibili.com/video/BV1Nz4y1A7BW/][视频 31]] 47 | 2) [[file:32-init-start.org][调度器启动及切换用户态地址空间]] | [[https://www.bilibili.com/video/BV1fu4y1N7D4/][视频 32]] 48 | 3) [[file:33-context-switch.org][上下文切换及内核态执行]] | [[https://www.bilibili.com/video/BV11g4y1Q7Ux/][视频 33]] 49 | 4) [[file:34-enter-shell.org][从 initcode 到 shell 启动]] | [[https://www.bilibili.com/video/BV1Fw411K7pE/][视频 34]] | [[https://www.bilibili.com/video/BV1eb4y1M7ie/][视频 34a]] | [[https://www.bilibili.com/video/BV1DG411U7vo/][视频 34b]] 50 | - 中断管理 51 | 1) [[file:41-intro-interrupt.org][中断初始化及调用流程]] | [[https://www.bilibili.com/video/BV14G411D74x][视频 41]] 52 | 2) [[file:42-exec-syscall.org][exec系统调用流程分析]] | [[https://www.bilibili.com/video/BV1194y1H7Wt/][视频 42]] 53 | 3) [[file:43-hw-interrupt.org][中断及设备初始化]] | [[https://www.bilibili.com/video/BV1XN411T7V7/][视频 43]] 54 | 4) [[file:44-multi-core-boot.org][多核处理器启动流程]] | [[https://www.bilibili.com/video/BV1Kb4y1u7zg/][视频 44]] 55 | 5) [[file:45-disk-driver.org][磁盘驱动]] | [[https://www.bilibili.com/video/BV1nC4y1P7yh/][视频 45]] 56 | - 锁与调度 57 | 1) [[file:51-locking.org][同步与锁]] | [[https://www.bilibili.com/video/BV1Lc411Q7Wr/][视频 51]] 58 | 2) [[file:52-sleeplock.org][sleeplock 与死锁]] | [[https://www.bilibili.com/video/BV1eM411o7cK/][视频 52]] 59 | 3) [[file:53-scheduling.org][调度、睡眠与唤醒]] | [[https://www.bilibili.com/video/BV1KN4y127NB/][视频 53]] 60 | 4) [[file:54-proc-lifecycle.org][进程生命周期]] | [[https://www.bilibili.com/video/BV1zG411i7fs][视频 54]] 61 | 5) [[file:55-pipe.org][管道]] | [[https://www.bilibili.com/video/BV1Lj411j7Fe/][视频 55]] 62 | - 文件系统 63 | 1) [[file:61-fs-overview.org][文件系统概览]] | [[https://www.bilibili.com/video/BV1ac411S7dL/][视频 61]] 64 | 2) [[file:62-buffer.org][Disk 和 Buffer]] | [[https://www.bilibili.com/video/BV1Lu4y1V73q/][视频 62]] 65 | 3) [[file:63-logging.org][Log 层]] | [[https://www.bilibili.com/video/BV1QN411L7S4/][视频 63]] 66 | 4) [[file:64-inode.org][inode 层]] | [[https://www.bilibili.com/video/BV1Nu4y1G757/][视频 64]] 67 | 5) [[file:65-directory-path.org][directory 层和 pathname 层]] | [[https://www.bilibili.com/video/BV1YC4y1R7Bn/][视频 65]] 68 | 6) [[file:66-file-descriptor.org][文件描述符层]] | [[https://www.bilibili.com/video/BV1ec41117jr/][视频 66]] 69 | - 总结 70 | 1) 总结 | [[https://www.bilibili.com/video/BV1eg4y1y7cm/][视频 90]] 71 | 72 | * 总结 73 | #+BEGIN_SRC sh 74 | date 75 | #+END_SRC 76 | 77 | #+RESULTS: 78 | : Tue Dec 12 11:18:46 PM CST 2023 79 | 80 | 1. 开始录制时间 2023 年 10 月 23 日 81 | 2. 内存相关 82 | - 但是很多操作系统的实现更加精巧;例如, 83 | - xv6 不能向磁盘中请求页, 84 | - 没有实现 copy-on-write 的 fork 操作 85 | - fork -> exec 86 | - 共享内存和惰性分配页(lazily-allocated page) 87 | - malloc(..) 88 | - x86 支持段式内存转换,但 xv6 仅用它来实现 proc 这种有固定地址 89 | - segment 90 | - paging 91 | - 在内存较多的机器上使用 x86 的 4MB 大小的“超级页”, xv6 不支持 92 | - database join/select 93 | 3. 进程相关 94 | - 我们最好以锁为基础来构建高级的同步队列,虽然 xv6 并没有这么做 95 | - spinlock/sleeplock 96 | - queue, TaskPoolExecutor/ 信号量 semaphore 97 | - 让每个进程都有优先级。主要思想是优先处理高优先级的可运行进程。 98 | - proc.c 优先级 nicevalue 99 | - 但是由于要权衡多项指标,例如要保证公平性和高的吞吐量,调度算法往往很快变得复杂起来 100 | - 复杂的调度算法还会无意中导致像优先级倒转(priority inversion)和护航(convoy)这样的现象 101 | - Linux 内核的 sleep 用一个显式的进程队列代替 xv6 中的等待队列(wait channel);而该队列本身内部还有锁 102 | - sleep/wakeup 103 | - 信号量是另一种合作机制 104 | 4. 驱动相关 105 | - 用户在读一个文件的时候,这个文件的数据将会被拷贝两次。 106 | - 第一次是由驱动从硬盘拷贝到内核内存,之后通过 read 系统调用,从内核内存拷贝到用户内存。 107 | - ide => bcache buf->data 108 | - dinode => inode 109 | - 零拷贝 110 | - 日志记录不是唯一的崩溃后的恢复机制 111 | - recover_from_log / redo log 112 | - 比如,UNIX 系统中的 fsck 命令来检查每个文件和目录以及各个块和 i 节点可用的链表, 113 | - 查找并解决出现的不一致问题 114 | - 如果磁盘操作失败,xv6 报警 115 | - 使用冗余来掩饰磁盘错误 116 | - RAID 冗余磁盘阵列 117 | 118 | * 答疑 119 | 1. 12a 答疑一 [[file:12a-Q&A.org]] | [[https://www.bilibili.com/video/BV1a94y1G7HV/][视频 12a]] 120 | 2. 34a 答疑二 [[file:34a-Q&A.org]] | [[https://www.bilibili.com/video/BV1eb4y1M7ie/][视频 34a]] 121 | 2. 34b 答疑三 [[file:34b-Q&A.org]] | [[https://www.bilibili.com/video/BV1DG411U7vo/][视频 34b]] 122 | 123 | * 资源 124 | 1. MIT 课程官网 [[https://pdos.csail.mit.edu/6.828/2018/][6.828]] 125 | 2. x86 版本 [[https://github.com/mit-pdos/xv6-public][xv6-public]] 126 | 3. riscv 版本 [[https://github.com/mit-pdos/xv6-riscv][xv6-riscv]] 127 | 4. gas 手册 [[https://sourceware.org/binutils/docs/as/index.html][gas]] 128 | 5. Unix 源代码 [[https://www.tuhs.org/][Unix Heritage Society]] 129 | -------------------------------------------------------------------------------- /scripts/build-bochs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | PREFIX=/opt/bochs-2.7 3 | FILEGZ="$HOME/down/bochs-2.7.tar.gz" 4 | 5 | sudo apt-get install -y libsdl2-dev xorg-dev libx11-6 libxpm4 libxrandr2 6 | 7 | if [ ! -f $FILEGZ ]; then 8 | curl https://jaist.dl.sourceforge.net/project/bochs/bochs/2.7/bochs-2.7.tar.gz -o $FILEGZ 9 | fi 10 | 11 | mkdir -p ~/build 12 | cd ~/build 13 | 14 | rm -rf bochs-2.7-* 15 | 16 | tar xzvf $FILEGZ 17 | mv bochs-2.7 bochs-2.7-gdb 18 | cd bochs-2.7-gdb 19 | 20 | ./configure --prefix=$PREFIX-gdb \ 21 | --enable-cpu-level=6 \ 22 | --enable-fpu \ 23 | --enable-x86_64 \ 24 | --enable-vmx \ 25 | --enable-svm \ 26 | --enable-avx \ 27 | --enable-all-optimizations \ 28 | --enable-readline \ 29 | --enable-gdb-stub \ 30 | --enable-debugger-gui \ 31 | --enable-x86-debugger \ 32 | --enable-iodebug \ 33 | --enable-logging \ 34 | --enable-ne2000 \ 35 | --enable-cdrom \ 36 | --disable-plugins \ 37 | --disable-docbook \ 38 | --with-x --with-x11 --with-term --with-sdl2 39 | 40 | make -j$(nproc) 41 | 42 | sudo make install 43 | 44 | cd ~/build 45 | tar xzvf $FILEGZ 46 | mv bochs-2.7 bochs-2.7-native 47 | cd bochs-2.7-native 48 | 49 | ./configure --prefix=$PREFIX-native \ 50 | --enable-cpu-level=6 \ 51 | --enable-all-optimizations \ 52 | --enable-x86-64 \ 53 | --enable-pci \ 54 | --enable-smp \ 55 | --enable-vmx \ 56 | --enable-readline \ 57 | --enable-debugger \ 58 | --enable-debugger-gui \ 59 | --enable-logging \ 60 | --enable-fpu \ 61 | --enable-3dnow \ 62 | --enable-sb16=dummy \ 63 | --enable-cdrom \ 64 | --enable-x86-debugger \ 65 | --enable-iodebug \ 66 | --disable-plugins \ 67 | --disable-docbook \ 68 | --with-x --with-x11 --with-term --with-sdl2 69 | 70 | make -j$(nproc) 71 | 72 | sudo make install 73 | -------------------------------------------------------------------------------- /scripts/build-qemu.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | VERSION=5.2.0 3 | # VERSION=2.3.0 4 | PREFIX=/opt/qemu 5 | FILEGZ="$HOME/down/qemu-${VERSION}.tar.xz" 6 | 7 | if [ ! -f $FILEGZ ]; then 8 | curl https://download.qemu.org/qemu-${VERSION}.tar.xz -o $FILEGZ 9 | fi 10 | 11 | # https://wiki.qemu.org/Hosts/Linux 12 | sudo apt-get install -y git libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev ninja-build \ 13 | git-email \ 14 | libaio-dev libbluetooth-dev libcapstone-dev libbrlapi-dev libbz2-dev \ 15 | libcap-ng-dev libcurl4-gnutls-dev libgtk-3-dev \ 16 | libibverbs-dev libjpeg8-dev libncurses5-dev libnuma-dev \ 17 | librbd-dev librdmacm-dev \ 18 | libsasl2-dev libsdl2-dev libseccomp-dev libsnappy-dev libssh-dev \ 19 | libvde-dev libvdeplug-dev libvte-2.91-dev libxen-dev liblzo2-dev \ 20 | valgrind xfslibs-dev \ 21 | libnfs-dev libiscsi-dev 22 | 23 | 24 | cd ~/build 25 | rm -rf qemu-* 26 | tar xvf $FILEGZ 27 | mkdir qemu-build-${VERSION} 28 | cd qemu-build-${VERSION} 29 | 30 | CFG_EXTRA="" 31 | if [ X"$VERSION" == X"2.3.0" ]; then 32 | CFG_EXTRA="--python=/usr/bin/python2" 33 | fi 34 | 35 | echo "configure" 36 | ../qemu-${VERSION}/configure --prefix=/opt/qemu-${VERSION} $CFG_EXTRA 37 | 38 | echo "make" 39 | make -j$(nproc) 40 | make install 41 | -------------------------------------------------------------------------------- /version: -------------------------------------------------------------------------------- 1 | v1.0.4 2 | --------------------------------------------------------------------------------