/* Overview: * Duplicate the file descriptor. * Post-Condition: * Return 'newfdnum' on success. * Return the corresponding error code on error. * Hint: * Use 'fd_lookup' or 'INDEX2FD' to get 'fd' to 'fdnum'. * Use 'fd2data' to get the data address to 'fd'. * Use 'syscall_mem_map' to share the data pages. */ intdup(int oldfdnum, int newfdnum) { int i, r; void *ova, *nva; u_int pte; structFd *oldfd, *newfd; /* Step 1: Check if 'oldnum' is valid. if not, return an error code, or get 'fd'. */ if ((r = fd_lookup(oldfdnum, &oldfd)) < 0) { return r; } /* Step 2: Close 'newfdnum' to reset content. */ close(newfdnum); /* Step 3: Get 'newfd' reffered by 'newfdnum'. */ newfd = (struct Fd *)INDEX2FD(newfdnum); /* Step 4: Get data address. */ ova = fd2data(oldfd); nva = fd2data(newfd); /* Step 5: Dunplicate the data and 'fd' self from old to new. */ if ((r = syscall_mem_map(0, oldfd, 0, newfd, vpt[VPN(oldfd)] & (PTE_D | PTE_LIBRARY))) < 0) { goto err; } if (vpd[PDX(ova)]) { for (i = 0; i < PDMAP; i += PTMAP) { pte = vpt[VPN(ova + i)]; if (pte & PTE_V) { // should be no error here -- pd is already allocated if ((r = syscall_mem_map(0, (void *)(ova + i), 0, (void *)(nva + i), pte & (PTE_D | PTE_LIBRARY))) < 0) { goto err; } } } } return newfdnum; err: /* If error occurs, cancel all map operations. */ panic_on(syscall_mem_unmap(0, newfd)); for (i = 0; i < PDMAP; i += PTMAP) { panic_on(syscall_mem_unmap(0, (void *)(nva + i))); } return r; }
.section .text.exc_gen_entry exc_gen_entry: SAVE_ALL #将当前上下文保存到内核异常栈中 /* * Note: When EXL is set or UM is unset, the processor is in kernel mode. * When EXL is set, the value of EPC is not updated when a new exception occurs. * To keep the processor in kernel mode and enable exception reentrancy, * we unset UM and EXL, and unset IE to globally disable interrupts. */ mfc0 t0, CP0_STATUS and t0, t0, ~(STATUS_UM | STATUS_EXL | STATUS_IE) #清除Status寄存器值中的UM、EXL、IE位,使CPU保持内核态、关中断并允许嵌套异常 mtc0 t0, CP0_STATUS #写入Status寄存器中 mfc0 t0, CP0_CAUSE andi t0, 0x7c #取Cause寄存器中的2-6位,即异常的类型码 lw t0, exception_handlers(t0) #以异常码为索引在exception_handlers数组里找对应中断处理函数 jr t0 #跳转到相应的中断处理程序处
#### 回答 代码如下: ```c static int _pipe_is_closed(structFd *fd, structPipe *p) { // The 'pageref(p)' is the total number of readers and writers. // The 'pageref(fd)' is the number of envs with 'fd' open // (readers if fd is a reader, writers if fd is a writer). // // Check if the pipe is closed using 'pageref(fd)' and 'pageref(p)'. // If they're the same, the pipe is closed. // Otherwise, the pipe isn't closed.
int fd_ref, pipe_ref, runs; // Use 'pageref' to get the reference counts for 'fd' and 'p', then // save them to 'fd_ref' and 'pipe_ref'. // Keep retrying until 'env->env_runs' is unchanged before and after // reading the reference counts. /* Exercise 6.1: Your code here. (1/3) */ runs = env->env_runs; fd_ref = pageref(fd); pipe_ref = pageref(p); while (runs != env->env_runs) { runs = env->env_runs; fd_ref = pageref(fd); pipe_ref = pageref(p); } return fd_ref == pipe_ref; }
/* Overview: * Read at most 'n' bytes from the pipe referred by 'fd' into 'vbuf'. * * Post-Condition: * Return the number of bytes read from the pipe. * The return value must be greater than 0, unless the pipe is closed and nothing * has been written since the last read. * * Hint: * Use 'fd2data' to get the 'Pipe' referred by 'fd'. * Use '_pipe_is_closed' to check if the pipe is closed. * The parameter 'offset' isn't used here. */ static int pipe_read(structFd *fd, void *vbuf, u_int n, u_int offset) { int i; structPipe *p; char *rbuf;
// Use 'fd2data' to get the 'Pipe' referred by 'fd'. // Write a loop that transfers one byte in each iteration. // Check if the pipe is closed by '_pipe_is_closed'. // When the pipe buffer is empty: // - If at least 1 byte is read, or the pipe is closed, just return the number // of bytes read so far. // - Otherwise, keep yielding until the buffer isn't empty or the pipe is closed. /* Exercise 6.1: Your code here. (2/3) */ p = (structPipe *)fd2data(fd); i = 0; rbuf = (char *)vbuf; while (i < n) { while (p->p_rpos >= p->p_wpos) { if (i >= 1 || _pipe_is_closed(fd, p)) { return i; } syscall_yield(); } *rbuf = p->p_buf[(p->p_rpos++) % PIPE_SIZE]; rbuf++; i++; } return n; user_panic("pipe_read not implemented"); }
/* Overview: * Write 'n' bytes from 'vbuf' to the pipe referred by 'fd'. * * Post-Condition: * Return the number of bytes written into the pipe. * * Hint: * Use 'fd2data' to get the 'Pipe' referred by 'fd'. * Use '_pipe_is_closed' to judge if the pipe is closed. * The parameter 'offset' isn't used here. */ static int pipe_write(structFd *fd, const void *vbuf, u_int n, u_int offset) { int i; structPipe *p; char *wbuf;
// Use 'fd2data' to get the 'Pipe' referred by 'fd'. // Write a loop that transfers one byte in each iteration. // If the bytes of the pipe used equals to 'PIPE_SIZE', the pipe is regarded as full. // Check if the pipe is closed by '_pipe_is_closed'. // When the pipe buffer is full: // - If the pipe is closed, just return the number of bytes written so far. // - If the pipe isn't closed, keep yielding until the buffer isn't full or the // pipe is closed. /* Exercise 6.1: Your code here. (3/3) */ p = (structPipe *)fd2data(fd); i = 0; wbuf = (char *)vbuf; while (i < n) { while (p->p_wpos - p->p_rpos >= PIPE_SIZE) { if (_pipe_is_closed(fd, p)) { return i; } syscall_yield(); } p->p_buf[(p->p_wpos++) % PIPE_SIZE] = *wbuf; wbuf++; i++; }
intspawn(char *prog, char **argv) { // Step 1: Open the file 'prog' (the path of the program). // Return the error if 'open' fails. int fd; if ((fd = open(prog, O_RDONLY)) < 0) { return fd; } // Step 2: Read the ELF header (of type 'Elf32_Ehdr') from the file into 'elfbuf' using // 'readn()'. // If that fails (where 'readn' returns a different size than expected), // set 'r' and 'goto err' to close the file and return the error. int r; u_char elfbuf[512]; /* Exercise 6.4: Your code here. (1/6) */ //此处从fd代表的文件中读取sizeof(Elf32_Ehdr)个字节数据到elfbuf中, //若不能读取正常数量的数据,则跳转到错误处理err r = readn(fd, (void *) elfbuf, sizeof(Elf32_Ehdr)); if (r != sizeof(Elf32_Ehdr)) { goto err; } const Elf32_Ehdr *ehdr = elf_from(elfbuf, sizeof(Elf32_Ehdr)); if (!ehdr) { r = -E_NOT_EXEC; goto err; } u_long entrypoint = ehdr->e_entry; // Step 3: Create a child using 'syscall_exofork()' and store its envid in 'child'. // If the syscall fails, set 'r' and 'goto err'. u_int child; /* Exercise 6.4: Your code here. (2/6) */ //此处调用系统调用syscall_excfork(),调用fork,返回相应的进程号 child = syscall_exofork(); if (child < 0) { r = child; goto err; } // Step 4: Use 'init_stack(child, argv, &sp)' to initialize the stack of the child. // 'goto err1' if that fails. u_int sp; /* Exercise 6.4: Your code here. (3/6) */ //使用init_stack初始化子进程,如果失败跳转到错误处理err1 if ((r = init_stack(child, argv, &sp)) < 0) { goto err1; } // Step 5: Load the ELF segments in the file into the child's memory. // This is similar to 'load_icode()' in the kernel. size_t ph_off; ELF_FOREACH_PHDR_OFF (ph_off, ehdr) { // Read the program header in the file with offset 'ph_off' and length // 'ehdr->e_phentsize' into 'elfbuf'. // 'goto err1' on failure. // You may want to use 'seek' and 'readn'. /* Exercise 6.4: Your code here. (4/6) */ //通过seek定位偏移,再通过readn读取ehdr->e_phentsize个字节数据从fd对应文件中读入elfbuf中 //期间发生失败即跳转到err1错误处理 if ((r = seek(fd, ph_off)) < 0) { goto err1; } if ((r = readn(fd, (void *)elfbuf, ehdr->e_phentsize)) != ehdr->e_phentsize) { goto err1; } Elf32_Phdr *ph = (Elf32_Phdr *)elfbuf; if (ph->p_type == PT_LOAD) { void *bin; // Read and map the ELF data in the file at 'ph->p_offset' into our memory // using 'read_map()'. // 'goto err1' if that fails. /* Exercise 6.4: Your code here. (5/6) */ //如下两个根据注释提示完成即可 if ((r = read_map(fd, ph->p_offset, &bin)) < 0) { goto err1; } // Load the segment 'ph' into the child's memory using 'elf_load_seg()'. // Use 'spawn_mapper' as the callback, and '&child' as its data. // 'goto err1' if that fails. /* Exercise 6.4: Your code here. (6/6) */ if ((r = elf_load_seg(ph, bin, spawn_mapper, &child)) < 0) { goto err1; } } } close(fd);
structTrapframetf = envs[ENVX(child)].env_tf; tf.cp0_epc = entrypoint; tf.regs[29] = sp; if ((r = syscall_set_trapframe(child, &tf)) != 0) { goto err2; } // Pages with 'PTE_LIBRARY' set are shared between the parent and the child. for (u_int pdeno = 0; pdeno <= PDX(USTACKTOP); pdeno++) { if (!(vpd[pdeno] & PTE_V)) { continue; } for (u_int pteno = 0; pteno <= PTX(~0); pteno++) { u_int pn = (pdeno << 10) + pteno; u_int perm = vpt[pn] & ((1 << PGSHIFT) - 1); if ((perm & PTE_V) && (perm & PTE_LIBRARY)) { void *va = (void *)(pn << PGSHIFT);
intparsecmd(char **argv, int *rightpipe) { int argc = 0; while (1) { char *t; int fd, r; int c = gettoken(0, &t); switch (c) { case0: return argc; case'w': if (argc >= MAXARGS) { debugf("too many arguments\n"); exit(); } argv[argc++] = t; break; case'<': if (gettoken(0, &t) != 'w') { debugf("syntax error: < not followed by word\n"); exit(); } // Open 't' for reading, dup it onto fd 0, and then close the original fd. // If the 'open' function encounters an error, // utilize 'debugf' to print relevant messages, // and subsequently terminate the process using 'exit'. /* Exercise 6.5: Your code here. (1/3) */ //以O_RDONLY模式打开t代表路径下的文件,并进行复制fd到标准输入并关闭fd的操作 if ((fd = open(t, O_RDONLY)) < 0) { debugf("failed to open %s\n", t); exit(); } if ((r = dup(fd, 0)) < 0) { debugf("dup error, type of error is %d\n", r); exit(); } close(fd); break; case'>': if (gettoken(0, &t) != 'w') { debugf("syntax error: > not followed by word\n"); exit(); } // Open 't' for writing, create it if not exist and trunc it if exist, dup // it onto fd 1, and then close the original fd. // If the 'open' function encounters an error, // utilize 'debugf' to print relevant messages, // and subsequently terminate the process using 'exit'. /* Exercise 6.5: Your code here. (2/3) */ //注意这里的模式不只有O_WRONLY,由于需要支持无文件创建文件,截断文件,还要有O_CREAT和O_TRUNC两个模式 //其余类似'<' if ((fd = open(t, O_WRONLY | O_CREAT | O_TRUNC)) < 0) { debugf("failed to open %s\n", t); exit(); } if ((r = dup(fd, 1)) < 0) { debugf("dup error, type of error is %d\n", r); exit(); } close(fd); break; case'|':; /* First, allocate a pipe. * Then fork, set '*rightpipe' to the returned child envid or zero. * The child runs the right side of the pipe: * - dup the read end of the pipe onto 0 * - close the read end of the pipe * - close the write end of the pipe * - and 'return parsecmd(argv, rightpipe)' again, to parse the rest of the * command line. * The parent runs the left side of the pipe: * - dup the write end of the pipe onto 1 * - close the write end of the pipe * - close the read end of the pipe * - and 'return argc', to execute the left of the pipeline. */ int p[2]; /* Exercise 6.5: Your code here. (3/3) */ //根据注释填写即可,注意父子进程返回值不同 if ((r = pipe(p)) < 0) { debugf("the open of pipe error, the type of error is %d\n", r); exit(); } int pid = fork(); if (pid >= 0) { *rightpipe = pid; } else { //error debugf("fork error\n"); exit(); } if (pid == 0) { //child dup(p[0], 0); close(p[0]); close(p[1]); return parsecmd(argv, rightpipe); } elseif (pid > 0) { //father dup(p[1], 1); close(p[0]); close(p[1]); return argc; } break; } } return argc; }
static int pipe_write(struct Fd *fd, const void *vbuf, u_int n, u_int offset) 与读类似,不过此处的n是需要写入恰好nB,非buf上限,因此完成全部nB写入前,若buf满了,则用syscall_yield()等待,并非返回,直到buf不满或pipe关闭。
关闭管道函数
static int pipe_close(struct Fd *fd)关闭管道的一端。
shell
shell分为两大类:
图形界面 shell(Graphical User Interface shell 即 GUI shell)。例如:应用最为广泛的是微软 Windows 系列操作系统的 Windows Explorer,也包括广为人知的 Linux 操作系统的XWindow Manager(BlackBox 和 FluxBox),以及功能更强大的 CDE、GNOME、KDE 和 XFCE等。
命令行式 shell(Command Line Interface shell ,即 CLI shell),也就是我们 MOS 操作系统最后即将实现的 shell 模式。