关于fork&exec之进程的创建和可执行程序的加载过程

中科大SA***243-石润成

一、预备阶段

实验环境:Ubuntu10.04

分析内核版本:Linux-2.6.11

1.编写一个fork和exec程序

myfork.c

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>

int main()
{
    pid_t pid;
    pid = fork();
    if(0 == pid)
    {
        execl("./son","son",NULL);
    }
    else
        if(pid > 0)
        {
            execl("./parent","parent",NULL);
        }
        else
        {
            fprintf(stderr, "fork failure!\n");
            return -1;
        }
    return 0;
}
myparent.c

#include<stdio.h>
int main()
{
    printf("I am parent process!\n");
    return 0;
}
myson.c

#include<stdio.h>
int main()
{
    printf("I am son process!\n");
    return 0;
}
各自生成myfork、myparent、myson的ELF格式文件。

打印结果:

I am parent process!
I am son process!

二、分析fork系统调用在内核中的执行过程

1)关于系统调用fork()、vfork()、clone()

    系统调用__clone()的主要用途是创建一个线程,这个线程可以使内核线程,也可以是用户线程。创建用户空间线程是,可以给定线程用户空间堆栈的位置,还可以指定子进程运行的起点。同时还可以用__clone()创建进程,有选择地复制父进程的资源。而fork(),则是全面地复制。还有一个系统调用vfork(),其作用也是创建一个线程,但主要只是作为创建进程的中间步骤,目的在于提高创建时的效率,减少系统开销,其设计接口与fork相同。这几个系统调用的代码在arch/i386/kernel/process.c中。 

asmlinkage int sys_fork(struct pt_regs regs)
{
	return do_fork(SIGCHLD, regs.esp, ®s, 0, NULL, NULL);
}

asmlinkage int sys_clone(struct pt_regs regs)
{
	unsigned long clone_flags;
	unsigned long newsp;
	int __user *parent_tidptr, *child_tidptr;

	clone_flags = regs.ebx;
	newsp = regs.ecx;
	parent_tidptr = (int __user *)regs.edx;
	child_tidptr = (int __user *)regs.edi;
	if (!newsp)
		newsp = regs.esp;
	return do_fork(clone_flags, newsp, ®s, 0, parent_tidptr, child_tidptr);
}

/*
 * This is trivial, and on the face of it looks like it
 * could equally well be done in user mode.
 *
 * Not so, for quite unobvious reasons - register pressure.
 * In user mode vfork() cannot have a stack frame, and if
 * done by calling the "clone()" system call directly, you
 * do not have enough call-clobbered registers to hold all
 * the information you need.
 */
asmlinkage int sys_vfork(struct pt_regs regs)
{
	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, ®s, 0, NULL, NULL);
}
    可以看出,三个系统调用的实现都是通过do_fork来实现的,只是对do_fork()调用的参数不同。

2)下面分析fork()开始系统调用的

    当从用户态调用fork()函数进入系统调用时,CPU切换到内核态并开始执行一个内核函数,详情请见这里,这里笔者简单用个图来描述一下。

关键之处:

        fork调用调用0x80中断,寄存器eax中的值为_NR_fork这是fork产给int $0x80唯一的参数。可以看到_NR_fork的调用号是2,如下所示,然后根据系统调用表sys_call_table找到偏移地址找到对应的函数,fork系统调用的对应函数时不带参数的,所以对应的调用是syscall0。

       查看”include/asm-i386/unistd.h”可以看到各函数对应的系统调用号:

#ifndef _ASM_I386_UNISTD_H_
#d
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值