进程控制相关内容

目录

一、进程

1、定义

2、特性

①动态性

②并发性

③独立性

④异步性

 ⑤结构性

3、进程和程序的区别 

4、进程的状态 

二、进程相关名词

1、父子进程 

2、祖先进程

3、守护进程 

4、僵尸进程

 5、孤儿进程

三、进程控制相关函数

1、获取进程ID

getpid

getppid

2、运行进程

system

3、替换进程

execl

 4、创建进程

fork

vfork

 5、摧毁进程

exit

_exit

6、等待进程 

wait

waitpid


一、进程

1、定义

进程是一种抽象的概念,从来没有统一的标准定义。

从不同的角度,进程可以有不同的定义,比较典型的定义有:

进程是程序的一次执行过程。

进程是一个程序及其数据在处理机上顺序执行时所发生的活动。

进程是具有独立功能的程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。

简单来说,进程就是程序的一次执行过程,程序是静态的,它作为系统中的一种资源是永远存在的。而进程是动态的,它是动态的产生,变化和消亡的,拥有其自己的生命周期。

举例子理解:同时挂三个 QQ 号,它们就对应三个 QQ 进程,退出一个就会杀死一个对应的进程。但是,就算你把这三个 QQ 全都退出了,QQ 这个程序死亡了吗?显然没有死亡,程序还是存储在存储器中的,除非你把这个程序从存储体中给删除了(这一操作就相当于我们卸载手机里的软件)。

进程不仅包含正在运行的程序实体,它还包括这个运行的程序中占据的所有系统资源,比如说 CPU、内存、网络资源等。一些人会把进程理解为它就是一个运行的实体,而忽略掉了进程所占据的资源。比如说,同样一个程序,同一时刻被两次运行了,那么他们就是两个独立的进程,这两个进程所占用的资源是相互独立的。

引入进程实体的概念后,我们可以把传统操作系统中的进程定义为:“进程是程序实体的运行过程,是系统进行资源配和调度的一个独立单位。”

总之,进程是操作系统中用于管理程序执行和资源分配的基本单位。

2、特性

①动态性

进程的动态性体现在其从创建到终止的整个生命周期中。这个过程可以概括为:

动态产生:当程序被加载到内存中并开始执行时,一个进程就被创建了。这个过程通常包括分配内存、初始化数据段、设置文件描述符等步骤。

执行程序:进程的核心任务是执行程序中的指令。在执行过程中,进程会占用CPU时间、内存和其他系统资源。

数据:进程在执行过程中需要处理的数据也是其重要组成部分。这些数据可能来自程序本身、用户输入、文件系统或其他进程。进程通过内存地址空间来访问这些数据,并确保数据的完整性和安全性。

动态消亡:当程序执行完毕或遇到错误时,进程会进入终止状态。在终止过程中,进程会释放所占用的资源,如内存、文件描述符等,并通知操作系统该进程已经结束。

并发性

1)并发性与并行性的区别

并发性和并行性是既相似又有区别的两个概念。并行性是指两个或多个事件在同一时刻发生,这通常发生在多处理器或多核系统中。而并发性则是指两个或多个事件在同一时间间隔内发生这可以在单处理器或多处理器系统中实现。在单处理器系统中,并发性是通过时间片轮转技术来实现的,即操作系统将CPU时间分割成多个时间片,每个时间片分配给一个进程执行,当时间片用完时,操作系统会切换到下一个进程,从而实现多个进程的交替执行

2)进程在并发执行中的表现

在并发执行中,进程具有以下特点:

a.宏观上同时执行:由于CPU在高速切换,用户或系统很难感知到进程的交替执行,因此在宏观上给人一种多个进程同时执行的错觉。

b.微观上交替运行:在微观上,由于CPU同一时间只能执行一个进程对应的代码,因此多个进程是交替运行的。

c.时间片:时间片是操作系统调度技术的一种,用于在多个进程间分配CPU时间。每个进程在执行时会获得一个时间片,当时间片用完时,操作系统会切换到下一个进程。

补充:linux系统里面有自己的调度器,它会决定让某个进程执行,也可以进行切换,让其他的进程执行(系统中断)。

③独立性

指进程实体是一个能独立运行、独立获得资源和独立接受调度的基本单位。凡未建立PCB的程序,都不能作为一个独立的单位参与运行

