linux查看native进程,oklinux 进程创建和native linux 的简单比较(arm)

1. 关于thread_info

thread_info  本来就是arch 相关的

但 arm oklinux thread_info 与arm native linux(下称nlinux)

thread_info

差别比较大,添加了一些和l4 相关的特性

struct thread_info {

L4_ThreadId_t           user_tid; /* user process L4-thread Id */

L4_ThreadId_t           user_handle; /* handle to check incoming IPC tid with read tid */

/* 这个handle 在  okl4_create_thread 获得 */

struct task_struct *task;  /* main task structure */

struct exec_domain *exec_domain; /* execution domain */

unsigned long  flags;  /* low level flags */

unsigned long  irq_flags; /* irq flags over context switch */

__u32   cpu;  /* current CPU */

__s32   preempt_count;  /* 0 => preemptable, <0 => BUG */

struct restart_block    restart_block;

struct arch_kernel_context context;/* architecture independent kernel context */

struct pt_regs  regs;  /* user process saved registers */

L4_MsgTag_t  tag;  /* user process L4 tag */

/*

* The tls bitmap is only used on the i386 architecture

* at this point in theory could be used to implement

* multiple slots for the thread local storage area.

*

* -gl

*/

unsigned long  tls_bitmap;

unsigned long  tp_value;

#define OP_NONE  0

#define OP_FORK  1       /* 指定fork 操作*/

#define OP_KTHREAD 2       /* 指定创建内核线程*/

#define OP_DELETE 3

#define OP_RESUME 4

struct {

long op;

union {

struct {

L4_Word_t user_ip;

L4_Word_t user_sp;

L4_Word_t user_start;

} fork, exec;

struct {

int (*proc)(void *);

void *arg;

} thread;

struct {

void (*proc)(void *);

void *arg;

} cb;

} u;

} request;

struct {

L4_Word_t user_ip;

L4_Word_t user_sp;

L4_Word_t user_flags;

} restart;

};

user_tid   : 用于保存l4 thread 的id (就是cap)

user_handle: 是在l4 creat thread 时获得的 thread handle

是用ktcb的tcb_index 创建一个capid_t 类的对象,

其实thread handle  就是 tcb_idx

注意thread id 是TYPE_CAP , thread handle 是TYPE_THREAD_HANDLE

context:     对应arm oklinux 保存r4 - pc 的arm reg

regs   :     原来在内核栈上保存的进程进入kernel mode 时的寄存器的值

现在保存在regs 上,reg 中的mode 保存了进程的mode

/* mode: 0 - in user, 1 - in kernel, 2 - isr */

tag:         放了接收到ipc msg 的tag ,通过L4_Label,可以知道tag

类型,然后在进程进入kernel mode 时用于判断什么原因

进入kernel mode

tls_bitmap:  x86 使用,飘过

request:    和进程创建的fork 有关, 对于用户进程fork,或者kthread 创建

使用union 中不同的struct

具体copy thread  分析时再说

restart:    保存重调度运行时的pc,sp

2.  fork 前

主要是指创建kernel thread, 和实际fork 的一些差别

首先看下kernel thread 创建

通过直接设置request  op 为 OP_KTHREAD,并指定union中thread 结构中函数指针

和参数

接下来是sys_fork,sys_vfork 这部分基本类似,

但要指定 requrest op 的值为  OP_FORK,给do_fork 中

copy_thread 做判断,做相应操作

接下来是sys_clone,因为navite sysclone 如下

asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,

int __user *parent_tidptr, int tls_val,

int __user *child_tidptr, struct pt_regs *regs)

所以要将parent_tidptr ,child_tidptr,从regs 中r2, r4 取出,做为do_fork 时

传入

3. do_fork 过程

首先oklinux 中do_fork 的流程和nlinux 一样

其中copy_process中copy_mm 执行过程一样,但主要区别在于

mm_struct 中mm_context_t 有变化

mm_context_t 在支持ASID 的arm 上用来存放地址空间ID,

而oklinux 中 ASID ,或者FSCE等硬件部分都是由l4 来管理

对应上层,代以space id ,

所以 mm_context_t,中对于space id 添加了L4_SpaceId_t space_id

