7.进程 C++/Linux

8.进程

基本相关概念

  • ps aux 查看进程信息linux

    • ps ajx 查看信息更全面
    • kill -9 pid 杀死进程:给进程发送9的信号,也可以写kill -SIGKILL pid
  • 概念:

    • 程序:编译好的二进制文件
    • 进程:运行着的程序,运行指令的过程,分配系统资源的基本单位。
  • MMU作用:

    • 虚拟内存和物理内存的映射
    • 修改内存访问级别
  • 进程控制块PCB

  • ulimit -a 查看所有资源的上限

  • 环境变量:

    • 写法:
      • key=value 中间不能有空格
    • 获取环境变量:
      • 头文件#include
      • cahr* getenv(const char*name);
      • getenv("HOME");

fork()进程控制

  • 进程api–fork()

    • fork创建一个新的进程

      • 头文件 #include #include
      • pid_t fork(void);
      • 返回值:
        • 一个进程返回子进程的pid,并进入本进程
        • 一个进程返回0,代表进入子进程
    • getpid()获得pid,进程id,获得当前进程

      • pid_t getpid(void);
    • getppid获得当前进程的父进程id

      • pid_t getppid(void);
    • 例子

      #include 
      #include 
      #include 
      
      int main()
      {
          int n = 5;
          int i =0;
          pid_t pid = 0;
          for(i = 0;i<5;i++){//父进程循环结束
              pid = fork();
              if(pid == 0){
                  printf("i am child,pid=%d,ppid=%d\n",getpid(),getppid());
                  break;//防止子进程再产生子进程
              }else if(pid>0){
                  //father
                  printf("i am father,pid=%d,ppid=%d",getpid(),getppid());
              }
          }
          sleep(i);
          if(i<5){
              printf("i am child,pid=%d,ppid=%d",getpid(),getppid());
          }else{
              printf("i am father");
          }
      
      }
      
    • 父进程与子进程在空间使用上,读时共享,写时复制(开辟新空间)

exce函数,执行程序

  • execl函数

    • 头文件#include

    • excel

      • int excel(const char* path, const char* arg,.../*(char * )NULL */)

      • 需要添加路径

      • excel("/bin/ls","ls","-l","--color=auto",NULL)
        
    • excelp

      • 不用添加路径
      • int excelp(const char * file,....)
        • file 要执行的程序
        • arg参数列表
          • 最后的NULL是作为哨兵
        • 返回值,只有失败才返回,-1

进程回收

  • 孤儿进程和僵尸进程

    • 孤儿进程:父进程被kill,子进程被init领养
    • 僵尸进程:子进程被kill,
      • init不直接回收僵尸进程,为了让父进程知道子进程为何被kill
      • 解决僵尸进程:kill掉父进程,僵尸进程会被init领养回收
  • wait函数回收子进程

    • 作用

      • 阻塞等待
      • 回收子进程资源
      • 查看死亡原因
    • 头文件#include #include

    • pid_t wait(int *status)

      • status 传出参数

      • 成功返回终止的子进程ID

      • 失败返回-1

      • pid_t wpid = wait(NULL);

      • 正常死亡WIFEXITED,WEXITSTATUS查看状态

      • 非正常死亡WIFSIGNALED,WTERSIG查看状态

      • int status;
        pid_t wpid = wait(&status);
        if(WIFEXITED(status)){
        	printf(WEXITSTATUS(status));
        }
        if(WIFSIGNALED(status)){
            printf(WTERSIG(status));
        }
        
    • pid_t waitpid(pid_t pid,int* status,int options);

      • pid
        • <-1 组id
        • -1 回收任意
        • 0 回收和调用组id相同组内的子进程
        • >0回收指定pid的子进程
      • options
        • 0与wait相同,也会阻塞
        • WNOHANG 不会阻塞,如果没有子进程退出,立即返回

进程间通信:IPC

概念

  • IPC:InterProcess Communication 进程间通信,通过内核提供的缓冲区进行数据交换的机制
  • 方式主要有
    • pipe 管道,父子进程之间,关系的进程之间,最简单
    • fifo 有名管道,各个进程之间
    • mmap 文件映射IO -速度最快
    • 本地socket 最稳定
    • 信号 携带信息最少
    • 共享内存
    • 消息队列
  • 常见通信:
    • 单工
    • 半双工
    • 全双工