进程是系统进程资源分配和调度的最小单位。创建完PCB后,进程都具有唯一的标识--PID,用非负整数表示进程的ID,这个ID就是进程的标识符。作用:就如同身份证具有唯一性。  

进程A和进程B,空间独立,互不干扰.

④异步性

由于进程的相互制约,使得进程按各自独立的、不可预知的速度向前推进。异步性会导致执行结果的不可再现性,为此在操作系统中必须配置相应的进程同步机制。

什么是同步? 硬件上->有CLK时钟线的话,就是同步;系统里面一般认为,有约束就是同步.例如:串口通信—没有约束—异步    IIC—同步半双工

什么是异步? 没有约束的就是异步

 ⑤结构性

进程的结构,主要有这三部分组成:进程控制块(PCB)、数据段、程序段

进程控制块

每个进程有且仅有一个进程控制块(PCB),或称作进程描述符,它是进程存在的唯一标识,是操作系统用来记录和刻画进程状态及环境信息的数据结构,也是操作系统掌握进程的唯一资料结构和管理进程的主要依据。创建进程和撤销进程等都是指对 PCB 的操作,当进程被创建时,操作系统为其创建 PCB,当进程结束时,会回收其 PCB。

进程控制块 PCB 一般包含如下四类信息:

        进程描述信息:用来让操作系统区分各个进程,当进程被创建时,操作系统会为该进程分配一个唯一的、不重复的 “身份证号”— PID(ProcessID,进程 ID)。另外,进程描述信息还包含进程所属的用户 ID(UID)

        进程控制和管理信息:记录进程的运行情况。比如 CPU 的使用时间、磁盘使用情况、网络流量使用情况等。

        资源分配清单:记录给进程分配了哪些资源。比如分配了多少内存、正在使用哪些 I/O 设备、正在使用哪些文件等。

        CPU 相关信息:进程在让出 CPU 时,必须保存该进程在 CPU 中的各种信息,比如各种寄存器的值。用于实现进程切换,确保这个进程再次运行的时候恢复 CPU 现场,从断点处继续执行。这就是所谓的保存现场信息。

数据段:

一个进程的数据段,可以是进程对应的程序加工处理的原始数据,也可以是程序执行时产生的中间或最终结果,即进程运行过程中的各种数据(比如程序中定义的变量)。

程序段:

程序段就是能被进程调度时程序调度到CPU执行的程序代码段(指令序列)。注意,程序可被多个进程共享,即多个进程可以运行同一个程序。例如:同时挂三个 QQ 号,会对应三个 QQ 进程,它们的 PCB、数据段各不相同,但程序段的内容都是相同的(都是运行着相同的 QQ 程序) 

3、进程和程序的区别 

程序以文件的形式保存在内存中(占用存储器的内存不会消失)。而进程只能能够运行于系统内存(运行内存系统关机之后都会消失)中。

进程是一个动态产生到动态消亡的过程,程序只是一个静态的概念。

进程和程序之间的对应关系,未必是一一对应的,一个程序(例如:main()fork()创建父子进程)可以对应多个进程。

4、进程的状态 

在这一整个过程中,进程所有的状态如下: 

(一)新建状态:进程正在被创建,创建好后会转到就绪状态。创建进程通常需要先申请一个空白的PCB,并向PCB中填写一些控制和管理进程的信息,然后由系统为该进程分配运行时所必须的资源,最后把该进程转入就绪状态。

(二)就绪状态:进程已经分配到了必要的内存、文件、设备等资源;程序和数据已经加载到内存中,等待CPU的调度(分配CPU时间片);系统中处于就绪状态的进程可能有多个,通常将他们排成一个队列,成为就绪队列。

(三)运行状态:当进程已经获得CPU,并且其程序正在执行时,该进程处于执行状态

(四)阻塞状态:又称等待状态。当进程因为等待某个事件(如I/O操作、资源申请、信号量等)而无法继续执行时,该进程处于阻塞状态;进程的状态信息会被保存在PCB(进程控制块)中,以便在事件完成后恢复执行.

(五)终止状态:进程正从系统中消失,可能是进程正常结束或其他原因中断退出运行。进程需要结束运行时,系统首先必须置该进程为结束状态,然后再进一步处理资源释放和回收等工作。

