Linux下fork()&vfork()的区别、getenv()&setenv()函数以及僵尸进程、孤儿进程讲解

在讲解僵尸进程前,我们先来说说其它知识,因为我们一会的代码需要用一个叫fork()的系统调用来创建子进程,所以我们先来聊聊fork()这个系统调用。

fork()、vfork()的区别:

#include<unistd.h>

pid_t fork(void);
//pid_t为int的类型别名
//fork()是一个比较特殊的函数,调用成功返回两个值,调用失败返回-1.
//那么到底fork()调用成功是如何返回两个值的呢?原来,fork()是用来
//创建子进程的,一但fork()调用成功,便会以父进程为模板创建子进程(
//代码段共享、数据段(写时拷贝)的拷贝),父、子进程都从fork()的下一句指令
//开始执行,但父、子进程的执行顺序无法确定,依赖于调度器的调度,父进程
//返回子进程的pid,子进程返回0,对于调用失败的情况可能有以下的两种情况,
//1)内存不足,内核没有足够的空间创建管理进程的数据结构(task_struct)
//2)进程数已经达到系统规定的上限
//与vfork()相关的还有一个叫做vfork()的函数,请看下面讲解:

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

pid_t vfork(void);
//vfork()的行为基本类似fork(),但两者有以下区别:
//1)fork()之后,子进程和父进程共享代码段、数据段写时拷贝,
//vfork()之后,代码段、数据段均共享。
//2)fork()、之后父、子进程的调度顺序依赖于调度器,vfork()之后,
//子进程先执行,在子进程调用exec()或_exit()之后父进程才可运行,
//不调用exec()或_exit(),则出现段错误,在调用exec()或_exit()之前,
//依赖父进程的动作,则会出现死锁。

僵尸进程:

所谓僵尸进程,是指那些父进程还在运行,子进程已经退出,但是父进程并没有查看子进程的退出状态,子进程一直以终止状态(Z状态)保存在进程表中等待父进程查看退出状态,此时,子进程便为僵尸进程。

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


int main(){
  pid_t ret = fork();
  if(ret < 0){
    //fork失败,打印错误信息
    perror("fork ");
    return 1;

  }else if(ret > 0){
    //father进程
    printf("father: pid_t = %d begin\n",getpid());//打印父进程pid
    sleep(30);//延时30s
    printf("father is end!\n");
  }else{
    //child进程
    printf("child: pid_t = %d is begin...\n",getpid());//打印子进程pid
    sleep(5);
    printf("child: is end\n");
  }
  return 0;
}

运行程序,利用ps aux | grep zombieprocess指令查看进程状态,结果如下:
程序运行结果:
结果
子进程结束前:
前
子进程结束后:
后
注意查看第八列的状态。

孤儿进程:

孤儿进程是指:父进程运行提前结束,子进程还在运行时,子进程便叫做孤儿进程。

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

int main(){
  pid_t ret = fork();
  if(ret < 0){ 
    perror("fork:");
    return 1;
  }else if(ret == 0){ 
    //child
    printf("child pid:%d is begin!\n",getpid());
    sleep(20);   
    printf("child father id:%d \n",getppid());
    printf("child is end! \n");
  }else{
    //father
    printf("father pid:%d is begin!\n",getpid());
    sleep(5);
    printf("father is end!\n");
  }
  return 0;
}

结果:
孤儿进程
1号进程为init进程。

环境变量:

下面简单的介绍关于环境变量的指令与函数。
echo $env_name:此命令查看相应环境变量的值。env_name为环境变量的名字。
export env_name="env_value":此命令创建一个新的环境变量。env_name为环境变量名,env_value为环境变量的值。
env:查看所有环境变量。
后面再介绍关于环境变量的两个函数getenv()、setenv():
char *getenv(const char *name);
此函数获得相应环境变量的值,参数为环境变量的字符串,成功返回对应变量的值字符串,失败返回空指针。

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

int main(){
  //char *getenv(const char *name);
  char *env = getenv("PATH");
  if(env){
    printf("%s\n",env);
  }
  return 0;
}

输出结果:

[DELL@MiWiFi-R1CL-srv class_code]$ ./environ
/usr/java/jdk/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:/home/DELL/.local/bin:/home/DELL/bin:./
[DELL@MiWiFi-R1CL-srv class_code]$

int setenv(const char *name, const char *value, int overwrite);
此函数用于创建环境变量或改变环境变量的值,name为所要改变或创建环境变量的值,value为环境变量的值,overwrite为标志位,当环境变量不存在时,无论overwrite为何值创建环境变量并以value赋值,当环境变量存在时,若overwrite非0时改变环境变量的值,当overwrite为0时忽略value的值,函数调用成功返回0,失败返回-1。

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

int main(){

  //int setenv(const char *name, const char *value, int overwrite);
  int ret = setenv("MYENV","hello world",1);
  if(ret == 0){ 
    printf("MYENV:");
    char *env = getenv("MYENV");
    if(env){
      printf("%s\n",env);
    }else{
      perror("getenv:");
    }   
  }else{
    perror("setenv:");
  }
  return 0;
}

输出结果:

[DELL@MiWiFi-R1CL-srv class_code]$ ./environ 
MYENV:hello world
[DELL@MiWiFi-R1CL-srv class_code]$ 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章