typedef struct {

L4_SpaceId_t space_id;     /* l4 地址空间id !!! */

#ifdef ARM_PID_RELOC /* XXX should move to asm-l4/arm/mmu.h -gl */

int pid;

unsigned largevm:1;

rfl_t mmap_free_list;

#endif

L4_ARCH_MMU_CONTEXT

} mm_context_t;

ARM_PID_RELOC 于arm9 fass 相关

init_new_context 也发生相应变化,主要执行过程:

先在space_caches上获得1个space id 如果没有

通过okl4_create_space 在spaceid_pool获得一个

spaceid_pool 是在    l4_process_init

通过okl4_env_get("MAIN_SPACE_ID_POOL")取得

其中space 设置最大 256个

可以从wearver.xml 中看到:

kernel_heap="0x400000" mutexes="256" name="oklinux" spaces="256">

它的设置在cell 的linux Sconscript 中如下:

rootserver_env.set_cell_config(spaces = 256, clists = 256, mutexes = 256)

接下来dup_mmap,和nlinux 一样,进行虚拟内存mmap 的copy ,以及页表的 copy

如前面pagefualt 文中所说,这里的linux 页表是shadow的概念,不在硬件mmu上

起作用,但是对于涉及到实际mmu 上个对页属性的改变部分,比如COW,要设置

写保护,那么完成shadow 页表的设置还需要call l4 map api 去重新map

该page , 关于oklinux pagetable  操作价构独立部分内容在

include\asm\l4\pgtable.h中

其实上面COW 所说的ptep_set_wrprotect,在其中多了个tlb_modify

该函数就是通过call l4 map api 实现从新map ,修改page 属性

最后是copy_thread了,在nlinux  中比较简单,

1.在子进程内核栈上造出pt_regs,然后copy 父进程的pt_regs

2.然后修改r0 =0 ,就是表示子进程从fork 系统调用返回0

3.子进程,用户态栈的设置(根据传入参数),具体可看 pthread_create到ret_fast_syscalls

参数和栈的变化一文

4.设置cpu_context,主要是当子进程被调度运行时的reg,栈,以及执行的pc地址

5.设置tls 的地址

而对于oklinux 这部分功能相同,但是实现方法不同,因为oklinux中

一切都是 l4 thread ,没有nlinux 进程的概念,也没正真的kernel

mode ,user mode 和其对应的栈,kernel 和user 都运行在l4 kernel

的外面,对l4 kernel 来讲大家都是user,下面看下具体实现:

oklinux copy_thread 中主要有两个分支, 就是判断fork ,还是

kthread 创建,判断条件就是前面request.op flag设置的OP_KTHREAD

OP_FORK,对于OP_FORK 情况

1.取得utcb_area地址, armv4,5 由l4 kernel 管理

armv6 以上自己分配,具体实现在arch\l4\kernel\setup.c 中setup_arch

2.算出fork_start 的位置,就是子进程被调度运行时执行的函数

根据TASK_SIG_BASE 和 __wombat_user_fork_handler,并且如果

是armv6,那么fork_start =0x98000004

3. 然后 通过okl4_create_thread  创建l4 thread,返回ktcb的tcb_idx

到thread_info 的 user_handle,并且返回l4 thread id

因为oklinux ,运行的实际单位是 l4 thread,而不是nlinux 的进程

其中pager,和schduler都是main_thread,即syscall_loop所在的

执行线程

4. 设置刚创建的 l4 thread 优先级

5. 使用l4 api L4_Copy_regs,将当前进程的所有reg copy到

子进程中

6. 接下来没有设置子进程获得执行时运行的sp,pc到cpu context

而是设置到fork request  的user_sp,user_ip,

另外多了个user_start,设置为fork_start,

7. 接下来如注释 :/* Start new user thread - at new_process_handler stub  */

就是先设置request op flag 为resume,将thread info中cpu context中

pc 设置为new_process_handler,其实这个new_process_handler,

就是执行fork_start的stub, l4 thread 的sp 设置为nlinux中kernel 栈的2page 的

顶端位置

在发生调度,通过__switch_to =>arch_switch ,实际切换执行到cpu context

的pc 也就是 new_process_handler,然后执行到 fork_start

