copy_process函数的执行
具体执行流程如下:
1.检查相关的标志组合是否合法。
- //共享文件命名空间又新创建命名空间,返回错误
-
if((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS| CLONE_FS))
-
/*
-
static inline void *ERR_PTR(long error)
-
{
-
return (void*)error
-
}
-
**/
-
return ERR_PTR(-EINVAL);
-
//线程组必须共享信号量
-
if((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND))
-
return ERR_PTR(-EINVAL);
-
//共享虚拟内存必须共享信号量
-
if((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM))
-
return ERR_PTR(-EINVAL);
2.创建线程
- p = dup_task_struct(current).
-
if(!p) goto fork_out;
-
dup_task_struct函数定义如下:
-
static struct task_struct *dup_task_struct(struct task_struct *orig)
-
{
-
struct task_struct *tsk;
-
struct thread_info *ti;
-
int err;
-
-
prepare_to_copy(orig);
-
/*内核中的所有内存动态分配都是由slab分配器负责*/
-
tsk = alloc_task_struct();
-
if(!task) return NULL;
-
/* 这里的分配有些特别
-
* #define alloc_thread_info(task) ((struct thread_info *) \
-
* __get_free_pages(GFP_KERNEL,get_order(THREAD_SIZE)))
-
*/
-
ti = alloc_thread_info();
-
if(!ti) {
-
free_task_struct(tsk);
-
return NULL;
-
}
-
*tsk = *orig;//所有的数据结构都一样
-
tsk->stack = ti;//只有内核态栈不相同
-
//初始化数据结构struct prop_local_single,事件计数器,具体不祥?
-
err = prop_local_init_single(&tsk->dirties);
-
if(!err) {
-
free_thread_info(ti);
-
free_task_struct(tsk);
-
return NULL;
-
}
-
//将内核栈也进行赋值,分配内存之后,开始进行赋值,这样的话,除了内核栈一样之外,其它的都相同
-
/* #define task_thread_info(task) ((struct thread_info *)(task) ->stack)
-
* *task_thread_info(tsk) = *task_thread_info(orig);//怎么栈又赋值到原来的去了?
-
* task_thread_info(tsk)->task = *tsk;
-
*/
-
setup_thread_stack(tsk,orig);
-
atomic_set(&tsk->usage,2);
-
atomic_set(&tsk->fs_excl,0);
-
tsk->splice_pipe = NULL;
-
return tsk;
-
}
上面主要工作是分配空间,进行用户栈,内核栈的复制工作,进程复制中最主要的区别是内核栈task_struct->stack,通常它存放在thread_union结构中,这里面保存着与cpu相关的所有信息.
- union thread_info {
-
struct thread_info thread_info;
-
//这里通过配置可以是4KB或者8KB,这样内核栈可以为4KB或8KB,它与thread_info保存在一起,这样thread_info就在内核栈中,同时位于栈顶。
-
unsigned long stack[THREAD_SIZE/sizeof(long)];
-
};
其中thread_info为内核栈,定义如下:
- struct thread_info {
-
struct task_struct *task;//该thread所属的task
-
struct exec_domain *exec_domain;
-
unsigned long flags;
-
unsigned long status;
-
__u32 cpu;
-
int preempt_count;
-
mm_segment_t addr_limit;
-
void *sysenter_return;
-
struct restart_block restart_block;//实现的信号机制
-
unsigned long previous_step;
-
__u8 supervisor_stack[0];//为了动态扩展
-
}
flags:能够保存进程相关的各种标记,其中有:
TIF_SIGPENDING:如果当前有悬挂的信号
TIF_NEED_RESCHED:该进程是否被调度
更多的设置在文件中。
cpu:当前进程正在执行的cpu号。
preempt_count:当前进程被抢占的次数。
addr_limit:进程可能使用的那一段虚拟地址空间,用户态线程有地址限制,但内核线程可以访问所有的虚拟地址空间。
通过栈顶指针来获取当前进程信息:
- register unsigned long current_stack_pointer ams(“esp”) __attributed_used__;
-
static inline struct thread_info *current_thread_info()
-
{
-
return (struct thread_info*)(current_stack_pointer & ~THREAD_SIZE-1))
-
}
当前进程也是通过该结构获取的。
2.检查资源限制
- //检查用户进程个数
-
if(atomic_read(&p->user->processes) >= p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
-
if(!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&
-
p->user != current->nsproxy->user_ns->root_user)
-
goto bad_fork_free;
-
}
-
atomic_inc(&p->user->__count);
-
atomic_inc(&p->user->processes);
-
get_group_info(p->group_info);
-
//检查线程个数
-
if(nr_threads >= max_threads)
-
goto bad_fork_cleanup_count;
-
//检查是否有执行模块
-
if(!try_module_get(task_thread_info(p) ->exec_domain->module))
-
goto bad_fork_cleanup_count;
-
if(p->binfmt && !try_module_get(p->binfm->module))
-
goto bad_fork_cleanup_put_domain;
3.初始化数据结构
- p->did_exec = 0;
-
//分配并初始化task中的delay数据结构
-
delayacct_tsk_init(p);
-
//赋值到p->flags中
-
copy_flags(clone_flags,p);
-
INIT_LIST_HEAD(&p->children);
-
INIT_LIST_HEAD(&p->sibling);
-
p->vfork_done = NULL;
-
spin_lock_init(&p->alloc_lock);
-
clear_tsk_thread_flag(p,TIF_SIGPENDING);
-
….......
4.调度程序
- sched_fork(p,clone_flags);
-
该函数原型如下:
-
void sched_fork(struct task_struct *p,int clone_flags)
-
{
-
int cpu = get_cpu();
-
/*__sched_fork(struct task_struct *p)
-
p->se.exec_start = 0;
-
p->se.sum_exec_runtime = 0;
-
p->se.prev_sum_exec_runtime = 0;
-
INIT_LIST_HEAD(&p->run_list);
-
p->state = TASK_RUNNING;
-
*/
-
__sched_fork(p);
-
set_task_cpu(p,cpu);
-
if(!rt_prio(p->prio))//如果不是实时进程就使用公平调度
-
p->sched_class = &fair_sched_class;
-
put_cpu();
-
}
5.copy_parent_information
上面在分配空间时,只是一个简单的赋值,下面才真正进行赋值操作
- //复制undo信号
-
if((retval = copy_semundo(clone_flags,p)))
-
goto bad_fork_cleanup_semudo;
-
//复制file_table表
-
if((retval = copy_files(clone_flags,p)))
-
goto bad_fork_cleanup_semudo;
-
//复制struct fs_struct结构
-
if((retval = copy_fs(clone_flags,p)))
-
goto bad_fork_cleanup_files;
-
//复制信号处理函数
-
if((retval = copy_sighand(clone_flags,p)))
-
goto bad_fork_cleanup_sighand;
-
//复制signal_struct结构
-
if((retval=copy_signal(clone_flags,p)))
-
goto bad_fork_cleanup_fs;
-
//复制内存空间
-
if((retval = copy_mm(clone_flags,p)))
-
goto bad_fork_cleanup_sighand;
-
//复制认证信息
-
if((retval=copy_keys(clone_flags,p)))
-
goto bad_fork_cleanup_mm;
-
//复制命名空间
-
if((retval=copy_namespaces(clone_flags,p)))
-
goto bad_fork_cleanup_keys;
-
//复制线程栈数据
-
retval = copy_thread(0,clone_flags,stack_start,stack_size,p,regs);
-
if(retval)
-
goto bad_fork_cleanup_namespaces;
这些复制函数都有一个标志参数clone_flags,因为如果是CLONE_ABC,则标明在父子进程之间共享,否则就新创建一个数据结构。
6.setID,parent information
- //从全局命名空间中获取pid
-
p->pid = pid_nr(pid);
-
p->tgid = p->pid;
-
if(clone_flags & CLONE_THREAD)
-
p->tgid = current->tgid;//主进程是进程组id
-
…....
-
if(clone_flags & (CLONE_PARENT|CLONE_THREAD))
-
//如果创建的是线程,当前进程的父进程才是该线程的双亲
-
p->real_parent = current->real_parent;
-
else p->real_parent = current;//否则就是当前进程了
-
p->parent = p->real_parent;
-
if(clone_flags & CLONE_THREAD)
-
{
-
//创建的是线程的话,线程组id就是当前线程的组id,同时加入列表中
-
p->group_leader = current->group_leader;
-
list_add_tail_rcu(&p->thread_group,&p->group_leader->thread_group);
-
…......
-
}
-
//将该进程加入父进程的链表中
-
add_parent(p);
-
//p==p->group_leader,是否是进程组领导者
-
if(thread_group_leader(p)) {
-
//命名空间中的,新命名空间中的init角色
-
if(clone_flags & CLONE_NEWPID)
-
p->nsproxy->pid_ns->child_reaper = p;
-
//继承父进程的信息
-
p->signal->tty = current->signal->tty;
-
set_task_pgrp(p,task_pgrp_nr(current));
-
set_task_session(p,task_session_nr(current));
-
attach_pid(p,PIDTYPE_PGID,task_pgrp(current));
-
attach_pid(p,PIDTYPE_SID,task_session(current));
-
//放入全局任务队列
-
list_add_tail_rcu(&p->tasks,&init_task.tasks);
-
//cpu局部进程数目自增
-
__get_cpu_var(process_counts)++;
-
}
-
attach_pid(p,PIDTYPE,pid);
-
nr_threads++;
-
}
-
//全局fork进程自增
-
total_forks ++;
-
spin_unlock(¤t->sighand->siglock);
-
write_unlock_irq(&tasklist_lock);
-
//设置proc事件
-
proc_fork_connector(p);
-
cgroup_post_fork(p);
-
return p;
参考资料:
linux-2.24.3源代码
professional linux kernel architecture
lxr.linux.no
阅读(1484) | 评论(0) | 转发(0) |