标签:
原创作品转载请注明出处+《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
在Linux中,task_struct其实就是通常所说的PCB。该结构定义位于:
/include/linux/sched.h
task_struct比较庞大,大致可以分为几个部分:
其中比较重要的几个参数:
volatile long state;
进程状态,可见/include/linux/sched.h
文件中的宏,TASK_RUNNING等unsigned int rt_priority;
实时优先级unsigned int policy;
调度策略pid_t pid;
进程标识符struct task_struct __rcu *real_parent;
real parentstruct list_head children;
list of my childrenstruct files_struct *files;
系统打开文件fork、vfork和clone三个系统调用实际上都是通过do_fork
来实现进程的创建.
见如下语句:
return do_fork(clone_flags, newsp, 0, parent_tidptr, child_tidptr);
而do_fork函数真正实现复制是copy_process
long do_fork(unsigned long clone_flags,
unsigned long stack_start,
unsigned long stack_size,
int __user *parent_tidptr,
int __user *child_tidptr)
{
...
p = copy_process(clone_flags, stack_start, stack_size,
child_tidptr, NULL, trace);
...
}
copy_process()主要完成进程数据结构,各种资源的初始化。
p = dup_task_struct(current);
p = dup_task_struct(current);
调用dup_task_struct()为新进程创建一个内核栈retval = sched_fork(clone_flags, p);
完成调度相关的设置,将这个task分配给CPUif (retval)
语句群,复制共享进程的的各个部分retval = copy_thread(clone_flags, stack_start, stack_size, p);
复制父进程堆栈的内容到子进程的堆栈中去.这其中,copy_thread()函数中的语句p->thread.ip = (unsigned long) ret_from_fork;
决定了新进程的第一条指令地址.static struct task_struct *dup_task_struct(struct task_struct *orig)
{
struct task_struct *tsk;
struct thread_info *ti;
int node = tsk_fork_get_node(orig);
int err;
tsk = alloc_task_struct_node(node);
if (!tsk)
return NULL;
ti = alloc_thread_info_node(tsk, node);
if (!ti)
goto free_tsk;
err = arch_dup_task_struct(tsk, orig);
if (err)
goto free_ti;
tsk->stack = ti;
# ifdef CONFIG_SECCOMP
tsk->seccomp.filter = NULL;
# endif
setup_thread_stack(tsk, orig);
clear_user_return_notifier(tsk);
clear_tsk_need_resched(tsk);
set_task_stack_end_magic(tsk);
# ifdef CONFIG_CC_STACKPROTECTOR
tsk->stack_canary = get_random_int();
# endif
atomic_set(&tsk->usage, 2);
# ifdef CONFIG_BLK_DEV_IO_TRACE
tsk->btrace_seq = 0;
# endif
tsk->splice_pipe = NULL;
tsk->task_frag.page = NULL;
account_kernel_stack(ti, 1);
return tsk;
free_ti:
free_thread_info(ti);
free_tsk:
free_task_struct(tsk);
return NULL;
}
tsk = alloc_task_struct_node(node);
为task_struct开辟内存ti = alloc_thread_info_node(tsk, node);
ti指向thread_info的首地址,同时也是系统为新进程分配的两个连续页面的首地址。err = arch_dup_task_struct(tsk, orig);
复制父进程的task_struct信息到新的task_struct里, (dst = src;)tsk->stack = ti;
task的对应栈setup_thread_stack(tsk, orig);
初始化thread info结构set_task_stack_end_magic(tsk);
栈结束的地址设置数据为栈结束标示(for overflow detection)value optimized out
的提示,这是因为Linux内核打开gcc的-O2选项优化导致.如果想要关掉,可以参考:这里
syscall_exit
后无法继续.如果想在本机调试system call,那么当你进入system call时,系统已经在挂起状态了。如果想要跟踪调试system_call,可以使用kgdb等在之前的分析中,谈到copy_process中的copy_thread()
函数,正是这个函数决定了子进程从系统调用中返回后的执行.
int copy_thread(unsigned long clone_flags, unsigned long sp,
unsigned long arg, struct task_struct *p)
{
...
*childregs = *current_pt_regs();
childregs->ax = 0;
if (sp)
childregs->sp = sp;
p->thread.ip = (unsigned long) ret_from_fork;
...
}
ENTRY(ret_from_fork)
CFI_STARTPROC
pushl_cfi %eax
call schedule_tail
GET_THREAD_INFO(%ebp)
popl_cfi %eax
pushl_cfi $0x0202 # Reset kernel eflags
popfl_cfi
jmp syscall_exit
CFI_ENDPROC
END(ret_from_fork)
*childregs = *current_pt_regs();
该句将父进程的regs参数赋值到子进程的内核堆栈,task_struct
结构.标签:
原文地址:http://www.cnblogs.com/20135126xff/p/5350424.html