操作系统课内实验报告
如果我们能够进行的所有工作只是fork (或者__clone ) ,那么我们就一次次建立同一个进程的拷贝就可以了——这样我们的Linux 系统就只能运行在系统中第一个创建的用户进程init 了。init 是很有用的,但是功能还没有如此强大;我们也还需要处理其他事情。 在我们创建新的进程以后,它通过调用exec 就能够变成独立于其他进程的进程了。 因此创建一个“真正”的新进程——与其祖先不同的程序运行镜像——任务分为两步,一步是fork ,另一步是exec。 do_execve处理三种工作:
1. 把一些定义信息从文件读入内存。
2. 准备新的参数和环境——这是C应用程序将它作为argc、argv和cnvp使用的内容。 3. 装载可以解析可执行文件的二进制处理程序,并让它处理剩余的修改内核数据的工作。 int do_execve( //准备新的参数和环境 char * filename,
char __user *__user *argv, char __user *__user *envp, struct pt_regs * regs) {
struct linux_binprm *bprm; // 建立struct linux_binprm struct file *file;
struct files_struct *displaced; int retval;
retval = unshare_files(&displaced); if (retval)
retval = -ENOMEM;
bprm = kzalloc(sizeof(*bprm), GFP_KERNEL); if (!bprm)
file = open_exec(filename); retval = PTR_ERR(file);
if (IS_ERR(file)) //判断可执行文件是否存在
goto out_kfree;
17
goto out_ret;
goto out_files;
操作系统课内实验报告
sched_exec(); bprm->file = file;
bprm->filename = filename; bprm->interp = filename; retval = bprm_mm_init(bprm); if (retval)
goto out_file;
bprm->argc = count(argv, MAX_ARG_STRINGS); if ((retval = bprm->argc) < 0)
goto out_mm;
bprm->envc = count(envp, MAX_ARG_STRINGS); if ((retval = bprm->envc) < 0)
goto out_mm;
retval = security_bprm_alloc(bprm); if (retval)
goto out;
retval = prepare_binprm(bprm); if (retval < 0)
goto out;
retval = copy_strings_kernel(1, &bprm->filename, bprm); //用copy_strings来把参数和环境变量拷贝到次年进程 if (retval < 0)
goto out;
bprm->exec = bprm->p;
retval = copy_strings(bprm->envc, envp, bprm); if (retval < 0)
goto out;
retval = copy_strings(bprm->argc, argv, bprm); if (retval < 0)
goto out;
current->flags &= ~PF_KTHREAD; retval = search_binary_handler(bprm,regs); //用search_binary_hander来找 到要执行的二进制处理程序 if (retval >= 0) {
/* execve success */
18
操作系统课内实验报告
} out:
security_bprm_free(bprm); acct_update_integrals(current); free_bprm(bprm); if (displaced)
put_files_struct(displaced);
return retval;
if (bprm->security) //判断bprm是否安全
security_bprm_free(bprm); //如果安全则释放它
out_mm: if (bprm->mm)
mmput (bprm->mm);
out_file: if (bprm->file) { } out_kfree: free_bprm(bprm); out_files: if (displaced)
reset_files_struct(displaced);
allow_write_access(bprm->file); //如果该文件有写权限则访问 fput(bprm->file);
out_ret: return retval; }
2.5.4 do_exit模块
exit用来让进程结束它在内核中是通过sys_exit实际是采用do_exit实现的当进程退出的时候内和会释放所有相关资源内存文件等等并停止给它CPU的机会但是这个时候进程并没有结束需要保留进程的task_struct结构等待它的父进程调用wait来查询子孙进程的退出状态所以内和必须保留子孙进程的PID直到wait发生为止这个时候的进程是僵进程
19
操作系统课内实验报告
图2.3 do_exit函数流程图
NORET_TYPE void do_exit(long code) //设置退出代码函数的参数 {
struct task_struct *tsk = current; int group_dead; profile_task_exit(tsk);
WARN_ON(atomic_read(&tsk->fs_excl)); if (unlikely(in_interrupt()))
panic(\
释放CPU 调用exit_notify告知进程已经退出 进入TASK_ZOMBIE状态,同时记录退出代码 退出信号 释放信号量和IPC、内存、文件、文件系统数据、信号量处理程序表 if (unlikely(!tsk->pid))
panic(\
tracehook_report_exit(&code); // 让进程进入TASK_ZOMBIE状态,并记录它的退出代码
if (unlikely(tsk->flags & PF_EXITING)) {
printk(KERN_ALERT
\
tsk->flags |= PF_EXITPIDONE; if (tsk->io_context)
exit_io_context();
set_current_state(TASK_UNINTERRUPTIBLE);
20