iT邦幫忙

0

[6.1810][code] xv6 的 FileSystem (九) : System Call (一)

  • 分享至 

  • xImage
  •  

系列文章 : [6.1810] 跟著 MIT 6.1810 學習基礎作業系統觀念

大綱

  • kernel/syscall.c/argraw
  • kernel/syscall.c/argint
  • kernel/sysfile.c/argfd
  • kernel/sysfile.c/fdalloc
  • kernel/sysfile.c/sys_dup
  • kernel/sysfile.c/sys_read
  • kernel/sysfile.c/sys_write
  • kernel/sysfile.c/create
  • kernel/sysfile.c/sys_open
  • kernel/sysfile.c/sys_close
  • kernel/sysfile.c/sys_fstat

kernel/syscall.c/argraw

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;
}
  • 在 user space 使用 syscall 的時候,會把參數放在相對應的 register 裡面。
  • 當我們從 user space trap 到 kernel space 的時候,會把 register 存在 TRAPFRAME 裡面
  • 於是我們在 kernel space,可以透過 trapframe 提取出 syscall 的參數


kernel/syscall.c/argint

// 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 ) 並回傳。



kernel/sysfile.c/argfd

// 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;
}
  • this function
    • 提取出第 n 個 syscall argument,並且將其視為一個 file descriptor,並且藉由 argfd function 的參數回傳 file descriptor ( int ) 以及相對應的 struct-file
  • (arg) int n
    • 要從 syscall 的參數中,拿出第 ‘n’ 個參數 ( 0-indexed )
  • (arg) int *pfd
    • 讓我們可以從參數回傳 file descriptor
  • (arg) struct file **pf
    • 讓我們可以從參數回傳 struct-file * ( 回傳指標 )。
  • return value
    • 0 : 成功
    • -1 : 失敗


kernel/sysfile.c/fdalloc

// 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;
}
  • this function
    • 這個 function 會去一個 process 的 ofile 陣列裡面去尋找看看有沒有空位,有找到的話,該空位的 index 就是該 process 對這個 struct-file 的 file descriptor ( int )。沒找到空位就算該 function 執行失敗。
  • (arg) strcut file *f
    • 這個 process 想要開啟的檔案的相對應的 struct-file
  • return value
    • val != -1 : 成功
    • val == -1 : 失敗


kernel/sysfile.c/sys_dup

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;
}
  • argfd(0, 0, &f)
    • 拿取第 0 個 syscall 參數,並把該參數視為 當前 process 的 file-descriptor ( 也就是 struct-proc->ofile[N] 陣列的 index )。
    • 利用參數 f 回傳相對應的 struct-file
    • 不會想回傳 file-descriptor 本身
  • fdalloc(f)
    • 為了 struct-file,在當前 process 的 struct-proc->ofile[N] 陣列裡面再找一個空位,有找到的話,就回傳該空位的 index ( file-descriptor )。
  • filedup(f)
    • 會把 struct-file 的 ref + 1
  • return value
    • val == -1 : 該 function 失敗。不過這個 function 的 return value type 是 uint64,有點神秘。
    • val != -1 : 利用 fdalloc 多 allocate 出來的 process-file-descriptor ( struct-proc->ofile[N] 陣列的 index )。
  • this function
    • 在同一個 process 的 file-descriptor-table 多 allocate 一個 file-descriptor 給已經存在於該 file-descriptor-table 的 struct-file


kernel/sysfile.c/sys_read

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);
}
  • argaddr(1, &p)
    • 提取第 1 個 syscall argument,將其視為 address ( uint64_t )。
  • argint(2, &n)
    • 提取第 2 個 syscall argument,將其視為 int,這裡會是想要讀取的 bytes 數。
  • argfd(0, 0, &f)
    • 提取第 0 個 syscall argument,將其視為 file-descriptor,並查看該 process-descriptor-table 裡面有沒有相對應的 struct-file
  • fileread
    • 讀取某個特定的 struct-file,並把讀取出來的資料放在 address : p
  • return value
    • 實際讀取的 byte 數量。
  • this function
    • 對特定的 process-file-descriptor 所代表的 struct-file 進行檔案讀取。


kernel/sysfile.c/sys_write

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);
}
  • argaddr
    • 提取第 1 個 syscall argument,將其視為 address ( uint64_t )。
  • argint
    • 提取第 2 個 syscall argument,將其視為 int,這裡會是想要寫入的 bytes 數。
  • argfd
    • 提取第 0 個 syscall argument,將其視為 file-descriptor,並查看該 process-descriptor-table 裡面有沒有相對應的 struct-file,有的話就回傳該 struct-file
  • filewrite
    • address : p 讀取資料,並寫入相對應的 struct-file
  • return value
    • 實際上寫了多少 bytes。
  • this function
    • 對特定的 process-file-descriptor 所代表的 struct-file 進行檔案寫入。


