当前位置:   article > 正文

Linux_进程控制(创建进程,等待进程,进程终止)_linux进程控制函数

linux进程控制函数

1.创建进程

1.1 fork()函数初识

linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。

pid_t fork(void);
  • 1
//fork函数的头文件
#include <unistd.h> 
  • 1
  • 2
  • 返回值:创建成功给子进程返回0,父进程返回子进程id,出错返回-1
  • 使用 fork() 函数得到的子进程是父进程的一个复制品,它从父进程处继承了整个进程的地址空间:包括进程上下文(进程执行活动全过程的静态描述)、进程堆栈、打开的文件描述符、信号控制设定、进程优先级、进程组号等。子进程所独有的只有它的进程号,计时器等(只有小量信息)

进程调用fork,当控制转移到内核中的fork代码后,内核(OS)做:

  • 分配新的内存块和内核数据结构给子进程
  • 将父进程部分数据结构内容拷贝至子进程(fork创建子进程是以父进程为“模板”的)
  • 添加子进程到系统进程列表当中
  • fork返回,开始调度器调度

1.2 fork()创建进程代码示例

#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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

运行结果:
在这里插入图片描述

2.等待进程

2.1 进程等待概念

之前说,子进程退出,父进程如果不管不顾,就可能造成“僵尸进程”的问题,进而造成内存泄露,进而,进程一旦变成僵尸状态,杀人不眨眼的”kill-9”也无能为力,因为谁也没有办法杀死一个已经死去的进程,最后,父进程派给子进程的任务完成的如何,我们需要知道,如子进程运行完成,结果是还是不对,或者是否正常退出,我们需要知道。

  • 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息

2.2进程等待必要性

  • 子进程退出,父进程如果不处理,就可能造成僵尸进程的问题,从而导致内存泄漏。
  • 另外,进程一旦变成僵尸状态,那就刀枪不入,kill -9 也没办法杀死,因为谁也没有办法杀死一个已经死去的进程。
  • 最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出。
  • 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息。

2.3 进程等待方法

2.3.1 wait

pid_t wait(int *status)
  • 1
//头文件
#include<sys/wait.h>
#include<sys/type.h>
  • 1
  • 2
  • 3

返回值:

  • 成功:返回被等待进程(子进程)pid
  • 失败:返回-1
  • 输出型参数,获取子进程退出状态,不关心则可以设置为NULL

wait进程等待代码示例:
在这里插入图片描述

2.3.2waitpid

pid_t waitpid(pid_t pid,int *status,int options)
  • 1
//头文件: 
#include<sys/type.h>
#include<sys/wait.h>
  • 1
  • 2
  • 3

返回值:

  • 当正常返回的时候waitpid返回收集到的子进程的进程ID
  • 如果设置了选项WNOHANG,而调用中waitpid发现已经没有已经可以退出的子进程可收集,则返回0
  • 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在

参数说明:

  • 1.pid
    pid=-1,等待任一个子进程,与wait等效
    pid>0,等待其进程ID与pid相等的子进程

  • 2.status
    WIFEXITED(status): 若为正常终止子进程返回的状态,则为真(此参数是查看进程是否是正常退出)
    WEXITSTATUS(status): 若WEXITSTATUS非零,提取子进程退
    出码(查看进程的退出码)

  • 3.options
    WNOHANG:若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待,若正常结束,则返回该子进程的ID

  • 如果子进程已经退出,调用wait/waitpid会立即返回,并且释放资源,获得子进程退出信息
  • 如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞
  • 如果不存在该子进程,则立即出错返回
  • 子进程的退出是个异步事件(子进程可以在父进程运行的任何时刻终止)

waitpid进程等待代码示例:
在这里插入图片描述

2.4 获取子进程status

wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充
如果传递NULL,表示不关心子进程的退出状态信息,否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程

status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特位):
在这里插入图片描述
正常终止:

status & 0x7f, 如果是0的话,那就表示正常退出
0xff是0111111,status按位与0x7f,表示取出status的低7位

当正常终止的时候,status高8位就是子进程的退出码
status & 0xff,就是取出status的高8位

异常终止:

status & 0x7f, 不为0的话,那就表示异常退出

代码示例:
在这里插入图片描述

3.进程终止

3.1 进程退出的情况

  1. 代码跑完了,结果正确
  2. 代码跑完了,结果不正确
  3. 代码没跑完,程序异常终止

正常终止(可以通过 echo $? 查看最近一次进程退出码):

  1. 从main返回
  2. 调用exit / _exit

异常退出:

  1. ctrl + c,信号终止

3.2 main函数用return退出进程

main函数退出时, return的数字就是程序的退出码。 问题: 为什么main的return一般会写成0?
因为0在函数设计中,一般代表正确。非0代表错误。

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
  
using namespace std;
 
int main()
{
	cout << "hello" << endl;
    return 0;//退出码为0                                                                     
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

3.3 exit函数退出

void exit(int status);
  • 1
//头文件
#include <unistd.h>
  • 1
  • 2
  • exit:终止整个进程,任何地方都会调用,都会终止
  • return:终止函数。main函数return 代表进程退出

3.4 _exit函数

void _exit(int status);
  • 1
//头文件
#include <unistd.h>
  • 1
  • 2
  • 参数:status 定义了进程的终止状态,父进程通过wait来获取该值
  • 说明:虽然status是int,但是仅有低8位可以被父进程所用。所以_exit(-1)时,在终端执行$?发现返回值是255。

代码示例:

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

 using namespace std;
  
int exe()
{
   _exit(12);
   return 12;
 }
  
 int main()
 {
   cout << "you should running here!";                                                    
   sleep(5);//sleep5秒
   exit(13);                            
   return 0;//退出码为0                 
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

在这里插入图片描述

3.5 _exit()和exit()的区别

  1. 最大的区别是 exit()函数在调用 _exit() 系统调用之前要检查文件的打开情况,把文件缓冲区中的内容写回文件 (即刷新缓冲区), 然后将控制权交给内核 . _exit() 则是执行后立即返回给内核,而exit()要先执行一些清除操作
  2. 调用_exit函数时,会关闭进程所有的文件描述符,清理内存以及其他一些内核清理函数,但不会刷新缓冲区, exit函数是在_exit函数之上的一个封装,其会调用_exit,并在调用之前先刷新缓冲区。
  • 补充: exit()函数在调用exit系统之前要检查文件的打开情况,把文件缓冲区的内容写回文件。由于Linux的标准函数库中,由于内存中都有一片缓冲区. 每次读文件时,会连续的读出若干条记录,这样在下次读文件时就可以直接从内存的缓冲区读取. 同样,每次写文件的时候也仅仅是写入内存的缓冲区,等满足了一定的条件(如达到了一定数量或遇到特定字符等),再将缓冲区中的内容一次性写入文件。这种技术大大增加了文件读写的速度,但也给编程代来了一点儿麻烦。比如有一些数据,认为已经写入了文件,实际上因为没有满足特定的条件,它们还只是保存在缓冲区内,这时用_exit()函数直接将进程关闭,缓冲区的数据就会丢失。因此,要想保证数据的完整性,就一定要使用exit()函数
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/469526
推荐阅读
相关标签
  

闽ICP备14008679号