通过前面linux 进程的创建和加载我们知道,调用ececve()系统调用后会加载指定的可执行程序并且运行起来,接下来我们分析这个加载过程,跑到do_execve()函数中。
linux-4.10/fs/exec.c
int do_execve(struct filename *filename,
1806 const char __user *const __user *__argv,
1807 const char __user *const __user *__envp)
1808 {
1809 struct user_arg_ptr argv = { .ptr.native = __argv };
1810 struct user_arg_ptr envp = { .ptr.native = __envp };
1811 return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
1812 }
关键的实现就在do_execveat_common()这个函数里面了
/*
1657 * sys_execve() executes a new program.
1658 */
1659 static int do_execveat_common(int fd, struct filename *filename,
1660 struct user_arg_ptr argv,
1661 struct user_arg_ptr envp,
1662 int flags)
1663 {
1664 char *pathbuf = NULL;
1665 struct linux_binprm *bprm;
1666 struct file *file;
1667 struct files_struct *displaced;
1668 int retval;
...
1693 retval = -ENOMEM;
1694 bprm = kzalloc(sizeof(*bprm), GFP_KERNEL); //申请一个linux_binprm 结构体
...
1705 file = do_open_execat(fd, filename, flags); //打开可执行文件
1706 retval = PTR_ERR(file);
1707 if (IS_ERR(file))
1708 goto out_unmark;
1709
1710 sched_exec(); //找到loading最低的cpu执行这个加载
1711
...
1736 retval = bprm_mm_init(bprm); //创建进程的内存地址空间
...
1748 retval = prepare_binprm(bprm); //读取可执行文件前面的128字节
...
1767 retval = exec_binprm(bprm); //加载可执行程序并执行
...
1771 /* execve succeeded */
1772 current->fs->in_exec = 0;
1773 current->in_execve = 0;
1774 acct_update_integrals(current);
1775 task_numa_free(current);
1776 free_bprm(bprm);
1777 kfree(pathbuf);
1778 putname(filename);
1779 if (displaced)
1780 put_files_struct(displaced);
1781 return retval;
...
1803 }
bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
sizeof(*bprm) 的大小是多少,是不是就是这个结构体的大小,有疑问的时候,我们可以通过代码实际的运行结果来验证。
main.c
#include <stdio.h>
struct debug_struct{
int a;
int b;
int c;
};
int main()
{
struct debug_struct *debug0;
printf("sizeof(*debug0) is:%d \n",sizeof(*debug0));
struct debug_struct debug1;
printf("sizeof(debug1) is:%d \n",sizeof(debug1));
return 0;
}
通过代码的实际运行情况,我们知道sizeof(*bprm) 就是结构体linux_binprm的大小。
linux-4.10/fs/exec.c
1634 static int exec_binprm(struct linux_binprm *bprm)
1635 {
1636 pid_t old_pid, old_vpid;
1637 int ret;
1638
1639 /* Need to fetch pid before load_binary changes it */
1640 old_pid = current->pid;
1641 rcu_read_lock();
1642 old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent));
1643 rcu_read_unlock();
1644
1645 ret = search_binary_handler(bprm);
1646 if (ret >= 0) {
1647 audit_bprm(bprm);
1648 trace_sched_process_exec(current, old_pid, bprm);
1649 ptrace_event(PTRACE_EVENT_EXEC, old_vpid);
1650 proc_exec_connector(current);
1651 }
1652
1653 return ret;
1654 }
因为我们是重点分析可执行程序文件的加载流程,所以我们理所当然的认为在调用exec_binprm()时,相关的初始化操作已经完成,并且bprm已经准备好了。
linux-4.10/include/linux/binfmts.h