作者:最苦的等待2502915147 | 来源:互联网 | 2023-07-18 16:41
原创作品转载请注明出处+《Linux内核分析》MOOC课程http:mooc.study.163.comcourseUSTC-1000029000作者:严哲璟以shell下执行ls
原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
作者:严哲璟
以shell下执行ls命令为例介绍Linux通过fork()和execve()类函数的执行程序启动过程:
父进程为shell,命令为ls,目录为/bin/ls
当输入ls时,shell进程通过fork()创建一个新的子进程,fork()进程,以及新建堆栈等之前已经说明,子进程有机会执行的时候,在ret_from_fork()开始,返回到子进程的用户堆栈中,执行其余的子进程的代码.
在这些子进程需要执行的代码中,有execve(/bin/ls,ls,NULL),ls是列出当前路径的目录的一个可执行文件,同理如./a.out等
为加载此可执行文件到内存中执行,关键的地方在于,execve返回之后,执行的代码变成了需要加载的可执行文件的代码,下面详细说明它是如何做到的.
首先 execve()函数是系统调用,陷入内核,调用do_execve_common()函数,此函数的作用是加载需要执行的可执行文件的ELF头,因为后面需要将可执行文件的信息压入代码段以及将PC指向可执行文件的起点
1430static int do_execve_common(struct filename *filename,
1431 struct user_arg_ptr argv,
1432 struct user_arg_ptr envp)
1433{
1434 struct linux_binprm *bprm;
1435 struct file *file;
1436 struct files_struct *displaced;
1437 int retval;
1438
1439 if (IS_ERR(filename))
1440 return PTR_ERR(filename);
1441
1442 /*
1443 * We move the actual failure in case of RLIMIT_NPROC excess from
1444 * set*uid() to execve() because too many poorly written programs
1445 * don‘t check setuid() return code. Here we additionally recheck
1446 * whether NPROC limit is still exceeded.
1447 */
1448 if ((current->flags & PF_NPROC_EXCEEDED) &&
1449 atomic_read(¤t_user()->processes) > rlimit(RLIMIT_NPROC)) {
1450 retval = -EAGAIN;
1451 goto out_ret;
1452 }
1453
1454 /* We‘re below the limit (still or again), so we don‘t want to make
1455 * further execve() calls fail. */
1456 current->flags &= ~PF_NPROC_EXCEEDED;
1457
1458 retval = unshare_files(&displaced);
1459 if (retval)
1460 goto out_ret;
1461
1462 retval = -ENOMEM;
1463 bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
1464 if (!bprm)
1465 goto out_files;
1466
1467 retval = prepare_bprm_creds(bprm);
1468 if (retval)
1469 goto out_free;
1470
1471 check_unsafe_exec(bprm);
1472 current->in_execve = 1;
1473
1474 file = do_open_exec(filename);
1475 retval = PTR_ERR(file);
1476 if (IS_ERR(file))
1477 goto out_unmark;
1478
1479 sched_exec();
1480
1481 bprm->file = file;
1482 bprm->filename = bprm->interp = filename->name;
1483
1484 retval = bprm_mm_init(bprm);
1485 if (retval)
1486 goto out_unmark;
1487
1488 bprm->argc = count(argv, MAX_ARG_STRINGS);
1489 if ((retval = bprm->argc) <0)
1490 goto out;
1491
1492 bprm->envc = count(envp, MAX_ARG_STRINGS);
1493 if ((retval = bprm->envc) <0)
1494 goto out;
1495
1496 retval = prepare_binprm(bprm);
1497 if (retval <0)
1498 goto out;
1499
1500 retval = copy_strings_kernel(1, &bprm->filename, bprm);
1501 if (retval <0)
1502 goto out;
1503
1504 bprm->exec = bprm->p;
1505 retval = copy_strings(bprm->envc, envp, bprm);
1506 if (retval <0)
1507 goto out;
1508
1509 retval = copy_strings(bprm->argc, argv, bprm);
1510 if (retval <0)
1511 goto out;
1512
1513 retval = exec_binprm(bprm)
Linux加载一个可执行程序并启动的过程