kernel/sysfile.c/create

static struct inode *
create(char *path, short type, short major, short minor)
  • this function
    • 這個 helper function 可以用來幫助建立新的 file, directory, device file
    • create 會去尋找特定路徑的 parent directory 的 struct-inode,並且 allocate 個新的 struct-inode 給想要建立的新檔案 ( 假如該檔案已經存在於 parent directory 的話,就回傳這個已經存在的檔案 ),最後更新 parent directory 在 disk-hardware 裡面的內容 ( e.g. directory entries )。
  • char *path
    • 檔案的路徑。例如說給予的路徑為 /a/b/c,則 create function 會嘗試去尋找到 /a/b 這個 directory 的 struct-inode,並嘗試新增 /a/b/c 這個新的檔案。
  • short type
    • 這裡用的是 T_FILE, T_DIR, T_DEVICE,也就是代表 struct-inode layer 的檔案種類
    • struct-file 用的是 FD_NONE, FD_PIPE, FD_INODE, FD_DEVICE
  • short major
    • major device number,只有在 T_DEVICE 時有意義
  • short minor
    • minor device number,只有在 T_DEVICE 時有意義
  • return value
    • 該檔案原本不存在 : 新 allocate 出來的 struct-inode
    • 該檔案原本存在 : 指定路徑 ( 由該 function 的 path 參數所指定 ) 的檔案的 struct-inode。
    • 執行失敗 : 回傳 0

{
  struct inode *ip, *dp;
  char name[DIRSIZ];

  if ((dp = nameiparent(path, name)) == 0)
    return 0;
  • nameiparent
    • 給一個 path ( e.g. /a/b/c )
    • return value : 會回傳 /a/b 的 struct-inode
    • 參數 name : 會回傳字串 “c”

  ilock(dp);
  • ilock
    • 拿取 dp 的 sleep-lock
    • 需要的話,會從 disk-hardware 讀取 struct-dinode 到 struct-inode

  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;
  }
  • dirlookup(dp, name, 0)
    • 嘗試在一個代表 directory 的 struct-inode 裡面,找尋目標檔案,並回傳目標檔案的 struct-inode
  • 這邊是判斷說,假如這個檔案已經存在了的話
    • request 的 type 是 T_FILE,且 struct-inode 的 type 也符合 T_FILE 或是 T_DEVICE,就直接 return 這個 struct-inode
    • 假如 type 不符合,就 return 0,表示 function 失敗。

  if ((ip = ialloc(dp->dev, type)) == 0) {
    iunlockput(dp);
    return 0;
  }
  • ialloc(dp->dev, type)
    • dev : device number,代表目前我們是使用哪一個 physical disk 或是 partition。xv6-riscv 的 root disk 的 device number 為 ROOTDEV (1)。
    • type 想要 alloc 的 inode 的 type。
  • iunlockput(dp)
    • 假如 ialloc 失敗的話,就要釋放 dp 這個 struct-inode。

  ilock(ip);
  ip->major = major;
  ip->minor = minor;
  ip->nlink = 1;
  iupdate(ip);

初始化這個新 create 出來的 inode。

  • ilock
    • 假如 ip 還沒有從 disk-hardware 載入 metadata,則 ilock 會從 disk-hardware 載入資訊到 in-memory-struct-inode。
  • iupdate
    • 更新完 in-memory-struct-inode 的 metadata 後,需要用 iupdate 把資料寫入到 disk-hardware

  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;
  }
  • 假如想要創建的 struct-inode 是 T_DIR ( 資料夾 ),就會需要走進這裡。每一個資料夾都會有 “.” 表示當前路徑,以及 “..” 表示要前往上一層資料夾 “父母資料夾”。
  • dirlink(ip, ".", ip->inum)
    • 把自己以檔案名稱 “.”,加進自己的 directory entries。
  • dirlink(ip, "..", dp->inum)
    • 把上一層資料夾,以 “..” 為檔案名稱,加進自己的 directory entries。

  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);
  }
  • 因為 ip 會指向 dp,所以 dp->nlink 需要 + 1。
  • 雖然 ip 也會指向自己 ( 路徑 “.” ),但這邊就不需要替 ip->nlink + 1 了。

  iunlockput(dp);

  return ip;
  • iunlockput(dp)
    • dp 的任務已經結束了,可以釋放 dp 的資源
  • return ip
    • 回傳新創建出來的 struct-inode

