首页 > > 详细

讲解 ECE391、辅导 C/C++设计编程

CP2.1
ECE391: Computer Systems Engineering Spring 2025
Machine Problem 3
Contents
1 Introduction
2 Using the Group Repository
3 The Pieces
Checkpoint 1: Mon April 7 18:00 CDT
Checkpoint 2:
Illinix 391
Fri April 18 18:00 CDT
3.1 Gettingstarted.......................................... 3 3.2 WorkPlan............................................ 3
4 Testing
5 What to Hand in
4
4
5.1 Checkpoint 1: Filesystem and Drivers and Program Loading, (Oh My!)
5.1.1 GroupRepository.................................... 4 5.1.2 VIRTIOBlockDevice ................................. 4 5.1.3 Read-OnlyFilesystem ................................. 5 5.1.4 YourFriendlyNeighborhoodMemio ......................... 6 5.1.5 Lockedand... ..................................... 6 5.1.6 ...(ELF)Loaded.................................... 7 5.1.7 CacheMeOutside ................................... 7 5.1.8 ExistingFiles...................................... 8 5.1.9 RunningYourCode .................................. 9 5.1.10 Troubleshooting/Debugging .............................. 9 5.1.11 Checkpoint1Handin.................................. 9
5.2 Checkpoint2:VirtualMemoryandProcessAbstraction. . . . . . . . . . . . . . . . . . . . 9
1
. . . . . . . . . . . 4
2
2
3

CP2.1
5.2.1 GivenCode....................................... 9 5.2.2 ViciousVirtualMemory ................................ 10 5.2.3 PeskyProcesses .................................... 11 5.2.4 SuspiciousSyscalls................................... 13 5.2.5 FullyImplementedFilesystem............................. 14 5.2.6 AdditionalModifications................................ 15 5.2.7 Checkpoint2Handin.................................. 16
6 Grading 17
7 Appendix A: The File System 18
7.1 FileSystemOverview...................................... 18 7.2 FileAbstractions ........................................ 20 7.3 BuildingtheFileSystem .................................... 21 7.4 TheBlob ............................................ 22
8 Appendix B: I/O Devices 23
8.1 Overview ............................................ 23 8.2 I/OOperations ......................................... 23 8.3 I/ODiagram........................................... 24
9 Appendix C: The Process Abstraction 25
9.1 History ............................................. 25 9.2 Overview ............................................ 25 9.3 Context Switching between User-Mode and Supervisor-Mode . . . . . . . . . . . . . . . . 25
10 Appendix D: RISC-V Paging 28
10.1IllinixMappings......................................... 28 10.2Sv39............................................... 29
11 Appendix H: Troubleshooting 30
11.1Debuggingwithgdb ...................................... 30 11.1.1 DebuggingtheKernel ................................. 30 11.1.2 DebuggingUserPrograms ............................... 31
2

CP2.1
11.1.3 UsefulgdbCommands................................. 31
3