进程中就绪状态、执行状态、阻塞状态是如何进行切换的?

就绪状态 → 执行状态:当操作系统调度程序选择了一个就绪进程并将其分配到CPU上时。

执行状态 → 就绪状态:当进程的时间片用完、被更高优先级的进程抢占CPU、或主动让出CPU时。

执行状态 → 阻塞状态:当进程等待某个事件(如I/O操作)而无法继续执行时。例子:进程可能需要从磁盘读取数据或向磁盘写入数据。由于磁盘I/O操作通常比CPU操作慢得多,进程必须等待I/O操作完成才能继续执行。在这种情况下,进程会从执行状态转变为阻塞状态,直到I/O操作完成并且数据准备就绪。

阻塞状态 → 就绪状态:当等待的事件完成时,进程被唤醒并重新放入就绪队列中。

二、进程相关名词

1、父子进程 

大多数进程都不是凭空产生的,往往需要另一个进程的产生的,此时这个两个进程就属于父子进程,一般是在一个进程中使用fork()函数产生父子进程的。其中父子进程,最顶层父进程称为祖先进程对应的PID号=1。

注意:子进程运行结束后,需要父进程收回子进程的空间,目的防止僵尸进程的产生

2、祖先进程

祖先进程是系统引导时创建的第一个用户级进程,它负责初始化系统环境,并作为后续所有进程的祖先。

特点:

        系统中第一个被创建的用户级进程(PID=1)。

        通常是由系统内核在引导过程中创建的,而不是由其他进程创建的。

        负责系统的初始化工作,如挂载文件系统、启动系统服务等。

功能:

        系统初始化:祖先进程在系统启动时负责初始化系统环境,包括挂载文件系统、设置系统变量、启动必要的服务等。

        进程管理:它作为系统中所有进程的祖先,负责管理和协调这些进程的运行。当某个进程结束时,如果它的父进程已经不存在,那么该进程会成为孤儿进程,由祖先进程来收养并处理其资源回收工作。

        服务启动:祖先进程通常会启动系统所需的各种服务,如网络服务、打印服务等,以确保系统的正常运行。

        系统关闭:在系统关闭时,祖先进程会负责关闭系统服务、卸载文件系统等工作,确保系统的安全退出。

3、守护进程 

守护进程又可以称为精灵进程(后台进程),运行在后台的一种特殊进程。并独立于控制终端并且周期性的执行某个任务

4、僵尸进程

        僵尸进程是指已经完成执行但仍在进程表中保留其信息的进程。这种状态通常发生在子进程结束后,父进程尚未读取其退出状态,导致子进程的信息仍保留在系统中

特点:

        僵尸进程不再执行任何代码,也不占用CPU或内存资源(除了进程表中的一个记录)。

        僵尸进程不会主动结束,需要父进程进行清理才能释放资源。

产生原因:

        当一个子进程完成其任务并调用exit()系统调用时,它会向操作系统报告自己的退出状态。如果父进程没有调用wait()或waitpid()等系统调用来获取子进程的退出状态,那么子进程就会变成僵尸进程。

解决方法:

     ①父进程应定期调用wait()或waitpid()来处理其子进程,确保获取其退出状态并释放资源。

     ②如果父进程无法处理子进程(例如,父进程是一个循环且不会结束),可以通过重启父进程来清理其所有子进程(包括僵尸进程)。

     ③在某些情况下,可以手动杀死父进程,使子进程成为孤儿进程,然后由init进程接管并处理。

 5、孤儿进程

在子进程结束之前,父进程已经结束,此时子进程将称为孤儿进程,直到祖先进程“收养”,子进程运行结束之后,有祖先进程将空间收回。

三、进程控制相关函数

1、获取进程ID

getpid

函数功能:

        获取当前进程的PID号

头文件:

        #include <sys/types.h>

        #include <unistd.h>

函数原型:

        pid_t getpid(void);

函数参数:无

函数返回值:

        类型:无符号的整数

        返回的就是当前进程的PID号

getppid

函数功能:

        获取当前进程父进程的PID号

头文件:

        #include <sys/types.h>

        #include <unistd.h>

函数原型:

        pid_t getppid(void);

函数参数:无

函数返回值:

        类型:无符号的整数

        返回的就是当前进程的父进程PID号

