linux init进程创建
Posted 为了维护世界和平_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux init进程创建相关的知识,希望对你有一定的参考价值。
目录
4、如何把0号进程内核栈,放在.data.init_data段里?
0号进程的创建
0号进程为init进程,执行顺序:系统上电->系统固件加载->汇编入口->kernel_main
1、进程的PCB task_struct
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;
;
2、0号进程静态创建
#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号进程的内核栈空间直接链接到数据段里。其他进程的内核栈是动态分配的。
3、定义内核栈task_union
union task_union
struct task_struct task;
unsigned long stack[THREAD_SIZE/sizeof(long)];
;
task_union数据结构存储在内核栈的底部。
4、如何把0号进程内核栈,放在.data.init_data段里?
通过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;
- 获取task_struct进程的内存,分配4KB页面存储内核栈
- 获得pid号
- copy进程信息
- 设置进程参数,状态,优先级,时间片
- 将进程加入到就绪队列中wake_up_process
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;
关键点
- x19:回调函数
- x20:回调函数的参数
- pc:指针指向ret_from_fork,SP寄存器指向内核的pt_regs栈框
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
- 如果next进程是内核线程,在创建时会使X19寄存器指向task_start
- 如果X19寄存器的值为0,说明这个next进程是用户进程,跳转到ret_to_user
- 如果next进程是内核线程,那么跳转到内核线程的回调函数里。
2、wake_up_process
将进程加入到就绪队列中,等待调度
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
以上是关于linux init进程创建的主要内容,如果未能解决你的问题,请参考以下文章
Linux下1号进程的前世(kernel_init)今生(init进程)----Linux进程的管理与调度