CP2.1
1 Introduction
Read the whole document before you begin, or you may miss points on some requirements.
In this machine problem, you collaborate to develop the core of an operating system roughly based on Unix Version 6, with modern concepts peppered in where appropriate. You’ll implement interrupt logic, user threading (a` la MP2), kernel and application paging, initialize some devices and a filesystem with the VIRTIO interface, and create a system call interface to support various system calls. The operating system will support running several tasks (“threads”) spawned by a number of user programs; programs will interface with the kernel via system calls.
Don’t worry, these aren’t all “boring” programs—you’ll get to run some cool games, too.
The goal for the assignment is to provide you with hands-on experience in developing the software used to interface between devices and applications, i.e., operating systems. You should notice that the work here builds on concepts from the other machine problems. Many of the abstractions used here (e.g., the VIRTIO interface) have been simplified to reduce the effort necessary to complete the project, but we hope that you will leave the class with the skills necessary to extend the implementation that you develop here along what- ever direction you choose, by incrementally improving various aspects of your system.
2 Using the Group Repository
You should receive access to a shared repository with your group members. Your group has a two-digit group number; your group number should be released in a Piazza post, and your repository should be named something like mp3 group xx, where xx is your number.
It is required to run the following commands on each computer that you clone the repository to, in order to make sure the line endings are set to LF (Unix style):
git config --global core.autocrlf input
git config --global core.eol lf
Some other tips:
As you work on MP3 with your teammates, you may find it useful to create additional branches to avoid conflicts while editing source files. Remember to git pull each time you sit down to work on the project and git commit and git push when you are done. Doing so will ensure that all members are working on the most current version of the sources. It is highly likely that you will benefit from proper usage of the git stashcommand,tocorrectlyretaindesired(local)changeswhendoingapull.
When using Git, keep in mind that it is, in general, bad practice to commit broken sources. You should make sure that your sources compile correctly before committing them to the repository. Make sure not to commit compiled ‘*.o’ files, besides what we have given you. You can modify your .gitignore in any way you want.
Finally, merge your changes into the main branch by each checkpoint deadline, as this is the only branch we will use for grading.
4

CP2.1
3 The Pieces
The basic OS is provided, but when you first get it, it won’t properly do a lot of the things we expect an OS to do: perhaps most importantly †, it can’t play any cool games!
For simplicity, we will stick to text-mode graphics (for the most part), but your OS will, by the end, run the games from previous MPs as well as some new ones. We’ve included a few helpful pieces that will allow you to debug easier, such as kprintf. We highly encourage use of gdb to debug. Print statements can only take you so far. See Appendix H for details.
3.1 Getting started
In order to effectively work on this MP, you will need to develop knowledge of a lot of different and difficult concepts. The documentation on the course website and lectures will give you background information, but the best way to learn is to read and understand the code we have provided. This includes the Makefiles, .ld linker files, and .c/.h source files.
This MP is difficult, which is why we do not expect you to work alone. While you cannot share code or discuss details with other groups or anyone outside your group, you should work together with your team to get unstuck and build a common understanding.
3.2 Work Plan
“Work expands so as to fill the time available for its completion.” - Cyril Parkinson
This project is somewhat daunting, and will require efforts from all your team members. You should partition the work accordingly to allow independent progress by all team members.
Setting up a clean testing interface will also help substantially with partitioning the work, since group mem- bers can finish and test components before your groupmates finish the other parts (yet). The abstractions suggested should allow for some spots where a “working part” can be substituted with a functionally equiv- alent placeholder, so to speak—more on that later.
While splitting up the work allows for you to make more progress, it is still crucial that you spend time working together to integrate all parts. You should also be maintaining active communication between group members to make sure you all have an understanding of how all your code works. Even if you did not work on a specific section, we expect you to be familiar with how the code works and be able to explain what it and how it fits into your kernel.
Throughout the first part of this semester and in most/all of your previous classes, MPs were more structured. You were given a set of functions to write, and you only modified those functions. One of the goals of this class is to make you into a more confident and thoughtful programmer by having you practice software design. We have deliberately left the implementation of certain sections open-ended. It is up to you and your group to find a way to meet the requirements - as with the real world, there is no “perfect” solution. The only requirement that we impose is that you follow the function interfaces that we have already specified - feel free create your own helper functions or creative implementations.
†Some may disagree that this is the most important thing, but who wants no games? 5

CP2.1
4 Testing
For this project, we strongly recommend that you write and run unit tests with adequate coverage of your source code.
As your operating system components are dependent on one another, you may find it useful to unit test each component individually to isolate any design or coding bugs.
You should create a main tests.c file and add it to your Makefile. This file should create a kernel image (e.g., test.elf) that you can load into QEMU and run tests with. As you add more components to your operating system, we encourage you to add corresponding tests that verify the functionality of each component at the interface level.
Keep in mind that passing all of your unit tests does not guarantee bug free code. However, the test suite provides a convenient means to run your tests frequently without having to re-debug older components as you add new functionality.
5 What to Hand in
5.1 Checkpoint 1: Filesystem and Drivers and Program Loading, (Oh My!)
The primary motivation of this checkpoint is to get 2 test programs, which we’ve given you, to run. hello will print some text to the UART screen, then stop. trek will run the same text-based Star Trek game that you know and love.
Rather than do this in a “hacky” way, we want to set up some key infrastructure now which will pay dividends as the project continues on.
Note: Please ensure that all the functions that we specify do not induce a kernel panic on an invalid input. You should return an error code (which one is up to you) if possible, otherwise do nothing.
For the checkpoint, you must have the following accomplished:
5.1.1 Group Repository
You must have your code in the shared group repository, and each group member should be able to demon- strate that they can read and change the source code in the repository.
5.1.2 VIRTIO Block Device
An operating system in general must communicate with external devices. One such device is obviously the real drive/disk (virtual, in this case) which contains programs and other files you want your operating system to have access to.
In order to set up this device (and any others down the line), we will need to set up the necessary framework for the VIRTIO block device. Your group must finish the implementation based on the VIRTIO documenta- tion linked on the course website.
6

CP2.1
You may find it especially helpful to read sections 2.1-2.7, 3.1, 4.2, and 5.2 and recall your implementation of viorng.c. Skip anything related to ”legacy” interface.
More information about the functions that you have to write can be found on the Doxygen site linked on the course website.
1. void vioblk attach(volatile struct virtio mmio regs * regs, int irqno)
2. int vioblk open(struct io ** ioptr, void * aux)
3. void vioblk close(struct io * io)
4. long vioblk readat(struct io * io, unsigned long long pos,
void * buf, long bufsz)
5. long vioblk writeat(struct io * io, unsigned long long pos, const void * buf, long len)
6. vioblk cntl(struct io * io, int cmd, void * arg)
7. vioblk isr(int irqno, void * aux)
See Appendix B for more information on the io struct. 5.1.3 Read-Only Filesystem
Broadly speaking, your filesystem driver should provide a comfortable interface to open, read and scan through files. In later checkpoints, you will add additional functionality.
Your ktfs.c file will need to interact with its backing device with some intermediate cache in cache.c to actually interact with the “physical” (well, virtual) device, so be sure that you understand what’s going on in files related to both the cache and the backing device. Additionally, as this interacts with virtual devices, you should be sure that you use the io struct in the proper way.
More information about the functions that you have to write can be found on the Doxygen site linked on the course website.
These functions should be written in ktfs.c:
1. int ktfs mount(struct io * io)
2. int ktfs open(const char * name, struct io ** ioptr)
3. void ktfs close(struct io * io)
4. long ktfs readat(struct io * io, unsigned long long pos, void * buf, long len)
5. int ktfs cntl(struct io * io, int cmd, void * arg)
6. int ktfs flush(void)
7

CP2.1
In order to use the filesystem, we have provided a mkfs ktfs function (see Appendix A) that generates a filesystem image for you. This filesystem image is mounted by QEMU as a drive (using the Makefile we provide) and is accessible through VIRTIO.
For every “device” that uses io, you will need to implement “iocntl” IOCTL GETEND and IOCTL GETBLKSZ. Keep in mind that IOCTL GETBLKSZ should not return the filesystem block size, but the “block size” of the file IO object - in this case, 1.
(Hint: implement the memio and its related functions to test your KTFS filesystem driver as well as the cache without the vioblk.)
See Appendix A for additional details.
5.1.4 Your Friendly Neighborhood Memio
You may wonder why we need a memio device if we already have a vioblk device? The memio device is a helper backing device that allows you to test your filesystem and ELF loader without using vioblk. Within your linker script kernel.ld, there is a section from kimg blob start to kimg blob start that you can use to place your whole filesystem image or an ELF file to load (see Appendix A about the blob).
More information about the functions that you have to write can be found on the Doxygen site linked on the course website.
For better unit testing and debugging, you must implement the memio device “driver” in io.c. The following is a list of functions you need to implement:
1. struct io * create memory io(void * buf, size t size)
2. int memio cntl(struct io * io, int cmd, void * arg)
3. long memio readat(struct io * io, unsigned long long pos, void * buf, long bufsz)
4. long memio writeat(struct io * io, unsigned long long pos, const void * buf, long bufsz)
5.1.5 Locked and . . .
Locks prevent concurrency issues when multiple threads are accessing the same resource. You’ll need to implement the following functions and make modifications to your existing functions in thread.c. You should also modify your current thread struct so that it includes a member called lock list that holds a linked list of all locks held by the thread.
Note: In order to prevent deadlocks, an exiting thread must release all held locks.
More information about the functions that you have to write can be found on the Doxygen site linked on the
course website.
You should implement the following functions in thread.c:
8

CP2.1
1. void lock init(struct lock * lock)
2. void lock acquire(struct lock * lock) 3. void lock release(struct lock * lock)
5.1.6 . . . (ELF) Loaded
One of the key roles of the operating system is to be able to run other programs.
We want to be able to run many user-level programs, but for now you can focus on hello and trek. While we’ve given you the pre-compiled binary of trek, you’ll have to compile hello using the usr/Makefile. Because of this, you also have access to hello.c. All the binaries are in a format called ELF (Executable and Linkable Format), which has a specific layout — it is the standard for Unix and Unix-like systems, historically, which means it is still very relevant. See the Tools, References, and Links page on the course website for the Linux manual page on ELF. Your loader will only need to deal with the program headers, not sections, so focus on that documentation.
Notice that since elf load should support any compliant I/O interface, that we can in general load an ELF from “any source” as long as ioread and ioseek are implemented in the given io (see Appendix B). (Hint: memio)
5.1.7 Cache Me Outside
“Software gets slower faster than hardware gets faster.” -Wirth’s Law
This semester, you will implement a caching system to cache blocks from a backing interface. As you’ve learned in this course, communicating with devices is extremely slow relative to the CPU’s clock cycle. Previously, you’ve implemented asynchronous communication (condition variables) so that while one thread is waiting on a device response, another thread can run.
While this significantly reduces the problem of “wasting” CPU cycles, it does not eliminate the problem of latency - the original thread still must wait a long time for the device’s response. A cache is a commonly used way to reduce this latency.
Once you complete this checkpoint, your backing interface will be the VIRTIO block device, but we will refer to it generically as the backing interface in this document. Rather than reading/writing a block directly from/to the backing interface, we first check whether the block exists in the cache. If the block exists in the cache, we access the block via the cache rather than sending a new request to the backing interface. If the block does not exist in the cache, we read it from the backing device into the cache. Note that you may or may not need to evict a block currently in the cache in order to bring in this new block.
Your cache may have any level of associativity and may be write-back, write-through, or some other con- coction. Please note that we are intentionally leaving the specific details of the cache vague; this is intended to be a design exercise.
Some additional scenarios/specifications for the cache are as follows (these would be good test cases for you to write):
9

CP2.1
• Initially (when the OS is first started), the cache is empty.
• If the cache is initially empty and we read 64 contiguous blocks into it (e.g. 5, 6, 7, ..., 68), all 64 of those blocks must remain in the cache. This means that a read within any of those blocks should not generate a request to the backing device. (Hint: this should indicate to you that your cache capacity must be at least 64 blocks.)
• If cache get block() is called and block k is read into the cache, block k should remain in the cache at least until cache get block() is called again (note that block k may remain in the cache for longer). This is to say, if we call cache get block() once and do not call it again, the block that was cached must remain in the cache.
More information about the functions that you have to write can be found on the Doxygen site linked on the course website.
You will implement the following 4 functions in cache.c:
1. int create cache(struct io * bkgio, struct cache ** cptr)
2. int cache get block(struct cache * cache, unsigned long long pos, void ** pptr) 3. void cache release block(struct cache * cache, void * pblk, int dirty)
4. int cache flush(struct cache * cache)
As a reminder, you can also create any other helper functions that you need for your cache implementation.
5.1.8 Existing Files
During MP2, you worked with the PLIC, UART, and some other devices. You will be re-using that code for this checkpoint. You should add the following files into sys from MP2. They must be fully functional and (besides thread.c) should have the same functionality as a completed MP2. You can collaborate with your MP3 groupmates to choose whose MP2 code to use.
• plic.c/.h
• rtc.c/.h
• uart.c/.h
• timer.c/.h • thread.c/.h • thrasm.s
• viorng.c
10

CP2.1
5.1.9 Running Your Code
Finally, once you have all of your code completed, you will need to finish sys/main.c to run your program. We’ve left some comments on how to run trek, you may also find it helpful to refer to your MP2 CP3 main function implementation. You can also run hello or another user program that you create.
5.1.10 Troubleshooting/Debugging
See Appendix H for more information about debugging and common issues.
5.1.11 Checkpoint 1 Handin
For handin, your work must be completed and pushed to the main branch of your team’s GitHub remote repository by the deadline.
For this checkpoint you must complete the following:
• Read-write operations on vioblk
• Read-only filesystem
• Read-write operations on memio
• Read-write operations on the cache
• ELF Load
• Locks
Checkpoint 2: Virtual Memory and Process Abstraction
5.2
5.2.1 Given Code
Linked on the course website are some extra files which are needed for the Checkpoint 2 implementation.
Add them to your repo before starting Checkpoint 2 development. Files to add to the sys directory:
1. memory.c 2. memory.h 3. process.c 4. process.h 5. syscall.c
11

CP2.1
6. heap0.c - This should replace your existing heap0.c and uses your virtual memory functions. trek cp2 - This is a new binary for trek and uses U-mode and system calls. You should place it in your
usr/bin directory.
5.2.2 Vicious Virtual Memory
In operating systems, paging is a memory management technique that allows non-contiguous allocation and efficient use of storage by dividing a process’s address space into fixed-size units called pages. Page tables exist to maintain mappings between physical and virtual memory. When a process accesses a virtual address, the page table is queried to find the corresponding physical address. A page fault occurs when a process tries to access a virtual address that is not mapped to a physical address. If you handle a page fault that occurs within the User-owned virtual memory address space (USER START VMA to USER END VMA) you should allocate a new page and map that address as U-mode accessible. This is known as “lazy allocation” or “demand paging”. For page faults that occur in other virtual memory regions, you should panic.
The operating system needs to support both single-page and multi-page allocations. When a process requires memory, the system selects the best-fit chunk from the free page list (the smallest chunk that fits the requested number of pages) to ensure optimal memory utilization. Pages can also be freed individually or in groups to ensure efficient reuse. When memory is no longer needed, in order to prevent security risks and memory leakage, the system must reset. This ensures clearing of non-global pages and freeing the associated memory.
Flags are used in memory management to control access permissions for maintaining security and track page usage. They are critical in allocating memory, mapping and unmapping of pages, validating memory access, as well as handling page faults. Access faults occur when a process tries to access a virtual memory address that they do not have permissions to access.
Note: This checkpoint will only contain a single memory space. You will not need to create new memory spaces for this checkpoint besides the ”main” memory space.
We’ve given you a file, memory.c, where you will implement all the functions declared in memory.h. Keep in mind, many of these functions have overlap and it may be useful to look at the provided helper functions as well as write some of your own. You must write the following functions for this checkpoint:
In order to set up virtual to physical page mappings in your kernel, you must have a way to keep track of what physical pages are available to be mapped. To do this, we have created the “free chunk list”. A page chunk is a contiguous region of physical memory addresses. Each page chunk contains a pointer to the next page chunk in the list as well as a size (in pages). Initially, all of the memory from the end of the heap (heap end, which is page-aligned) until the end of RAM (defined in conf.h) are free physical pages. You must modify memory init to place all of these pages (in a single chunk) on the free chunk list.
Allocating and freeing physical pages must also interact with the free chunk list. To allocate physical page(s), you should go through the free chunk list and find a chunk that is greater than or equal the number of contiguous physical pages that you need to allocate. If there is no chunk large enough, you can panic. If there is a chunk, you should break off an appropriately-sized piece and provide a pointer to the start of the physical address range that was allocated. To free physical page(s), you can simply place the chunk back on the free chunk list - no need to coalesce chunks together.
Extra Credit Opportunity: Implement a more complex memory allocator (coalescing, buddy allocator, 12

CP2.1
etc.) and integrate it into your kmalloc function.
1. void memory init(void), most of this is given besides the initializing the free chunk list 2. void reset active mspace(void)
3. mtag t discard active mspace(void)
4. void * alloc phys page(void)
5. void * alloc phys pages(unsigned int cnt)
6. void free phys page(void * pp)
7. void free phys pages(void * pp, unsigned int cnt)
8. unsigned long free phys page count(void)
9. void * map page(uintptr t vma, void * pp, int rwxug flags)
10. void * map range(uintptr t vma, size t size, void * pp, int rwxug flags) 11. void * alloc and map range(uintptr t vma, size t size, int rwxug flags) 12. void set range flags(const void * vp, size t size, int rwxug flags)
13. void unmap and free range(void * vp, size t size)
14. int handle umode page fault(struct trap frame *tfr, uintptr t vma)
Some of these functions may build off of others. Some functions will also be called by functions used to
implement processes (§5.2.3). Your code must meet the functionality requirements outlined in the rubric. Consult Appendix D for more information about virtual memory.
5.2.3 Pesky Processes
The process abstraction is one of the key abstractions of an operating system. A process can be defined informally as just a ”running user program”. A user often wants to run multiple processes at once which requires common resources like processing power, devices, and memory to be managed.
An instance of a process structure contains everything that a process owns and uses internally. Each user process is actually just a wrapper around a kernel thread. What this means is whenever a user process is created, a process struct will have to be initialized to contain the information below:
1. An identifier for the current process
2. An identifier for the kernel thread related to this process 3. An identifier for the memory space of the process
13

CP2.1
4. A list of I/O interfaces for this process ; remember that an I/O interface can currently represent
• A terminal device
• An open file
• A block device
• An in-memory buffer
In this checkpoint, all user processes will share the same memory space, dubbed the ”main” memory space. The execution lifecycle of a process will be as follows
1. The kernel launches in S-mode.
2. procmgr init is called, creating a process struct around the main thread
3. process exec jumps to user mode and starts executing a user program. Effectively turning the main kernel thread into a user process
4. When the user process exits, the kernel exits
Our kernel space process API is made up of the functions below. These functions will reside in process.c and should be written by you unless if stated otherwise:
1. void procmgr init(void) (Provided)
Initializes processes globally by initializing a process structure for the main user process (init). The
init process should always be assigned process ID (PID) 0.
2. int process exec(struct io * exeio, int argc, char ** argv)
Executes a program referred to by the I/O interface passed in as an argument. We only require a
maximum of 16 concurrent processes.
Executing a loaded program with process exec has 4 main requirements:
(a) First any virtual memory mappings belonging to other user processes should be unmapped.
(b) Then a fresh 2nd level (root) page table should be created and initialized with the default map- pings for a user process. (This is not required for Checkpoint 2, as in Checkpoint 2 any user process will live in the ”main” memory space.)
(c) Next the executable should be loaded from the I/O interface provided as an argument into the mapped pages. (Hint: elf load)
(d) Finally, the thread associated with the process needs to be started in user-mode. (Hint: An as- sembly function in trap.s would be useful here)
Context switching was relatively trivial when both contexts were at the same privilege level (i.e. machine-mode to machine-mode switching or supervisor-mode to supervisor-mode switching), but now we need to switch from a more privileged mode (supervisor-mode) to less privileged mode (user-mode).
Doing so requires using clever tricks with supervisor-mode CSRs and supervisor-mode instruc- tions. Here are some tips to consider while implementing a context switch from supervisor-mode to user-mode
14

CP2.1
i. Considerinstructionsthatcantransitionbetweenlower-privilegemodesandhigherprivilege modes. Can you repurpose them for context switching purposes?
ii. If you did repurpose them for context switching purposes, what CSRs would you need to edit so that the transition would start the thread’s start function in user-mode?
It’s a useful exercise to try to figure out how such an approach could work with the CSRs and supervisor-mode instructions on your own. However, implementation on its own is a sufficient chal- lenge and we don’t require you to figure this out. You can read Appendix C to find out how you can carry out a context switch between user-mode to supervisor mode.
3. void process exit(void)
Cleans up after a finished process by reclaiming the resources of the process. Anything that was
associated with the process at initial execution should be released. This covers:
• Process memory space
• Open I/O interfaces
• Associated kernel thread
You will also have to modify your current threading library to accommodate for processes. To do this you will have to both declare the specified functions in your thread.h file as well as define them in your thread.c file.
The following functions will have to be implemented by you:
1. struct process * thread process(int tid)
2. struct process * running thread process(void)
3. void thread set process(int tid, struct process * proc)
Youalsowillhavetoaddastruct process * proctoyourthreadstruct
More information about the functions that you have to write can be found on the Doxygen site linked on the
course website.
5.2.4 Suspicious Syscalls
You will need to implement a series of system calls (syscalls) for this checkpoint. The user program uses these to request actions from the kernel.
The syscalls you must implement for this checkpoint are:
1. static int sysexit(void)
2. static int sysprint(const char *msg)
3. static int sysdevopen(int fd, const char *name, int instno);
15

CP2.1
4. static int sysfsopen(int fd, const char *name)
5. static int sysclose(int fd)
6. static long sysread(int fd, void *buf, size t bufsz)
7. static long syswrite(int fd, const void *buf, size t len) 8. static int sysioctl(int fd, int cmd, void *arg)
9. static int sysexec(int fd)
10. static int syswait(int tid)
11. static int sysusleep(unsigned long us)
12. static int sysfscreate(const char * name)
13. static int sysfsdelete(const char * name ) 14. void handle syscall(struct trap frame *tfr) 15. int64 t syscall(const struct trap frame *tfr)
The function handle syscall is used for system call linkage, through it calling the syscall function. Looking into the syscall.S, we see that sy
联系我们
  • QQ:99515681
  • 邮箱:99515681@qq.com
  • 工作时间:8:00-21:00
  • 微信:codinghelp
热点标签

联系我们 - QQ: 99515681 微信:codinghelp
程序辅导网!