• 进程控制原语


    #include<stdio.h>
    #include<sys/types.h>
    #include<unistd.h>                                                           
    #include<stdlib.h>
    
    int main(void)
    {
        pid_t fpid; //fpid表示fork函数返回的值
        int count=0;
        fpid=fork(); 
        if (fpid < 0) 
            printf("error in fork!
    "); 
        else if (fpid == 0) {
            printf("i am the child process, my process id is %d
    ",getpid()); 
            printf(" my parent process id is %d
    ",getppid()); 
            count++;
        }
        else {
            printf("i am the parent process, my process id is %d
    ",getpid()); 
            count++;
            
    
        }
        printf("count 统计结果是: %d
    ",count);
        while(1);//无限阻塞
        return 0;
    }

    fork 函数,创建子进程。

    函数原型:

     

    关于其返回值:

     

    fork函数一次调用,两次返回。子进程中返回0,父进程中,返回子进程的ID。如果fork失败,返回-1.并且不会创建子进程,同时错误代码errno会被设置。

    fork的读时共享,写时复制机制。子进程拥有和父进程一样的0-3G用户空间,但是3-4G内核空间中PCB(进程控制块)的进程ID号并不相同。子进程和父进程有如此多相同的地方,如果仅仅是读取0-3G用户空间的数据,则没有必要复制一份父进程的数据到内存,但如果需要写入数据,就必须开辟新的空间了。

    ——————————————————》》》分割线

    但是,子进程创建出来如果所做的任务和父进程完全一致,那么也就没有必要创建子进程的意义了。通常而言,子进程被创建,是要做与父进程不一样的事情。

    #include<stdio.h>
    #include<sys/types.h>
    #include<unistd.h>                                                           
    #include<stdlib.h>
    
    int main(void)
    {
        pid_t fpid; 
        int count=0;
        fpid=fork(); 
        if (fpid < 0) 
            printf("error in fork!
    "); 
        else if (fpid == 0) {
            char *const argv[]={"ls","-l",NULL};
            printf("i am a child process
    ");
            execvp("ls",argv);
            printf("where is my process?
    ");
        }
        else {
            printf("i am a parent process
    "); 
    
        }
        while(1);
        return 0;
    }

    execvp:属于exec族中的一个函数。

    当进程调用了exec族的某一函数之后,该进程执行的程序被替换成全新的程序,新程序从其main函数开始执行。由于调用exec函数不会创建新的进程,所以替换前后的进程ID并不会改变。exec只是用磁盘上的一个新程序替换了当前进程的正文段、数据段、堆段和栈段。

    在上面的程序中,使用了ls –l这个指令,可以发现该指令确实正确执行了,但是显示的列表中,a.out没有颜色信息。其次,在char *const argv[]={"ls","-l",NULL};中,第一个“ls”字符串只有一个占位符的作用,该字符串无论是什么都不会影响程序的运行结果,因为在execvp("ls",argv);中已经指定了ls程序,在argv指针数组中的argv[0]只用作占位符。哪怕将其改为”hehe”:

    程序依然正常运行,exec函数会从argv[1]开始,直到NULL结束。

    现在新建一个upper.c文件:

    /* upper.c */
    #include<stdio.h>
    #include <ctype.h>
    
    int main(void)
    {
        in ch;
        while((ch=getchar())!=EOF)
            putchar(toupper(ch));//小写转大写
        return 0;
    }

    把之前的test.c改成:

    #include<stdio.h>
    #include<sys/types.h>
    #include<unistd.h>                                                           
    #include<stdlib.h>
    
    int main(void)
    {
        pid_t fpid; 
        int count=0;
        fpid=fork(); 
        if (fpid < 0) 
            printf("error in fork!
    "); 
        else if (fpid == 0) {
            char *const argv[]={"upper",NULL};
            printf("i am a child process
    ");
            execv("./app",argv);
            printf("where is my process?
    ");
        }
        else {
            printf("i am a parent process
    "); 
    
        }
        while(1);
        return 0;
    }

    先编译upper.c生成app,然后编译test.c生成a.out,运行a.out:

    exec函数运行之后,只有出错才会返回,这也就意味着,exec函数后面的语句不会被执行。同样,这里的char *const argv[]={"upper",NULL};中的“upper”字符串也可以是其他任何字符串(即使为NULL也行,但通常使用能代表其含义的名称)。

    wait和waitpid函数:

    僵尸进程: 子进程退出,父进程没有回收子进程资源(PCB),则子进程变成僵尸进程。

    孤儿进程: 父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为1号进程init进程,称为init进程领养孤儿进程。

    一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用wait或waitpid获取这些信息,并彻底清除掉这个进程。我们知道一个进程的退出状态可以在Shell中用特殊变量$?查看(echo $?),因为Shell是它的父进程,当它终止时Shell调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程。如果一个进程已经终止,但是它的父进程尚未调用wait或waitpid对它进行清理,这时的进程状态称为僵尸(Zombie)进程。任何进程在刚终止时都是僵尸进程,正常情况下,僵尸进程都立刻被父进程清理了。

    目前,只用知道,wait和waitpid的基本用法即可,至于到底怎么应用在程序中,可以暂时不理会。

    如果使用了WNOHANG参数调用waitpid,即使没有子进程退出,它也会立即返回,不会像wait那样永远等下去。

  • 相关阅读:
    Github
    Vocabulary in Computer
    js中三种定义变量的方式const, var, let的区别
    Node.js-1
    JSON_in_js
    JSON快速入门
    Mysql tinyint长度为1时在java中被转化成boolean型
    maven上解决循环依赖、又不想新加第三模块的方法
    关于springboot和tomcat的服务能力做下简单的测试
    tomcat各个端口的作用
  • 原文地址:https://www.cnblogs.com/yangguang-it/p/10793193.html
Copyright © 2020-2023  润新知