- 资源分配和拥有的基本单位
- 进程CPU环境(栈,寄存器,页表,和文件句柄等)的保存以及新调度的进程CPU环境的设置
- 操作系统控制
- 内核栈
- CPU资源、内存资源、文件资源和句柄等
- 不同进程之间切换实现并发,各自占有CPU实现并行
- 切换虚拟地址空间,切换内核栈和硬件上下文,CPU高速缓存失效、页表切换,开销很大。
- 进程间通信需要借助操作系统
并行和并发程序:是编译好的二进制文件,存放在磁盘上,占用的是磁盘空间,是一个静态的概念。
进程:一个启动的程序,占用系统资源。如:物理内存,CPU,终端等;是一个动态的概念
PCB-进程控制块并行:多个CPU
并发:多个时间片
进程的状态 创建进程 fork函数(创建子进程)每个进程在内核中都有一个进程控制块(PCB)
进程ID,唯一。
进程的状态:就绪、运行、挂起、停止等状态。
CPU寄存器,哟用于**切换进程,**保留进程信息。
虚拟地址空间的信息
控制终端的信息
当前工作目录
umask掩码
文件描述符表,包含很多指向file结构体的指针
和信号相关的信息。
用户ID和组ID
会话(Session)和进程组
进程可以使用的资源上限ulimit -a
fork函数示例函数作用:创建子进程
谁先抢到时间片,谁先执行
//原型: pid_t fork(void); //函数参数 无 //返回值 //父进程返回子进程PID 子进程返回0
循环创建n个进程#include#include #include #include #include int main() { printf("before fork pid==[%d]n",getpid());//执行一次 //创建子进程 //pid_t fork(void) pid_t pid = fork(); if(pid<0){ perror("fork error"); return -1; } else if(pid>0){ //父进程执行 printf("father: pid==[%d],fpid==[%d]n",getpid(),getppid()); sleep(10);//确保父进程后执行完 } else if(pid==0){ //子进程执行 printf("chlid: pid==[%d],fpid==[%d]n",getpid(),getppid()); sleep(10);//确保父进程后执行完 } printf("after fork pid==[%d]n",getpid()); //执行二次 return 0; } 1、执行顺序
2、父进程先退出
子进程被[1]进程接受
父子进程不能共享全局变量函数示例:
#include#include #include int main() { int i = 0; for(i=0;i<3;i++){ //创建子进程 pid_t pid = fork(); if(pid<0){ perror("fork error"); return -1; } else if(pid>0){ printf("father: pid==[%d],fpid==[%d]n",getpid(),getppid()); //sleep(1);//确保父进程后执行完 } else if(pid==0){ printf("chlid: pid==[%d],fpid==[%d]n",getpid(),getppid()); break;//防止生成孙子进程 } } if(i==0){ //子进程1 printf("[%d]-----[%d] :childn",i,getpid()); } if(i==1){ //子进程2 printf("[%d]-----[%d] :childn",i,getpid()); } if(i==2){ //子进程3 printf("[%d]-----[%d] :childn",i,getpid()); } if(i==3){ //父进程 会打印三次 printf("[%d]-----[%d] :childn",i,getpid()); } //sleep(10); return 0; } 结果
father: pid==[76192],fpid==[75878] //i=0 father: pid==[76192],fpid==[75878] //i=1 father: pid==[76192],fpid==[75878] //i=2 [3]-----[76192] :child //父进程 chlid: pid==[76195],fpid==[1] //子进程 父进程早就死了 [2]-----[76195] :child chlid: pid==[76194],fpid==[1] [1]-----[76194] :child chlid: pid==[76193],fpid==[1] [0]-----[76193] :child
ps命令和kill命令
- 父子进程变量不共享,但指向同一个地址
- 读时共享,写时不共享
读写示意图
写时,会将物理内存映射到一个新的地方。
代码示例
#include#include #include #include #include int g_var = 99; int main() { int i = 0; for(i=0;i<3;i++){ //创建子进程 pid_t pid = fork(); if(pid<0){ perror("fork error"); return -1; } else if(pid>0) { printf("father: g_var==[%d]n",g_var); g_var++; printf("father: g_var==[%d]n",g_var); sleep(1);//确保父进程后执行完 printf("father: pid==[%d],fpid==[%d]n",getpid(),getppid()); printf("[%p]",&g_var);//地址相同 } else if(pid==0) { sleep(1); printf("chlid: pid==[%d],fpid==[%d]n",getpid(),getppid()); printf("child: g_var==[%d]n",g_var);//值不同 printf("[%p]",&g_var);//地址相同 break; } } //sleep(10); return 0; }
调用程序 exec函数族(拉起一个程序)查看进程
ps -aux | grep "xxx" //配合管道查找指定内容 ps -ajx | grep "xxx" -a:(all) 当前系统所有用户的进程 -u:查看进程所有者及其他一些信息 -x:显示没有控制终端的进程 -j:累出与作业相关的信息结束进程
//查看系统有哪些信号 kill -l //杀死某个线程 kill -9 pid
进程回收在一个子进程里拉起一个可执行程序(系统命令或应用程序)
execl函数(自己的程序)
一个进程里拉起一个可执行程序
//函数原型: int execl(const char *path,const char *arg, ...); //参数介绍: //path: 要执行程序的决定路径 + 名称 //变参arg: 要执行的程序的需要的参数 //arg: 占位,通常写应用程序的名字 //arg后面的: 命令的参数 //参数写完之后:NULL //返回值 //若是成功,则不返回,不会再执行exec函数后面的代码;若是失败,会执行execlp函数(系统命令)
//函数原型: int execlp(const char *file,const char *arg, ...); // 不带路径 // file:执行命令的名字,根据PATH环境变量来搜索该命令内部原理
- 代码段 .txt被替换
- 子进程pid,地址空间不变
execl函数代码示例
#includeint main(int argc;char *argv[]) { int i = 0; for(i=0; i printf("[%d]:[%s]n",i,argv[i]); } return 0; } #include#include #include #include #include int main() { //函数原型: //创建子进程 pid_t pid = fork(); if(pid<0){ perror("fork error"); return -1; } else if(pid>0){ printf("father: pid==[%d],fpid==[%d]n",getpid(),getppid()); } else if(pid==0){ printf("chlid: pid==[%d],fpid==[%d]n",getpid(),getppid()); //execl("/bin/ls","ls","-l",NULL); //execlp("ls","ls","-l",NULL); 搜索环境变量 execl("./test","test","hello","world","ni","hao",NULL); perror("execl error");//成功不会执行 } return 0; }
孤儿进程(不是一个错误)当一个进程退出之后,进程能够回收自己的用户区的资源,但不能回收内核空间的PCB资源,必须由它的父进程调用wait或者waitpid函数完成对子进程的回收,避免造成系统资源的浪费。
- 系统自动回收用户区资源
- 不能回收内核空间资源
- 必须通过父进程回收内核资源
僵尸进程(重点)1、若父进程死掉。
2、会被init进程领养,父进程pid变为1。会被init进程回收资源
wait函数子进程先退出,但是父进程没有回收子进程的资源,子进程为僵尸进程
无法用kill处理子进程
简单处理方法:
1、kill -9 强制删除父进程
2、init进程领养僵尸进程,最后由inti进程回收僵尸进程。
waitpid函数函数作用:
阻塞并等待子进程退出
回收子进程残留资源
获取子进程结束状态(退出原因)特点
- 阻塞 确保父进程后退出
- 父进程中调用
函数原型
//父进程中调用 pid_t wait(int *status); //返回值: 成功:清除掉的子进程PID 失败:-1 //status参数: 各种宏 保存子进程退出状态 //WIFEXITED(status) 为非0 进程正常结束 //WEXITSTAUS(status) 获取进程退出状态 //WIFSIGNALED(status) 为非0 进程异常终止 //WTERMSIG(status) 取得进程终止的信号编号 // man 2 wait 查看wait函数示例
#include#include #include #include #include #include int main() { //创建子进程 pid_t pid = fork(); if(pid<0){ perror("fork error"); return -1; } else if(pid>0){ //父进程 printf("father: pid==[%d],fpid==[%d]n",getpid(),getppid()); //pid_t wpid = wait(NULL); //printf("wpid==[%d]n",wpid); int status; pid_t wpid = wait(&status); printf("wpid==[%d]n",wpid); if(WIFEXITED(status)){ //正常退出 printf("child normal exit,status ==[%d]n",WEXITSTATUS(status));// =9返回状态 } else if(WIFSIGNALED(status)){ //被信号杀死 printf("child killed by signal, signo ==[%d]n",WTERMSIG(status)); } } else if(pid==0){ //子进程 printf("chlid: pid==[%d],fpid==[%d]n",getpid(),getppid()); sleep(10);//确保子进程后退出 return 9;//返回状态 } return 0; }
守护进程 基本概念基本语法
pid_t waitpid(pid_t pid, int *status,in options); //函数参数: pid = -1 等待任一子进程 pid > 0 等待进程ID与pid相等的子进程 pid = 0 回收组内所有进程 pid < -1 等待其组ID等于pid的绝对值的任一子进程(子进程在其他组的情况 //options 设置为WNOHANG,函数非阻塞 设置为0, 函数阻塞 //函数返回值 >0:返回回收掉的子进程ID -1:无子进程 =0:参数3为WNOHANG,且子进程正在运行waitpid函数示例
#include#include #include #include #include #include int main() { //创建子进程 pid_t pid = fork(); if(pid<0){ perror("fork error"); return -1; } else if(pid>0){ printf("father: pid==[%d],fpid==[%d]n",getpid(),getppid()); int status; while(1) { //pid_t wpid = waitpid(pid,&status,0); pid_t wpid = waitpid(-1,&status,WNOHANG); //printf("wpid==[%d]n",wpid); if(wpid > 0){ if(WIFEXITED(status)){ //正常退出 printf("child normal exit,status ==[%d]n",WEXITSTATUS(status)); } else if(WIFSIGNALED(status)){ //被信号杀死 printf("child killed by signal, signo ==[%d]n",WTETTMSIG(status)); } } else if(wpid == 0) { //子进程还活着 printf("cild is living ,wpod==[%d]n",wpid); } else(wpid == -1){ //没有子进程 printf("no cild is living ,wpod==[%d]n",wpid); break; } } } else if(pid==0){ //子进程 printf("chlid: pid==[%d],fpid==[%d]n",getpid(),getppid()); sleep(10); return 9; } return 0; }
后台服务进程,通常独立于控制终端,并周期性地执行任务。
不能通过终端进行交互。
不受用户登入退出影响。
一般以d结尾。
…



