linux 进程的创建和加载

本文详细介绍了Linux系统中cameraserver进程如何通过init进程的fork()和execve()函数被创建和加载。首先,通过cameraserver.rc文件找到对应的服务程序,然后在init进程中,fork()函数创建子进程,其内部调用sys_clone()并在arm64架构下涉及entry.S等关键代码。接着,execve()负责加载进程,涉及do_execveat_common()函数和binfmt_elf.c的elf二进制格式处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们还是一cameraserver进程为例,看它是如何被系统加载运行起来的。

frameworks/av/camera/cameraserver/cameraserver.rc

1 service cameraserver /system/bin/cameraserver
2     class main
3     user cameraserver
4     group audio camera input drmrpc
5     ioprio rt 4
6     writepid /dev/cpuset/camera-daemon/tasks /dev/stune/top-app/tasks

从cameraserver.rc我们可以知道cameraserver 这个service对应的程序文件为/system/bin/cameraserver,在init进程解析完对应的rc文件后,会把这个进程运行起来,解析过程就不详述。

system/core/init/builtins.cpp

261  static int do_exec(const std::vector<std::string>& args) {
262      Service* svc = ServiceManager::GetInstance().MakeExecOneshotService(args);
263      if (!svc) {
264          return -1;
265      }
266      if (!svc->Start()) {
267          return -1;
268      }
269      waiting_for_exec = true;
270      return 0;
271  }
init进程会调用do_exec()函数就是把service对应的进程运行起来,这个函数在不同版本的android源码中,里面的代码有些不一样。

system/core/init/service.cpp

318  bool Service::Start() {
         ... 
388      NOTICE("Starting service '%s'...\n", name_.c_str());
389  
390      pid_t pid = fork();  //创建出一个新进程
391      if (pid == 0) {  //如果是子进程,会走进这个条件
392          umask(077);
          ... 
476          if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) {  //加载进程
477              ERROR("cannot execve('%s'): %s\n", strs[0], strerror(errno));
478          }
479  
480          _exit(127);
481      }
482  
483      if (pid < 0) {  //如果fork 进程失败
484          ERROR("failed to start '%s'\n", name_.c_str());
485          pid_ = 0;
486          return false;
487      }
488      //父进程,也就是init进程会跑下面的代码
489      time_started_ = gettime();
490      pid_ = pid;
         ...
499      NotifyStateChange("running");
500      return true;
501  }

到这里基本上就很清晰了,init启动service进程主要是通过fork()和execve()实现的,所以下面我们重点跟踪和了解这两个函数的实现。


1、fork创建子进程

fork()函数声明在  bionic/libc/include/unistd.h

…
81  extern pid_t  fork(void);
82  extern pid_t  vfork(void);
…

fork()实现在  bionic/libc/bionic/fork.cpp

34  #define FORK_FLAGS (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD)
36  int fork() {
37    __bionic_atfork_run_prepare();
38  
39    pthread_internal_t* self = __get_thread();
40  
41    // Remember the parent pid and invalidate the cached value while we fork.
42    pid_t parent_pid = self->invalidate_cached_pid();
43  
44  #if defined(__x86_64__) // sys_clone's last two arguments are flipped on x86-64.
45    int result = syscall(__NR_clone, FORK_FLAGS, NULL, NULL, &(self->tid), NULL);
46  #else
47    int result = syscall(__NR_clone, FORK_FLAGS, NULL, NULL, NULL, &(self->tid));
48  #endif
49    if (result == 0) {
50      self->set_cached_pid(gettid());
51      __bionic_atfork_run_child();
52    } else {
53      self->set_cached_pid(parent_pid);
54      __bionic_atfork_run_parent();
55    }
56    return result;
57  }
里面又通过syscall()系统调用函数来创建进程,注意传入的参数是__NR_clone和FORK_FLAGS。

syscall在 arm64的实现   bionic/libc/arch-arm64/bionic/syscall.S

29  #include <private/bionic_asm.h>
30  
31  ENTRY(syscall)
32      /* Move syscall No. from x0 to x8 */
33      mov     x8, x0
34      /* Move syscall parameters from x1 thru x6 to x0 thru x5 */
35      mov     x0, x1
36      mov     x1, x2
37      mov     x2, x3
38      mov     x3, x4
39      mov     x4, x5
40      mov     x5, x6
41      svc     #0  //可以认为执行了svc指令后,会到kernel中断相应函数
42  
43      /* check if syscall returned successfully */
44      cmn     x0, #(MAX_ERRNO + 1)  //x0 - MAX_ERRNO -1
45      cneg    x0, x0, hi    //
46      b.hi    __set_errno_internal
47  
48      ret  //调用返回
49  END(syscall)

kernel对应的处理流程

linux-4.10/arch/arm64/kernel/entry.S

…
328  	ventry	el0_sync			// Synchronous 64-bit EL0  
329  	ventry	el0_irq				// IRQ 64-bit EL0
330  	ventry	el0_fiq_invalid			// FIQ 64-bit EL0
331  	ventry	el0_error_invalid		// Error 64-bit EL0
…

我们继续看el0_sync:的实现

511  /*
512   * EL0 mode handlers.
513   */
514  	.align	6
515  el0_sync:
516  	kernel_entry 0    //kernel_entry 很重要,后面会讲
517  	mrs	x25, esr_el1			// read the syndrome register
518  	lsr	x24, x25, #ESR_ELx_EC_SHIFT	// exception class
519  	cmp	x24, #ESR_ELx_EC_SVC64		// SVC in 64-bit state  //比较两个值是否相等
520  	b.eq	el0_svc  //如果上面比较的结果相等,就跳转到el0_svc
...
接着看el0_sev里面的处理
/*
795   * SVC handler.
796   */
797  	.align	6
798  el0_svc:
799  	adrp	stbl, sys_call_table		// load syscall table pointer  //处理函数表格地址
        //在syscall.S中通过指令 mov x8, x0 将__NR_clone 参数保存到x8,下面的w8 也就是x8
800  	uxtw	scno, w8			// syscall number in w8  
801  	mov	sc_nr, #__NR_syscalls   //系统调用number的最大值
802  el0_svc_naked:					// compat entry point
803  	stp	x0, scno, [sp, #S_ORIG_X0]	// save the original x0 and syscall number
804  	enable_dbg_and_irq
805  	ct_user_exit 1
806  
807  	ldr	x16, [tsk, #TSK_TI_FLAGS]	// check for syscall hooks
808  	tst	x16, #_TIF_SYSCALL_WORK
809  	b.ne	__sys_trace
810  	cmp     scno, sc_nr                     // check upper syscall limit
811  	b.hs	ni_sys
812  	ldr	x16, [stbl, scno, lsl #3]	// address in the syscall table
813  	blr	x16				// call sys_* routine 
814  	b	ret_fast_syscall       
815  ni_sys:
816  	mov	x0, sp
817  	bl	do_ni_syscall
818  	b	ret_fast_syscall
819  ENDPROC(el0_svc)

我们重点关注下面这两条指令

812        ldr    x16,[stbl, scno, lsl #3]   // address in thesyscall table

813        blr    x16                             // call sys_*routine

LDR R0,[R1,R2,LSL #

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值