首页 > 系统相关 >【Linux】多进程基础

【Linux】多进程基础

时间:2024-06-03 23:29:22浏览次数:29  
标签:文件 映射 int pid 基础 管道 Linux 进程

文章目录

查看进程相关命令

  • ps -ef: System V 风格查询所有的进程信息,-e 参数表示显示所有进程,-f 表示使用全格式输出,包含更多的列信息。输出是静态的,即显示的是执行命令那一刻的快照。
  • ps aux/ajx:BSD 风格查询所有进程信息其中 aux 是一个快捷方式,等同于 -a -u -x,a 表示所有用户的所有进程,j 添加了作业控制相关信息,而 x 显示没有控制终端的进程,u则显示进程的详细信息。
  • top:实时监视进程信息
  • kill:并非杀死进程,而是给进程发送一个信号
  • kill -l:列出所有信号
  • kill -9:杀死进程 kill -9 进程ID(PID)

进程相关函数

  • 每个进程都由唯一进程号来标识,其类型为pid_t(int整形),当某个进程终止后回收该进程号,可以分配给其他进程使用。
  • 除init进程外,任何进程都是由另一个进程创建的,该进程为父进程,对应的进程号为父进程号(PPID),被创建的进程为其子进程。
  • 进程组是一个或者多个进程的集合,互相关联,进程组可以接收同一终端的信号,关联的进程有一个进程组号(PGID)
pid_t getpid(void);//获取进程号
pid_t getppid(void);//获取父进程号
pid_t getpgid(pid_t pid);//获取进程的组进程号
  • 进程创建函数fork():调用 fork()后,会创建一个与父进程状态几乎一致的子进程。但子进程与父进程采用读时共享、写时复制的策略,在fork()后父进程和子进程共享同一个虚拟地址空间映射(页表),映射到同一物理地址。只有在父进程或子进程需要进行写入操作时才会为相应的进程分配物理地址空间,从而使各自进程拥有各自的地址空间。子进程也可以创建其新的子进程。fork()会返回两次,通过返回的pid判断是父进程还是子进程,返回值>0为父进程,==0为子进程,交替抢CPU使用。
#include <sys/types.h>
#include <iostream>
#include <unistd.h>
int main(){
    int num=10;
    //创建子进程
    pid_t pid=fork();

    //如果使用循环创建多个子进程一定注意,只让父进程创建,对于创建的子进程不做处理即需要判断if(pid==0){break;}

    //判断父进程还是子进程
    if(pid>0){
        std::cout<<"父进程"<<getpid()<<std::endl;
        num+=10;
        std::cout<<num<<std::endl;
    }

    //判断子进程
    else if(pid==0){
        std::cout<<"子进程"<<getpid()<<"父进程"<<getppid()<<std::endl;
        num+=100;
        std::cout<<num<<std::endl;
    }

    return 0;
}
  • 进程退出函数
#include <stdlib.h>//c语言的进程退出,通过调用Linux的进程退出来实现,并且引入了缓冲区,同c的文件IO函数fopen和fclose也是调用Linux的文件IO函数
void exit(int status)

#include <unistd.h>//Linux的进程退出
void _exit(int status)

孤儿进程

  • 父进程结束但是子进程还在运行,该子进程就叫孤儿进程,孤儿进程的父进程会被init进程替代,由init进程对其资源进行释放操作,无危害。

僵尸进程

  • 每个进程结束后用户区的数据可以自行释放,而内核区的PCB需要父进程释放,如果子进程终止,但父进程没有对其内核区的PCB进行回收,子进程的资源残留在内核中,变成僵尸进程,一直占用PID等资源且无法被kill -9杀死,只有杀死其父进程使其变成孤儿进程最后才能被init进程回收。

进程回收

  • 父进程通过调用wait()或waitpid()函数进行进程的回收,二者不同的是前者会阻塞等待进程结束,而后者可以设置WNOHANG不阻塞且waitpid()可以指定等待哪个子进程结束,一次调用只能清理一个子进程。

进程通信(IPC)

  • 管道通信pipe:例如常见的管道符|就是管道通信,ps -ef | grep ssh,将前一个指令的输出作为后一个指令的输入,管道通信是单向的半双工通信,一端读另一端写。管道实际是内存维护的一片缓冲区,可以使用操作文件的方式操作管道,使用文件描述符fd以及open()、close()、read()和write()

