系列文章 : [6.1810] 跟著 MIT 6.1810 學習基礎作業系統觀念
// Is the directory dp empty except for "." and ".." ?
static int
isdirempty(struct inode *dp)
{
int off;
struct dirent de;
for (off = 2 * sizeof(de); off < dp->size; off += sizeof(de)) {
if (readi(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de))
panic("isdirempty: readi");
if (de.inum != 0)
return 0;
}
return 1;
}
off = 2 * sizeof(de); off < dp->size; off += sizeof(de)
// Create the path new as a link to the same inode as old.
uint64
sys_link(void)
這個 function 實作了 link 的系統呼叫。它會 create 一個新的 hard link,這個新的 hard link 會指向一個已經存在了的檔案 ( inode )。
這個 function 成功了的話,舊的檔案名稱 ( syscall 第 0 個參數 ),以及新的檔案名稱 ( syscall 第 1 個參數 ) 會指向 disk-hardware 上的同一個檔案。
{
char name[DIRSIZ], new[MAXPATH], old[MAXPATH];
struct inode *dp, *ip;
if (argstr(0, old, MAXPATH) < 0 || argstr(1, new, MAXPATH) < 0)
return -1;
0 個 parameter,並視為字串,意思是舊有的檔案路徑。1 個 parameter,並視為字串,意思是新的檔案路徑。 begin_op();
一段 transaction 的開始。
if ((ip = namei(old)) == 0) {
end_op();
return -1;
}
namei : 從路徑 ( 字串 ) 取得相對應的 struct-inode
ilock(ip);
if (ip->type == T_DIR) {
iunlockput(ip);
end_op();
return -1;
}
struct-inode 的資訊 ip->nlink++;
iupdate(ip);
iunlock(ip);
if ((dp = nameiparent(new, name)) == 0)
goto bad;
ilock(dp);
if (dp->dev != ip->dev || dirlink(dp, name, ip->inum) < 0) {
iunlockput(dp);
goto bad;
}
iunlockput(dp);
dp 的資訊name
iput(ip);
end_op();
return 0;
bad:
ilock(ip);
ip->nlink--;
iupdate(ip);
iunlockput(ip);
end_op();
return -1;
}
假如失敗的話,需要把剛剛修改的狀態復原。
uint64
sys_unlink(void)
{
這個 function 實作了 unlink 系統呼叫。這個 function 可以用來從檔案系統中 unlink 一個檔案。
假如刪除後沒有任何 link 指向目標檔案,且沒有任何正在 open 這個檔案的話,那這個檔案在 disk-hardware 中的資料就會被刪除。
例如說某個檔案在 “/abc/bcd/fff” 有一個 link,”/efg/hed/aaa” 也有一個 link ( 兩個路徑皆指向 disk-hardware 裡面的同樣的 blocks )。 當我們 unlink(“/abc/bcd/fff”) 的時候,並不會把目標檔案在 disk-hardware 內佔用的空間清除,並且 “efg/hed/aaa” 這個 link 也不會有影響。
struct inode *ip, *dp;
struct dirent de;
char name[DIRSIZ], path[MAXPATH];
uint off;
if (argstr(0, path, MAXPATH) < 0)
return -1;
argstr : 把 syscall 第 0 個參數視為字串的 address ( 該 address 是 user-space 的 virtual address ),並放到 kernel space 的 path[MAXPATH] 指標所指向的地方
begin_op();
if ((dp = nameiparent(path, name)) == 0) {
end_op();
return -1;
}
ilock(dp);
// Cannot unlink "." or "..".
if (namecmp(name, ".") == 0 || namecmp(name, "..") == 0)
goto bad;
不可以 unlink “.” 或是 “..”,這邊會檢查給予的 name 是否為這兩種禁止的情況。
if ((ip = dirlookup(dp, name, &off)) == 0)
goto bad;
ilock(ip);
if (ip->nlink < 1)
panic("unlink: nlink < 1");
if (ip->type == T_DIR && !isdirempty(ip)) {
iunlockput(ip);
goto bad;
}
ip->type == T_DIR && !isdirempty(ip)
memset(&de, 0, sizeof(de));
if (writei(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de))
panic("unlink: writei");
memset(&de, 0, sizeof(de))
writei(dp, 0, (uint64)&de, off, sizeof(de)
dirlookup 有讓我們拿到目標 directory entry 在 struct-inode(dp)->directory-entries 內的偏移量。 if (ip->type == T_DIR) {
dp->nlink--;
iupdate(dp);
}
iunlockput(dp);
ip->type == T_DIR,則在建立這個 struct-inode 的時候,會有 “..” 指向 parent struct-inode。所以當我們要刪掉一個 T_DIR 的時候,需要把 parent 的 nlink 減回去。 ip->nlink--;
iupdate(ip);
iunlockput(ip);
end_op();
return 0;
bad:
iunlockput(dp);
end_op();
return -1;
}
假如發生意外,就退出這個 function,並 return -1 代表 unlink 失敗。
uint64
sys_mkdir(void)
{
給予一個特定的檔案路徑,並嘗試在該路徑建立一個資料夾。
char path[MAXPATH];
struct inode *ip;
begin_op();
if (argstr(0, path, MAXPATH) < 0 || (ip = create(path, T_DIR, 0, 0)) == 0) {
end_op();
return -1;
}
iunlockput(ip);
end_op();
return 0;
}
uint64
sys_mknod(void)
{
給予一個特定的檔案路徑,並嘗試在該路徑建立一個 T_DEVICE 類型的檔案,讓我們可以透過這個檔案跟特定的周邊裝置 ( peripheral device ) 互動,例如 Uart
struct inode *ip;
char path[MAXPATH];
int major, minor;
begin_op();
argint(1, &major);
argint(2, &minor);
if ((argstr(0, path, MAXPATH)) < 0 ||
(ip = create(path, T_DEVICE, major, minor)) == 0) {
end_op();
return -1;
}
iunlockput(ip);
end_op();
return 0;
}
major 去呼叫相對應的 device 的 read/write function。 major 即是 struct devsw 陣列的 index。T_DEVICE 的檔案,並且設定相對應的 major 以及 minor。uint64
sys_chdir(void)
{
這個 function 實作了 chidir 系統呼叫,可以用來改變呼叫的 process 的當前 working directory。
char path[MAXPATH];
struct inode *ip;
struct proc *p = myproc();
begin_op();
if (argstr(0, path, MAXPATH) < 0 || (ip = namei(path)) == 0) {
end_op();
return -1;
}
ilock(ip);
if (ip->type != T_DIR) {
iunlockput(ip);
end_op();
return -1;
}
ip->type != T_DIR : 假如我們想要前往的 struct-inode 不是資料夾,那這個 function 就宣告失敗。
iunlock(ip);
iput(p->cwd);
end_op();
p->cwd = ip;
return 0;
}
uint64
sys_exec(void)
{
char path[MAXPATH], *argv[MAXARG];
int i;
uint64 uargv, uarg;
argaddr(1, &uargv);
if (argstr(0, path, MAXPATH) < 0) {
return -1;
}
也是一個字串陣列
可執行檔的路徑。 memset(argv, 0, sizeof(argv));
for (i = 0;; i++) {
if (i >= NELEM(argv)) {
goto bad;
}
if (fetchaddr(uargv + sizeof(uint64) * i, (uint64 *)&uarg) < 0) {
goto bad;
}
if (uarg == 0) {
argv[i] = 0;
break;
}
argv[i] = kalloc();
if (argv[i] == 0)
goto bad;
if (fetchstr(uarg, argv[i], PGSIZE) < 0)
goto bad;
}
memset(argv, 0, sizeof(argv));
i >= NELEM(argv)
uarg。bad 標籤。uarg 所指向的字串載入到 argv[i] 這個字串指標。 int ret = kexec(path, argv);
for (i = 0; i < NELEM(argv) && argv[i] != 0; i++)
kfree(argv[i]);
把每一個剛剛 allocate 出來的字串指標釋放掉。
return ret;
正常的跳出 function
bad:
for (i = 0; i < NELEM(argv) && argv[i] != 0; i++)
kfree(argv[i]);
return -1;
}
假如該 function 有地方出錯了,就釋放掉所有字串指標,並回傳 -1
uint64
sys_pipe(void)
{
pipe,pipe 可以用於 inter-process communication ( IPC )。一個 process 可以對 pipe 進行寫入,另外一個 process 可以對 pipe 進行讀取。 uint64 fdarray; // user pointer to array of two integers
struct file *rf, *wf;
int fd0, fd1;
struct proc *p = myproc();
argaddr(0, &fdarray);
argaddr : 會把 syscall 第 0 個參數視為 address,該 address 指向一個有著兩個 int 的陣列。
if (pipealloc(&rf, &wf) < 0)
return -1;
fd0 = -1;
if ((fd0 = fdalloc(rf)) < 0 || (fd1 = fdalloc(wf)) < 0) {
if (fd0 >= 0)
p->ofile[fd0] = 0;
fileclose(rf);
fileclose(wf);
return -1;
}
假如 fdalloc 失敗之後,就把資源釋放,並 return -1。
if (copyout(p->pagetable, fdarray, (char *)&fd0, sizeof(fd0)) < 0 ||
copyout(p->pagetable, fdarray + sizeof(fd0), (char *)&fd1, sizeof(fd1)) <
0) {
p->ofile[fd0] = 0;
p->ofile[fd1] = 0;
fileclose(rf);
fileclose(wf);
return -1;
}
return 0;
}
kernel to user
fileclose 釋放資源,並 return -1