xv6笔记:chapt1 Operating system interfaces
xv6 - the operating system
xv6 takes the traditional form of a kernel, a special program that provides services to running programs.
Each process has memory containing instructions, data, and a stack. the stack organizes the program’s procedure calls.
When a process needs to invoke a kernel service, it invokes a system call, one of the calls in the operating system’s interface. The system call enters the kernel; the kernel performs the service and returns. Thus a process alternates between executing in user space and kernel space.
The kernel uses the hardware protection mechanisms provided by a CPU: each process in the user space can access only its own memory
When a user program invokes a system call, the hardware raises the privilege level and starts executing a pre-arranged function in the kernel.
xv6 provides a subset of system calls that Unix kernel traditionally offer:
xv6’s services
shell - no mystery about it
- shell is a user program, not part of the kernel.
- shell illustrates the power of system call interface
- xv6’s shell is a simple implementation of the essence of Unix Bourne shell.
- It’s implementation can be found at(user/sh.c)
Processes and memory
create the process
int pid = fork();
1 | int |
exit the process
exit(0);
- exit system call let the calling process stop executing and to release resources (memory and open files)
wait the process
wait(0);// doesn't care about the exit status of the a child , pass 0
- wait system call return PID of an exited (or killed) child of current process, and copies the exit status of the child to the address passed to wait
- If the caller has no children, wait immediately returns -1.
exec new process
1 | case EXEC: |
The exec system call replaces the calling process’s memory with a new memory image loaded from a file stored in the file system.
When exec succeeds, it does not return to the calling program
Exec takes two arguments: the name of the file containing the executable and an array of string arguments.
I/O and File descriptors
File descriptor
What is file descriptor?
- A small integer representing a kernel managed object that a process may read from or write to.
How can we obtain file descriptor?
- By opening a file, directory, device
- By creating a pipe
- By duplicating an existing descriptor
What’s the benefit of the file descriptor interface?
- Abstracts away the difference between files, pipes, devices
- Making them all look like streams of bytes
File descriptors and process
1 | // Ensure that three file descriptors are open. |
- shell ensures that it always has three file descriptors open
Read and Write to Files
example : the essence of cat
: copies data from its standard input to its standard output
1 | char buf[512]; |
**Abstraction: **
- Cat doesn’t know whether it is reading from a file, console, or a pipe.
- Similarly cat doesn’t know whether it is printing to a console, a file, or whatever.
Close the file
releases a file descriptor
making it free for reuse by a future
open
,pipe
, ordup
system call (see below)A newly allocated file descriptor is always the lowest-numbered unused descriptor of the current process
IO redirection
1 | char *argv[2]; |
- fork copies file descriptor table, but share underlying file offset
1 | if(fork() == 0) { |
- dup duplicates the existing file descriptor, returning a new one refers to the same underlying object
1 | fd = dup(1); |
what is the meaning of 2>&1?
Tells the shell to give command a file descriptor 2 that is a duplicate of descriptor 1.
The xv6 shell doesn’t support I/O redirection for the error file descriptor, but now you know how to implement it.
Pipes
what is pipe?
- A pipe is a small kernel buffer exposed to process as a pair of file descriptors, one for reading and one for writing
- Writing data to one end of the pipe makes that data available for reading from the other end of the pipe.
- provide a way for processes to communicate
an example:
1 | int p[2]; |
if no data is available, a read on a pipe waits for either data to be written or for all file descriptors to be closed.
It’s important for the child to close the write end of the pipe before executing
wc
above.if one of wc ’s file descriptors referred to the write end of the pipe, wc would never see end-of-file
How does xv6 implement
grep fork sh.c | wc -l
?
1 | case PIPE: |
may create a tree of processes, The leaves of the tree are commands and the interior nodes are processes that wait until the left child and right child complete.
use pipes is much more efficient over temporary files:
echo hello world >/tmp/xyz; wc </tmp/xyz
File System
The following two code segments open the same file:
1
2
3
4
5chdir("/a");
chdir("b");
open("c", O_RDONLY);
open("/a/b/c", O_RDONLY);
create new files and directories
mkdir
creates a new directoryopen
with the O_CREATE flag creates a new data filemknod
creates a new device file.
1 | mkdir("/dir"); |
- file itself is code a inode, it has many names called links.
- each link consist of an entry in a directory
- the entry contains a file name and a reference to an inode
- the inode holds metadata about a file, including:
- its type(file, directory or device)
- length
- location on disk
- number of links
fstat
can retrieves info from the inode that a file descriptor refers to:
1 |
|
link
system call : creates another file system name referring to the same inode
1 | open("a", O_CREATE|O_WRONLY); |
unlink
system all: removes a name from the file system
1 | fd = open("/tmp/xyz", O_CREATE|O_RDWR); |
- Post title:xv6笔记:chapt1 Operating system interfaces
- Post author:sixwalter
- Create time:2023-08-05 11:14:26
- Post link:https://coelien.github.io/2023/08/05/course-learning/MIT6.S081/chapt1/
- Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.