【linux】进程管理:进程控制块、进程号、fork创建进程、特殊进程及exec函数族解析

一、进程的概述

        可执行程序运行起来后(没有结束之前),它就成为了一个进程。程序是存放在存储介质上的一个可执行文件,而进程是程序执行的过程。进程的状态是变化的,其包括进程的创建、调度和消亡。程序是静态的,进程是动态的。

1、程序和进程的区别

        程序 静态的 占磁盘空间(存放在存储介质上的一个可执行文件)

        进程 动态的 (调度、执行、消亡),占内存空间。(进程是程序执行到结束间的这个过 程)

 2、单道和多道程序设计

        单道程序设计 所有进程一个一个排队执行。若A阻塞,B只能等待,即使CPU处于空闲状 态。这种模型在系统资源利用上及其不合理,大部分已被淘汰了。

        多道程序设计 在计算机内存中同时存放几道相互独立的程序,它们在管理程序控制之 下,相互穿插的运行。当下常见CPU为纳秒级,由于人眼的反应速度是毫秒级,所以看似同时在运行。
而多道程序设计必须有硬件基础作为保证。时钟中断(强制让进程让出cpu资源)即为多道程序设计模型的理论基础。

3、并行和并发的区别

        并行(微观)和并发(宏观)都是多个任务同时执行(多道程序)。

        并行(parallel):指在同一时刻,有多条指令在多个处理器上同时执行(微观上同时执行)(多核)

        并发(concurrency):指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执 行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时 间分成若干段,使多个进程快速交替的执行(单核 )

 4、进程控制块(PCB)

        进程运行时,内核为每个进程分配一个PCB(进程控制块),维护进程相关的信 息,Linux内核的进程控制块是task_struct结构体。 PCB存在于进程的内核空间里面。系统会为每一个进程分配一个进程ID,其类型为pid_t(非负整数) ,进程的状态,有就绪、运行、挂起、停止等状态。进程状态切换时需要保存和恢复的一些CPU寄存器。进程是系统分配资源基本单位

5、进程的状态

进程的三大状态:就绪态、执行态、等待态

  • 就绪态:执行条件全部满足,等待CPU的执行调度
  • 执行(运行)态:正在被CPU调度执行
  • 等待态:不具备CPU调度执行的执行条件,等待条件满足。 

ps命令查看进程信息: 

选项含义
-a显示终端上的所有进程,包括其他用户的进 程
-u显示进程的详细状态
-x显示没有控制终端的进程
-w显示加宽,以便显示更多的信息
-r只显示正在运行的进程

查看进程状态:ps -aux 

 stat中的参数意义如下:

以树状显示进程:pstree

 二、进程号PID

        每个进程都由一个进程号来标识,其类型为 pid_t(整型),进程号的范围:0~32767。 进程号总是唯一的,但进程号可以重用。当一个进程终止后,其进程号就可以再次使用 。

  • 进程号(PID): 标识进程的一个非负整型数
  • 父进程号(PPID):父进程号
  • 进程组号(PGID): 进程组是一个或多个进程的集合。 

1、获取进程号的函数 

头文件:

#include<sys/type.h>

#include<unistd.h>

函数:

pid_t getpid(void);  

功能: 获取本进程号(PID)

参数: 无

返回值: 本进程号 

 2、获取父进程的ID

#include<sys/type.h>

#include<unistd.h>

pid_t getppid(void);

功能: 获取调用此函数的进程的父进程号(PPID)

参数: 无

返回值: 调用此函数的进程的父进程号(PPID)

3、获取进程组的ID

#include<sys/type.h>

#include<unistd.h>

pid_t getpgid(pid_t pid);

功能: 获取进程组号(PGID)

参数: pid:进程号

返回值: 参数为 0 时返回当前进程组号,否则返回参数指定的进程的进程组号  

查看父子进程号:ps -ef 

 查看所有进程号:ps -ajx

getchar();防止进程结束。 

三、 fork创建进程

1、fork函数

        系统允许一个进程创建新进程,新进程即为子进程,子进程还可以创建新的子进程,形成进 程树结构模型。

#include<sys/types.h>

#include<unistd.h>

pid_t fork(void);

