文章目录
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
私有映射,父子进程各自独占映射区。