进程的创建

父子进程的创建

父子进程的创建

父子进程是来回交替执行的

#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>

int main()
{
#if 0 //
    fork();  // 通过fork函数创建一个子进程
    printf("hello world\n");

    while (1) ;
#endif



    pid_t pid;
    pid = fork();   // 通过fork函数的返回值来区分父子进程的独立代码区
    if(pid <0)
    {

        perror("fail to fork");
        return -1;
    }
    else if(pid > 0)  // 父进程的代码区
    {
        printf("parent: pid = %d, ppid = %d\n",getpid(), getppid());
        printf("pid = %d\n", pid);
        while (1) {
            printf("this is a parent process\n");
            sleep(1);
        }
    }
    else  // 子进程的代码区
    {
        printf("son: pid = %d, ppid = %d\n",getpid(), getppid());
        while (1) {
            printf("this is a son process\n");
            sleep(2);
        }
    }
    return 0;
}


centos man手册问题

centos man手册问题
参考参考

man 2 open
查询open手册

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

父子进程拥有独立的地址空间

父子进程拥有独立的地址空间

#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>



int a = 666;

int main()
{
#if 0 //
    fork();  // 通过fork函数创建一个子进程
    printf("hello world\n");

    while (1) ;
#endif

    static int b = 777;
    int c = 888;


    // 子进程会复制父进程fork之前的所有内容
    // 但是fork之后,父子进程完全独立,所以不管双方怎么改变(堆区、栈区、数据区等)都不会受到对方影响

    pid_t pid;
    pid = fork();   // 通过fork函数的返回值来区分父子进程的独立代码区
    if(pid <0)
    {

        perror("fail to fork");
        return -1;
    }
    else if(pid > 0)  // 父进程的代码区
    {
        printf("this is a parent process\n");
        a++;
        b++;
        c++;
        printf("a = %d, b=%d,c=%d\n",a,b,c);

    }
    else  // 子进程的代码区  这里是pid等于0
    {
        sleep(1);
        printf("this is a son process\n");
        printf("a = %d, b=%d,c=%d\n",a,b,c);
    }

    while (1) {

    }
    return 0;
}


执行结果
在这里插入图片描述

子进程继承父进程的空间

子进程继承父进程的空间

#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>



int main()
{

    int fd;
    if( (fd = open("file.txt", O_RDONLY))  == -1 )
    {
        perror("fail to open");
        return -1;
    }

    // 子进程会继承父进程的一些公有区域,比如磁盘空间、内核空间
    // 文件描述符的偏移量保存在内核空间中,所以父进程改变偏移量,则子进程获取的偏移量是改变之后的

    pid_t pid;
    pid = fork();
    if(pid <0)
    {
        perror("fail to fork");
        return -1;
    }
    else if(pid > 0)  // 父进程的代码区
    {
        printf("this is a parent process\n");

        char buf[32] = "";
        if(read(fd, buf, 12) == -1)
        {
            perror("fail to read");
        }

        printf("buf = [%s]\n", buf);

    }
    else  // 子进程的代码区  这里是pid等于0
    {
        sleep(1);
        printf("this is a son process\n");

        char buf[32] = "";
        if(read(fd, buf, 12) == -1)
        {
            perror("fail to read");
        }

        printf("buf = [%s]\n", buf);
    }

    while (1) {

    }
    return 0;
}


在这里插入图片描述
在这里插入图片描述

进程的等待

进程的等待
wait

#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>

#include <sys/types.h>
#include <sys/wait.h>


int main()
{

    pid_t pid;
    pid = fork();
    if(pid <0)
    {
        perror("fail to fork");
        return -1;
    }
    else if(pid > 0)  // 父进程的代码区
    {
        // 使用wait在父进程中阻塞等待子进程的退出
        // 不接收子进程的退出状态
        // wait(NULL);

        // 接收子进程的退出状态
        // 子进程中必须使用exit或者_exit函数退出进程时发送退出状态
        int status =0;
        wait(&status);

        if(WIFEXITED(status) != 0)  // 如果子进程是正常终止的,取出的字段值非零
        {
            // 返回子进程的退出状态
            // 在使用此宏 WEXITSTATUS 前,应先使用宏WIFEXITED判断子进程是否正常退出,正常退出才可以使用此宏
            printf("The son process return status: %d\n", WEXITSTATUS(status));

        }


        printf("this is a parent process\n");

    }
    else if(pid == 0) // 子进程的代码区
    {
        int i = 0;
        for(i=0;i<5;i++)
        {
            printf("this is a son process\n");
            sleep(1);
        }

        // 使用exit退出当前进程并设置退出状态
        exit(2);

    }

    return 0;
}


在这里插入图片描述

进程的中止

进程的中止

exit是库函数,_exit是系统调用
exit会刷新缓冲区

