一.什么是进程

        通俗的来讲,进程就是程序在操作系统里运行起来后的一个实例,它有自己的资源、能和别的进程并发执行,系统通过进程来管理和调度任务。

二.进程的创建

        创建进程有两种⽅式:1)操作系统创建 2)父进程创建。
        进程的创建可以通过system函数、exec系列函数(execl、execlp等)、fork函数

实现。

1)system函数

函数原型为:int system(sonst char*filename)

使用时需要包含头文件:stdlib.h 主要用于执行shell命令.

功能为:简历独立进程,拥有独立的代码空间,内存空间,等待新的进程执行完

毕,system才返回,system函数其实内部会调用fork函数来产生子进程。

2)复制进程fork函数

函数原型为:pid_t fork(void)
功能:fork 函数会新生成⼀个进程,调⽤ fork 函数的进程为父进程,新生成的进 程为子进程。子进程复制了父进程打开的文件描述符。
函数返回值:函数返回类型 pid_t 实质是 int 类型
在父进程中返回子进程的 pid,在子进程中返回 0,失败返回-1。
所属头文件:unistd.h

示例代码

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


void task1()
{
    printf("#current id=%d\n",getpid());
    for(int i=0;i<10;i++)
     {
         printf("##############---%d\n",i+1);
         sleep(1);
     }
}

void task2()
{
    printf("$current id=%d\n",getpid());
    for(int i=0;i<10;i++)
     {
         printf("$$$$$$$$$$$$---%d\n",i+1);
         sleep(1);
     }
}

int main()
{
  
   int start=time(NULL);
   printf("start=%d\n",start);

   pid_t pid=fork();
   if(pid>0)
   task1();//父进程
    else
   task2(); //子进程
   wait(NULL);
   int end=time(NULL);
   printf("end=%d\n",end);
   
  return 0;
}

fork()函数出错可能有两种原因:

1、当前的进程数已经达到了系统规定的上限,这时errno的值被设置为EAGAIN
2、系统内存不足,这时errno的值被设置为ENOMEM

3)孤儿进程

        如果⼀个子进程的父进程先与子进程结束,子进程就变成孤儿进程;孤儿进程将被 init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作(包括资源回收)。

示例代码

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

int main()
{
    printf("parent process id=%d\n", getpid());
    
    pid_t pid = fork();
    
    if(pid > 0)
    {
        printf("father process\n");
        sleep(1);
    }
    else if(pid == 0)
    {
        printf("father id=%d child id=%d\n", getppid(), getpid());
        sleep(5);
        printf("father id=%d child id=%d\n", getppid(), getpid());
    }
    
    printf("end==%d\n", getpid());
    return 0;
}

运行结果

注意:带图形界⾯的ubuntu下上述打印孤儿进程的父进程可能不是1,是因为代替 了init进程来接管孤儿进程的进程名字叫做init–user,如果将程序在命令行下运就是发现是1。

形成过程

  1. 父进程先退出(62710):在子进程还在睡眠5秒时,父进程已经睡眠1秒后退出

  2. 子进程成为孤儿:父进程退出后,子进程被init进程(PID=1)收养

  3. 父进程ID变化:子进程第二次打印时,getppid()返回的不再是原来的父进程ID(62710),而是init进程的ID(1853)

4)进程等待

        创建⼀个子进程后,父进程与子进程会争夺cpu,抢到者先执⾏,另⼀个挂起等待, 如果想要父进程等待⼦进程执行完毕以后再继续执行,可以在fork之后,调用wait 或waitpid。同时父进程通过上述函数还可以取得子进程的终止状态。
等待进程结束的函数主要有:
1、pid_t wait(int *status);
功能:wait函数会在父进程中阻塞,等待子进程结束,如果子进程结束,则返回子进程的PID。如果没有子进程则立刻返回-1。
注意:如果要获取wait的状态码,需要用该宏包裹WEXITSTATUS(status)才可以拿到。
2、pid_t waitpid(pid_t pid, int *status, int options);
waitpid函数等待子进程结束(options设置为WNOHANG时为⾮阻塞,设置为0 表示阻塞),如果子进程结束,则返回子进程的PID。如果没有子进程则立刻返 回-1,如果是非阻塞的并且子进程还没有结束,则返回0。
两者的区别:
wait函数作用就是父进程调用,将父进程挂起,直到所等待的进程结束,然后在执 行父进程。而如果有多个子进程的话,只要第⼀个子进程结束,wait就会接受到信 号,并结束等待。而waitpid就会指定⼀个进程号,父进程将会等待pid这个子进程 结束才会执行。

5)僵尸进程

(1) 僵尸进程概念:子进程先于父进程结束,父进程没有调用 wait 获取子进程退出
码。
(2) 如何处理僵死进程:父进程通过调用 wait()完成。

示例代码

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


int main()
{
  int retval;
  printf("parent process id=%d\n",getpid());
  pid_t pid=fork();
  if(pid>0)
  {
     printf("father process\n");
     wait(NULL);
     sleep(10);
      printf("father over\n");

  }else if(pid==0)
  {
     printf("entry child process\n");
     sleep(1);     

  }
 return 0;
}

运行结果

过程分析

时间点0: 父子进程同时开始
时间点1: 子进程退出,成为僵尸进程
时间点1-11: 父进程仍在睡眠,子进程保持僵尸状态
时间点11: 父进程执行wait(NULL),回收子进程
时间点11+: 僵尸进程被清除

避免僵尸进程的方法

及时调用wait()或waitpid()

使用信号处理SIGCHLD

双fork技术(让孙子进程被init收养)

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