p->per_cpu_utime[cpu_logical_map(i)]=
p->per_cpu_stime[cpu_logical_map(i)]=0;
spin_lock_init(&p->sigmask_lock);
}
#endif
p->array=NULL;
p->lock_depth=-1;/*-1=没有锁*/
p->start_time=jiffies_64;/*将当前的jiffies值作为子进程的创建时间*/
/*task_struct结构初始化完毕*/
retval=-ENOMEM;
/*copyalltheprocessinformation*/
if(copy_files(clone_flags,p))/*复制所有的进程信息,根据clone_flags复制或共享父进程的打开文件表*/
gotobad_fork_cleanup;
if(copy_fs(clone_flags,p))/*根据clone_flags复制或共享父进程的系统信息*/
gotobad_fork_cleanup_files;
if(copy_sighand(clone_flags,p))/*根据clone_flags复制或共享父进程的信号处理句柄*/
gotobad_fork_cleanup_fs;
if(copy_mm(clone_flags,p))/*根据clone_flags复制或共享父进程的存储管理信息*/
gotobad_fork_cleanup_sighand;
if(copy_namespace(clone_flags,p))/*为子进程复制父进程系统空间堆栈*/
gotobad_fork_cleanup_mm;/*若系统空间堆栈复制失败跳转至bad_fork_cleanup_mm*/
retval=copy_thread(0,clone_flags,stack_start,stack_size,p,regs);
if(retval)
gotobad_fork_cleanup_namespace;
p->semundo=NULL;
/*将子进程task_struct结构的self_exec_id赋给parent_exec_id*/
p->parent_exec_id=p->self_exec_id;
p->swappable=1;/*新进程已经完成初始化,可以换出内存,所以将p->swappable赋1*/
p->exit_signal=clone_flags&CSIGNAL;/*设置系统强行退出时发出的信号*/
p->pdeath_signal=0;/*设置p->pdeath_signal*/
/**Sharethetimeslicebetweenparentandchild,thusthe
*totalamountofpendingtimeslicesinthesystemdoesntchange,
*resultinginmoreschedulingfairness.*/
__save_flags(flags);
__cli();
if(!
current->time_slice)/*将父进程的时间片减半*/
BUG();
p->time_slice=(current->time_slice+1)>>1;
p->first_time_slice=1;
current->time_slice>>=1;
p->sleep_timestamp=jiffies;
if(!
current->time_slice){
current->time_slice=1;
scheduler_tick(0,0);
}
__restore_flags(flags);
retval=p->pid;/*如果一切顺利,将子进程的pid作为返回值*/
p->tgid=retval;
INIT_LIST_HEAD(&p->thread_group);
/*Needtasklistlockforparentetchandling!
*/
write_lock_irq(&tasklist_lock);/*给进程队列加锁*/
/*CLONE_PARENTre-usestheoldparent*/
p->p_opptr=current->p_opptr;
p->p_pptr=current->p_pptr;
if(!
(clone_flags&CLONE_PARENT)){
p->p_opptr=current;
if(!
(p->ptrace&PT_PTRACED))
p->p_pptr=current;
}
if(clone_flags&CLONE_THREAD){
p->tgid=current->tgid;
list_add(&p->thread_group,¤t->thread_group);
}
SET_LINKS(p);/*将子进程的task_struct结构链入进程队列*/
hash_pid(p);/*将子进程的task_struct结构链入进程hash表*/
nr_threads++;/*系统进程计数递增一*/
write_unlock_irq(&tasklist_lock);/*解除对进程队列的封锁*/
if(p->ptrace&PT_PTRACED)
send_sig(SIGSTOP,p,1);
wake_up_forked_process(p);/*最后做这件事,唤醒子进程*/
++total_forks;/*total_forks增一*/
if(clone_flags&CLONE_VFORK)
wait_for_completion(&vfork);
else
current->need_resched=1;
fork_out:
/*若是vfork()调用do_fork,发down信号*/
returnretval;/*退出do_fork(),返回retval值*/
bad_fork_cleanup_namespace:
exit_namespace(p);
bad_fork_cleanup_mm:
exit_mm(p);
bad_fork_cleanup_sighand:
/*处理子进程task_struct结构与信号处理相关的数据成员,并删除信号队列中与子进程相
*关的信号量*/
exit_sighand(p);
bad_fork_cleanup_fs:
/*处理子进程task_struct结构与文件系统信息相关的数据成员*/
exit_fs(p);/*blocking*/
bad_fork_cleanup_files:
/*处理子进程task_struct结构与打开文件表相关的数据成员,并释放子进程的files_struct
*结构*/
exit_files(p);/*blocking*/
bad_fork_cleanup:
/*若正在执行的代码是符合iBCS2标准的程序,则减少相对应模块的引用数目*/
put_exec_domain(p->exec_domain);
if(p->binfmt&&p->binfmt->module)
__MOD_DEC_USE_COUNT(p->binfmt->module);
bad_fork_cleanup_count:
/*若正在执行的代码属于全局执行文件结构格式则减少相对应模块的引用数目*/
atomic_dec(&p->user->processes);
free_uid(p->user);/*清除子进程在user队列中的信息*/
bad_fork_free:
free_task_struct(p);/*释放子进程的task_struct结构*/
gotofork_out;
}
三)、程序框图如下:
2、exec.c
一)、概述
进程通常是由父进程复制出来的(由fork()或clone())。
假若子进程只是父进程的“影子”,那么没有什么意义了。
所以,执行一个新的可执行程序才是创建进程的意义所在。
在Linux中,提供了一个系统调用execve(),其内核入口是sys_execve()。
二)、代码分析
asmlinkageintsys_execve(structpt_regsregs)
{
interror;
char*filename;
filename=getname((char*)regs.ebx);
error=PTR_ERR(filename);
if(IS_ERR(filename))
gotoout;
error=do_execve(filename,(char**)regs.ecx,(char**)regs.edx,®s);
if(error==0)
current->ptrace&=~PT_DTRACE;
putname(filename);
out:
returnerror;
}
regs.ebx中的内容为应用程序中调用相应库函数时的第一个参数。
getname()把regs.ebx所指向的字符串从用户空间拷贝到系统空间,在系统空间建立起一个副本。
getname()通过dogetname()从用户空间拷贝字符串。
建立起一个可执行文件路径名的副本后,sys_execve()调用do_execve()以完成其主体部分的工作。
intdo_execve(char*filename,char**argv,char**envp,structpt_regs*regs)
{
structlinux_binprmbprm;
//用于组织运行可执行文件所需的信息,通过此变量与负责处理其部分工作的其他//函数通信;在do_execve返回时废弃;
structfile*file;
intretval;
inti;
file=open_exec(filename);//找到给定的可执行文件并打开;
retval=PTR_ERR(file);
if(IS_ERR(file))
returnretval;//检测打开文件是否有错;
首先将给定路径名的可执行文件找到并打开,因而调用open_exec()来实现。
函数返回一个file结构指针,代表读入可执行文件的上下文,保存在变量bprm中。
代码开始定义了一个linux_binprm结构的变量bprm,用于将运行一个可执行文件所需的信息组织在一起。
linux_binprm结构定义(在include/linux/binfmts.h中)如下:
structlinux_binprm{
charbuf[BINPRM_BUF_SIZE];
structpage*page[MAX_ARG_PAGES];
unsignedlongp;/*currenttopofmem*/
intsh_bang;
structfile*file;
inte_uid,e_gid;
kernel_cap_tcap_inheritable,cap_permitted,cap_effective;
intargc,envc;
char*filename;/*Nameofbinary*/
unsignedlongloader,exec;
};
在do_execve()中,接下来的代码是:
bprm.p=PAGE_SIZE*MAX_ARG_PAGES-sizeof(void*);//初始化bprm结构的128k页表除去第一个argv[0]);
memset(bprm.page,0,MAX_ARG_PAGES*sizeof(bprm.page[0]));
//将参数页面指针数组初始化为零;
bprm.file=file;//可执行文件的上下文;
bprm.filename=filename;//可执行文件的路径名;
bprm.sh_bang=0;//可执行文件的性质;
bprm.loader=0;
bprm.exec=0;
if((bprm.argc=count(argv,bprm.p/sizeof(void*)))<0){
allow_write_access(file);
fput(file);
returnbprm.argc;
}//根据argv数组计算非空指针个数并赋给argc成员;
if((bprm.envc=count(envp,bprm.p/sizeof(void*)))<0){
allow_write_access(file);
fput(file);
returnbprm.envc;
}//统计环境变量个数并且赋值给envc的各个成员;
retval=prepare_binprm(&bprm);////进行访问权限等内容的安全检测后,读入可执行文件前128字节;
if(retval<0)
gotoout;
变量bprm.sh_bang表示可执行文件的性质,此时初始化为0。
其他两个变量也设置为0,因为现在还不知道文件性质。
Bprm中定义了一个参数页面指针数组,通过memset()将此数组全设置为0;将bprm.p设置为这些页面的总和减去一个指针的大小,原因是argv[0]是可执行文件的路径名。
函数count()对用户空间作为参数传过来的字符串指针数组argv[]和环境变量envp[]进行计数。
完成计数后,do_execve()调用prepare_binprm()对bprm中的其他成员准备信息。
可执行文件的开头128个字节包含了文件属性的一些重要信息,并将这128个信息读入到bprm的缓冲区中。
retval=copy_strings_kernel(1,&bprm.filename,&bprm);//从系统空间中拷贝可执行文件路径名;
if(retval<0)
gotoout;
bprm.exec=bprm.p;
retval=copy_strings(bprm.envc,envp,&bprm);//从用户空间拷贝环境信息;
if(retval<0)
gotoout;
retval=copy_strings(bprm.argc,argv,&bprm);//从用户空间拷贝参数信息;
if(retval<0)
gotoout;
由于可执行文件的路径名已经在系统空间中了,所以调用copy_strings_kernel()拷贝到bprm中;其他的argv[]和envp[]还存在于用户空间,调用copy_strings()拷贝到bprm中。
至此,可执行文件运行所需的所有信息都已组织到变量bprm中了,接下来的代码就是装入并运行了。
retval=search_binary_handler(&bprm,regs);//从formats中搜索能够识别的二进制处理程序并将其装入内核,以便执行;
if(retval>=0)//找到能够识别的二进制处理程序;
/*