在这里插入图片描述
在这里插入图片描述

进程的创建 vfork函数

进程的创建vfork函数

vfork保证子进程先运行,在它调用exec或exit之后,父进程才可能被调度运行。
vfork和fork一样都创建一个子进程,但它并不将父进程的地址空间完全复制到子进程中,因为子进程会立即调用exec(或exit)
在子进程中调用exec或exit之前,它在父进程的地址空间中运行,在exec之后子进程会有自己的进程空间

  • 子进程在父进程之前执行
#include <stdio.h>
#include <stdlib.h>

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


int main()
{
    pid_t pid;
    pid = vfork();   // 使用vfork函数创建完子进程后,子进程会先执行直到子进程执行exec或exit后,父进程才会执行
    if(pid <0)
    {
        perror("fail to fork");
        exit(1);
    }
    else if(pid > 0)  // 父进程的代码区
    {
        while (1)
        {
            printf("this is a father process\n");
            sleep(1);
        }
    }
    else if(pid == 0) // 子进程的代码区
    {
        int i=0;
        for(i=0;i<5;i++)
        {
            printf("this is a son process\n");
            sleep(1);
        }
        exit(0);
    }

    return 0;
}


在这里插入图片描述

  • 子进程和父进程共享同一块空间
#include <stdio.h>
#include <stdlib.h>

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

int a = 10;

int main()
{
    int b = 9;

    pid_t pid;
    pid = vfork();  // 使用vfork创建完子进程,在子进程执行exec或exit之前,父子进程共有同一块地址空间
    if(pid <0)
    {
        perror("fail to fork");
        exit(1);
    }
    else if(pid > 0)  // 父进程的代码区
    {
        printf("this is a father process\n");
        printf("in father a=%d,b=%d\n",a,b);
    }
    else if(pid == 0) // 子进程的代码区
    {
        a++;
        b++;
        printf("this is a son process\n");
        printf("in son a=%d,b=%d\n",a,b);
        exit(0);
    }

    return 0;
}


在这里插入图片描述

进程的替换

进程的替换

1、exec函数族取代调用进程的数据段、代码段和堆栈段
2、调用exec函数的进程并不创建新的进程,故调用exec前后,进程的进程号并不会改变,其执行的程序完全由新的程序替换,而新程序则从其main函数开始执行。
3、一个进程调用exec后,除了进程ID,进程还保留了这些特征不变:父进程号、进程组号、控制终端、根目录、当前工作目录、进程信号屏蔽集、未处理信号等等

六个exec函数中只有execve是真正意义上的系统调用(内核提供的接口),其他函数都是在此基础上经过封装的库函数

centos里可以通过whereis 查询命令的路径
在这里插入图片描述

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


int main()
{

    pid_t pid;
    pid = fork();
    if(pid <0)
    {
        perror("fail to fork");
        exit(1);
    }
    else if(pid > 0)  // 父进程的代码区
    {
        printf("this is a father process\n");

        wait(NULL);
        printf("The child process has quited\n");

    }
    else if(pid == 0) // 子进程的代码区
    {

        printf("this is a son process\n");

        // 使用exec函数族调用shell命令

        // 不带p的函数,命令的路径一定要用绝对路径
#if 0
        if(execl("/bin/ls", "ls", "-l", NULL) == -1)
        {
            perror("fail to execl");
            exit(1);
        }
#endif

        // 带p的函数,第一个参数既可以是相对路径,也可以是绝对路径
#if 0
        if(execlp("ls", "ls", "-l", NULL) == -1)
        {
            perror("fail to execlp");
            exit(1);
        }
#endif


        // 带v的函数需要使用指针数组来传递
#if 0
        char *str[] = {"ls", "-l", NULL};
        if(execv("/bin/ls", str) == -1)
        {
            perror("fail to execv");
            exit(1);
        }
#endif

        // exec函数族调用可执行文件
#if 0
        if(execlp("./hello", "./hello", NULL) == -1)
        {
            perror("fail to execlp");
            exit(1);
        }
#endif

        // exec函数族调用可执行文件  不带p的函数 调用可执行文件 可以是相对路径
#if 0
        if(execl("./hello", "./hello", NULL) == -1)
        {
            perror("fail to execl");
            exit(1);
        }
#endif


        // exec函数族调用shell脚本
#if 1
        if(execlp("./myshell.sh", "./myshell.sh", NULL) == -1)
        {
            perror("fail to execlp");
            exit(1);
        }
#endif


        // exec函数族取代调用进程的数据段、代码段和堆栈段
        // 所以当exec函数执行完毕后,当前进程就结束了,所以原本进程中的后续代码不会再执行
        printf("hello world\n");

    }

    return 0;
}


关于信号
进程间通信

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值