一、进程的概念
1、程序和进程
程序:二进制文件,占用磁盘空间
进程:启动程序
所有的数据都在内存中
需要占用更多的系统资源
2、并发和并行
并发是在某个时间段内并发,比如在7点到8点回家、吃饭在这个时间段内,回家和吃饭并发
并行是某个时刻的并行,比如在7点10分,我吃饭的时候看着新闻,同时进行的
二、进程控制
fork函数,在进程执行到fork()函数的时候,会产生一个一模一样的子进程这里的一样是指用户区的数据是一样的。
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
int main(){
int a=0;
pid_t pid;
//产生一个子进程
pid=fork();
//子进程中的代码和父进程中的代码一模一样,从这里开始执行,之所以在这里开始执行,如果从上一步执行
//子进程会不断的创建孙子进程,无穷无尽。
//子进程中的变量的数据和父进程产生子进程时候的数据是一样的,比如父进程在产生子进程的时候a为0
//那么子进程中的数据变量a也为0.
if(pid==0){
printf("当前进程为子进程\n");
}
if(pid>0){
printf("当前进程为父进程\n");
}
if(pid<0){
printf("子进程创建失败\n");
}
return 0;
}
fork是叉子的意思,也就说一分为二,一个叉子为父进程,一个叉子为子进程
fork函数的返回值:
int fork(void);//由父进程和子进程返回有两个返回值
pid>0 父进程返回产生的子进程的ID,pid为子进程的值,大于0
pid=0子进程返回值为0,如果pid=0则代表当前进程为新产生的子进程,通过这个判断就可以让 子进程和父进程做不同的事情。
pid<0 如果pid的值<0 则表示进程创建失败
子进程创建成功之后,代码执行位置:
父进程执行到了哪里,子进程就从哪里执行
父子进程的执行顺序?
不确定,谁抢到cpu谁执行
如何区分父子进程?
通过fork函数返回值,如果大于0为父进程等于0为子进程小于0创建进程失败
这里好说明的两个函数:
getpid()返回当前进程的ID
getppid()返回父进程的ID
执行上面的程序可能会出现这种情况
一般情况下是这样的:
第一种现象出现的原因是什么呢?
shell进程不知道a.out创建了子进程
shell检测到父进程执行完毕只有,shell切换到了前台
父进程和子进程的关系是什么样的呢?
Linux每一个运行的程序(进程)操作系统都会为其分配一个0~4G的地址空间(虚拟地址空间)
首先看一下虚拟内存空间分布:
每个进程都有一个独立的地址空间。如果全局变量赋值0放到.data.不为0放到.bss,实际的处理是在物理内存中进行的。
进程的PCB进程控制块是在存储在内核区。
kill 的意思是发信号给某个进程。
进程相关的指令:
ps
ps aux | grep “xxx”
ps ajx | grep “xxx”
kill 向指定进程发送信号
查看信号 kill -l
杀死某个进程:kill -9(SIGKILL) pid
fork出来的子进程和父进程两个地址空间用户区数据完全相同
后续各个进程的地址空间中数据是完全独立的。
读时共享
写时复值
如上一个程序,如果只对a记性读操作则他们共享的是同一个物理区域。但是如果进行其它操作的话比如a++
会复制一份各自操作。这就是父子进程之间不能通过全局变量通信。
三、exec函数
学习fork()函数的时候就在想,fork()函数能生出子进程,那子进程有什么用呢?难道是做一模一样的工作吗?肯定不是,需要子进程去做其它的工作。
1、exec函数族:
让父子进程执行不相干的操作
能够替换进程地址空间的源代码.txt段
当前程序中调用另外一个应用程序
首先在exec之前需要fork
返回值:
如果函数执行成功,不返回。
如果执行失败,打印错误信息,退出当前进程
2、执行指定目录下的程序
int execl(const char * path,const char *arg,...);
//path:要执行程序的绝对路径
//变参arg:要执行的程序的需要的参数
//第一个arg:占位
//后边的arg:命令的参数
//参数写完之后:NULL
一般执行自己写的程序
四、进程回收
1、孤儿进程
父进程生子进程
父进程先死,子进程还活着,这个子进程就叫孤儿进程
孤儿进程被init进程两样,init进程就变为孤儿进程的父亲
为了释放子进程占用的系统资源
进程结束之后,能够释放用户占用进程
释放不了pcb,必须由子进程的父进程释放
2、僵尸进程
子进程死了,父进程还活着,但是父进程不去释放子进程的pcb,这个自己子进程就变成了僵尸进程。僵尸进程并没有活着,是一个死进程,只是pcb还没有释放
3、进程回收
wait–阻塞函数 //等待进程死亡,死一个进程,他就回收一个进程
pid_t wait(int *staus);
//返回值:
-1:回收失败,已经没有子进程,所有子进程都回收成功了
>0:回收是子进程对应的pid
//参数:
判断子进程是如何死的
正常退出
被某一个信号杀死了
调用一次只能回收一个子进程