Linux系统编程之进程及相关API

什么是进程

简单来说进程是程序的动态的概念,进程是当程序运行起来时就会存在进程在这里插入图片描述
图中的a.out就是一个程序,而当其运行起来后,可以通过指令

ps -aux |grep a.out 来查看进程

在这里插入图片描述
可以看到进程的pid号,以及运行的状态等信息

父进程

指已创建一个或多个子进程的进程。在UNIX里,除了进程0以外的所有进程都是由其他进程使用系统调用fork创建的,这里调用fork创建新进程的进程即为父进程,而相对应的为其创建出的进程则为子进程,因而除了进程0以外的进程都只有一个父进程,但一个进程可以有多个子进程。

子进程

指的是由另一进程(对应称之为父进程)所创建的进程。子进程继承了对应的父进程的大部分属性,如文件描述符。在Unix中,子进程通常为系统调用fork的产物。在此情况下,子进程一开始就是父进程的副本。

进程创建

上文提到fork,而进程创建里就需要依赖fork函数来创建,而创建进程有两种
创建进程方式一:fork()

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

       pid_t fork(void);

由fork的返回值来判断是进入了父进程还是子进程,当返回值大于零时是进入了父进程,当返回值等于零时是进入了子进程,由以下例子可以看到父进程fork的返回值是其子进程的pid号,获取当前进程的pid号可以用getpid(),若想获取其父进程的pid号可以用getppid()

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

//       pid_t getpid(void);
  //     pid_t getppid(void);
int main(){
        pid_t pid,pid1;

        pid1=getpid();
        printf("创建进程前的进程号是:%d\n",pid1);
        pid=fork();

        pid_t pid2;
        pid2=getpid();
        if(pid>0){
                printf("这是父进程pid号是:%d\n",pid2);
                printf("父进程fork的返回值:%d\n",pid);
        }
        else{
                printf("这是子进程pid号是:%d\n",pid2);
                printf("其父进程%d\n",getppid());
                printf("子进程fork的返回值:%d\n",pid);
        }


        return 0;
}

在这里插入图片描述
创建进程方式二:vfork()

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

       pid_t vfork(void);

这两种方法都可以用来创建子进程,两者的区别是这几种。

区别一:当用了fork来创建进程时,父子进程同时抢夺cpu的资源来运行,而当用了vfork来创建子进程时父进程必须等待子进程结束后,才能轮到父进程运行。
区别二:vfork创建的子进程是共享父进程的数据和储存,而fork创建的子进程,是写实拷贝,也就是只有当子进程中改变了数据段的时候,子进程会复制一份数据给自己,然后只改动自己的部分,父进程的数据不会随着子进程改变而改变。

这里可以举例说明。

①fork创建
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

//       pid_t getpid(void);
  //     pid_t getppid(void);
int main(){
        pid_t pid,pid1;

        pid1=getpid();
        int a=0;
        pid=fork();

        pid_t pid2;
        pid2=getpid();
        if(pid>0){
                while(1){
                        a++;
                        printf("父进程%d\n",a);
                        sleep(1);
                }
        }
        else{
                while(a<=5){
                        a++;
                        printf("子进程%d\n",a);
                        sleep(1);
                }
        }


        return 0;
}

在这里插入图片描述
可以看到父子进程在争夺cpu,而且子进程复制了一份数据

②vfork创建
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
//       pid_t getpid(void);
  //     pid_t getppid(void);
int main(){
        pid_t pid,pid1;
        int a=0;
        pid1=getpid();
        pid=vfork();
        pid_t pid2;
        pid2=getpid();
        if(pid>0){
                while(1){
                        a++;
                        printf("父进程%d\n",a);
                        sleep(1);
                }
        }
        else{
                while(1){
                        a++;
                        printf("子进程%d\n",a);

                        sleep(1);
                        if(a>4){
                                exit(3);
                        }
                }
        }


        return 0;
}

