linux进程的创建 - 初识fork, execvp

1. fork()

1.1 函数原型

  • fork() 用于创建一个子进程,使用时需要包含头文件 <unistd.h>
  • <unistd.h> 定义了一些符合 POSIX 标准的系统调用接口,fork() 本质是一个系统调用,fork() 的函数原型如下:

    pid_t fork (void)
    返回值定义:

    • -1 : 创建子程序失败
    • 0 : 在子进程中中返回0
    • >0 : 在父进程中返回子进程的pid

1.2 fork()示例

  • 运行如下程序:
    #include <unistd.h>
    #include <stdio.h>
    
    int main (void)
    {
    	int fork_pid = fork();
    	if(fork_pid) { /* 父进程,返回子进程pid */
        	printf("parent process ...\n\r");
        	printf("    fork_pid = %d\n\r", fork_pid);
        	printf("    pid = %d\n\r", getpid());
    	} else { /* 子进程, 返回0 */
        	printf("child process ...\n\r");
        	printf("    fork_pid = %d\n\r", fork_pid);
        	printf("    pid = %d\n\r", getpid());
    	}
    }
    
  • 运行结果如下,就结果而言 if 和 else 分支的程序都被执行了, 为什么?直接说答案:原因是if 和 else 是在不同的进程中执行的
    wsl_dmf@DESKTOP-7SAFD5K:~/tmp$ ./a.out 
    parent process ...
    	fork_pid = 2059
    	pid = 2058
    child process ...
    	fork_pid = 0
    	pid = 2059
    

1.3 fork()总结

  • fork() 是一个系统调用,所以当程序执行 fork() 时,程序会由 用户态 进入 内核态
  • fork() 会创建一个子进程,子进程是父进程的拷贝:
    • 子进程 进程控制块(task_struct) 会完全复制自 父进程 ,所以子进程中程序运行的上下文和父进程是完全一致的。例如 PC(程序计数指针/程序运行的位置)SP(栈指针)
    • 父进程的整个代码段和数据段都会被复制,子进程和父进程的 虚拟内存 是完全一致的。
    • 物理内存 的角度来说,父进程子进程的 物理内存是完全一样且单独存储的,子进程 的物理内存是 父进程 的复制,父子进程页表 分别指向不同的物理空间
  • 子进程 不会抢占父进程,创建好的子进程会被加入到就绪列表中等待调度。

2 execvp()

2.1 execvp()函数原型

  • execvp() 将当前进程的空间全部由file 所指定的程序覆盖,并从头开始运行,execvp()不会创建进程,使用时需要包含头文件 <unistd.h>
  • <unistd.h> 定义了一些符合 POSIX 标准的系统调用接口,execvp() 本质是一个系统调用,execvp() 的函数原型如下:

int execvp(const char *file, char *const argv[]);

  • 功能说明:将当前进程的空间全部由file 所指定的程序覆盖,并从头开始运行;
  • 参数说明:
    • file:要执行的程序名。
    • argv: 要执行程序的参数列表(本文暂不展开)
  • 返回值:
    • 0:成功
    • -1:失败

2.2 execvp()示例

  • 示例程序1

    #include <unistd.h>
    #include <stdio.h>
    int main (void)
    {
    	char* argvs[] = {"ls", "-l", "-a", NULL};
    	execvp("ls", argvs);
    
    	/* 执行execvp() 时当前进程空间的内容会被全部替换,所以下面代码不会被执行 */
    	printf("Hello, world"); 
    	return 0;
    }
    
  • 示例程序1执行结果

    wsl_dmf@DESKTOP-7SAFD5K:~/tmp$ ./a.out 
    total 32
    drwxr-xr-x  3 wsl_dmf wsl_dmf  4096 May  6 23:21 .
    drwxr-x--- 10 wsl_dmf wsl_dmf  4096 May  6 23:08 ..
    drwxr-xr-x  2 wsl_dmf wsl_dmf  4096 Apr 29 23:53 .vscode
    -rwxr-xr-x  1 wsl_dmf wsl_dmf 16016 May  6 23:21 a.out
    -rw-r--r--  1 wsl_dmf wsl_dmf   144 May  6 23:21 main.c
    
  • 示例程序2:

    /* 编译目标文件 : main.out */
    #include <unistd.h>
    #include <stdio.h>
    int main (void)
    {
     	char* argvs[] = {NULL};
    	printf("main_pid = %d\n\r", getpid()); 
    	execvp("./exec.out", argvs);
    
    	/* 执行execvp() 时当前进程空间的内容会被全部替换,所以下面代码不会被执行 */
    	printf("Hello, world"); 
    	return 0;
    }
    
    /* 编译目标文件 : ecec.out */
    #include <unistd.h>
    #include <stdio.h>
    int main (void)
    {
    	printf("exec_pid = %d\n\r", getpid()); 
    	return 0;
    }
    
    • 示例程序2执行结果
    wsl_dmf@DESKTOP-7SAFD5K:~/tmp$ ./main.out 
    main_pid = 8581
    exec_pid = 8581
    wsl_dmf@DESKTOP-7SAFD5K:
    

2.3 execvp()总结

  • execvp() 是一个系统调用,所以当程序执行 execvp() 时,程序会由 用户态 进入 内核态。
  • execvp()并不会创建进程(使用同一个pid),而是将指定的程序覆盖当前进程空间,并从头开始执行。
  • execvp() 通常会和 fork() 一起使用,使用 fork() 创建子进程,并在子进程中执行 execvp() 加载对应的程序执行。
  • 24
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值