进程控制——进程创建、退出、回收

本文详细介绍了Linux系统中进程的创建过程,包括使用fork()函数创建进程,阐述了进程创建的步骤及原理。此外,讨论了进程的正常退出和异常退出情况,特别强调了如何通过wait()和waitpid()函数回收僵尸子进程,防止内存泄漏,确保系统资源的有效管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、进程创建

(一)进程创建过程

  进程创建的一般过程:
    (1)分配一个唯一的标识符,在内核中创建出task_struct
    (2)复制父进程的环境信息;
    (3)给新进程分配资源(栈、堆等);
    (4)拷贝父进程的地址空间内容;
    (5)将进程放入就绪队列;

(二)进程创建函数——fork()

  1、fork()函数

头文件:#include<unistd.h>
fork()函数:pid_t  fork(void);
返回值:
	父进程返回子进程的pid;
	子进程返回0;
	返回-1则出错;

  2、简单的创建进程例子
  (1)
在这里插入图片描述
在这里插入图片描述
  从结果中可以看出,fork()创建出来的子进程是将父进程的信息进行了拷贝,并且继续接着父进程执行下去;
  (2)
在这里插入图片描述
在这里插入图片描述

  从结果中我们可以得到,子进程返回0,父进程返回子进程的pid;

(三)进程创建原理

  从例(1)中可以看出,fork()创建出来的子进程实际上就是对父进程的一个拷贝;
在这里插入图片描述
  一般来说,fork()出的子进程的虚拟地址空间是拷贝的父进程的虚拟地址空间,所以无论是代码还是数据又或是环境变量等信息都是与父进程相同的;
  在映射到物理内存时,如果不对子进程的信息进行修改的话,是可以让子进程和父进程共享同一个物理内存中;如果需要对子进程信息进行修改的话,则会改变其物理内存的位置,我们称之为写时拷贝。写时拷贝会大大提高存储效率。

二、进程退出

  进程退出有两种退出:
    1、正常退出:exti()函数退出
    2、异常退出:子进程被杀死,从而变成僵尸子进程;

三、回收僵尸子进程

(一)简单回收僵尸子进程函数——wait

    当子进程非正常死亡时,为了避免内存泄漏,需要通过父进程将其回收,这里用到的函数就是wait函数;
  (1)头文件:#include<sys/wait.h>
  (2)函数:pid_t wait(int* status);
  (3)返回值:成功返回子进程pid;错误返回-1;
  (4)参数:
    status:获得子进程死亡信息,有以下几个参数:

			WIFEXITED:返回真,子进程正常退出;
			WEXITSTATUS:在WIFEXITTED为真的前提下,获取子进程的退出状态信息;
			WIFSINGALED:返回真说明子进程被信号打断;

  wait()函数使用例子:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#include<errno.h>
#include<string.h>

#define ERR_EXIT(msg) \
	do{ \
		printf("[%s][%d]   %s:  %s\n", __FILE__, __LINE__, msg, strerror(errno));  \
		exit(EXIT_FAILURE);  \
	} while(0)

int main()
{
	pid_t pid;

	if ((pid = fork()) == 0)
		ERR_EXIT("fork");
	
	if (pid == 0)
	{
		printf("child pid = %d\n", getpid());   //getpid()获取自己的进程pid
		sleep(3);
		exit(0);
	}
	else
	{
		int status;
		pid_t ret;
		
		if ((ret = wait(&status)) == -1)
			ERR_EXIT("wait");
		
		if (WIFEXITED(status))    //WIFEXITED返回真,子进程正常退出
		{
			printf("normal exit: %d\n", WEXITSTATUS(status));  //获取子进程退出状态信息
		}
		else if(WIFSIGNALED(status))   //如果为真,子进程被信号打断
		{
			printf("killed by kill cmd\n");
		}
		printf("ret = %d\n", ret);

		while(1)
		{
			printf(".");
			fflush(stdout);
			sleep(1);
		}
	}
	
	return 0;
}