PID和PPID之间的区别

标识对象不同:PID标识的是当前进程本身,而PPID标识的是创建当前进程的父进程。

用途不同:PID主要用于进程管理和调度,而PPID主要用于构建进程之间的层级关系和追踪进程来源。

数值不同:在大多数情况下,一个进程的PID和它的父进程的PPID是不同的(除非是在进程创建时发生的特殊情况,但这种情况非常罕见)


示例1:如何获取对应进程的PID号,输出当前进程父进程的PID号

现象:

代码:

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

int main(int argc,char *argv[])
{
   //先输出一下,当前进程的PID号
   pid_t pid,ppid;
   pid=getpid();
   printf("pid=%d\r\n",pid);
   
   //再输出一下,当前进程父进程PID号
   ppid=getppid();
   printf("ppid=%d\r\n",ppid);
   

   return 0;
}

示例2:如何获取对应进程的PID号,输出当前进程父进程的PID号,并且杀死对应的进程

现象:

代码:

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

int main(int argc,char *argv[])
{
   //先输出一下,当前进程的PID号
   pid_t pid,ppid;
   pid=getpid();
   printf("pid=%d\r\n",pid);
   
   //再输出一下,当前进程父进程PID号
   ppid=getppid();
   printf("ppid=%d\r\n",ppid);
   
   
   while(1);

   return 0;
}

2、运行进程

system

函数功能:

        接收一个命令行指令或者可执行程序,然后执行

头文件:

        #include <stdlib.h>

函数原型:

        int system(const char *string);

函数参数:

        const char *string:表示待执行的命令或者可执行程序

函数返回值:

        成功 返回0

        失败 返回-1

 


示例:利用system()函数,验证一下它的现象。

现象:

代码:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
   //先输出一下,当前进程的PID号
   pid_t pid,ppid;
   pid=getpid();
   printf("pid=%d\r\n",pid);
   
   //再输出一下,当前进程父进程PID号
   ppid=getppid();
   printf("ppid=%d\r\n",ppid);
   
   //调用system()函数
   int res = system("ls -l");
   printf("res=%d",res);

   return 0;
}

3、替换进程

execl

函数功能:

        用参数指定的进程替换当前的进程,并且执行完替换进程后,原来的进程不再继续执行

头文件:

        #include <unistd.h>

函数原型:

        int execl(const char *path,char *arg....)

函数参数:

        const char *path:表示所需要替换执行程序的路径,一般情况当前路径下,也要加上可执行程序的名字

        char *arg:表示执行当前程序的路径argv[0],还需要加上NULL

函数返回值:

        成功 不会返回,也就说,execl()之后的代码不会执行。

        失败 返回-1


示例:用参数指定的进程替换当前的进程 ,并且PID号不发生变化的

现象:

代码: 

进程A

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


int main(int argc,char *argv[])
{
   //输出一下当前进程的pid号,ppid号
   printf("pid=%d\r\n",getpid());
   printf("ppid=%d\r\n",getppid());
   
   //调用一下对应的替换进程
   execl(argv[1],argv[0],NULL);
   
   //如果execl函数执行成功,后面的代码是无法执行的
   printf("hello\r\n");
   return 0;

}

进程B

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
   //先输出一下,当前进程的PID号
   pid_t pid,ppid;
   pid=getpid();
   printf("pid=%d\r\n",pid);
   
   //再输出一下,当前进程父进程PID号
   ppid=getppid();
   printf("ppid=%d\r\n",ppid);
   
   printf("123\r\n");

   return 0;
}

 4、创建进程

fork

函数功能:

        用于从当前进程(称为父进程)创建一个新的进程(称为子进程)。调用 fork() 后,会有两个几乎完全相同的进程,唯一的区别在于它们的返回值和进程 ID(PID)。

头文件:

        #include <unistd.h>

函数原型:

        pid_t fork(void);

函数参数:

函数返回值:

        成功  在父进程中,返回新创建的子进程的PID

                 在子进程中,返回0

        失败  返回-1

注意:先父后子的执行顺序


示例1:利用一下对应的fork()函数,观察一下对应的现象

现象:

代码:

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

int main(int argc,char *argv[])
{
	  int i = 0;
   i++;
   printf("parent i:%d\r\n",i);
   //创建子进程
   fork();
   
   printf("i:%d\r\n",i);
   printf("hello\r\n");
   
   while(1);
   return 0;
}

