系列文章 : [6.1810] 跟著 MIT 6.1810 學習基礎作業系統觀念
static uint64
argraw(int n)
{
struct proc *p = myproc();
switch (n) {
case 0:
return p->trapframe->a0;
case 1:
return p->trapframe->a1;
case 2:
return p->trapframe->a2;
case 3:
return p->trapframe->a3;
case 4:
return p->trapframe->a4;
case 5:
return p->trapframe->a5;
}
panic("argraw");
return -1;
}
// Fetch the nth 32-bit system call argument.
void
argint(int n, int *ip)
{
*ip = argraw(n);
}
argraw 回傳的值是一個 uint64 ( 8 bytes )。在不同情景下,這個 return value 會有不同的意義。
這裡是把 argraw 的 return value 視為 int ( 4 bytes ) 並回傳。
// Fetch the nth word-sized system call argument as a file descriptor
// and return both the descriptor and the corresponding struct file.
static int
argfd(int n, int *pfd, struct file **pf)
{
int fd;
struct file *f;
argint(n, &fd);
if (fd < 0 || fd >= NOFILE || (f = myproc()->ofile[fd]) == 0)
return -1;
if (pfd)
*pfd = fd;
if (pf)
*pf = f;
return 0;
}
n 個 syscall argument,並且將其視為一個 file descriptor,並且藉由 argfd function 的參數回傳 file descriptor ( int ) 以及相對應的 struct-file。// Allocate a file descriptor for the given file.
// Takes over file reference from caller on success.
static int
fdalloc(struct file *f)
{
int fd;
struct proc *p = myproc();
for (fd = 0; fd < NOFILE; fd++) {
if (p->ofile[fd] == 0) {
p->ofile[fd] = f;
return fd;
}
}
return -1;
}
該 process 對這個 struct-file 的 file descriptor ( int )。沒找到空位就算該 function 執行失敗。struct-file
uint64
sys_dup(void)
{
struct file *f;
int fd;
if (argfd(0, 0, &f) < 0)
return -1;
if ((fd = fdalloc(f)) < 0)
return -1;
filedup(f);
return fd;
}
0 個 syscall 參數,並把該參數視為 當前 process 的 file-descriptor ( 也就是 struct-proc->ofile[N] 陣列的 index )。struct-file
uint64
sys_read(void)
{
struct file *f;
int n;
uint64 p;
argaddr(1, &p);
argint(2, &n);
if (argfd(0, 0, &f) < 0)
return -1;
return fileread(f, p, n);
}
1 個 syscall argument,將其視為 address ( uint64_t )。2 個 syscall argument,將其視為 int,這裡會是想要讀取的 bytes 數。0 個 syscall argument,將其視為 file-descriptor,並查看該 process-descriptor-table 裡面有沒有相對應的 struct-file。struct-file,並把讀取出來的資料放在 address : p
uint64
sys_write(void)
{
struct file *f;
int n;
uint64 p;
argaddr(1, &p);
argint(2, &n);
if (argfd(0, 0, &f) < 0)
return -1;
return filewrite(f, p, n);
}
1 個 syscall argument,將其視為 address ( uint64_t )。2 個 syscall argument,將其視為 int,這裡會是想要寫入的 bytes 數。0 個 syscall argument,將其視為 file-descriptor,並查看該 process-descriptor-table 裡面有沒有相對應的 struct-file,有的話就回傳該 struct-file。address : p 讀取資料,並寫入相對應的 struct-file。static struct inode *
create(char *path, short type, short major, short minor)
file, directory, device file。create 會去尋找特定路徑的 parent directory 的 struct-inode,並且 allocate 個新的 struct-inode 給想要建立的新檔案 ( 假如該檔案已經存在於 parent directory 的話,就回傳這個已經存在的檔案 ),最後更新 parent directory 在 disk-hardware 裡面的內容 ( e.g. directory entries )。/a/b/c,則 create function 會嘗試去尋找到 /a/b 這個 directory 的 struct-inode,並嘗試新增 /a/b/c 這個新的檔案。T_FILE, T_DIR, T_DEVICE,也就是代表 struct-inode layer 的檔案種類FD_NONE, FD_PIPE, FD_INODE, FD_DEVICE
path 參數所指定 ) 的檔案的 struct-inode。{
struct inode *ip, *dp;
char name[DIRSIZ];
if ((dp = nameiparent(path, name)) == 0)
return 0;
/a/b 的 struct-inode ilock(dp);
if ((ip = dirlookup(dp, name, 0)) != 0) {
iunlockput(dp);
ilock(ip);
if (type == T_FILE && (ip->type == T_FILE || ip->type == T_DEVICE))
return ip;
iunlockput(ip);
return 0;
}
if ((ip = ialloc(dp->dev, type)) == 0) {
iunlockput(dp);
return 0;
}
ilock(ip);
ip->major = major;
ip->minor = minor;
ip->nlink = 1;
iupdate(ip);
初始化這個新 create 出來的 inode。
if (type == T_DIR) { // Create . and .. entries.
// No ip->nlink++ for ".": avoid cyclic ref count.
if (dirlink(ip, ".", ip->inum) < 0 || dirlink(ip, "..", dp->inum) < 0)
goto fail;
}
if (dirlink(dp, name, ip->inum) < 0)
goto fail;
這裡把自己加進上一層資料夾的 directory entries 裡面。
if (type == T_DIR) {
// now that success is guaranteed:
dp->nlink++; // for ".."
iupdate(dp);
}
iunlockput(dp);
return ip;
fail:
// something went wrong. de-allocate ip.
ip->nlink = 0;
iupdate(ip);
iunlockput(ip);
iunlockput(dp);
return 0;
}
假如發生任何錯誤,就把 ip 給 de-allocate,並且把 dp ( struct-inode ) 的資源釋放掉。
uint64
sys_open(void)
{
這個 function 實作了 open 系統呼叫。
user-space 可以呼叫這個 function ( 透過 syscall ),用以
open an existing file
create a new file
open a device
這個 function 為 user space 提供的 file path,以及 kernel file descriptors 搭建了一個橋樑。
return value
char path[MAXPATH];
int fd, omode;
struct file *f;
struct inode *ip;
int n;
argint(1, &omode);
if ((n = argstr(0, path, MAXPATH)) < 0)
return -1;
user space virtual address 的字串的指標。 begin_op();
接下來會進行檔案操作,用 begin_op() 開始進入一個 transaction。
if (omode & O_CREATE) {
ip = create(path, T_FILE, 0, 0);
if (ip == 0) {
end_op();
return -1;
}
} else {
if ((ip = namei(path)) == 0) {
end_op();
return -1;
}
ilock(ip);
if (ip->type == T_DIR && omode != O_RDONLY) {
iunlockput(ip);
end_op();
return -1;
}
}
path 參數所指定 ) 的檔案的 struct-inode。ip->type == T_DIR … : 假如預期該 struct-inode 是資料夾 ( T_DIR ) if (ip->type == T_DEVICE && (ip->major < 0 || ip->major >= NDEV)) {
iunlockput(ip);
end_op();
return -1;
}
假如目標檔案的 ip->type 是 T_DEVICE ( 該檔案代表的是一個 device, e.g. uart ),就會去檢查 major device number 以及 minor device number 是否合法。
if ((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0) {
if (f)
fileclose(f);
iunlockput(ip);
end_op();
return -1;
}
global open file table 裡面去尋找可使用的 ( 目前沒被使用的 ) struct-file。 if (ip->type == T_DEVICE) {
f->type = FD_DEVICE;
f->major = ip->major;
} else {
f->type = FD_INODE;
f->off = 0;
}
f->ip = ip;
f->readable = !(omode & O_WRONLY);
f->writable = (omode & O_WRONLY) || (omode & O_RDWR);
if ((omode & O_TRUNC) && ip->type == T_FILE) {
itrunc(ip);
}
開始對新 allocate 出來的 struct-file 進行初始化
itrunc ( 把原本檔案的內容給拋棄 )。 iunlock(ip);
end_op();
return fd;
}
uint64
sys_close(void)
{
int fd;
struct file *f;
if (argfd(0, &fd, &f) < 0)
return -1;
myproc()->ofile[fd] = 0;
fileclose(f);
return 0;
}
0 個 syscall argument 視為 process-file-descriptor,並回傳相對應的 process-file-descriptor, struct-file。myproc()->ofile[fd] = 0
fd 所對應 的 file-descriptor table 的 entry 清空。struct-file 進行關閉檔案。struct-file,並進行關閉檔案。uint64
sys_fstat(void)
{
struct file *f;
uint64 st; // user pointer to struct stat
argaddr(1, &st);
if (argfd(0, 0, &f) < 0)
return -1;
return filestat(f, st);
}
struct-file 提取出 metadatastruct-file 的 metadata。