C++学习记录 - 进程创建

本文介绍了C/C++中进程的创建与回收,包括fork()、exec函数族、孤儿和僵尸进程,重点讲解了wait函数和waitpid函数。此外,还详细讨论了进程间通信(IPC),涉及管道、有名管道、共享映射区(共享内存)和本地套接字的原理和使用。

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

1. 创建进程

c/c++中,使用fork()函数创建一个进程。

pid_t pid = fork();

子进程自fork()以后开始执行。

fork()函数返回值,为pid_t类型,父子进程依靠pid的不同进行区分。如果fork失败,pid为-1;如果fork成功的话,对于父进程来说,pid是一个正整数,对于子进程来说,pid为0。

子进程创建后,为了节省资源,父子进程之间的共享,遵循读时共享,写时复制的原则。

父子进程共享:1. 文件描述符。 2. mmap建立的映射区。注意,程序中的全局变量什么的,都是独立的,不共享。

fork以后,父进程先执行还是子进程先执行,取决于内核所使用的进程调度算法

2. exec函数族

进程当中去执行另外的程序可以使用exec函数族。

当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行,调用exec并不会创建新的进程,所以调用exec前后该进程的id并未改变。

2.1 execlp

2.2 execl

3. 回收子进程

孤儿进程:父进程比子进程先结束,则该子进程为孤儿进程。此时,子进程的父进程成为init进程,称为init进程领养孤儿进程。但是,实际测试发现,在ubuntu16.04环境下,不是被init进程领养,而是被/sbin/upstart进程领养
在这里插入图片描述

僵尸进程:子进程进程终止,父进程尚未回收,子进程残留资源(PCB)存放于内核中,变成僵尸进程。
使用kill命令无法终止僵尸进程,因为kill命令只能用来终止进程,而僵尸进程已经终止了。

3.1 wait函数

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

pid_t waitpid(pid_t pid, int *status, int options);
  • 阻塞等待子进程退出
  • 回收子进程残留资源
  • 获取子进程结束状态(退出原因)

一个wait函数只能回收一个子进程,如果有多少子进程的话,谁先结束,就回收谁。

3.2 waitpid函数

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

pid_t waitpid(pid_t pid, int *status, int options);

功能上包含了wait函数。

4. 进程间通信(IPC)

  • 管道(使用最简单)
  • 信号(开销最小)
  • 共享映射区(也叫共享内存)(无血缘关系)
  • 本地套接字(最稳定)

4.1 管道(PIPE)

  • 管道是一个伪文件,是内核中的一块缓冲区(默认4k,可以通过ulimit -a查看)。

  • 管道一端读,一端写。

  • 管道是半双工通信(单向交替传输)。

  • 管道中的数据,读出了就没了,管道不进行保存,也就是说不能反复读取。

  • 管道只能在有血缘关系的进程间使用。

关键函数是pipe()

#include <unistd.h>

int pipe(int pipefd[2]);

// fd[0] 读端。
// fd[1] 写端。

4.2 有名管道(FIFO)

4.3 共享映射区(共享内存)

#include <sys/mman.h>

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

有血缘关系的进程间通信,测试代码:

#include <iostream>
#include <sys/types.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <error.h>
#include <stdio.h>
#include <sys/wait.h>
#include <string.h>

#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>


using std::cout;
using std::cin;
using std::endl;

int var = 100;

int main(int argc, char const *argv[])
{   

	
	int *p;
	int len = 4;  // 建立映射区的大小

	
	p = (int *)mmap(nullptr, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
	
	if(p == MAP_FAILED){  // 注意  不是 p == -1
		cout << "mmap error" << endl;
		return -1;
	}
	
	pid_t pid = fork();
	if(pid == 0){
		*p = 2000;
		var = 1000;
		printf("child,*p = %d, var = %d\n", *p, var);

	}else{
	
		// 父进程
		sleep(1);
		printf("parent,*p = %d, var = %d\n", *p, var);   // 这里的var输出100,因为父子进程间不共享全局变量
		wait(nullptr);
		
		
		int ret = munmap(p, len);
		if(ret == -1){
			cout << "munmap error" << endl;
			return -1;
		}
	
	}
	

	
    return 0;
}
  • MAP_SHARED 共享映射,父子进程共享映射区。
  • MAP_PRIVATE 私有映射,父子进程各自独占映射区。

4.4 本地套接字

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值