匿名管道pipe通信的使用

创建管道
#include <unistd.h>
int pipe(int pipefd[2]);
//包括读端pipefd[0]和写端pipefd[1]
//如果成功创建管道,pipe()函数返回0。若出现错误(比如资源不足),则返回-1,并用perror输出。

//创建管道
int pipefd[2];
int ret=pipe(pipefd);
if(ret==-1){
    perror("pipe");
    return 0;
}

//读管道,类似读文件
....
close(pipefd[1]);//关闭写端
char buff[1024]={0};
while(1){
int len=read(pipefd[0],buff,sizeof(buff));
if(len==0){
    break;
}
}
....

//写管道,类似写文件
....
close(pipefd[0]);//关闭读端
const char *str="HELLO!HAHAHA!" ;
write(pipefd[1],str,sizeof(str));
....
  • 匿名管道只能在有公共祖先的进程下使用,因为父进程fork()后,其子进程都共享父进程的虚拟地址空间映射,包括文件描述符表,其中包含读端pipefd[0]和写端pipefd[1],所以才能进行通信。同时因为半双工通信,读进程需要关闭写端close(pipefd[1]),写进程需要关闭读端close(pipefd[0])
  • ulimit -a:查看管道缓冲的大小

有名管道:FIFO

  • 通过创建管道名称(实则是文件作为管道),可以用于无关系之间的进程的通信,也是通过文件描述符和文件IO进行操作。
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
//pathname:一个指向包含命名管道路径名的字符串指针。
//mode:指定命名管道的权限位,类似于普通文件的权限设置。
//如果成功创建命名管道,返回0。如果失败则返回-1,并设置errno变量

//创建管道test
int ret=mkfifo("test",0664);
if(ret==-1){
    perror("管道创建错误");
    return 0;
}

//只写打开管道即关闭读端,类似打开文件操作。
int fd=open("test",O_WRONLY);
if(fd==-1){
    perror("open");
    return 0;
}
//写管道,类似写文件
....
const char *str="HELLO!HAHAHA!";
write(fd,str,sizeof(str));
....


//只读打开管道即关闭写端,类似打开文件操作。
int fd=open("test",O_RDONLY);
if(fd==-1){
    perror("open");
    return 0;
}
//读管道,类似读文件
....
char buff[1024]={0};
while(1){
int len=read(fd,buff,sizeof(buff));
if(len==0){
    break;
}
}
....

读写管道总结

读管道:

  • 管道中有数据则read返回实际读到的字节数,
  • 若管道中无数据:
    1. 若写端全部关闭则read返回0相当于读完
    2. 若写端没有完全关闭则read阻塞

写管道:

  • 管道读端全部被关闭,则进程异常终止
  • 若管道读端没有全部关闭:
    1. 管道已满则write阻塞
    2. 管道未满则write将数据写入并返回实际字节数

mmap内存映射

  • mmap是一种内存映射文件的方法,即将一个文件映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的对映关系。实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作而不必再调用read, write等系统调用函数。相反,内核空间对这段区域的修改也直接反映用户空间,从而可以实现不同进程间的文件共享。
  • 函数原型:
    void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);成功时返回映射区域的起始地址,失败时返回MAP_FAILED
    1. addr:期望映射区域的起始地址,通常设为NULL让系统自动选择合适的地址。
    2. length:映射区域的长度,以字节为单位,使用文件大小即可
    3. prot:映射区域的保护标志,可以是以下值的组合(使用|操作符):
      PROT_READ:可读。
      PROT_WRITE:可写。
      PROT_EXEC:可执行。
      PROT_NONE:不可访问。
    4. flags:映射类型和行为标志,常见组合有:
      MAP_SHARED:映射区域可被多个进程共享,对映射内容的修改会影响原文件或共享内存。
      MAP_PRIVATE:创建映射的私有副本,对映射内容的修改不会影响原文件。
      MAP_ANONYMOUS:用于创建匿名映射,不关联文件。
    5. fd:当映射文件时,此参数为文件描述符;如果是匿名映射,则通常设为-1。
    6. offset:文件中映射区域的偏移量,必须是页对齐的。
  • 具体使用:
#include <sys/mman.h>
....

//打开文件
int fd=open("text",O_RDWR);
int size= lseek(fd,0,SEEK_END);//通过计算偏移量获取文件的大小

