目录
0号进程的创建
1、进程的PCB task_struct
2、0号进程静态创建
3、定义内核栈task_union
4、如何把0号进程内核栈,放在.data.init_data段里?
进程创建过程
1、设置子进程的上下文信息
2、wake_up_process
0号进程为init进程,执行顺序:系统上电->系统固件加载->汇编入口->kernel_main
struct task_struct {
enum task_state state;
enum task_flags flags;
int pid;
struct cpu_context cpu_context;
struct list_head run_list;
int counter;
int priority;
int need_resched;
int preempt_count;
struct task_struct *next_task;
struct task_struct *prev_task;
};
#define INIT_TASK(task) \
{ \
.state = 0, \
.priority = 20, \
.counter = DEF_COUNTER, \
.flags = PF_KTHREAD, \
.pid = 0, \
.preempt_count = 0, \
.need_resched = 0, \
.next_task = (struct task_struct *)&task, \
.prev_task = (struct task_struct *)&task, \
}
需要为0号进程分配栈空间。通常做法把0号进程的内核栈空间直接链接到数据段里。其他进程的内核栈是动态分配的。
union task_union {
struct task_struct task;
unsigned long stack[THREAD_SIZE/sizeof(long)];
};
task_union数据结构存储在内核栈的底部。
通过GCC的__attribute__属性 把task_union编译连接到.data.init_task段中
#define __init_task_data __attribute__((__section__(".data.init_task")))
0号进程为init进程
union task_union init_task_union __init_task_data = {INIT_TASK(init_task_union.task)};
链接文件 增加.data.init_task
SECTIONS
{
...
_erodata = .;
_data = .;
.data : {
*(.data)
. = ALIGN(PAGE_SIZE);
*(.data.init_task)
}
...
}
int do_fork(unsigned long clone_flags, unsigned long fn, unsigned long arg)
{
struct task_struct *p;
int pid;
p = (struct task_struct *)get_free_page();
if (!p)
goto error;
memset(p, 0, sizeof(*p));
pid = find_empty_task();
if (pid < 0)
goto error;
if (copy_thread(clone_flags, p, fn, arg))
goto error;
p->state = TASK_RUNNING;
p->pid = pid;
p->counter = (current->counter + 1) >> 1;
current->counter >>= 1;
p->need_resched = 0;
p->preempt_count = 0;
p->priority = 2;
total_forks++;
g_task[pid] = p;
SET_LINKS(p);
wake_up_process(p);
return pid;
error:
return -1;
}
static int copy_thread(unsigned long clone_flags, struct task_struct *p,
unsigned long fn, unsigned long arg)
{
struct pt_regs *childregs;
childregs = task_pt_regs(p);
memset(childregs, 0, sizeof(struct pt_regs));
memset(&p->cpu_context, 0, sizeof(struct cpu_context));
if (clone_flags & PF_KTHREAD) {
childregs->pstate = PSR_MODE_EL1h;
p->cpu_context.x19 = fn;
p->cpu_context.x20 = arg;
}
p->cpu_context.pc = (unsigned long)ret_from_fork;
p->cpu_context.sp = (unsigned long)childregs;
return 0;
}
关键点
ret_from_fork的汇编实现,此函数是新进程执行的开始,x19保存了进程的回调函数,x20保存了回调函数的参数。
.align 2
.global ret_from_fork
ret_from_fork:
bl schedule_tail
cbz x19, 1f
mov x0, x20
blr x19
1:
b ret_to_user
将进程加入到就绪队列中,等待调度
void wake_up_process(struct task_struct *p)
{
struct run_queue *rq = &g_rq;
p->state = TASK_RUNNING;
enqueue_task(rq, p);
}
参考
https://course.0voice.com/v1/course/intro?courseId=2&agentId=0