看下new_process_handler,他根据l4 thread id 将thread 停下来

然后设置restart 的sp,pc 为 thread_info 中reqeset的user_start就是fork_start

然后set_user_ipc_cancelled,将thread_info flag TIF_DONT_REPLY_USER标志

clear 掉,他的作用 下面讲 返回到user mode 时说

最后  call syscall_loop

4. 返回到用户mode

nlinux 创建进程返回到用户空间的代码是真刀真枪的,设置cpsr,

现场恢复,pc出栈,可是现在oklinux kernel 和user 都相对l4 kernel

的user mode

正真的kernel mode 在l4 kernel 不在 oklinux kernel ,怎么办

只能造假,就是装,比如你是个平民百姓,不会有人觉得你很真实

但是如果你演好了一个平民百姓,你就是影帝,说明要装得象要了解

平民百姓的本质,就是养家糊口

来看下oklinux 是如何了解mode 切换的本质的,并且如何演的

还是syscall_loop:

goto return_to_user ,很直接

返回user mode 先要enable 中断,和nlinux 一样

接下来判断reply_user_ipc

前面new_process_handler 中set_user_ipc_cancelled

起作用了,所以if (likely(reply_user_ipc(curinfo))) 不满足

走到else :

先判断user_need_restart,肯定满足,new_process_handler

做了set_need_restart

那么就call l4 api L4_Start_SpIpFlags,设置该tid 的l4 thread

sp,pc(ip) 到reqeset restart 字段的 sp,ip,前面new_process_handler

set_need_restart,设置为request fork字段的 user_sp,和

user_start(fork_start)

接着

curinfo->regs.mode = 0;

然后tag = L4_Wait (&from);

结束了,是的就这么简单,kernel mode 和user mode 的本质是什么

就是谁获得运行权,现在L4_Wait 导致 main_thread 让出运行权

引发调度,那么user mode 下的thread,谁的优先级高谁运行,

但不管怎样轮到user thread运行了,就是回到user mode了

轮到父进程的thread 运行,父进程就回到user mode

轮到fork 出的子进程的l4 thread 运行,子进程就回到了user mode

5.fork_start 做了什么

曾经在oklinux arm pagefault 流程简单分析一文

中提到过关于TASK_SIG_BASE(0x98000000) 处pagefault 的情况

这个地址和信号处理有关,也和fork 有关, 在user.S中有个

.section .exregspage ,vmlinux.lds中定义为1page 大小

在该段中有很多函数,我们这里只关心__wombat_user_fork_handler

它就是fork_start,

当执行fork_start 时地址在0x98000004,该处地址在当前地址空间

没有被map,就是没有具体的物理地址,引发page fault ,

page fault 处理将0x98000000 map到__user_exregs_page

他也是个虚地址,但没关系,因为他在vmlinux 的text段

init 时 被map 过了,有实际物理地址,这样okl4_find_segment

可以获得,然后将0x98000000 1page 也map 过去

这样就可以执行了

为什么这样做,因为在不同的地址空间,所有要回到user thread的地址空间运行

必须map 过去,看下 oklinux main thread的创建:

main_thread =okl4_create_sys_thread(linux_space,

在linux_space 中的, 其他oklinux user thread , 如上面init_new_context

是不同的space

(linux_space = L4_SpaceId(*(OKL4_ENV_GET_MAIN_SPACEID("MAIN_SPACE_ID")));)

下面看 __wombat_user_fork_handler

get User defined handle ,

设置 R0 =0 ,熟悉吧,就是子进程从do_fork 返回0

设置pc 为User defined handle ,继续执行

至于__L4_TCR_USER_DEFINED_HANDLE,的设置可以在3.0的Tarps.spp中找到一点影子

/* Set new UTCB XXX - if we fault after this, (before switch) is this bad? */

ldr     tmp5,   [to_tcb, #OFS_TCB_UTCB]

......

str     tmp5,   [tmp2, #0xff0]          /* UTCB ref */

但具体含义占个位置,以后继续迭代

最后补充下kthread,这部分很简单,没有设置fork_start,然后stub 是new_thread_handler

并且也没有新建l4 thread ,不需要,还是main_thread,只是pc 换成其他函数而已

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值