exec系列函数主要实现装入新的可执行文件或脚本镜像,并执行;调用后不再返回,而是跳转到新镜像的入口去执行。
exec在linux上均是对execve系统调用的封装,除了下述内容外,进程其它内容均保持不变:
1.将设置了处理函数的信号handler,重置为默认SIG_DFL
2.内存映射,mmap
3.SysV共享内存,shmat
4.POSIX共享内存,shm_open
5.POSIX消息队列,mq_overview
6.POSIX信号量,sem_overview
7.打开的目录,open_dir
8.内存锁,mlock/mlockall
9.exit处理,atexit/on_exit
10.浮点数环境,fenv
等
execve的主要作用为:
1.分配进程新的地址空间,将环境变量、main参数等拷贝到新地址空间的推栈中;
2.解析可执行文件,将代码、数据装入/映射到内存
3.进程新环境的设置,如关闭设置FD_CLOEXEC的文件等
4.设置execve函数返回到用户态时的执行地址;解析器入口地址或程序的入口地址
I.do_execve
execve系统调用是通过do_execve实现的,其处理流程如下:
1.进程分配并使用自己的文件描述符表,不再使用共享的文件描述符表
2.分配进程新的地址空间mm_struct,并复制老mm_struct的context,并分配一页大小的堆栈内存区。
3.打开可执行文件;检查可执行文件权限,并预读可执行文件的前BINPRM_BUF_SIZE=128字节数据到buf中
4.计算环境变量、main参数个数;并将这些参数复制到新地址空间的堆栈中
5.根据可执行文件的格式,查找对应的handler作后续处理;
do_execve的实现fs/exec.c:
1342 /*
1343 * sys_execve() executes a new program.
1344 */
1345 int do_execve(char * filename,
1346 char __user *__user *argv,
1347 char __user *__user *envp,
1348 struct pt_regs * regs)
1349 {
1355
1356 retval = unshare_files(&displaced);
1357 if (retval)
1358 goto out_ret;
1359
1375 file = open_exec(filename);
1376 retval = PTR_ERR(file);
1377 if (IS_ERR(file))
1378 goto out_unmark;
1379
1385
1386 retval = bprm_mm_init(bprm);
1387 if (retval)
1388 goto out_file;
1389
1390 bprm->argc = count(argv, MAX_ARG_STRINGS);
1391 if ((retval = bprm->argc) < 0)
1392 goto out;
1393
1394 bprm->envc = count(envp, MAX_ARG_STRINGS);
1395 if ((retval = bprm->envc) < 0)
1396 goto out;
1397
1398 retval = prepare_binprm(bprm);
1399 if (retval < 0)
1400 goto out;
1401
1402 retval = copy_strings_kernel(1, &bprm->filename, bprm);
1403 if (retval < 0)
1404 goto out;
1405
1406 bprm->exec = bprm->p;
1407 retval = copy_strings(bprm->envc, envp, bprm);
1408 if (retval < 0)
1409 goto out;
1410
1411 retval = copy_strings(bprm->argc, argv, bprm);
1412 if (retval < 0)
1413 goto out;
1414
1415 current->flags &= ~PF_KTHREAD;
1416 retval = search_binary_handler(bprm,regs);
1417 if (retval < 0)
1418 goto out;
1419
1453 return retval;
1454 }
字符串复制copy_strings函数:
1、将字符串从当前地址空间复制到新地址空间的堆栈中;
2、复制字符串时,使用的仍是老地址空间页表,所以不能使用新地址空间的堆栈地址直接复制,必须映射堆栈页帧到内核空间后再做复制操作。
3、由于字符串可能跨页,一个字符串可能存在两次页帧映射与复制
II.ELF格式文件处理
ELF格式文件处理主要对ELF文件进行解析,将可执行程序的代码及数据装载到内存中;并根据ELF文件信息,设置进程的环境
ELF格式:
ELF的管理信息主要由三部分组成:ELF头、程序头(程序段)表、节区表;一个段的地址空间通常包括一个或多个节区。
如数据段包括数据节区、BSS节区等
linux对ELF格式文件处理代码为load_elf_binary,主要流程如下:
i.根据ELF文件头数据,检查ELF文件的合法性
ii.根据ELF文件头中程序头表偏移和程序头数目,读出程序头表数据。
iii.根据解析器程序头信息,读出解析器可执行文件头数据放入buf中,以便后续处理
iv.清除原exec环境
1.线程处理
a.发送SIGKILL到线程组其它线程,杀死除自己以外所有的线程
b.如果自己不是主线程,继承主线程信息并将自己设置成主线程
c.删除信号时钟,并清空时钟信号
d.如果信号处理handler表与其它进程共享,则创建并复制自己的handler表
2.将可执行文件句柄设置成新的文件句柄
3.切换地址空间,使用新的地址空间
v.设置新exec环境
1.设置进程名称
2.更新信号handler表,非忽略信号设置成行为,并清空mask
3.关闭有FD_CLOEXEC标识的文件
vi. 堆栈内存区最后处理,更新内存区标识、权限,内存区可能重定位和扩容
vii.将ELF文件中PT_LOAD类型的程序段映射到内存中
1.根据PT_LOAD类型的程序头信息,将可执行文件中代码段、数据段等通过文件映射方式映射到内存
2.PT_LOAD一般包括代码段和数据段,数据段虚拟内存大小通常大于文件大小,多出的部分包括bss内存空间等;采用文件映射且程序段虚拟内存大小大于文件大小时,将多出部分清0(最后一个程序段中,多出部分通常采用匿名映射)
3.设置start_code,end_code,start_data,end_data,elf_bss,elf_brk;一般情况,elf_bss=数据段虚拟地址+文件大小,elf_brk=数据段虚拟地址+虚拟内存大小
viii.对elf_bss与elf_brk之间的部分(BSS等)作匿名映射;
ix.设置exec返回用户空间后,执行代码的地址;如果有解析器(可执行程序通常是/lib/ld-linux.so.2,ld-linux.so.2作用是装载可执行文件所需的动态链接库),装载解析器,并将程序入口设置成解析器的入口;否则设置成镜像的入口点。
x.设置elf_table
1.将main参数个数,参数字符串地址,NULL写到堆栈中
2.将环境变量字符串地址,NULL写到堆栈中
3.设置elf表,表项为(id,val)对;如程序的入口地址等
xi.设置进程参数,如IP更改为新程序入口地址,堆栈切换;然后返回用户空间执行解析器代码或程序代码
ELF格式文件处理的实现fs/binfmt_elf.c:
563 static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
564 {
565 struct file *interpreter = NULL; /* to shut gcc up */
566 unsigned long load_addr = 0, load_bias = 0;
567 int load_addr_set = 0;
568 char * elf_interpreter = NULL;
569 unsigned long error;
570 struct elf_phdr *elf_ppnt, *elf_phdata;
571 unsigned long elf_bss, elf_brk;
572 int retval, i;
573 unsigned int size;
574 unsigned long elf_entry;
575 unsigned long interp_load_addr = 0;
576 unsigned long start_code, end_code, start_data, end_data;
577 unsigned long reloc_func_desc = 0;
578 int executable_stack = EXSTACK_DEFAULT;
579 unsigned long def_flags = 0;
580 struct {
581 struct elfhdr elf_ex;
582 struct elfhdr interp_elf_ex;
583 } *loc;
584
585 loc = kmalloc(sizeof(*loc), GFP_KERNEL);
586 if (!loc) {
587 retval = -ENOMEM;
588 goto out_ret;
589 }
590
591 /* Get the exec-header */
592 loc->elf_ex = *((struct elfhdr *)bprm->buf);
593
594 retval = -ENOEXEC;
595 /* First of all, some simple consistency checks */
596 if (memcmp(loc->elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
597 goto out;
598
599 if (loc->elf_ex.e_type != ET_EXEC && loc->elf_ex.e_type != ET_DYN)
600 goto out;
601 if (!elf_check_arch(&loc->elf_ex))
602 goto out;
603 if (!bprm->file->f_op||!bprm->file->f_op->mmap)
604 goto out;
605
606 /* Now read in all of the header information */
607 if (loc->elf_ex.e_phentsize != sizeof(struct elf_phdr))
608 goto out;
609 if (loc->elf_ex.e_phnum < 1 ||
610 loc->elf_ex.e_phnum > 65536U / sizeof(struct elf_phdr))
611 goto out;
612 size = loc->elf_ex.e_phnum * sizeof(struct elf_phdr);
613 retval = -ENOMEM;
614 elf_phdata = kmalloc(size, GFP_KERNEL);
615 if (!elf_phdata)
616 goto out;
617
618 retval = kernel_read(bprm->file, loc->elf_ex.e_phoff,
619 (char *)elf_phdata, size);
620 if (retval != size) {
621 if (retval >= 0)
622 retval = -EIO;
623 goto out_free_ph;
624 }
625
626 elf_ppnt = elf_phdata;
627 elf_bss = 0;
628 elf_brk = 0;
629
630 start_code = ~0UL;
631 end_code = 0;
632 start_data = 0;
633 end_data = 0;
634
635 for (i = 0; i < loc->elf_ex.e_phnum; i++) {
636 if (elf_ppnt->p_type == PT_INTERP) {
637 /* This is the program interpreter used for
638 * shared libraries - for now assume that this
639 * is an a.out format binary
640 */
641 retval = -ENOEXEC;
642 if (elf_ppnt->p_filesz > PATH_MAX ||
643 elf_ppnt->p_filesz < 2)
644 goto out_free_ph;
645
646 retval = -ENOMEM;
647 elf_interpreter = kmalloc(elf_ppnt->p_filesz,
648 GFP_KERNEL);
649 if (!elf_interpreter)
650 goto out_free_ph;
651
652 retval = kernel_read(bprm->file, elf_ppnt->p_offset,
653 elf_interpreter,
654 elf_ppnt->p_filesz);
655 if (retval != elf_ppnt->p_filesz) {
656 if (retval >= 0)
657 retval = -EIO;
658 goto out_free_interp;
659 }
660 /* make sure path is NULL terminated */
661 retval = -ENOEXEC;
662 if (elf_interpreter[elf_ppnt->p_filesz - 1] != '\0')
663 goto out_free_interp;
664
665 interpreter = open_exec(elf_interpreter);
666 retval = PTR_ERR(interpreter);
667 if (IS_ERR(interpreter))
668 goto out_free_interp;
669
670 /*
671 * If the binary is not readable then enforce
672 * mm->dumpable = 0 regardless of the interpreter's
673 * permissions.
674 */
675 if (file_permission(interpreter, MAY_READ) < 0)
676 bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP;
677
678 retval = kernel_read(interpreter, 0, bprm->buf,
679 BINPRM_BUF_SIZE);
680 if (retval != BINPRM_BUF_SIZE) {
681 if (retval >= 0)
682 retval = -EIO;
683 goto out_free_dentry;
684 }
685
686 /* Get the exec headers */
687 loc->interp_elf_ex = *((struct elfhdr *)bprm->buf);
688 break;
689 }
690 elf_ppnt++;
691 }
692
693 elf_ppnt = elf_phdata;
694 for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++)
695 if (elf_ppnt->p_type == PT_GNU_STACK) {
696 if (elf_ppnt->p_flags & PF_X)
697 executable_stack = EXSTACK_ENABLE_X;
698 else
699 executable_stack = EXSTACK_DISABLE_X;
700 break;
701 }
702
703 /* Some simple consistency checks for the interpreter */
704 if (elf_interpreter) {
705 retval = -ELIBBAD;
706 /* Not an ELF interpreter */
707 if (memcmp(loc->interp_elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
708 goto out_free_dentry;
709 /* Verify the interpreter has a valid arch */
710 if (!elf_check_arch(&loc->interp_elf_ex))
711 goto out_free_dentry;
712 }
713
714 /* Flush all traces of the currently running executable */
715 retval = flush_old_exec(bprm);
716 if (retval)
717 goto out_free_dentry;
718
719 /* OK, This is the point of no return */
720 current->flags &= ~PF_FORKNOEXEC;
721 current->mm->def_flags = def_flags;
722
723 /* Do this immediately, since STACK_TOP as used in setup_arg_pages
724 may depend on the personality. */
725 SET_PERSONALITY(loc->elf_ex);
726 if (elf_read_implies_exec(loc->elf_ex, executable_stack))
727 current->personality |= READ_IMPLIES_EXEC;
728
729 if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
730 current->flags |= PF_RANDOMIZE;
731
732 setup_new_exec(bprm);
733
734 /* Do this so that we can load the interpreter, if need be. We will
735 change some of these later */
736 current->mm->free_area_cache = current->mm->mmap_base;
737 current->mm->cached_hole_size = 0;
738 retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP),
739 executable_stack);
740 if (retval < 0) {
741 send_sig(SIGKILL, current, 0);
742 goto out_free_dentry;
743 }
744
745 current->mm->start_stack = bprm->p;
746
747 /* Now we do a little grungy work by mmaping the ELF image into
748 the correct location in memory. */
749 for(i = 0, elf_ppnt = elf_phdata;
750 i < loc->elf_ex.e_phnum; i++, elf_ppnt++) {
751 int elf_prot = 0, elf_flags;
752 unsigned long k, vaddr;
753
754 if (elf_ppnt->p_type != PT_LOAD)
755 continue;
756
757 if (unlikely (elf_brk > elf_bss)) {
758 unsigned long nbyte;
759
760 /* There was a PT_LOAD segment with p_memsz > p_filesz
761 before this one. Map anonymous pages, if needed,
762 and clear the area. */
763 retval = set_brk (elf_bss + load_bias,
764 elf_brk + load_bias);
765 if (retval) {
766 send_sig(SIGKILL, current, 0);
767 goto out_free_dentry;
768 }
769 nbyte = ELF_PAGEOFFSET(elf_bss);
770 if (nbyte) {
771 nbyte = ELF_MIN_ALIGN - nbyte;
772 if (nbyte > elf_brk - elf_bss)
773 nbyte = elf_brk - elf_bss;
774 if (clear_user((void __user *)elf_bss +
775 load_bias, nbyte)) {
776 /*
777 * This bss-zeroing can fail if the ELF
778 * file specifies odd protections. So
779 * we don't check the return value
780 */
781 }
782 }
783 }
784
785 if (elf_ppnt->p_flags & PF_R)
786 elf_prot |= PROT_READ;
787 if (elf_ppnt->p_flags & PF_W)
788 elf_prot |= PROT_WRITE;
789 if (elf_ppnt->p_flags & PF_X)
790 elf_prot |= PROT_EXEC;
791
792 elf_flags = MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE;
793
794 vaddr = elf_ppnt->p_vaddr;
795 if (loc->elf_ex.e_type == ET_EXEC || load_addr_set) {
796 elf_flags |= MAP_FIXED;
797 } else if (loc->elf_ex.e_type == ET_DYN) {
798 /* Try and get dynamic programs out of the way of the
799 * default mmap base, as well as whatever program they
800 * might try to exec. This is because the brk will
801 * follow the loader, and is not movable. */
802 #ifdef CONFIG_X86
803 load_bias = 0;
804 #else
805 load_bias = ELF_PAGESTART(ELF_ET_DYN_BASE - vaddr);
806 #endif
807 }
808
809 error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt,
810 elf_prot, elf_flags, 0);
811 if (BAD_ADDR(error)) {
812 send_sig(SIGKILL, current, 0);
813 retval = IS_ERR((void *)error) ?
814 PTR_ERR((void*)error) : -EINVAL;
815 goto out_free_dentry;
816 }
817
818 if (!load_addr_set) {
819 load_addr_set = 1;
820 load_addr = (elf_ppnt->p_vaddr - elf_ppnt->p_offset);
821 if (loc->elf_ex.e_type == ET_DYN) {
822 load_bias += error -
823 ELF_PAGESTART(load_bias + vaddr);
824 load_addr += load_bias;
825 reloc_func_desc = load_bias;
826 }
827 }
828 k = elf_ppnt->p_vaddr;
829 if (k < start_code)
830 start_code = k;
831 if (start_data < k)
832 start_data = k;
833
834 /*
835 * Check to see if the section's size will overflow the
836 * allowed task size. Note that p_filesz must always be
837 * <= p_memsz so it is only necessary to check p_memsz.
838 */
839 if (BAD_ADDR(k) || elf_ppnt->p_filesz > elf_ppnt->p_memsz ||
840 elf_ppnt->p_memsz > TASK_SIZE ||
841 TASK_SIZE - elf_ppnt->p_memsz < k) {
842 /* set_brk can never work. Avoid overflows. */
843 send_sig(SIGKILL, current, 0);
844 retval = -EINVAL;
845 goto out_free_dentry;
846 }
847
848 k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz;
849
850 if (k > elf_bss)
851 elf_bss = k;
852 if ((elf_ppnt->p_flags & PF_X) && end_code < k)
853 end_code = k;
854 if (end_data < k)
855 end_data = k;
856 k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz;
857 if (k > elf_brk)
858 elf_brk = k;
859 }
860
861 loc->elf_ex.e_entry += load_bias;
862 elf_bss += load_bias;
863 elf_brk += load_bias;
864 start_code += load_bias;
865 end_code += load_bias;
866 start_data += load_bias;
867 end_data += load_bias;
868
869 /* Calling set_brk effectively mmaps the pages that we need
870 * for the bss and break sections. We must do this before
871 * mapping in the interpreter, to make sure it doesn't wind
872 * up getting placed where the bss needs to go.
873 */
874 retval = set_brk(elf_bss, elf_brk);
875 if (retval) {
876 send_sig(SIGKILL, current, 0);
877 goto out_free_dentry;
878 }
879 if (likely(elf_bss != elf_brk) && unlikely(padzero(elf_bss))) {
880 send_sig(SIGSEGV, current, 0);
881 retval = -EFAULT; /* Nobody gets to see this, but.. */
882 goto out_free_dentry;
883 }
884
885 if (elf_interpreter) {
886 unsigned long uninitialized_var(interp_map_addr);
887
888 elf_entry = load_elf_interp(&loc->interp_elf_ex,
889 interpreter,
890 &interp_map_addr,
891 load_bias);
892 if (!IS_ERR((void *)elf_entry)) {
893 /*
894 * load_elf_interp() returns relocation
895 * adjustment
896 */
897 interp_load_addr = elf_entry;
898 elf_entry += loc->interp_elf_ex.e_entry;
899 }
900 if (BAD_ADDR(elf_entry)) {
901 force_sig(SIGSEGV, current);
902 retval = IS_ERR((void *)elf_entry) ?
903 (int)elf_entry : -EINVAL;
904 goto out_free_dentry;
905 }
906 reloc_func_desc = interp_load_addr;
907
908 allow_write_access(interpreter);
909 fput(interpreter);
910 kfree(elf_interpreter);
911 } else {
912 elf_entry = loc->elf_ex.e_entry;
913 if (BAD_ADDR(elf_entry)) {
914 force_sig(SIGSEGV, current);
915 retval = -EINVAL;
916 goto out_free_dentry;
917 }
918 }
919
920 kfree(elf_phdata);
921
922 set_binfmt(&elf_format);
923
924 #ifdef ARCH_HAS_SETUP_ADDITIONAL_PAGES
925 retval = arch_setup_additional_pages(bprm, !!elf_interpreter);
926 if (retval < 0) {
927 send_sig(SIGKILL, current, 0);
928 goto out;
929 }
930 #endif /* ARCH_HAS_SETUP_ADDITIONAL_PAGES */
931
932 install_exec_creds(bprm);
933 current->flags &= ~PF_FORKNOEXEC;
934 retval = create_elf_tables(bprm, &loc->elf_ex,
935 load_addr, interp_load_addr);
936 if (retval < 0) {
937 send_sig(SIGKILL, current, 0);
938 goto out;
939 }
940 /* N.B. passed_fileno might not be initialized? */
941 current->mm->end_code = end_code;
942 current->mm->start_code = start_code;
943 current->mm->start_data = start_data;
944 current->mm->end_data = end_data;
945 current->mm->start_stack = bprm->p;
946
947 #ifdef arch_randomize_brk
948 if ((current->flags & PF_RANDOMIZE) && (randomize_va_space > 1))
949 current->mm->brk = current->mm->start_brk =
950 arch_randomize_brk(current->mm);
951 #endif
952
953 if (current->personality & MMAP_PAGE_ZERO) {
954 /* Why this, you ask??? Well SVr4 maps page 0 as read-only,
955 and some applications "depend" upon this behavior.
956 Since we do not have the power to recompile these, we
957 emulate the SVr4 behavior. Sigh. */
958 down_write(¤t->mm->mmap_sem);
959 error = do_mmap(NULL, 0, PAGE_SIZE, PROT_READ | PROT_EXEC,
960 MAP_FIXED | MAP_PRIVATE, 0);
961 up_write(¤t->mm->mmap_sem);
962 }
963
964 #ifdef ELF_PLAT_INIT
965 /*
966 * The ABI may specify that certain registers be set up in special
967 * ways (on i386 %edx is the address of a DT_FINI function, for
968 * example. In addition, it may also specify (eg, PowerPC64 ELF)
969 * that the e_entry field is the address of the function descriptor
970 * for the startup routine, rather than the address of the startup
971 * routine itself. This macro performs whatever initialization to
972 * the regs structure is required as well as any relocations to the
973 * function descriptor entries when executing dynamically links apps.
974 */
975 ELF_PLAT_INIT(regs, reloc_func_desc);
976 #endif
977
978 start_thread(regs, elf_entry, bprm->p);
979 retval = 0;
980 out:
981 kfree(loc);
982 out_ret:
983 return retval;
984
985 /* error cleanup */
986 out_free_dentry:
987 allow_write_access(interpreter);
988 if (interpreter)
989 fput(interpreter);
990 out_free_interp:
991 kfree(elf_interpreter);
992 out_free_ph:
993 kfree(elf_phdata);
994 goto out;
995 }
execve过程中堆栈的变化情况:
III.缺页异常
用户态进程的动态内存分配跟内核的动态内存分配不同。当内核申请内存时直接分配页帧,而用户态进程内存申请时只获得了地址空间的使用权,而并没有分配实际页帧。
这是因为,进程做内存申请时被认为不是急需的(比如装载代码段到内存后,进程并不会访问所有的代码页),这样内核就可以延后内存分配,延后分配主要是通过缺页异常来实现的
在exec过程中,内核没有为进程分配实际的页帧,而只是作了内存映射,也就是获得了地址空间的使用权。
当没有分配页帧时,页表中的Present标识位为0,此时访问该页时会产生缺页异常;如果进程有该地址的使用权,缺页异常处理handler就会为进程分配页帧。
exec过程中主要做的有代码段和数据段的文件内存映射,BSS段的匿名映射,堆栈的匿名映射
i.文件内存映射缺页异常处理
会调用文件中的映射函数做相应处理
ii.BSS段的匿名映射缺页异常处理
BSS段中的页帧分配后应该是全0的,缺页异常的处理如下:
1.当读内存引发的缺页异常(Present=0),匿名映射时会分配zero page;
do_page_fault->handle_mm_fault-> handle_pte_fault->do_anonymous_page->pte_mkspecial
2.当写内存引发的缺页异常(Present=0),匿名映射会分配页帧并将页帧数据清0
do_page_fault->handle_mm_fault-> handle_pte_fault->do_anonymous_page->alloc_zeroed_user_highpage_movable
3.先读分配zero page(Present=1),再写时由于写保护产生的缺页异常会触发COW;COW处理时,如果原页帧是zero page,就会分配新页并将页帧数据清0
do_page_fault->handle_mm_fault-> handle_pte_fault->do_wp_page->is_zero_pfn&&alloc_zeroed_user_highpage_movable
iii.堆栈的匿名映射缺页异常处理
堆栈是唯一一种在超出地址使用范围,缺页异常还会分配页帧情况
1081 vma = find_vma(mm, address);
1082 if (unlikely(!vma)) {
1083 bad_area(regs, error_code, address);
1084 return;
1085 }
1086 if (likely(vma->vm_start <= address))
1087 goto good_area;
1088 if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) {
1089 bad_area(regs, error_code, address);
1090 return;
1091 }
1092 if (error_code & PF_USER) {
1093 /*
1094 * Accessing the stack below %sp is always a bug.
1095 * The large cushion allows instructions like enter
1096 * and pusha to work. ("enter $65535, $31" pushes
1097 * 32 pointers and then decrements %sp by 65535.)
1098 */
1099 if (unlikely(address + 65536 + 32 * sizeof(unsigned long) < regs->sp)) {
1100 bad_area(regs, error_code, address);
1101 return;
1102 }
1103 }
1104 if (unlikely(expand_stack(vma, address))) {
1105 bad_area(regs, error_code, address);
1106 return;
1107 }
find_vma是以address小于地址区间结尾为查找条件,最块情况找到的也是堆栈区间(当然堆栈增长方向为VM_GROWSUP除外);
如果是堆栈引发的缺页异常,就会自动对堆栈进行扩容。