功能: 用于从一个已存在的进程中创建一个新进程,新进程称为子进程,原进程称为父进程。

参数: 无

返回值: 成功:子进程中返回 0父进程中返回子进程 ID。pid_t,为整型。 失败:返回-1。

失败的两个主要原因:

        1)当前的进程数已经达到了系统规定的上限,这时 errno 的值被设置为 EAGAIN。

        2)系统内存不足,这时 errno 的值被设置为 ENOMEM 

2、fork出来的子进程和父进程之间的关系 

         使用fork函数得到的子进程是父进程的一个复制品,它从父进程处继承了整个进程的地址空间。 地址空间: 包括进程上下文、进程堆栈、打开的文件描述符、信号控制设定、进程优 先级、进程组号等。 子进程所独有的只有它的进程号,计时器等。因此,使用fork函数的代价是很大的 。

        父子进程 从fork后开始继续执行。父子进程是同时运行,空间独立,子进程复制父进程的所有空间,谁先运行不确定。 

#include <stdio.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
    //创建子进程
    pid_t pid=fork();
    if(pid<0)
    {
        perror("创建失败\n");
    }
    else if(pid==0)
    {
        printf("%d为子进程\n",getpid());
    }
    else if(pid>0)
    {
        printf("%d为父进程\n",getpid());
    }
    getchar();
    return 0;
}   

3、子进程 复制 父进程的资源(各自独立)

4、父子进程同时运行

5、父进程 给子进程 足够的准备时间时 

 四、特殊的进程

        孤儿进程、僵尸进程、守护进程。 

1、孤儿进程(无危害)

        父进程先结束、子进程就是孤儿进程,会被1号进程接管(1号进程负责给子进程回收资 源)

终止子进程: 

 2、僵尸进程(有害)

        子进程结束,父进程没有回收子进程资源(PCB),子进程就是僵尸进程。 

当父进程结束后,僵尸进程的资源被回收。 

 3、守护进程

        守护进程 是脱离终端的 孤儿进程。在后台运行。为特殊服务存在的。(一般用于服务器)

五、父进程回收子进程的资源

        在每个进程退出的时候,内核释放该进程所有的资源、包括打开的文件、占用的内存等。但 是仍然为其保留一定的信息,这些信息主要主要指进程控制块PCB的信息(包括进程号、退 出状态、运行时间等)

        父进程可以通过调用waitwaitpid得到它的退出状态同时彻底清除掉这个进程。 wait()和 waitpid()函数的功能一样,区别在于,wait()函数会阻塞,waitpid()可以设置不阻塞。注意:一次wait或waitpid调用只能清理一个子进程,清理多个子进程应使用循环。 wait、waitpid基本上都是在父进程调用。

1、wait函数

#include<sys/types.h>

#include<sys/wait.h>

pid_t wait(int *status);  

        功能: 等待任意一个子进程结束,如果任意一个子进程结束了,此函数会回收该子进程的资源。

        参数: status : 进程退出时的状态信息。

        返回值: 成功:已经结束子进程的进程号 失败: -1

        注意:

  • 调用wait的进程会阻塞(挂起)、直到该函数返回才被唤醒。
  • 若调用进程没有子进程,该函数立即返回 子进程已经结束,该函数同样会立即返回,并且会回收那个早已结束进程的资源。
  • 如果参数status 的值不是NULL,wait()就会把子进程退出时的状态取出并存入其中。这是一个整数值( int),指出了子进程是正常退出还是被非正常结束的。

        状态值: int中包含了多个字段,直接使用这个值是没有意义的,WIFEXITED(status) 如果子进程是正常终止的,取出的字段值非零。 WEXITSTATUS(status) 返回子进程的退出状态,退出状态保存在status变 量的8~16位,在用此宏前应先用宏WIFEXITED判断子进程是否正常退出,正常退出才可以使用此宏。

 2、waitpid函数

        wiatpid常用于等待多个子进程结束。

#include<sys/type.h>

#include<sys/wait.h>

pid_t waitpid(pid_t pid, int *status, int options);  

 功能: 等待子进程终止,如果子进程终止了,此函数会回收子进程的资源。

参数:

pid : 参数 pid 的值有以下几种类型:

  • pid > 0 等待进程 ID 等于 pid 的子进程。
  • pid = 0 等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组, waitpid 不会等待它。
  • pid = -1 等待任一子进程,此时 waitpid 和 wait 作用一样。
  • pid < -1 等待指定进程组中的任何子进程,这个进程组的 ID 等于 pid 的绝对值。

status : 进程退出时的状态信息。和 wait() 用法一样。

options : options 提供了一些额外的选项来控制 waitpid()。

  • 0:同 wait(),阻塞父进程,等待子进程退出。
  • WNOHANG:没有任何已经结束的子进程,则立即返回。
  • WUNTRACED:如果子进程暂停了则此函数马上返回,并且不予以理会子进程的结束状态。(由于涉及到一些跟踪调试方面的知识,极少用到)

返回值: waitpid() 的返回值比 wait() 稍微复杂一些,一共有 3 种情况:

  1. 当正常返回的时候,waitpid() 返回收集到的已经回收子进程的进程号;
  2. 如果设置了选项 WNOHANG,而调用中 waitpid() 还有子进程在运行,且没有子进程退出,返回0; 父进程的所有子进程都已经退出了 返回-1; 返回>0表示等到一个子进 程退出
  3. 如果调用中出错,则返回-1,这时 errno 会被设置成相应的值以指示错误所在, 如:当 pid 所对应的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid() 就会出错返回,这时 errno 被设置为 ECHILD

waitpid等价于wait的案例:

六、创建多个子进程

1、创建2个子进程出现的问题

2、防止子进程 创建孙进程 

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#define N 3
int main(int argc,char *argv[])
{
    //创建3个子进程
    int i=0;
    for(;i<N;i++)
    {
        pid_t pid=fork();
        if(pid==0)//防止子进程创建孙进程
        {
            break;
        }
    }
    //判断具体的子进程
    if(i==0)//子进程1
    {
        //完成任务A
        int i=3;
        for (; i > 0; i--)
        {
            printf("子进程%d工作剩余时间%d\n",getpid(),i);
            sleep(1);
        }
        _exit(-1);
    }
    else if(i==1)//子进程2
    {
        //完成任务B
        int i=5;
        for (; i > 0; i--)
        {
            printf("子进程%d工作剩余时间%d\n",getpid(),i);
            sleep(1);
        }
        _exit(-1);
    }
    else if(i==2)//子进程3
    {
        //完成任务C
        int i=7;
        for (; i > 0; i--)
        {
            printf("子进程%d工作剩余时间%d\n",getpid(),i);
            sleep(1);
        }
        _exit(-1);
    }
    else if(i==N)//父进程
    {
        //回收子进程资源
        while(1)
        {
            pid_t pid=waitpid(-1,NULL,WNOHANG);//不阻塞
            if(pid>0)
            {
                printf("子进程%d退出\n",pid);
            }
            else if(pid==0)//还有子进程在运行
            {
                continue;
            }
            else if(pid==-1)
            {
                printf("所有子进程已结束\n");
                break;
            }

        }
    }
    return 0;
}   

 

七、 进程相关

1、终端 

        用户通过终端登录系统后得到一个Shell进程,这个终端成为Shell进程的控制终端(Controlling Terminal),进程中,控制终端是保存在PCB中的信息,而 fork会复制PCB中的信息,因此由Shell进程启动的其它进程的控制终端也是这个终端。

2、进程组 

        一个或多个进程的集合,也称之为作业。当父进程创建子进程的时候,默认子进程与父进程属于同一进程组。当bash创建进程时,该进程自己创建与自己ID相同的进程组,不与父进程同属于一个进程组。

        进程组ID为第一个进程ID(组长进程): 进程ID和进程组ID相同的进程就是 组长进程。

        可以使用kill -SIGKILL -进程组ID(负的)(正的为组长进程)来将整个进程组内的进程全部杀死 只要进程组中有一个进程存在,进程组就存在,与组长进程是否终止无关。 进程组生存期:进程组创建到最后一个进程离开(终止或转移到另一个进程组)。

#include<unistd.h>

pid_t getpgrp(void); 