fail:
  // something went wrong. de-allocate ip.
  ip->nlink = 0;
  iupdate(ip);
  iunlockput(ip);
  iunlockput(dp);
  return 0;
}

假如發生任何錯誤,就把 ip 給 de-allocate,並且把 dp ( struct-inode ) 的資源釋放掉。



kernel/sysfile.c/sys_open

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

  • val == -1 : 失敗
  • val != -1 : 會回傳該檔案在 struct-proc->ofile[N] 陣列裡的 index,也就是 process-file-descriptor

  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;
  • argint(1, &omode)
    • 把第一個 syscall 參數視為 int,視為開啟檔案的模式。
  • argstr(0, path, MAXPATH)
    • 把 syscall 第 0 個參數視為一個指向 user space virtual address 的字串的指標。
    • 把該字串存到 path 裡面
    • 最多存 MAXPATH 個字元

  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;
    }
  }
  • 假如 omode 顯示要創建檔案的話
    • create(path, T_FILE, 0, 0)
      • (arg) path : 目標檔案的路徑
      • (arg) type : 想要新增的 struct-inode 的 type 該是什麼。
      • (arg) major number : 0
        • 因為 type 是 T_FILE,major number 沒有意義,所以直接給 0
      • (arg) minor number : 0
        • 因為 type 是 T_FILE,minor number 沒有意義,所以直接給 0
      • return value
        • 該檔案原本不存在 : 新 allocate 出來的 struct-inode
        • 該檔案原本存在 : 指定路徑 ( 由該 function 的 path 參數所指定 ) 的檔案的 struct-inode。
        • 執行失敗 : 回傳 0
  • 假如 omode 沒有要創建檔案的話,會走到這裡
    • namei(path) : 透過 file-path,取得相對應的 struct-inode
    • ilock(ip) : 把這個 ip 在 disk-hardware 的資訊載入到 ram
    • 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;
  }
  • filealloc()
    • 這個 function 會嘗試去 allocate 一個 struct-file。會去 global open file table 裡面去尋找可使用的 ( 目前沒被使用的 ) struct-file。
    • return value
      • 成功 : allocate 出來的 struct-file 的指標。
      • 失敗 : return 0。
  • fdalloc(f)
    • return value
      • 成功 : val != -1
      • 失敗 : val == -1
  • 假如 filealloc 失敗,或是 fdalloc 失敗的話,就會把 f 用 fileclose 關掉,並把 ip 釋放掉,並 return -1。

  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 進行初始化

  • 根據 ip->type 的不同,給予不同的 f->type, f->major, f->off
  • 因為 f->off 只有在 f->type == FD_INODE 的時候有意義,所以只在 f->type == FD_INODE 的時候才會進行初始化
  • 因為 f->major 只有在 f->type == FD_DEVICE 的時候有意義,所以只在 f->type == FD_DEVICE 的時候才會進行初始化
  • 根據 omode,對 readable, writeable 進行初始化
  • 根據 omode,決定要不要先對這個 file 進行 itrunc ( 把原本檔案的內容給拋棄 )。

  iunlock(ip);
  end_op();

  return fd;
}
  • iunlock(ip)
    • 把 struct-inode 的 sleep-lock 釋放掉
  • end_op()
    • 結束這一次的 transaction
  • return fd
    • 回傳 struct-proc->ofile[N] 陣列的 index,也就是 process-file-descriptor。


kernel/sysfile.c/sys_close

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;
}
  • argfd(0, &fd, &f)
    • 把第 0 個 syscall argument 視為 process-file-descriptor,並回傳相對應的 process-file-descriptor, struct-file
  • myproc()->ofile[fd] = 0
    • 把該 process,fd 所對應 的 file-descriptor table 的 entry 清空。
  • fileclose(f)
    • 對特定的 struct-file 進行關閉檔案。
  • return value
    • 0 : 成功
    • -1 : 失敗
  • this function
    • 給予 process-file-descriptor,提取出 struct-file,並進行關閉檔案。


kernel/sysfile.c/sys_fstat

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);
}
  • argaddr(1, &st)
    • 從 syscall 第一個參數提取出 address
    • 這個 address 是 user space 的 address
  • argfd(0, 0, &f)
    • 從 syscall 第 0 個參數提取出 process-file-descriptor
  • filestat(f, st)
    • struct-file 提取出 metadata
  • return value
    • -1 : 失敗
    • 0 : 成功
  • this function
    • 提取 struct-file 的 metadata。



圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言