PIPE通信

  • int pipe(int pipfd[2]);

    • pipfd 文件描述符,0表示读端,1表示写段

    • 返回值;失败返回-1,成功返回0

    • 父子进程实现pipe通信,实现 ps aux | grep bash功能

      #include 
      #include 
      
      int main()
      {
          int fd[2];
          pipe(fd);
          
          pid_t pid = fork();
          if(pid == 0){//子进程
              //关闭读端
              close(fd[0]);
              //先重定向
              dup2(fd[1],STDOUT_FILENO);
              //execlp
              execlp("ps","ps","aux",NULL);
          }else if(pid > 0){
              //父进程
              //关闭写段
              close(fd[1]);
              //重定向
              dup2(fd[0],STDIN_FILENO);
              //execlp
              execlp("grep","grep","bash",NULL);
          }
          
          return 0;
      }
      
    • 读写管道

      • 读管道:
        • 写端全部关闭–read读到0,相当于读到文件末尾
        • 写段没有全部关闭
          • 有数据 – read读到数据
          • 没有数据 --read阻塞 fcntl函数可以更改非阻塞
      • 写管道:
        • 读端全部关闭 ---- 产生一个信号SIGPIPE,程序异常终止
        • 读端未全部关闭
          • 管道已满 --write阻塞
          • 管道未满 – write正常写入
    • 管道大小:默认512*8

      • long fpathconf(int fd,int name);
      • ulimit -a

FIFO通信

  • 有名通道,实现无血缘关系的进程通信

    • 创建一个管道的伪文件
      • mkfifo myfifo 命令创建
      • 也可以用函数int mkfifo(const char *pathname,mode_t mode);
    • 内核会针对fifo开辟一个缓存区,操作fifo文件,可以操作缓存区,实现进程通信,实际上是文件读写
  • FIFOs

  • 注意事项:打开fifo文件时,read端会阻塞等待write端open,write端同里也会等待另一端打开

  • w端代码

    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main(int argc,char *argv)
    {
        if(argc !=2){
            printf("./a.out fifoname\n");
            return -1;
        }
        
        int fd = open(argv[1],O_WRONLY);
        //write
        char buf[256];
        int num = 1;
        while(1){
            memset(buf,0x00,sizeof(buf));//清空缓存区
            sprintf(buf,"xiaoming%04d",num++);
            write(fd,buf,strlen(buf));
            sleep(1);
        }
        close(fd);	
        return 0;
    }
    
  • r端代码

#include 
#include 
#include 
#include 
#include 

int main(int argc,char *argv)
{
    if(argc !=2){
        printf("./a.out fifoname\n");
        return -1;
    }
    
    int fd = open(argv[1],O_WRONLY);
    //write
    char buf[256];
    int num = 1;
    while(1){
        num = read(fd,buf,sizseof(buf));
        if(num>0){
            printf("read:%s\n",buf);
        }
    }
    close(fd);	
    return 0;
}

mmap 和 munmap

​ 头文件#include

​ mmap 创建映射区

  • void *mmap(void *addr,size_t length,int prot,int flags,int fd,off_t offset);
    • addr 传NULL
    • length 映射区长度
    • prot
      • PROT_READ 可读
      • PROT_WRITE 可写
    • flags
      • MAP_SHARED 共享的,对内存的修改会影响到源文件
      • MAP_PRIVATE 私有的
    • fd 文件描述符
    • offset 偏移量,4k的倍数
    • 返回值
      • 成功,返回可用的内存地址
      • 失败,返回MAP_FAILED

munmap 释放映射区

  • int munmap(void *addr,size_t length);
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main()
{
    int fd = open("mem.txt",O_RDWR);
    //创建映射区
    char *mem = mmap(NULL,8,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    
    if(mem == MAP_FAILED){
        perror("mmap error.\n");
        return -1;
    }
    
    strcpy(mem,"hello");
    //释放mmp
    munmap(mem,8);
    close(fd);
    
    return 0;
}
  • 常见问题

    • 更改mem变量地址,将不能成功释放
    • 对mem越界操作,文件大小对映射区有影响,尽量避免
    • 文件描述符先关闭,对mmap映射没有影响
    • 可以用open新创建一个文件来创建映射嘛,可以,但不能是空文件
    • open文件选择O_WRONLY可以吗,不可以,权限不够
  • 匿名映射–仅限于有关系的进程之间

    • 使用宏:MAP_ANON MAP_ANONYMOUS,在unix可以使用下面两个文件

      • dev/zero 可以随便映射,聚宝盆
      • dev/null 无底洞
    • 父子进程案例-

      #include 
      #include 
      #include 
      #include 
      #include 
      #include 
      #include 
      
      int main()
      {
          int *mem = mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANON,-1,0);
          
          if(mem == MAP_FAILED){
              perror("map error");
              return -1;
          }
          
          pid_t pid = fork();
          
          if(pid == 0){
              //子进程
              *mem = 101;
              printf("child,*mem = %d\n",*mem);
              sleep(3);
              printf("child, *mem = %d\n",*mem);
          }else if(pid > 0){
              sleep(1);
              printf("parent,*mem = %d\n",*mem);
              *mem = 201;
              printf("parent,*mem = %d\n",*mem);
              //等待回收
              wait(NULL);
          }
          munmap(*mem,4); 	
          return 0;
      }
      

你可能感兴趣的:(C/C++Linux学习记录,linux,c++,运维)