示例2:利用fork函数,实现父子进程完成不同的功能

现象:

代码:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
		int res;
		int i = 0;
  	i++;
  	printf("parent i:%d\r\n",i);
    //创建子进程
    res = fork();
    if(res == 0)//子进程
    {	
		 printf("son i:%d\r\n",i);
    }
    else//父进程
    {
   	     sleep(1);
		 printf("parent:hello\r\n");
    }
   
   
   return 0;
}

示例3:利用父子进程实现交替报数 ---父进程—报2  子进程—报1   利用sleep(1)

                思路:1:父进程--负责报偶数   2:子进程--负责报奇数

现象:

代码:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
	int res;
    //创建子进程
    res = fork();
    if(res == 0)//子进程
    {	
		int i = 1;
		while(1)
		{
			printf("son i:%d\r\n",i);
			i+=2;
			sleep(2);
		}
    }
    else//父进程
    {
   	int i = 2;
		while(1)
		{
			sleep(1);
			printf("parent i:%d\r\n",i);
			i+=2;
			sleep(1);
		}
		
    }
   
   
   return 0;
}	

改进对应的报数,采用的方法:判断语句---if(i%2 ==0) else ---sleep(1)

现象一样

代码:

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

int main(int argc,char *argv[])
{
   int ret;
   int i=1;
   //利用fork函数,创建对应的子进程
   ret=fork();
   
   if(ret == 0)
   {
      //子进程事情
	  	while(1)
	  	{
	     if((i%2) != 1)
	     {
	         printf("child i=%d\r\n",i);
	         sleep(1);
	     }else
	     {
	         sleep(1); 
	     }
	      //将对应的值加1
	      i++;
		  
      }
   
   }
else
   {
      //父进程事情
	 	while(1)
	 	{
	     if((i%2) == 1)
	     {
	          printf("parent i=%d\r\n",i);
	          sleep(1);
	     }
	     else
	     {
	          sleep(1);
	     }
	     
	     //将对应的值加1
	     i++;
	 
	     
 	 }
   
   }
   
   
   return 0;
}

示例4:利用三个进程实现交替报数  

分析思路:主函数里面fork()

  在创建对应的子进程的时候,需要将它们放到对应的判断语句里面(放到父进程里面或者放到子进程里面)

多进程的框架(方案一):

   int main

   {

         //创建子进程

         ret=fork();

         if(ret == 0)

         {

             //子进程里面再创建子进程

              ret1=fork();

              if(ret1 == 0)

              {

                  //孙子进程做的事情

              }

              else

             {

                //子进程做的事情

             }

        }

        else

        {

              //父进程完成的事情

        }

}

现象:

代码:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
	int res;
	
	//创建子进程
	res = fork();
	if(res == 0)//子进程
	{	
		//创建子进程
		res = fork();
		if(res == 0)//孙进程
		{
			int i = 3;
			while(1)
			{
				sleep(2);
				printf("sun i:%d\r\n",i);
				i+=3;
				sleep(1);
			}
		
	
		}
		else//子进程
		{
			int i = 2;
			while(1)
			{
				sleep(1);
				printf("son i:%d\r\n",i);
				i+=3;
				sleep(2);
			}
	
		}
			
	}
   else
   {	
	
		int i = 1;
		while(1)
		{
			printf("parent i:%d\r\n",i);
			i+=3;
			sleep(3);
		}
		
   }

	return 0;
}

多进程的框架(方案二):

int main

{

         //创建子进程

         ret=fork();

         if(ret == 0)

          {

              //子进程1完成的事情

          }

        else

        {

            //父进程完成的事情

           res=fork();

           if(ret2 == 0)

            {

                //子进程2—完成的事情

            }

            else

           {

                //父进程完成的事情

           }

        }

}

现象:

代码:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
	int res,ret;
  	
   //创建子进程
   res = fork();
   if(res == 0)//子进程1
   {	
		int i = 2;
		while(1)
		{	
			sleep(1);
			printf("son1 i:%d\r\n",i);
			i+=3;
			sleep(2);
		}
		
   }
   else
   {	
		//创建子进程
		ret = fork();
		if(ret == 0)//子进程2
		{
			int i = 3; 
			while(1)
			{	
				sleep(2);
				printf("son2 i:%d\r\n",i);
				i+=3;
				sleep(1);
			}
		}
		else//父进程
		{
			int i = 1;
			while(1)
			{	
				printf("parent i:%d\r\n",i);
				i+=3;
				sleep(3);
			}
		}
		
   }
   
   return 0;
}
vfork

功能:

        创建对应的子进程,直到子进程运行结束后才执行父进程

头文件:

        #include <sys/types.h>

        #include <unistd.h>

函数原型:

        pid_t vfork(void);

函数参数:

函数返回值:

        在父进程中,返回新创建的子进程的PID

        在子进程中,返回0

        失败,则返回-1

注意:新进程和父进程共享相同的地址空间


示例1:利用vfork,输出子进程的PID和PPID再加入变量,也输出父进程的PID和PPID再加入一个变量

现象:

代码:

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
	int ret;
	
	//输出PID号
	printf("parent pid=%d\r\n",getpid());
	ret = vfork();
	if(ret == 0)
	{
		sleep(1);
		//输出PID号
		printf("child pid=%d\r\n",getpid());
		
		exit(0);
	}
	else
	{
		printf("hello\r\n");
	}
	
	
}

fork和 vfork的异同

①:共同点,都可以创建一个子进程,并具有相同的返回值

②:不同点,fork()函数,父子进程的运行顺序可以调整,而vfork()函数的执行顺序不能调整,必须等待子进程运行结束。

③:不同点,fork()执行前,父子进程空间独立,而vfork执行前父子进程空间是公用的

示例2利用vfork函数,验证一下空间是否是公用的。

现象:

代码:

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
	int ret;
	int i = 1;
	
	//输出PID号
	printf("parent pid=%d\r\n",getpid());
	ret = vfork();
	if(ret == 0)
	{
		i++;
		sleep(1);
		//输出PID号
		printf("child pid=%d\r\n",getpid());
		printf("child i=%d\r\n",i);
		i++;
		exit(0);
	}
	else
	{
		printf("hello\r\n");
		printf("parent i=%d\r\n",i);
	}
	
	
}

 5、摧毁进程

exit

功能:
    正常结束进程,并把参数返回给父进程,会将进程缓冲区的内容全部写回并关闭文件
头文件:
    #include <stdlib.h>
函数原型:
    void exit(int status);
函数参数:
    int status:用于返回数据给父进程,一般填写为0(不传递参数的时候)  
函数返回值:无

 

_exit

功能:
    正常终止目前正在执行的进程,但是不会将缓存区的内容写回
头文件:
    #include <unistd.h>
函数原型:
    void _exit(int status);
函数参数:
    int status:用于返回数据给父进程,一般填写为0(不传递参数的时候)  
函数返回值:无


示例:利用exit函数,观察对应的现象。

现象:

代码:

#include <stdlib.h>
int main(int argc,char *argv[])
{
	printf("hello");
	
	sleep(1);
	
	exit(0);
	
	return 0;
}

exit()和_exit()区别

exit和_exit函数,都可以结束对应的进程

exit函数,可以将对应缓存里面的内容写回

_exit函数,不能将对应缓存里面的内容写回

6、等待进程 

wait

功能:

        父进程为了等待对应的子进程运行结束

头文件:

        #include <sys/types.h>

        #include <sys/wait.h>

函数原型:

        pid_t wait(int *status);

函数参数:

         int *status就是子进程退出的时候,传递过来的数据->NULL

函数返回值:

        成功 对应退出子进程的PID号  

        失败 -1

补充

WIFEXITED(status) --->主要是为了判断对应的子进程,是否正常退出(正常退出返回1)

WEXITSTATUS(status) --->主要是为了获取到子进程退出的时候,传递过来的参数


示例1:利用wait函数,等待对应的子进程运行结束

现象:

