Linux系统编程之进程(Linux进程,fork()函数,僵尸进程,孤儿进程)

1.Linux进程

1.1进程概念:

  • 从用户角度:进程就是一个正在运行中的程序。
  • 操作系统角度:操作系统运行一个程序,需要描述这个程序的运行过程,这个描述通过一个结构体task_struct{}来描述,统称为PCB,因此对操作系统来说进程就是PCB(process control block)程序控制块
  • 进程的描述信息有:标识符PID,进程状态,优先级,程序计数器,上下文数据,内存指针,IO状态信息,记账信息。都需要操作系统进行调度。

1.2进程状态:

/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = {
    
    
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};

2.fork()函数

2.1概述:

使用 fork() 函数得到的子进程是父进程的一个复制品,它从父进程处继承了整个进程的地址空间:
包括进程上下文(进程执行活动全过程的静态描述)、进程堆栈、打开的文件描述符、信号控制设定、进程优先级、进程组号等。子进程所独有的只有它的进程号,计时器等(只有小量信息)。

pid_t fork(void);

头文件:

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

功能:

  • 用于从一个已存在的进程中创建一个新进程,新进程称为子进程,原进程称为父进程。

返回值:

  • 成功:子进程中返回 0,父进程中返回子进程 ID。pid_t,为无符号整型。
  • 失败:返回 -1。

代码示例:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
 
int main(int argc, char *argv[])
{
    
    
	pid_t pid;
	pid = fork();
	
	if( pid < 0 ){
    
    	// 没有创建成功
		perror("fork");
	}
	
	if(0 == pid){
    
     // 子进程
		while(1){
    
    
			printf("I am son\n");
			sleep(1);
		}
	}
	else if(pid > 0){
    
     // 父进程
		while(1){
    
    
			printf("I am father\n");
			sleep(1);
		}
	}
	
	return 0;
}

2.2 运行结果:

在这里插入图片描述

2.3总结:

  • 通过运行结果,可以看到,父子进程各做一件事(各自打印一句话)。这里,我们只是看到只有一份代码,实际上,fork()
    以后,有两个地址空间在独立运行着,有点类似于有两个独立的程序(父子进程)在运行着。需要注意的是,在子进程的地址空间里,子进程是从fork() 这个函数后才开始执行代码。
  • 在 fork() 之后是父进程先执行还是子进程先执行是不确定的。这取决于内核所使用的调度算法。

3.僵尸进程

一个进程使用 fork 创建子进程,如果子进程退出而父进程并没有调用 wait() 或者 waitpid() 获取子进程信息,那么子进程的描述符仍然保存在系统中,这种进程就被称为僵尸进程。

Z 进程

3.1代码模拟僵尸进程:

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

int main()
{
    
    
  printf("输入一遍\n");
  pid_t ret = fork();
  if(ret > 0)
  {
    
    
    //parent    
    while(1)    
    {
    
        
    printf("I am parent! pid is : %d\n", getpid());    
    sleep(1);    
    }    
  }    
  else if(ret == 0)    
  {
    
        
    /child    
    int count = 0;    
    while(count<5)    
    {
    
        
      printf("I am child! pid is : %d, ppid: %d\n", getpid(), getppid());    
      count++;    
      sleep(2);    
    }    
    exit(0);    
  }    
  else    
  {
    
        
    printf("fork error\n");    
  }    
  sleep(1);    
  return 0;    
}

3.2僵尸进程结果展示:

在这里插入图片描述

3.3僵尸进程危害:

子进程的退出状态会一直被维持下去,维护退出状态本身就是要用数据维护,也属于进程基本信息,被保存在task_struct中。也就是说只要Z状态不退出,那么PCB要一直维护退出信息。如果一个父进程创建了很多的子进程,都不进行回收,就会造成内存资源的极大浪费,也会造成内存泄露

4.孤儿进程

父进程提前退出,子进程就称为“孤儿进程”。一旦父进程退出,子进程要退出的时候就是Z状态,因为没有进程接收它的退出信息,所以孤儿进程要被1号init进程领养回收,从而避免子进程变为僵尸进程。

4.1代码模拟孤儿进程:

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

int main()
{
    
    
  pid_t ret = fork();
  if(ret < 0)
  {
    
    
    printf("创建进程失败!\n");
  }
  else if(ret > 0)
  {
    
    
    int count = 0;
    while(count++ < 5)
    {
    
    
      printf("我是父进程(id:%d)\n",getpid());
      sleep(2);
    }
    exit(0);
  }
  else
  {
    
    
    while(1)
    {
    
    
      printf("我是子进程(id:%d)\n",getpid());
      sleep(1);
    }
  }
  sleep(1);
  return 0;
}

4.2孤儿进程结果展示:

在这里插入图片描述

4.3总结:

os考虑了这种情况的发生,在父进程退出,子进程还在运行(子进程被称为孤儿进程)的时候重新给他找一个父亲,如图所示就是1号进程systemd。

1号进程(init)扩展:

由0进程创建,完成系统的初始化. 是系统中所有其它用户进程的祖先进程。
Linux中的所有进程都是有init进程创建并运行的。首先Linux内核启动,然后在用户空间中启动init进程,再启动其他系统进程。在系统启动完成完成后,init将变为守护进程监视系统其他进程。

猜你喜欢

转载自blog.csdn.net/weixin_45313447/article/details/114443655