• 0725------Linux基础----------进程2


    1. fork

      1.1 通过 fork 创建的父子进程对于fork之前打开的fd,共享文件偏移量。这是因为,父进程fork一个子进程后,会有自己的进程表项,因此二者各有一套相同的文件描述符表,他们共享了文件表项,因而也就共享了偏移量。此外,close 的关闭采用的是引用计数,当执行close时,是把该fd 窒息那个的的内核中的文件表现的引用计数减1,仅当引用计数为0 时,才是真正的销毁该结构。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <fcntl.h>
    #define ERR_EXIT(m) 
        do { 
            perror(m);
            exit(EXIT_FAILURE);
        }while(0)
    
    /*
     * 父子继承共享文件偏移量
     */
    
    int main(int argc, const char *argv[])
    {
        int fd = open("test.txt", O_RDONLY);
        if(fd == -1){
            ERR_EXIT("open");
        }
        pid_t pid;
        if((pid = fork()) < 0){
            ERR_EXIT("fork");
        }
        else if(pid == 0){
            char buf[10] = {0};
            read(fd, buf, 3);
            printf("in Child buf = %s
    ", buf);
            close(fd); //此处引用计数 减 1
        }
        else{
            sleep(3);
            char buf[10] = {0};
            read(fd, buf, 3);
            printf("in parent buf = %s
    ", buf);
            close(fd);
        }
        return 0;
    }  

      1.2 目前我们碰到的共享文件偏移量的情况有 2 种:

        a)通过dup 等手段复制fd,此时两个fd 共享文件偏移量(文件表项);

        b)fork 父子进程,二者共享文件偏移量。

      1.3 shell的工作原理:当我们在键盘上敲入”ls”的时候

        a)shell(bash、zsh)先fork一个子进程

        b)将子进程的代码使用exec替换为“ls”

        c)shell负责该子进程的回收

      1.4 对于经典的fork+exec的组合模式,fork出子进程再进行替换,那么复制完整的子进程的地址空间是无意义的。所以提出两种解决方案:

        a)vfork:vfork的目的就是为了exec;

        b)对于fork采用写时复制技术

      1.5 fork的写时复制技术:

        a)fork子进程时,仅仅复制页表项,而不是具体的进程空间。同时将地址空间设为只读

        b)每当任何一方试图修改地址空间时,就自己复制一份

      1.6 写时复制(COW)使得父子进程,在物理上共享地址空间的,但是在逻辑上地址空间是相互独立的

    2.关于父子进程的处理

      2.1 处理僵尸进程的手段:

        a)处理SIGCHLD信号;

        b)采用wait、waitpid。

      2.2 如果没有任何子进程,那么执行wait时,会立刻返回-1,同时errno为ECHLD。否则阻塞使用WNOHANG可以避免阻塞。

      2.3 waitpid不是按照顺序回收子进程。

        2.3.1 不按照顺序回收的例子。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #define ERR_EXIT(m) 
        do { 
            perror(m);
            exit(EXIT_FAILURE);
        }while(0)
    #define N 10
    int main(int argc, const char *argv[])
    {
        int i;
        pid_t pid;
        for(i = 0; i < N; i++){
            if((pid = fork()) < 0){
                ERR_EXIT("fork");
            }
            else if(pid == 0){
                exit(100 + i); //子进程返回退出码
            }
        }
        int status;
        while((pid = waitpid(-1, &status, 0)) > 0){//回收所有的子进程
            if(WIFEXITED(status)){// 判断子进程是否正常退出
                printf("child %d return success %d
    ", pid, WEXITSTATUS(status));
            }
            else
                printf("chidl %d return errno
    ", pid);
        }
        if(errno != ECHILD){
            ERR_EXIT("waitpid");
        }
        return 0;
    }

      

        2.3.2 若要顺序回收,可以用waitpid 一次等待每个特定的pid,若没有等到,会一直阻塞。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #define ERR_EXIT(m) 
        do { 
            perror(m);
            exit(EXIT_FAILURE);
        }while(0)
    #define N 10
    /*
     * 顺序回收
     *
     */
    int main(int argc, const char *argv[])
    {
        int i;
        pid_t pid[N];
        for(i = 0; i < N; i++){
            if((pid[i] = fork()) < 0){
                ERR_EXIT("fork");
            }
            else if(pid[i] == 0){
                exit(100 + i); //子进程返回退出码
            }
        }
        int status;
        i = 0;
        pid_t ret;
        while((ret = waitpid(pid[i], &status, 0)) > 0){//回收所有的子进程
            if(WIFEXITED(status)){// 判断子进程是否正常退出
                printf("child %d return success %d
    ", ret, WEXITSTATUS(status));
            }
            else
                printf("chidl %d return errno
    ", pid[i]);
            i++;
        }
    
    
        if(errno != ECHILD){
            ERR_EXIT("waitpid");
        }
        return 0;
    }
    

     

      2.4 system与exec区别:

        a)exec替换的是当前进程

        b)system则是创建子进程,然后调用exec替换

        2.4.1 exec的例子。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <errno.h>
    #define ERR_EXIT(m) 
        do { 
            perror(m);
            exit(EXIT_FAILURE);
        }while(0)
    
    int main(int argc, const char *argv[])
    {
        printf("Enter main
    ");
    
        execlp("ls", "ls", "-l", NULL);//替换当前子进程
    
        printf(" Leave main
    ");
    }

     

        2.4.2 system的例子。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <errno.h>
    #define ERR_EXIT(m) 
        do { 
            perror(m);
            exit(EXIT_FAILURE);
        }while(0)
    
    int main(int argc, const char *argv[])
    {
        printf("Enter main
    ");
    
        system("ls -l");
        printf(" Leave main
    ");
    }

      2.5 system的实现:

        a)创建子进程

        b)子进程采用exec进行进程替换

        c)父进程回收子进程,注意EINTR

      2.6 守护进程与普通进程的区别:(这个有时间要再看一遍)

        a)守护进程不属于shell所在的会话组

        b)当shell退出的时候,守护进程不受影响

     

  • 相关阅读:
    javascript私有静态成员
    javascript公有静态成员
    javascript沙箱模式
    javascript构造函数模块
    javascript模块模式
    javascript私有方法揭示为公有方法
    javascript命名空间
    javascript构造函数强制使用new
    javascript惰性函数
    javascript柯里化
  • 原文地址:https://www.cnblogs.com/monicalee/p/3868165.html
Copyright © 2020-2023  润新知