//创建映射
void *ptr = mmap(NULL,size,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);//返回映射区域起始地址
if(ptr==MAP_FAILED){
    perror("mmap");
    return 0;
}

//实现进程间通信
pid_t pid = fork();
if(pid>0){
    //等待子进程写结束
    wait(NULL);
    //父进程,读
    char buff[1024];
    strcpy(buff,(char *)ptr);//读取数据
}else if(pid==0){
    //子进程,写
    const char *str="HAHAHA!";
    strcpy((char *)ptr,str);
}

//关闭映射区
munmap(ptr,size);

标签:文件,映射,int,pid,基础,管道,Linux,进程
From: https://blog.csdn.net/csdnabcaq/article/details/139422323

相关文章

  • (十三)统计学基础练习题七(选择题T301-350)
    本文整理了统计学基础知识相关的练习题,共50道,适用于想巩固统计学基础或备考的同学。来源:如荷学数据科学题库(技术专项-统计学二)。序号之前的题请看往期文章。301)302)303)304)305)306)307)308)309)310)311)312)313)314)315)316)317)318)319)320)321)322)......
  • 在Linux kali下载、安装Perl环境
    目录Perl介绍下载安装官网下载在Windows安装在Linux和MacOS安装Perl介绍Perl一种功能丰富的计算机程序语言,运行在超过100种计算机平台上,适用广泛,从最初是为文本处理而开发的,现在用于各种任务,包括系统管理,Web开发,网络编程、GUI开发等。Perl易于使用、高效、完整,而不......
  • Java基础——抽象类与抽象方法
    抽象方法:    将共性的行为(方法)抽取到父类之后。由于每一个子类执行的内容不一样,所以,在父类中不能确定具体的方法体。该方法可以定义为抽象方法抽象类:    如果一个类中存在抽象方法,那么该类就必须声明为抽象类抽象方法的定义格式:    publicabstra......
  • Linux引导过程、修复MBR扇区
    目录引导过程开机自检寻找boot引导设备加载MBR引导记录GRUB菜单加载内核init进程初始化进程树系统初始化进程init进程守护进程运行级别Systemd修复MBR扇区故障故障原因故障现象解决思路示例备份MBR扇区模拟故障恢复MBR扇区GRUB备份模拟GRUB故障模拟GRU......
  • [FreeRTOS 基础知识] 堆
    文章目录堆的概念使用C语言实现堆堆空间解析堆的概念所谓的堆就是一块空间的内存,可以来管理这块内存。从这块内存中取出一部分然后再释放回去。使用C语言实现堆charheap_buf[1024];//定义一个堆空间intpos=0;//当前位......
  • 【前端每日基础】day42——关于 Vuex 共有几个属性,哪里可以书写同步任务,哪里可以书写
    Vuex是Vue.js的一个状态管理模式,它集中式地存储和管理应用的所有组件的状态。Vuex提供了以下几个核心属性,每个属性在状态管理中有不同的用途:Vuex共有的几个属性:State:用于存储应用的状态。可以通过this.$store.state或在组件中通过mapState辅助函数访问。Gette......
  • Qt 进程间通讯,Qt子进程嵌入主进程
    本程序实现的功能是将子进程嵌入主进程,通过进程间通讯将子进程的窗口句柄发送给服务端,然后服务端将子进程窗口嵌入主进程;服务端主进程#ifndefMAINWINDOW_H#defineMAINWINDOW_H#include<QLocalServer>#include<QMainWindow>#include<QLocalSocket>#include"ui_mainw......
  • Linux管理员必备——Linux系统服务(daemon)与systemctl管理服务(上)
    ......
  • 【Linux 网络】高级 IO -- 详解
    一、IO的基本概念I/O(input/output)也就是输入和输出,在冯诺依曼体系结构当中,将数据从输入设备拷贝到内存就叫作输入,将数据从内存拷贝到输出设备就叫作输出。对文件进行的读写操作本质就是一种IO,文件IO对应的外设就是磁盘。对网络进行的读写操作本质也是一种IO,网络IO对......
  • Linux基础命令
    Linux基础命令Linux命令大全搜索工具(可能需要翻墙)web镜像文章目录lspwdcdtouchmkdirrmdirrmmancpmvcatmorelesshead1taildatecalfindgrepzipunziptarbcunameshutdownLinux系统对于大小写是敏感的,因此输入命令需要注意大小写1.ls指令语法:ls[选项]目录/文件......