谈谈进程创建与程序加载

少于 1 分钟读完

#fork

在Linux中,创建新进程使用fork/vfork/clone/kernel_thread方法,除了第一个进程pid=0,该进程是手工构造启动的, 然后由进程0创建进程1(init process),init进程是所有进程的祖先。 也就是说后来所有的进程均有上述四个方法创建, 而这四个函数中,均调用`do_fork()->copy_process()完成进程的创建,子进程获取父进程的数据空间,堆和栈,暂时共享正文段。

#execve

通常创建新进程后,会调用一个exec函数来替换新进程的正文、数据、堆栈段,来执行新的功能。exec函数族包含6个

  • execlp
  • execvp
  • execl
  • execv
  • execle
  • execve (system call)

所有另外5个exec函数都是库函数,只有execve是系统调用,是所有exec函数族的入口,这6个函数只是参数(argv, env)有所区别。

#cred 之前写过一篇linux进程安全上下文struct cred,但是并没有清楚如何设置cred,注意

 struct task_struct{
   ...
   /* process credentials */
   const struct cred __rcu *real_cred; /* objective and real subjective task credentials (COW) */
   const struct cred __rcu *cred;  /* effective (overridable) subjective task credentials (COW) */
   ...
 }

task_struct中是const,不能通过task-struct->cred直接修改。通过源码分析,好像只有在可执行程序加载的过程中通过linu_binprm来设置新进程的cred;

从系统调用execve 到 commit_creds 的调用过程如下

  SYSCALL_DEFINE3(execve, filename, argv, envp)
  
	int do_execve(...)
	
	static int do_execveat_common(...)
	
	static int exec_binprm(struct linux_binprm *bprm)
	
	int search_binary_handler(struct linux_binprm *bprm)
	{
	 retval = fmt->load_binary(bprm);
	}
	
	static int load_elf_binary(struct linux_binprm *bprm)
	
	//install the new credentials for this executable
	void install_exec_creds(struct linux_binprm *bprm)
	
	//commit_creds - Install new credentials upon the current task
	int commit_creds(struct cred *new)

关于cred,新创建的子进程cred是继承父进程的(do_fork),只有当子进程加载可执行程序时(execve),通过linux_binprm,才能获取新的cred;

  linux_binprm: This structure is used to hold the arguments that are used when loading binaries.

留下评论