• Linux进程学习—进程的创建、控制与退出


    Linux进程学习—进程的创建、控制与退出

    本文记录一些Linux学习过程中关于进程创建与控制的相关函数与概念,主要包括fork、exec函数族、system、popen的使用示例。

    1、进程的创建

    1.1进程定义

    进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

    1.2进程创建

    实际操作过程中可以利用forkvfork函数实现进程的创建,进程创建后分为子进程父进程,函数的两个返回值分别为0和子进程的进程PID号。由此可以判定返回值为0的进程为子进程;返回值为非0值的进程为父进程

    #include 
    #include 
    #include 
    
    
    int main()
    {
            pid_t pid;
    
            pid = getpid();
    
            int key=fork();
    
            if(key>0)
            {
                    printf("father pid is :%d  \n",getpid());
            }
            else
            {
                    printf("child pid is :%d  \n",getpid());
    
            }
    
            return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    同时,需要注意forkvfork两个函数在内存空间与执行次序两方面有所区别:

    1. 内存空间

    fork (): 子进程拷贝父进程的数据段,代码段
    vfork(): 子进程与父进程共享数据段

    2. 执行次序

    fork (): 父/子进程的执行次序不确定
    vfork(): 子进程先运行,在调用exec 或exit 之前与父进程数据是共享的, 子进程调用exec或exit 之后,父进程才可能被调度运行。如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。

    2、进程退出

    正常退出:

    main函数调用return
    进程调用_exit() 或者 _Exit(),系统调用
    进程调用exit(),标准C库( exit是_exit和_Exit的封装,前者是处理完缓存区中的内容再退出,而后两者是直接退出)
    
    • 1
    • 2
    • 3

    异常退出:

    调用abort
    当进程收到某些信号时,如ctrl + c
    最后一个线程对取消(cancellation)请求做出响应
    
    • 1
    • 2
    • 3

    不管是正常退出还是异常退出,进程退出时都会将打开的描述符关闭,并且释放存储器。值得注意的是子进程终止进程时,需要在父进程中调用wait或waitpid函数来取得子进程的运行状态(exit的参数)。

    #include 
    #include 
    #include 
    #include 
    #include 
    
    
    int main()
    {
            pid_t pid;
    
            int cnt=0;
            int status=10;
    
            pid=fork();//创建子进程 子进程中的返回值是0,父进程中的返回值是子进程的pid号
    
            if(pid>0)//父进程
            {
    
                    wait(&status);
            //      waitpid(pid,&status,WNOHANG);
                    printf("father pid is :%d,status:%d  \n",getpid(),WEXITSTATUS(status));
    
                    while(1)
                    {
                            printf("cnt=%d\n",cnt);
                            printf("This is Father print,pid=%d\n",getpid());
                            sleep(1);
                    }
            }
            else//pid==0子进程
            {
                    while(1)
                    {
                            printf("child pid is :%d  \n",getpid());
                            cnt++;
                            if(cnt == 5)
                            {
                                    exit(1);
                            }
                    }
    
            }
    
            return 0;
    }                                                                                                        39,1-8       全部
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    需要注意的是:

    子进程退出后状态不被收集(wait),最终会变成僵尸进程
    子进程尚未结束而父进程已经over,则会变成孤儿进程

    3、进程控制

    3.1、创建进程的目的

    1.一个父进程希望复制自己,使父、子进程同时执行不同的代码段。这在网络服务进程中是常见的------父进程等待客户端的服务请求。当这种请求到达时,父进程调用fork,使子进程处理此请求。父进程则继续等待下一个服务请求到达。
    2.一个进程要执行一个不同的程序,这对shell是常见的情况。在这种情况下,子进程从fork返回后立即调用exec

    3.2、exec函数族

    用fork函数创建新进程后,经常会在新进程中调用exec函数去执行另外一个程序。当进程调用exec函数时,该进程被完全替换为新程序。因为调用exec函数并不创建新进程,所以前后进程的ID并没有改变。
    头文件

     #include 
    
    • 1

    函数原型

    **1.execl**   int execl(const char *path, const char *arg, ...);
    **2.execlp**  int execlp(const char *file, const char *arg, ...);
    **3.execv**   int execv(const char *path, char *const argv[]);
    **4.execvp**  int execvp(const char *file, char *const argv[]);
    
    **5.execle**  int execle(const char *path, const char *arg,
                      ..., char * const envp[]);
    **6.execvpe** int execvpe(const char *file, char *const argv[],char *const envp[]);         
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    返回值
    exec函数族的函数执行成功后不会返回,调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行。
    exec使用示例

    #include 
    #include 
    #include 
    #include 
    //函数原型:int execl(const char *path, const char *arg, ...);
    
    int main(void)
    {
        printf("before execl\n");
        if(execl("/bin/ls","ls","-l",NULL) == -1)
        {
            printf("execl failed!\n");
            perror("why");
        }
        printf("after execl\n");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    #include 
    #include 
    #include 
    
    //函数原型:int execl(const char *path, const char *arg, ...);
    
    int main(void)
    {
        printf("before execl\n");
        if(execl("/bin/date","date",NULL) == -1)
        {
            printf("execl failed!\n");
            perror("why");
        }
        printf("after execl\n");
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3.3 system函数

    3.4 popen函数

  • 相关阅读:
    Java前端知识积累——CSS样式知识积累
    《TCP/IP网络编程》阅读笔记--I/O复用
    client-go controller-runtime kubebuilder
    Coinbase: AI+区块链的投资与创业机会
    Java相关编程思想
    【Java第29期】:Tomcat的安装和使用
    什么是走索引?
    yolov5 OpenVINO windows部署实战
    如何使用ChatPPT生成PPT文档
    vue 封装菜单组件 来回跳转使菜单高亮
  • 原文地址:https://blog.csdn.net/ONERYJHHH/article/details/126377637