功能:获取当前进程的进程组ID

参数:无

返回值:总是返回调用者的进程组ID

pid_t getpgid(pid_t pid);

功能:获取指定进程的进程组ID

参数: pid:进程号,如果pid = 0,那么该函数作用和getpgrp一样

返回值: 成功:进程组ID         失败:-1

int setpgid(pid_t pid, pid_t pgid)

功能: 改变进程默认所属的进程组。通常可用来加入一个现有的进程组或创建一个新进程组。

参数: 将参1对应的进程,加入参2对应的进程组中

返回值: 成功:0 失败:-1

 3、会话

        会话是一个或多个进程组的集合。 一个会话可以有一个控制终端。 一个会话中的几个进程组可被分为一个前台进程组以及一个或多个后台进程组;如果一个会话有一个控制终端,则它有一个前台进程组,其它进程组为后台进程组;如果终端接口检测到断开连接,则将挂断信号发送至控制进程(会话首进程)。

        如果进程ID==进程组ID==会话ID 那么该进程为会话首进程。

创建新会话的步骤:

  1. 调用进程不能是进程组组长,该进程变成新会话首进程(session header)
  2. 该调用进程是组长进程,则出错返回 。
  3. 该进程成为一个新进程组的组长进程
  4. 需有root权限(ubuntu不需要)
  5. 新会话丢弃原有的控制终端,该会话没有控制终端
  6. 建立新会话时,先调用fork, 父进程终止,子进程调用setsid 

#includ<unistd.h>

pid_t getsid(pid_t pid);

功能:获取进程所属的会话ID

参数: pid:进程号,pid为0表示查看当前进程session ID

返回值: 成功:返回调用进程的会话ID 失败:-1

#include<unistd.h>

pid_t setsid(void);

功能: 创建一个会话,并以自己的ID设置进程组ID,同时也是新会话的ID。调用了setsid函数的进程,既是新 的会长,也是新的组长。

参数:无

返回值: 成功:返回调用进程的会话ID 失败:-1  

 4、创建守护进程

  1. 创建子进程,父进程退出(必须) 所有工作在子进程中进行形式上脱离了控制终端
  2. 子进程中创建新会话(必须) setsid()函数 使子进程完全独立出来,脱离控制。
  3. 改变当前目录为根目录(不是必须) chdir()函数 防止占用可卸载的文件系统 也可以换成其它路径 
  4. 重设文件权限掩码(不是必须) umask()函数 防止继承的文件创建屏蔽字拒绝某些权限,增 加守护进程灵活性。
  5. 关闭文件描述符(不是必须) 继承的打开文件不会用到,浪费系统资源,无法卸载
  6. 开始执行守护进程核心工作(必须) 守护进程退出处理程序模型 

八、 vfork创建子进程

1、vfork函数说明 

         vfork函数:创建一个新进程

pid_t vfork(void);

功能: vfork函数和fork函数一样都是在已有的进程中创建一个新的进程,但它们创建的子进程是有区别的。

返回值: 创建子进程成功,则在子进程中返回0,父进程中返回子进程ID。出错则返回-1。 

2、vfork函数和fork函数的区别 

         区别1:vfork创建的子进程 会保证子进程先运行,只有当子进程退出(调用 exec)的时候,父进程才运行。

 区别2:vfork创建的子进程 和父进程 共用一个空间。

九、exec函数族

        exec函数族功能:在进程中 启动另一个进程。 

#include<unistd.h>

  • int execl(const char *path, const char *arg, .../* (char *) NULL */);
  • int execlp(const char *file,cconst char *arg, ... /* (char *) NULL */);
  • int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */);
  • int execv(const char *path, char *const argv[]);
  • int execvp(const char *file, char *const argv[]);
  • int execvpe(const char *file, char *const argv[], char *const envp[]);
  • int execve(const char *filename, char *const argv[], char *const envp[]);  

         函数中有l(list)表明使用列表方式传参,函数中有v(vector)表明使用指针数组传参。 函数中有p(path)表明 到系统环境中 找可执行性文件 函数中有e(evn) 表明exec可以使用环境变量值

案例1:在代码中使用execl执行ls命令

