栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

基于Ubuntu 多进程基础知识笔记

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

基于Ubuntu 多进程基础知识笔记

进程的相关概念 基本概念
  • 资源分配和拥有的基本单位
  • 进程CPU环境(栈,寄存器,页表,和文件句柄等)的保存以及新调度的进程CPU环境的设置
  • 操作系统控制
  • 内核栈
  • CPU资源、内存资源、文件资源和句柄等
  • 不同进程之间切换实现并发,各自占有CPU实现并行
  • 切换虚拟地址空间,切换内核栈和硬件上下文,CPU高速缓存失效、页表切换,开销很大。
  • 进程间通信需要借助操作系统
程序和进程

程序:是编译好的二进制文件,存放在磁盘上,占用的是磁盘空间,是一个静态的概念。

进程:一个启动的程序,占用系统资源。如:物理内存,CPU,终端等;是一个动态的概念

并行和并发

并行:多个CPU

并发:多个时间片

PCB-进程控制块

每个进程在内核中都有一个进程控制块(PCB)

进程ID,唯一。
进程的状态:就绪、运行、挂起、停止等状态。
CPU寄存器,哟用于**切换进程,**保留进程信息。
虚拟地址空间的信息
控制终端的信息
当前工作目录
umask掩码
文件描述符表,包含很多指向file结构体的指针
和信号相关的信息。
用户ID和组ID
会话(Session)和进程组
进程可以使用的资源上限

ulimit -a

进程的状态

创建进程 fork函数(创建子进程)

函数作用:创建子进程

谁先抢到时间片,谁先执行

//原型:      
pid_t fork(void);
//函数参数    无
//返回值      
//父进程返回子进程PID  子进程返回0
fork函数示例
#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]进程接受

循环创建n个进程

函数示例:

#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
父子进程不能共享全局变量
  • 父子进程变量不共享,但指向同一个地址
  • 读时共享,写时不共享

读写示意图

写时,会将物理内存映射到一个新的地方。

代码示例

#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;
}
ps命令和kill命令

查看进程

ps -aux |  grep "xxx"  //配合管道查找指定内容
ps -ajx |  grep "xxx"

-a:(all) 当前系统所有用户的进程
-u:查看进程所有者及其他一些信息
-x:显示没有控制终端的进程
-j:累出与作业相关的信息

结束进程

//查看系统有哪些信号
kill -l 
//杀死某个线程
kill -9 pid 
调用程序 exec函数族(拉起一个程序)

在一个子进程里拉起一个可执行程序(系统命令或应用程序)

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函数代码示例

#include
int 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进程回收资源

僵尸进程(重点)

子进程先退出,但是父进程没有回收子进程的资源,子进程为僵尸进程

无法用kill处理子进程

简单处理方法:
1、kill -9 强制删除父进程
2、init进程领养僵尸进程,最后由inti进程回收僵尸进程。

wait函数

函数作用:

阻塞并等待子进程退出
回收子进程残留资源
获取子进程结束状态(退出原因)

特点

  • 阻塞 确保父进程后退出
  • 父进程中调用

函数原型

//父进程中调用
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;
}
waitpid函数

基本语法

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结尾。

…

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1006000.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号