在这里插入图片描述

可以看到这里父进程在等待子进程结束后,才开始的父进程,且数据段是共享的

父进程等待子进程

我们可以知道用vfork()时父进程必须等待子进程退出后,才能执行,而用fork()也可以使父进程等待子进程退出,这里需要用到wait函数。

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

       pid_t wait(int *wstatus);

  		pid_t waitpid(pid_t pid, int *wstatus, int options);

#include <stdlib.h>

       void exit(int status);

①wait

wait函数需要配合着exit来使用,二者的status就是进程退出的状态码,如果子进程退出后父进程没有用wait来收集子进程的状态码,那么子进程就会变成僵尸进程(z+),我们可以用进程指令来查看。

 ps -aux |grep a.out

在这里插入图片描述

这里可以用一个例子来说明
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(){
        pid_t pid,pid1;

        pid1=getpid();
        int a=0;
        int states;
        pid=fork();

        pid_t pid2;
        pid2=getpid();
        if(pid>0){
        //      wait(NULL);//不关心子进程退出的状态
                wait(&states);
                printf("子进程状态码:%d\n",WEXITSTATUS(states));
                printf("father%d\n",a);

        }
        else{
                while(1){
                        a++;
                        printf("child%d\n",a);
                        sleep(1);
                        if(a>5){
                                exit(3);
                        }
                }

        }


        return 0;
}

在这里插入图片描述

可以看到wait里的参数可以是子进程退出的状态码,也可以是NULL,而这时就是不关心子进程的退出的状态,但也会等待,只是不关心,而且子进程结束后也不会变成僵尸进程,而如果想要打印出状态码,还需要借助WEXITSTATUS(states)。

②waitpid

参数pid
pid>0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。
pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。
pid=0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。
pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。

参数wstatus
和wait一样根据关心状态与否来选择参数

参数options
这里如果打算不等待子进程退出还依然用waitpid的话,可以选择用WNOHANG,如果选择了WNOHANG,子进程还是会变成僵尸进程,若需要等待则把该值设为0,那么效果会等同于wait函数

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(){
        pid_t pid,pid1;

        pid1=getpid();
        int a=0;
        int states;
        pid=fork();

        pid_t pid2;
        pid2=getpid();
        if(pid>0){
//              wait(NULL);//不关心子进程退出的状态
        //`     wait(&states);
                waitpid(-1,&states,0);
                printf("子进程状态码:%d\n",WEXITSTATUS(states));
                while(1){
                printf("father%d\n",a);
                sleep(1);
                }
        }
        else{
                while(1){
                        a++;
                        printf("child%d\n",a);
                        sleep(1);
                        if(a>5){
                                exit(3);
                        }
                }

        }


        return 0;
}

孤儿进程

孤儿进程顾名思义就是子进程还在运行,但是父进程以及提前结束了,而linux系统为了避免过多的孤儿进程而做了一些处理,当其父进程提前结束自己时,linux中的init进程会收留子进程,可以举个例子来测试一下。

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(){
        pid_t pid,pid1;

        pid1=getpid();
        printf("创建进程前的进程号是:%d\n",pid1);
        int a=0;
        pid=fork();

        pid_t pid2;
        pid2=getpid();
        if(pid>0){
                printf("这是父进程pid号是:%d\n",pid2);
                printf("父进程fork的返回值:%d\n",pid);

                        printf("father:%d\n",a);
                        sleep(2);
        }
        else{
                printf("这是子进程pid号是:%d\n",pid2);
                printf("其父进程%d\n",getppid());
                printf("子进程fork的返回值:%d\n",pid);
                while(a<=5){
                        printf("其父进程%d\n",getppid());
                        a++;
                        printf("child%d\n",a);
                        sleep(1);
                }
        }


        return 0;
}

在这里插入图片描述

从这里可以看到,子进程在父进程运行到一半时结束了,子进程的pid号也随之发生了改变
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值