基本概念
进程和程序
程序是一个剧本,进程是演绎成戏
程序
程序:死的,只占用磁盘空间
进程
运行:活的,运行起来的程序,占用内存、CPU等系统资源
并发
单核的情况下,分时复用
单道程序设计
必须一个进程执行完
多道程序设计
有时钟中断控制并发
虚拟内存系统
进程 = 内核空间(PCB之类的) + 用户空间(数据段之类的)
虚地址 = 虚页号 + 页内地址
用户空间的相同虚拟地址映射到不同物理区域,虚页号不同。
内核空间的相同虚拟地址映射到相同物理区域,估计虚页号相同。因为人家叫内核空间,你又只有一个内核,当然共享一个区域。
这样做的好处是,你买个内存条也没有分块,但是你可以人为规定一下:
- 相同区域可以相互访问,即进程之间可以通信
- 使不同区域不能互相访问,即可以通信,但你不能随便访问我的数据,也可以依此实现权限控制。
fork函数
原理
相当于某个时间点出现了相同的平行时间线
- 使用fork之前只有father在运行
- fork之后相当于复制了一份完整的父进程
- 然后父进程执行fork之后的剩余部分。
- 子进程不执行fork之前的部分,也并发执行fork之后的剩余的部分。
fork
pid_t fork()
返回值:
- 成功:在子进程里的返回值是0,在父进程里的返回值是子进程pid
- 失败:-1
getpid()
获得自身的pid
getppid()
获得父进程的pid
示例
如果并发执行的过程中,父进程先关闭,那么子进程变成孤儿进程,其父进程号会有特殊值
创建一个子进程
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(){
//父进程1
printf("before fork-1-\n");
printf("before fork-2-\n");
printf("before fork-3-\n");
printf("before fork-4-\n");
//fork
pid_t pid=fork();
if(pid==-1){ //失败
perror("fork error");
exit(1);
}else if(pid==0){ //如果是子进程
printf("--child is created, pid=%d, parent-pid=%d\n",getpid(),getppid());
}else if(pid>0){ //如果是父进程
printf("---parent process: my child is %d, my pid:%d, my parent pid:%d\n",pid,getpid(),getppid());
}
printf("=====end of file\n");
return 0;
}
循环创建子进程
//你子进程里也会继续执行未执行完的循环,你生,你的儿子也会生
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(){
int i;
for(i=0;i<5;i++){
pid_t pid=fork();
if(pid==-1){
perror("fork error");
exit(1);
}else if(pid==0){
printf("--child is created, pid=%d, parent-pid=%d\n",getpid(),getppid());
}else if(pid>0){
printf("---parent process: my child is %d, my pid:%d, my parent pid:%d\n",pid,getpid(),getppid());
}
}
printf("=====end of file\n");
}
指定创建N个子进程
在循环创建子进程的基础上,不允许子进程继续创建
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(){
int i;
for(i=0;i<5;i++){
pid_t pid=fork();
if(pid==-1){
perror("fork error");
exit(1);
}else if(pid==0){
break;
}
}
if(i==5){ //自然结束,是父亲进程
sleep(i); //睡眠,相当于阻塞,保证并发时依然最后结束
printf("父进程结束");
}else{
sleep(i); //保证顺序结束
printf("这是第%d个子进程\n",i);
}
}
进程共享
刚fork时
父子进程不同:
进程id,fork返回值,各自的父进程,进程创建创建时间,闹钟,未决信号集合
fork之后
父子进程之间遵循 读时共享写时复制 的原则。
虽然说上面fork原理说的子进程完全拷贝一份父进程,但是你总不能干什么都把人家的用户空间和内核空间都复制一份吧,这样进程一多,内存就爆了。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//父子进程不共享全局变量
//读时共享写时复制
int var =100;
int main(){
pid_t pid;
pid = fork();
if(pid==-1){
perror("fork error");
exit(1);
}else if(pid>0){
//父进程,只进行了读,所以一直用的是共享的那一份
sleep(1);
printf("parent, var = %d\n", var);
printf("I'am parent pid = %d, getppid = %d\n",getpid(),getppid());
}else if(pid==0){
//子进程进行了写,所以拷贝了一份自己用
var=200;
printf("child, var=%d\n",var);
printf("I'am child pid= %d, ppid = %d\n",getpid(),getppid());
}
}