代码:

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc,char *argv[])
{
	int ret,sta;
	int i = 1;
	
	//输出PID号
	printf("parent pid=%d\r\n",getpid());
	ret = fork();
	if(ret == 0)
	{
		i++;
		sleep(1);
		//输出PID号
		printf("child pid=%d\r\n",getpid());
		printf("child i=%d\r\n",i);
		i++;
		exit(0);
	}
	else
	{
		//等待子进程运行完
		pid_t PID = wait(NULL);
		printf("PID :%d\r\n",PID);
		printf("parent i=%d\r\n",i);
	}

示例2:利用exit函数参数的传递,传递给对应的父进程,输出观察是否一致。

现象:

代码:

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc,char *argv[])
{
	int ret,sta;
	int i = 1;
	
	//输出PID号
	printf("parent pid=%d\r\n",getpid());
	ret = fork();
	if(ret == 0)//子进程
	{
		i++;
		sleep(1);
		//输出PID号
		printf("child pid=%d\r\n",getpid());
		printf("child i=%d\r\n",i);
		i++;
		
		exit(123);
	}
	else//父进程
	{
		//等待子进程运行完,并且接收子进程返回的PID
		pid_t PID = wait(&sta);
		printf("PID :%d\r\n",PID);
		printf("parent i=%d\r\n",i);
		
		//验证exti退出是否成功
		int p = WIFEXITED(sta);
		if(p == 1)
		printf("EXIT success\r\n");
		//验证exti返回的参数
		int val = WEXITSTATUS(sta);
		printf("val=%d\r\n",val);
		
		//退出函数
	    exit(0);
	}

waitpid

功能:
    等待对应的子进程结束---可以指定对应的PID号
头文件:
    #include <sys/types.h>
    #include <sys/wait.h>
函数原型:
    pid_t waitpid(pid_t pid,int *status,int options);
函数参数:
pid_t pid:指定等待的进程的PID号—子进程的PID号
                  如果pid = -1 等待任何子进程结束  相当于wait()
                  如果pid > 0  对应等待相应子进程结束-->在父进程中,返回新创建的子进程的PID
int *status:就是子进程退出的时候,传递过来的数据
int options:表示选项参数,一般设置为0
函数返回值:
    成功 对应退出子进程的PID号  
    失败 -1

 


示例1:调用一下waitpid函数,观察一下对应的现象

现象:

 

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc,char *argv[])
{
	int ret,sta;
	int i = 1;
	
	//输出PID号
	printf("parent pid=%d\r\n",getpid());
	ret = fork();
	if(ret == 0)//子进程
	{
		i++;
		sleep(1);
		//输出PID号
		printf("child pid=%d\r\n",getpid());
		printf("child i=%d\r\n",i);
		i++;
		
		exit(123);
	}
	else//父进程
	{
		//等待子进程运行完,并且接收子进程返回的PID
		pid_t PID = waitpid(-1,&sta,0);
		printf("PID :%d\r\n",PID);
		printf("parent i=%d\r\n",i);
		
		//验证exti退出是否成功
		int p = WIFEXITED(sta);
		if(p == 1)
		printf("EXIT success\r\n");
		//验证exti返回的参数
		int val = WEXITSTATUS(sta);
		printf("val=%d\r\n",val);
		
		//退出函数
	    exit(0);
	}
	
	
}

 

示例2:改进之后,waitpid函数,观察指定进程退出的参数信息

现象:

代码:

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc,char *argv[])
{
	int ret,sta;
	int i = 1;
	
	//输出PID号
	printf("parent pid=%d\r\n",getpid());
	ret = fork();
	if(ret == 0)//子进程
	{
		i++;
		sleep(1);
		//输出PID号
		printf("child pid=%d\r\n",getpid());
		printf("child i=%d\r\n",i);
		i++;
		
		exit(123);
	}
	else//父进程
	{
		
		//在父进程中,返回新创建的子进程的PID
		printf("childpid=%d\r\n",ret);
		
		//等待子进程运行完,并且接收子进程返回的PID
		pid_t PID = waitpid(ret,&sta,0);
		printf("PID :%d\r\n",PID);
		printf("parent i=%d\r\n",i);
		
		//验证exti退出是否成功
		int p = WIFEXITED(sta);
		if(p == 1)
		printf("EXIT success\r\n");
		//验证exti返回的参数
		int val = WEXITSTATUS(sta);
		printf("val=%d\r\n",val);
		
		//退出函数
	    exit(0);
	}

}

wait()和waitpid()区别

相同点:都可以等待对应的子进程运行结束,可以获取对应的子进程退出的时候参数信息

不同点:waitpid函数可以等待指定子进程结束。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值