fork/vfork

前言

unix操作系统提供了一系列进程的控制原语来操作进程,fork()/vfork()就是其中用于创建新进程的两种系统调用。

fork

fork()函数创建的新的子进程是原本父进程的副本,从虚拟地址空间的角度来看,可以用下图表示。此时的子进程几乎和父进程一摸一样,包括代码的执行位置。
在这里插入图片描述
fork()函数在unistd.h文件中声明,下面是函数原型:

pid_t fork();

pid_t是进程id的类型,fork()失败时,返回值为-1,fork()成功时,父进程返回子进程的进程id,子进程返回0。下面的代码用于创建一个子进程。

int main(){
    pid_t pid =fork();
    if(pid<0){
        return 0;
    }
    else if(pid==0){
        std::cout << "i am child process, my process id is " << getpid() << " my father process id is " << getppid() << std::endl;
    }
    else{
        std::cout << "i am father process, my process id is " << getpid() << " my father process id is " << getppid() << std::endl;
    }
    if(waitpid(pid, nullptr, 0)<0){
        return 0;
    }
    return 0;
}

运行结果如下:
在这里插入图片描述
虽然上面的运行结果中,父进程先运行,但实际上父进程和子进程的优先级是相同的,它们需要争抢cpu,也就是说父进程和子进程的运行顺序是不确定的。
通常我们创建一个新的进程是为了装载一个新的程序,这可以通过exec函数族来实现,但在装载一个新程序之前,我们需要复制父进程的整个虚拟地址空间,这需要消耗很多资源,为此linux引入了写时复制(copy on write,COW)技术。
写时复制是在fork()执行时,不对虚拟地址空间进程复制,仅复制父进程的页目录表和页表,并将虚拟地址空间的权限从RW(读写)改为(RO)只读,如果此时调用exec函数族加载新的可执行程序,子进程将直接获得新的虚拟地址空间。即使此时没有装载新的可执行程序,如果只是对内存进行读取,也不需要任何新的操作,当需要对内存进行修改时,才需要复制对应虚拟地址空间的地址,复制后父进程权限再从RO修改为RW。
综合上面的叙述,其实在fork()执行后,虚拟地址空间基本没有变化,只有需要写入内存时才修改虚拟地址空间。此外,调用fork创建的子进程需要父进程通过wait()或waitpid()进行回收,否则子进程的pcb资源不能释放,成为僵尸进程。

vfork

vfork()函数也可用于创建一个子进程,与fork()不同的是,vfork()会将父进程挂起,只执行子进程中的逻辑,等待子进程退出后再解除对父进程的阻塞。vfork()本质上是将父进程的虚拟地址空间借用给子进程,子进程使用完毕后归还给父进程,在没有写时复制之前,vfork由于不需要复制虚拟地址空间,相较fork有很大优势。写时复制引入后,vfork()相比fork()欠缺了灵活性,因此现在主要以使用fork为主。

守护进程的创建

守护进程是不依赖于终端的,只在后台运行的进程,通过本文和之前对exec函数族的讨论,可以实现守护进程的创建。可以通过以下几步创建守护进程:
1.父进程通过fork()创建子进程,父进程exit()退出,此时子进程变为孤儿进程,被init进程收养。
2.子进程调用setsid()改变会话属性,担任会话首领。
3.子进程再次fork()并调用exit()退出,这一步是为了使进程不再是会话首进程,避免打开终端。
4.调用chdir是子进程工作路径为根目录。
5.调用umask清除文件掩码。
6.关闭不需要的文件描述符,通常为默认打开的前三个文件描述符。
对应的代码如下:

#include<stdio.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
     
int main(){
    pid_t pid;

    pid=fork();
    if(pid>0){ 
        exit(0);
    }

    setsid();   

    pid = fork();
    if(pid>0){ 
        exit(0);
    }
    chdir("/"); 
    umask(0);   
    int i;
    for(i=0;i<1024;i++){    
        close(i);            
    }
    while(1){
        /*
        ...  //运行守护进程
         */
    }
    close(fd);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值