Linux的fork函数和进程替换



一、fork函数

在Linux中,fork函数具有非常重要的作用;它可以在原有进程(父进程)中创建一个新进程(子进程)。

在这里插入图片描述
fork函数所在的头文件即其返回值类型(上图)。

父进程的返回值为子进程id,子进程的返回值为0。
若进程出错则返回-1。

1.使用fork建立子进程

代码如下(示例):

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 
  5 int main()
  6 {
  7   pid_t id=fork();
  8   if(id==0){
  9     printf("I am child\n");
 10     sleep(3);
 11     exit(0);
 12 
 13   }
 14   else{
 15     printf("I am father\n");
 16     sleep(4);                                                                       
 17     exit(0);
 18   }
 19 
 20   return 0;
 21 }

运行结果(下图)
在这里插入图片描述

2.fork后

不同进程中的同一变量的虚拟地址

fork创建子进程后,对于同一个变量test,父子进程在运行时有何不同,如果对父进程的test进行修改,子进程的test会改变吗?
看下面代码

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 
  5 int main()
  6 {
  7   int test=666;
  8 
  9   pid_t id=fork();
 10   if(id==0){
 11     printf("I am child,test is %d,test_id:%p\n",test,&test);
 12     sleep(3);
 13     exit(0);
 14 
 15   }
 16   else{
 17     test=999;
 18     printf("I am father,test is %d,test_id:%p\n",test,&test);                       
 19     sleep(3);
 20     exit(0);
 21   }
 22 
 23   return 0;
 24 }

其运行结果如下图
该处使用的url网络请求的数据。
我们可以看到对父进程的test修改后,子进程的test并未改变,这是因为进程具有独立性。

但是为什么父子进程中变量test的地址相同呢?在同一个地址中为什么会有两个不同的值?
首先我们知道地址分为物理地址和虚拟地址,虚拟地址通过页表转换为物理地址。而我们所打印出来的地址属于虚拟地址,而两个test的值不同说明test所在的物理地址不同(说明不同进程的同一虚拟地址可以指向不同的物理地址)。

写时拷贝

写时拷贝是指在刚刚创建子进程时,父子进程的代码和数据都是共享的,如果子进程仅仅只有对数据的读操作那么只需要让子进程空间的指针指向原空间,只有当子进程进行“写操作”或者修改代码内容后才真正为子进程开辟新空间。

从上面我们知道,在创建子进程后,即使父子进程的内容发生改变,他们的虚拟地址也没有改变,因此写时拷贝时发生在物理层面上。

写时拷贝可以做到资源的按需分配和延时分配,更高效的使用内存空间。

在这里插入图片描述

二、进程替换

fork创建一个子进程后,为了让子进程执行不同的任务,我们需要让子进程调用一个exec函数来执行其他程序。

注意:调用exec函数并不是创建一个新进程,而是对原进程内容进行替换,其进程id不会变换。
返回值问题:调用exec函数后,如果调用成功,那么该函数不再返回;如果调用失败则返回-1.

man手册对exec函数的解释(下图)
在这里插入图片描述

l(list):参数采用列表形式
v(vector):参数采用数组形式
p(path):自动搜索环境变量PATH,不需要用户给出路径
e(env):自己维护环境变量

execl,execlp,execv,execvp调用ls -al命令

     #include<stdlib.h>
     #include <unistd.h>
     int main()                                                                        
     {
        char *const myargv[] = {"ls", "-la", NULL};
        char *const myenvp[] = {"PATH=/bin", NULL};
        
        //l表示参数采用列表形式                                 
        execl("/bin/ls", "ls","-l","-a" ,NULL);                 
        //p无需写全路径            
        execlp("ls", "ls", "-la", NULL); 
                        
        //v参数采用数组形式                                                         
        execv("/bin/ls", myargv);                                 
        //p           
        execvp("ls", myargv);                                     
                                                        
        exit(0);
    }

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值