linux init进程创建

目录

0号进程的创建

1、进程的PCB task_struct

2、0号进程静态创建

3、定义内核栈task_union

4、如何把0号进程内核栈,放在.data.init_data段里?

进程创建过程

1、设置子进程的上下文信息

 2、wake_up_process


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);
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 在Linux中,进程可以创建多个线程。线程是轻量级的执行单元,它们共享进程的资源,如内存、文件描述符等。在Linux中,线程是通过pthread库来实现的。要创建一个线程,可以使用pthread_create函数。该函数需要四个参数:线程标识符、线程属性、线程函数和函数参数。线程函数是线程的入口点,它会在新线程中执行。函数参数是传递给线程函数的参数。在创建线程后,可以使用pthread_join函数等待线程结束,并获取线程的返回值。 ### 回答2: 在Linux操作系统中,一个进程可以创建多个线程,每个线程都拥有自己的堆栈和程序计数器。线程是进程中可独立执行的最小单元,不同线程之间可以共享进程的资源,如内存、文件和网络连接。 Linux系统提供了pthread库来支持线程的创建和管理。线程的创建通过调用pthread_create()函数完成,该函数的参数包括了线程标识符、线程属性、线程函数及其参数等。线程属性包括线程栈的大小、调度策略、优先级等,可以通过pthread_attr_init()和pthread_attr_set*()系列函数来进行设置。 线程函数是线程所执行的函数,它需要符合特定的格式,即void * function(void *arg),其中arg是传入线程函数的参数。可以把需要在不同线程中执行的代码封装成线程函数,进程通过创建多个线程来实现并发执行。 线程之间的通信可以通过共享内存、信号量、互斥锁等方式进行。多线程编程需要注意的问题包括线程安全、共享资源的管理、竞态条件等。在多线程编程中,必须保证线程间的同步和互斥,以防止竞争条件的出现,同时还需要避免死锁和饥饿等问题。 总之,在Linux操作系统中,通过使用pthread库可以方便地创建多个线程,实现并发执行,从而提高应用程序的性能和响应速度。但同时需要注意多线程编程中的安全性和性能问题,以确保程序的正确性和效率。 ### 回答3: Linux进程可以创建多个线程,这些线程会共享进程的内存空间和资源。线程是轻量级的执行单元,可以独立运行,也可以相互协同完成任务。线程之间的协作和同步可以通过线程库中提供的锁、条件变量、信号量等机制实现。 在Linux中,可以使用pthread库来创建线程。pthread库提供了一套完整的API,包括创建、同步、销毁等操作。线程的创建需要指定线程函数的名称、传递给线程函数的参数,以及线程属性等信息。线程属性包括:线程的调度策略、优先级、堆栈大小等。 当进程创建多个线程时,这些线程可以并发执行,以提高程序的并发性能。但是需要注意的是,多个线程之间共享进程的资源,如果没有进行合理的同步和互斥处理,就会导致数据竞争和死锁等问题。因此,在多线程编程中,需要特别注意线程之间的互斥和同步机制,以保证程序的正确性和稳定性。 总之,Linux进程可以创建多个线程,线程是轻量级的执行单元,可以并发执行,在多线程编程中需要注意线程之间的同步和互斥处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

为了维护世界和平_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值