(二)wait()函数进阶版——waitpid()函数

    wait()函数能够回收僵尸子进程,但是并不能指定回收,而且如果没有回收到就会一直阻塞;而waitpid()函数功能更加强大,它能够指定回收僵尸子进程,并且没有回收到可以不阻塞;
  (1)头文件:#include<sys/wait.h>
  (2)函数:pid_t waitpid(pid_t pid, int* status, int options);
  (3)返回值:成功返回子进程pid;错误返回-1;
  (4)参数:
    第一个参数pid,在这个参数中就可以控制想要回收的子进程:

			>0:明确指定要回收的子进程
			=0:回收和调用者进程在同一组的任何一个子进程
			=-1:回收自己的任何一个子进程
			<-1:|pid|进程组的任何一个子进程死亡,都能被回收

    第二个参数和wait中的参数一样,都是获取子进程的死亡信息;
    第三个参数options:该选项选择阻塞还是不阻塞

			WNOHANG:如果调用这个函数时,有子进程是僵尸状态,回收并返回,如果没有,立即返回而不会阻塞
### Linux 进程控制管理机制 #### 1. 进程的概念 在 Linux 中,进程个动态实体,表示正在执行中的程序。它不仅包含了程序的指令集合,还包括其运行时所需的数据以及上下文环境。更严格地说,进程是程序在个独立数据集上的执行过程[^1]。 #### 2. 进程创建 Linux 使用 `fork()` 系统调用来创建进程。`fork()` 的核心功能是复制当前进程(称为父进程),从而生成个新的子进程。尽管两个进程共享相同的代码段,但在内存中它们拥有完全独立的地址空间[^3]。这意味着即使父子进程看起来像是在运行同份代码,但实际上它们彼此隔离并独立运作。 当调用 `fork()` 后,返回值用于区分父进程和子进程:对于父进程而言,`fork()` 返回的是子进程 ID;而对于子进程,则返回零。这种设计使得开发者能够轻松编写逻辑来分别处理父、子进程的任务分配。 #### 3. 进程终止 任何进程最终都会进入终止状态。正常情况下,可以通过调用 `exit()` 或者 `_exit()` 函数显式结束进程的生命期。除此之外,如果某个进程接收到特定信号 (Signal),也可能被迫停止运行甚至销毁自己[^2]。 值得注意的是,“僵尸进程”的概念——即那些已经完成工作却未被清理掉残留信息的进程对象。这类现象发生的原因通常是由于父进程未能及时回收进程的状态报告而导致资源泄漏问题存在。只有等到操作系统成功获取到该类已故进程的所有必要细节(比如退出码等)之后,才能彻底释放相关联的切硬件软件资产[^4]。 #### 4. 进程间通信(IPC) 为了实现不同进程之间的协作需求,Linux 提供了多种方式来进行进程间通讯(Internel-Process Communication, IPC). 主要有如下几种方法: - **管道(pipe)** 和命名管道(named pipe): 实现单向或双向流式传输. - **消息队列(message queue)** : 存储结构化记录以便于多个消费者读取. - **共享内存(shared memory)** : 让若干个合作进程共同存取块给定大小的虚拟存储区域. - **套接口(socket)** : 支持网络环境下跨主机节点之间交换资料包. - **信号(signal)**: 发送异步通知事件至目标接收方. 以上每种技术都有各自的优缺点及适用场景,在实际开发过程中需根据具体业务要求选择合适的方案组合应用[^2]. ```c #include <stdio.h> #include <unistd.h> int main(){ pid_t pid; printf("Before fork\n"); pid = fork(); if(pid ==0){ // Child Process Code Block printf("Child process is running with PID %d \n", getpid()); } else{ // Parent Process Code Block wait(NULL); // Wait until child finishes execution before continuing further actions here... printf("Parent process has finished waiting for its offspring.\n"); } return 0; } ``` 上面展示了个简单的 C 编程例子演示如何利用 POSIX API 创建基本父子关系模型并通过标准输出查看两者行为差异情况。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值