查看ls命令的路径: 

 

一个进程调用exec后不会返回,exec函数族取代调用进程的数据段、代码段和堆栈段。除了进程ID,进程还保留了下列特征不变: 父进程号 进 程组号 控制终端 根目录 当前工作目录 进程信号屏蔽集 未处理信号 ... 

案例2:在代码中使用execlp执行ls命令 

案例3:在代码中使用execvp执行ls命令 

 案例4:vfork和exec配合使用,会为子进程开辟新的空间

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/457220.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于蓝牙技术的资产管理

随着物联网技术的不断发展&#xff0c;蓝牙技术已经成为了许多领域中不可或缺的一部分。在资产管理领域&#xff0c;基于蓝牙技术的资产定位管理方案正在逐渐普及&#xff0c;为企业提供了更加高效、精准的资产管理方式。本文将从蓝牙技术的原理、资产定位管理的需求、系统架构…

全栈之路-新坑就绪-星野空间

感觉自己的技术栈一直没有形成一个很好的闭环 开新坑&#xff0c;准备把自己的技术栈链路打通&#xff0c; Don‘t think too much&#xff0c; just act&#xff01;[得意]

python retry装饰器使用

第一个例子 import time from functools import wraps from typing import Callable, Any from time import sleepdef retry(retries: int 3, delay: float 1) -> Callable:"""Attempt to call a function, if it fails, try again with a specified dela…

基于单片机的恒压供水控制器设计

摘 要 随着我国现代化的进程不断加快&#xff0c;城市居民生活水平不断提高&#xff0c;随之而来的是房屋的翻新和重建&#xff0c;但建筑层数的不断增高&#xff0c;使得供水所需压力不断提高&#xff0c;若建筑设计时对压力判断不足&#xff0c;会导致供水时无法供应到高楼层…

NO9 蓝桥杯单片机之串口通信的使用

1 基本概念 简单来说&#xff0c;串口通信是一种按位&#xff08;bit&#xff09;传输数据的通信方式。 其他一些知识就直接贴图吧&#xff08;单工&#xff0c;半双工这些学过通信的同学应该都知道&#xff0c;可以上网查询一下具体概念。&#xff09; 来源还是&#xff1a;…

第16届大广赛XPPen都有哪些参赛命题

截至到发文时间&#xff0c;2024年3月14日&#xff0c;第16届大广赛已经累计公布了6个品牌命题&#xff0c;本文就给大家介绍一下XPPen命题的详细细节。 XPPen为汉王友基旗下全球知名数字艺术创新品牌&#xff0c;专注消费级用户创作需求&#xff0c;品牌产品覆盖全球160多个国…

一张图搞清楚wait、sleep、join、yield四者区别,面试官直接被征服!

写在开头 在线程的生命周期中&#xff0c;不同状态之间切换时&#xff0c;可以通过调用sleep()、wait()、join()、yield(&#xff09;等方法进行线程状态控制&#xff0c;针对这一部分知识点&#xff0c;面试官们也会做做文章&#xff0c;比如问你这些方法的作用以及之间的区别…

Linux中mysql的安装、远程访问、基础操作、文件导入

Linux中mysql的安装、远程访问、基础操作、文件导入 cheet card1. 安装1. 使用root账号安装mysql 2. 启动mysql并创建root、管理员两个账号3. 基础操作3.1 数据库的查看、创建、修改、删除3.2 mysql的数据类型3.3 数据表的基本操作3.4 数据表结构的修改3.5 表中数据的增、删、改…

Kotlin编程权威指南学习知识点预览

一、变量、常量和类型&#xff1a; 变量、常量以及 Kotlin 基本数据类型。变量和常量在 应用程序中可用来储值和传递数据。类型则用来描述常量或变量中保存的是什么样的数据。 1、声明变量: // 变量定义关键字 —— 变量名 —— 类型定义 —— 赋值运算符 —— 赋值var na…

用户案例|向量引擎在携程酒店搜索中的应用场景和探索

Zilliz AI 初创计划是面向 AI 初创企业推出的一项扶持计划&#xff0c;预计提供总计 1000 万元的 Zilliz Cloud 抵扣金&#xff0c;致力于帮助 AI 开发者构建高效的非结构化数据管理系统&#xff0c;助力打造高质量 AI 服务与运用&#xff0c;加速产业落地。访问https://zilliz…

log4j2 burp插件-Log4j2Scan(二)

该工具为被动扫描Log4j2漏洞CVE-2021-44228的BurpSuite插件&#xff0c;具有多DNSLog&#xff08;后端&#xff09;平台支持&#xff0c;支持异步并发检测、内网检测、延迟检测等功能。 一、安装方法 建议使用BurpSuite 2020或以上更高版本&#xff0c;低版本BurpSuite未经严…

4种小众的能力,帮你更好地适应未来

新年伊始&#xff0c;又是一个全新的开始。 未来的社会究竟需要什么样的能力&#xff1f;这已经是一个很老的话题&#xff0c;已经有许许多多讨论了。但这其中&#xff0c;可能有一些是容易被人忽略的&#xff0c;或者不容易被注意到的。 我想跟你一起分享&#xff0c;我对这个…

IAB视频广告标准《数字视频和有线电视广告格式指南》之 概述- IAB受众和技术标准 - 我为什么要翻译介绍美国人工智能科技公司IAB系列(2)

第二篇 - 概述- IAB受众和技术标准 本文目录 一、IAB技术实验室简介 二、概述及IAB受众 三、资源- IAB倡导的相关视频广告技术标准 四、案例分享-介绍一家数字化营销服务公司 - SproutSocial 五、数字营销工兵观察 六、资料来源及推荐阅读 一、IAB技术实验室简介 随着近…

河南大学数据结构实验-顺序栈和链栈的实现

计算机与信息工程学院实验报告 姓名&#xff1a;杨馥瑞 学号&#xff1a;2212080042 专业&#xff1a;数据科学与大数据技术 年级&#xff1a;2022 课程&#xff1a;数据结构 主讲教师&#xff1a;袁彩虹老师 辅导教师&#xff1a;_______ 实验时间&…

第五十八回 吴用赚金铃吊挂 宋江闹西岳华山-飞桨图像分割套件PaddleSeg初探

鲁智深被贺太守抓住&#xff0c;押入死牢。武松得信后&#xff0c;正想回梁山报信&#xff0c;正好戴宗来了&#xff0c;就请戴宗赶快回梁山搬救兵。宋江说兄弟有难&#xff0c;怎能不救&#xff1f; 于是带了十六个头领来到少华山。 因为华州城池厚壮&#xff0c;宋江等无计可…

Jmeter+ant,ant安装与配置

1.ant含义 ant&#xff1a;Ant翻译过来是蚂蚁的意思&#xff0c;在我们做接口测试的时候&#xff0c;是可以用来做JMeter接口测试生成测试报告的工具 2.ant下载 下载地址&#xff1a;Apache Ant - Ant Manual Distributions download中选择ant 下载安装最新版zip文件 3.…

【C++面向对象】C++飞机购票订票系统(源码+说明)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

HTML视频笔记

学习arm开发板&#xff0c;涉及到网页HTML PHP什么的&#xff0c;简单看个视频学习下 HTML&#xff5c;超简单HTML入门教程 https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element ​​​​​​vscode需要的libc6等需要的版本高​​​​​​ 为了安装vscode&#xff0c;将…

电商场景下 ES 搜索引擎的稳定性治理实践

继上文在完成了第一阶段 ES 搜索引擎的搭建后&#xff0c;已经能够实现对千万级别的商品索引的读写请求的支持。目前&#xff0c;单机房读流量在 500&#xff5e;1000 QPS 之间&#xff0c;写流量在 500 QPS 左右。 但随着业务的发展&#xff0c;问题也逐渐开始暴露&#xff0…

Qt篇——QChartView获取鼠标停留位置的数值

需求&#xff1a;鼠标停留在QChartView上时&#xff0c;想要计算停留位置的数值。 一开始的方法是想要通过鼠标移动事件计算鼠标在QChartView上的坐标&#xff0c;在换算成数值&#xff0c;后来发现QChartView中除了图表数据&#xff0c;还有坐标轴与坐标轴数值标签占了高度&a…