进程:
程序:死的。只占磁盘空间 --剧本
进程:活得。运行起来的程序。 占用内存、cup等系统资源 ---戏
虚拟内存物理内存映射
PCB进程控制块:struct task struct 结构体
进程id
文件描述符表
进程状态: 初始态 、就绪态、 运行太、挂起态、终止态
进程工作目录位置
umask 掩码
信号相关信息资源
用户id组id
fork函数:
fork之后父进程子进程执行顺序不确定,取决于内核所使用的调度算法
pid_t fork(void)
创建子进程。父子进程各自返回。父进程返回子进程id 子进程返回0
getpid() getppid()
//测试fork demo
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
int main(int argc ,char* argv[]){
printf("before fork -1-\n");
printf("before fork -1-\n");
printf("before fork -1-\n");
printf("before fork -1-\n");
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){
sleep(1);
printf("---parent process: my child is %d\n",pid);
}
printf("========end of file\n");
return 0;
}
输出结果:
before fork -1-
before fork -1-
before fork -1-
before fork -1-
--child is created,pid=4733,parent pid=4732
========end of file
---parent process: my child is 4733
========end of file
循环创建N个子进程模型。 每个子进程标识自己的身份
//循环创建N个子进程模型。 每个子进程标识自己的身份
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
int main(int argc ,char* argv[]){
int i;
for(i=0;i<5;++i){
if(fork()==0)
break;
}
if(5==i){
sleep(5);
printf("i'm parent\n");
}else{
sleep(i);
printf("i'm %dth child\n",i+1);
}
return 0;
}
输出:
i'm 1th child
i'm 2th child
i'm 3th child
i'm 4th child
i'm 5th child
i'm parent
父子进程相同:
刚fork后 (几乎0-3G用户空间)
data段、text段、堆、栈、环境变量、全局变量、宿主目录位置、进程工作目录位置、信号处理方式
父子进程不同:
进程id、返回值、各自的父进程、进程创建时间、闹钟、未决信号集
注意:似乎 ,子进程复制了父进程 0-3G 用户空间的 内容,以及父进程的 PCB ,但pid 不同。真的没fork一个子进程都要
将父进程 0-3G地址空间完全拷贝一份,然后映射至物理内存吗?
当然不是! 父子进程间遵循 读时共享写时复制 的原则。这样设计,无论子进程执行父进程逻辑还是执行自己的逻辑都能节省内存开销。
父子进程共享:
读时共享,写时复制
1.文件描述符 2.mmap映射区
exec函数族:
让父子进程执行不相干的操作
能够替换进程地址空间中的源代码、txt段
当前程序中调用另外一个应用程序
exec之前 要fork
返回值:
如果成功,不返回。
失败 打印错误信息 ,推出当前进程
execl 函数:
int execl(const char* path,const char* arg,...);
参数:
path:要执行的程序的绝对路径
变惨 arg:要执行的程序的需要的参数
第一 arg:占位(写啥都行 ,一般写替换的命令名称)
后边的arg:命令的参数
参数写完之后:NULL
一般执行自己写的程序
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
int main(int argc,char* argv[]){
//父进程数数
for(int i=0;i<5;++i){
printf("parent i=%d\n",i);
}
pid_t pid = fork();
//让子进程执行ls 命令
if(pid==0){
//ls程序使用子进程的地址空间
execl("/bin/ls","ls",NULL);
}
for(int i=0;i<3;++i){
printf("-----------\n");
}
return 0;
}
输出:parent i=0
parent i=1
parent i=2
parent i=3
parent i=4
-----------
-----------
-----------ls 结果
execlp函数:
执行PATH环境变量能够搜索到的程序
int execlp(const char* file,const char* arg,...);
参数:
file:执行命令的名字
变惨 arg:要执行的程序的需要的参数
第一 arg:占位(写啥都行 ,一般写替换的命令名称)
后边的arg:命令的参数
参数写完之后:NULL
执行系统自带的程序
execlp执行自定义的程序:file参数绝对路径
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
int main(int argc,char* argv[]){
//父进程数数
for(int i=0;i<5;++i){
printf("parent i=%d\n",i);
}
pid_t pid = fork();
//让子进程执行ps命令
if(pid==0){
//ps程序使用子进程的地址空间
execlp("ps","ps","-aux",NULL);
perror("execlp error");
exit(1);
}
for(int i=0;i<3;++i){
printf("-----------\n");
}
return 0;
}
结果:
parent i=0
parent i=1
parent i=2
parent i=3
parent i=4
-----------
-----------
-----------
ps aux 结果
进程回收:
1.孤儿进程:
爹生孩子
爹先死,孩子还活着,孩子叫孤儿进程
孩子被 init 进程(1 号进程)领养,init进程变为孤儿进程的父亲
为了释放子进程占用的系统资源
进程结束之后 能够释放用户区空间
无法释放PCB 必须由父进程释放
2.僵尸进程:
孩子死了,爹还活着,爹不去释放子进程的 PCB,孩子就变成了僵尸进程
是一个已经死掉的进程
3.进程回收:
wait 阻塞函数 调用一次只能回收一个子进程资源
pid_t wait(int* status);
函数作用:
1.阻塞并等待子进程退出
2.回收子进程残留资源
3.获取子进程结束状态(退出原因)
返回值:
成功:返回清理掉子进程的pid
失败:-1(没有子进程)
参数:子进程退出状态 ---传出参数
1.WIFEXITED(status):为非0 进程正常结束
WEXITSTATUS(status):
如果宏为真 使用此宏 获取进程退出状态(exit/return 的参数)
2.WIFSIGNALED(status):为非0 进程异常终止
WTERMSIG(status):
如果宏为真 ,使用此宏 取得使进程终止的那个信号的编号
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/wait.h>
int main(int argc,char* argv[]){
pid_t pid = fork();
if(pid>0){
printf("parent process pid = %d,ppid =%d\n",getpid(),getppid());
//回收子进程
int status;
pid_t pid= wait(&status);
//判断是否正常退出
if(WIFEXITED(status)){
printf("exit value:%d\n",WEXITSTATUS(status));
}
//是否被信号杀死
if(WIFSIGNALED(status)){
printf("exit by signal:%d\n",WTERMSIG(status));
}
printf("died child=%d\n",pid);
}else if(pid==0){
//测试被信号杀死
//while(1){
// sleep(1);
// printf("child process pid=%d,ppid=%d\n",getpid(),getppid());
// }
printf("child process pid=%d,ppid=%d\n",getpid(),getppid());
}
//return 0;
exit(12);
}
1.测试父子进程之间是否共享文件描述符
打开一个文件 -》fork -》父进程 write -》子进程 read
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
int main(int argc,char* argv[]){
int fd = open("temp",O_RDWR|O_CREAT,0664);
if(fd==-1){
perror("open error");
exit(1);
}
pid_t pid=fork();
if(pid==-1){
perror("fork error");
exit(1);
}
if(pid>0){
char* p = "我想她了!!!!";
write(fd,p,strlen(p)+1);
close(fd);
}else if(pid==0){
//睡 保证父进程已经完成写操作
sleep(1);
char buf[1024];
lseek(fd,0,SEEK_SET);
int len = read(fd,buf,sizeof(buf));
printf("%s\n",buf);
close(fd);
}
return 0;
}
2.父进程fork 三个子进程
1.调用 ps 命令 2.调用自定义程序 3 调用出现段错误的程序
父进程回收三个子进程 并且打印退出状态
#include<sys/wait.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
int main(int argc,char* argv[]){
int num = 3;
int i=0 ;
pid_t pid;
for(i=0;i<num;++i){
pid = fork();
if(pid==0){
break;
}
}
if(i==0){
execlp("ps","ps","-aux",NULL);
perror("execlp ps");
exit(1);
}else if(i==1){
execl("./test","test",NULL);
perror("execl test");
exit(1);
}else if(i==2){
execl("./error","error",NULL);
perror("execl error");
exit(1);
}
else if(i==num){
//父进程回收
int status;
pid_t wpid;
while( (wpid = waitpid(-1,&status,WNOHANG)) !=-1){
if(wpid==0){
continue;
}
printf("----child died pid = %d\n",wpid);
if(WIFEXITED(status)){
printf("return value %d\n",WEXITSTATUS(status));
}
else if(WIFSIGNALED(status)){
printf("died by signal %d\n",WTERMSIG(status));
}
}
}
return 0;
}
段错误:
1.访问了非法内存
2.不可写的区域进行写操作
3,栈溢出
pid_t waitpid(pid_t pid,int* status,int iptions)
参数:
pid:
pid》0 某个子进程的pid
pid == -1回收所有的子进程
循环回收
while((wpid=waitpid()!=-1))
pid==0 回收当前进程组所有子进程
pid《0 子进程pid 取反(加负号)
options:
0 :waitpid 阻塞
WNOHANG 非阻塞
返回值:
-1: 回收失败,没有子进程
》0: 被回收子进程的pid
如果非阻塞:=0 子进程处于运行状态