进程_PCB 的理解

目录

一. PCB 的概念

1. 为什么需要PCB

2. PCB的属性

二. task struct

1. task struct 介绍

2. 查看进程指令

3. PID

4. PPID

父进程是什么?

为什么要有父进程?

5. fork 创建子进程

1) fork 后的现象

为什么会打印两次?

2) 的返回值

getpid petppid

3) fork 如何办到?

三. 总结


一. PCB 的概念

在我们的操作系统中,如果我们想要运行一个程序,那么我们显然是需要将程序加载到内存中的,所以我们可以先不怎么准确的理解一下进程是什么——加载到内存中的程序。

更为官方的概念是这样的,进程 PCB(Process Control Block,进程控制块)是操作系统中用于描述和管理进程的数据结构。每个正在运行的进程都有一个对应的进程 PCB。

那么我们首先谈论一下为什么需要进程

1. 为什么需要PCB

我们前面说了,我们的进程就是一个程序加载到内存,那么我们的系统中显然是不可能只有一个进程的,因为我们需要运行很多软件(进程),所以我们是需要对这么多的进程需要进行管理的,而我们的操作系统如何管理?   先描述再组织。

那么我们如何描述一个进程呢?通过用数据结构来描述进程的特定的属性来描述进程,所以我们用来描述进程的数据结构就叫做PCB,而我们的操作系统也就是通过管理PCB来管理我们的进程。

但是我们上面一直是在说PCB,那么PCB就等于进程吗? 这里说一下,其实PCB并不等于进程,我们说了PCB只是系统给我们创建的一个用来描述进程的控制块,但是我们不仅仅需要用来描述,我们还是需要我们自己程序的代码和数据,所以我们的进程应该是: 进程 = PCB + 代码和数据

根据上面说的,我们的PCB就是用来描述我们的进程的一个控制块,而PCB的作用就是方便操作系统对我们的进程进行管理。 

2. PCB的属性

我们现在知道,PCB就是系统用来描述进程的一个数据结构,那么既然是一个数据结构里面当然是有各种关于进程的属性的,我们下面看一下关于进程的各种属性。

  • 进程标识符(Process ID):用于唯一标识一个进程。
  • 程序计数器(Program Counter):记录当前进程正在执行的指令位置。
  • 寄存器状态(Register State):包括通用寄存器、堆栈指针和程序状态字等寄存器的内容。
  • 进程状态(Process State):表示进程的当前状态,如运行、就绪、等待等。
  • 内存信息:包括进程的代码段、数据段和堆栈段的起始地址和大小。
  • 调度信息:包括进程的优先级、调度算法使用的时间片大小等。
  • 进程控制信息:包括父进程标识符、子进程标识符、打开的文件列表等。

这上面只是笼统的介绍一下,后面会详细说的~

二. task struct

前面说的是大多数操作系统的一个概念,但是如果我们想要学习操作系统,那么我们必须要学习的是一款具体的操作系统,我们这里说的就是 linux

1. task struct 介绍

我们这里说的 task struct 就是 PCB ,而PCB是所有操作系统里面进程的一个叫法,而我们的
task struct 是一款具体的操作系统(linux)里面的PCB的一个叫法。

task struct 里面的属性和PCB里面的都是差不多的,但是肯定是有差别的。

下面我们就先简单的看一下 linux 里面的进程

2. 查看进程指令

指令:ls /proc(查看所有进程)

指令:ps axj (查看所有进程)

我们现在写一个死循环,然后我们执行该程序,在执行的时候我们的该程序会被加载到内存,然后被执行,我们使用命令查看该进程

测试代码:

#include<stdio.h>
#include<unistd.h>

int main()
{
  while(1)
  {
    printf("I am a process...\n");
    sleep(2);
  }
  return 0;
}


上面是 ps axj 查看,下面是 ls /proc查看 

其实这样看的管感并不太好,我们还可以使用 grep 命令来查看

 [lxy@hecs-165234 linux2]$ ps axj | head -1 && ps axj | grep myprocess
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
18563 18611 18611 18563 pts/1    18611 S+    1000   0:00 ./myprocess
18169 18859 18858 18169 pts/0    18858 D+    1000   0:00 grep --color=auto myprocess

前面的那个是为了查看第一行

我们看到,我们查到的内容里面有几个属性,我们这里先值说一个,PID(进程的标识符),也就是在系统中表示唯一的一个进程

既然我们现在知道了PID,那么我们看到 ls /proc查看到的里面的那些数字,其实就是PID

我们可以通过 ls /proc 查看一下我们的 myproc 进程,然后我们关掉我们的程序后在查看一下

[lxy@hecs-165234 linux2]$ ls /proc | grep 18611
18611

[lxy@hecs-165234 linux2]$ ls /proc | grep 18611
[lxy@hecs-165234 linux2]$ 

 我们看到我们第一次查到了,然后关掉程序后就查看不到了

我们继续启动程序,然后我们查看该程序

[lxy@hecs-165234 linux2]$ ps axj | grep myprocess
18563 18940 18940 18563 pts/1    18940 S+    1000   0:00 ./myprocess
18169 18942 18941 18169 pts/0    18941 R+    1000   0:00 grep --color=auto myprocess
[lxy@hecs-165234 linux2]$ ls /proc/18940 -dl
dr-xr-xr-x 9 lxy lxy 0 Jul 12 19:04 /proc/18940
[lxy@hecs-165234 linux2]$ 

我们看到我们的 18940 是一个文件,那么我们打开看一下里面有一些什么内容

lxy@hecs-165234 linux2]$ cd /proc/18940
[lxy@hecs-165234 18940]$ ll
total 0
dr-xr-xr-x 2 lxy lxy 0 Jul 12 19:06 attr
-rw-r--r-- 1 lxy lxy 0 Jul 12 19:06 autogroup
-r-------- 1 lxy lxy 0 Jul 12 19:06 auxv
-r--r--r-- 1 lxy lxy 0 Jul 12 19:06 cgroup
--w------- 1 lxy lxy 0 Jul 12 19:06 clear_refs
-r--r--r-- 1 lxy lxy 0 Jul 12 19:04 cmdline
-rw-r--r-- 1 lxy lxy 0 Jul 12 19:06 comm
-rw-r--r-- 1 lxy lxy 0 Jul 12 19:06 coredump_filter
-r--r--r-- 1 lxy lxy 0 Jul 12 19:06 cpuset
lrwxrwxrwx 1 lxy lxy 0 Jul 12 19:06 cwd -> /home/lxy/108/linux2
-r-------- 1 lxy lxy 0 Jul 12 19:06 environ
lrwxrwxrwx 1 lxy lxy 0 Jul 12 19:06 exe -> /home/lxy/108/linux2/myprocess
dr-x------ 2 lxy lxy 0 Jul 12 19:04 fd
dr-x------ 2 lxy lxy 0 Jul 12 19:06 fdinfo
-rw-r--r-- 1 lxy lxy 0 Jul 12 19:06 gid_map
-r-------- 1 lxy lxy 0 Jul 12 19:06 io
-r--r--r-- 1 lxy lxy 0 Jul 12 19:06 limits
-rw-r--r-- 1 lxy lxy 0 Jul 12 19:06 loginuid
dr-x------ 2 lxy lxy 0 Jul 12 19:06 map_files
-r--r--r-- 1 lxy lxy 0 Jul 12 19:06 maps
-rw------- 1 lxy lxy 0 Jul 12 19:06 mem
-r--r--r-- 1 lxy lxy 0 Jul 12 19:06 mountinfo
-r--r--r-- 1 lxy lxy 0 Jul 12 19:06 mounts
-r-------- 1 lxy lxy 0 Jul 12 19:06 mountstats
dr-xr-xr-x 5 lxy lxy 0 Jul 12 19:06 net
dr-x--x--x 2 lxy lxy 0 Jul 12 19:06 ns
-r--r--r-- 1 lxy lxy 0 Jul 12 19:06 numa_maps
-rw-r--r-- 1 lxy lxy 0 Jul 12 19:06 oom_adj
-r--r--r-- 1 lxy lxy 0 Jul 12 19:06 oom_score
-rw-r--r-- 1 lxy lxy 0 Jul 12 19:06 oom_score_adj
-r--r--r-- 1 lxy lxy 0 Jul 12 19:06 pagemap
-r-------- 1 lxy lxy 0 Jul 12 19:06 patch_state
-r--r--r-- 1 lxy lxy 0 Jul 12 19:06 personality
-rw-r--r-- 1 lxy lxy 0 Jul 12 19:06 projid_map
lrwxrwxrwx 1 lxy lxy 0 Jul 12 19:06 root -> /
-rw-r--r-- 1 lxy lxy 0 Jul 12 19:06 sched
-r--r--r-- 1 lxy lxy 0 Jul 12 19:06 schedstat
-r--r--r-- 1 lxy lxy 0 Jul 12 19:06 sessionid
-rw-r--r-- 1 lxy lxy 0 Jul 12 19:06 setgroups
-r--r--r-- 1 lxy lxy 0 Jul 12 19:06 smaps
-r--r--r-- 1 lxy lxy 0 Jul 12 19:06 stack
-r--r--r-- 1 lxy lxy 0 Jul 12 19:04 stat
-r--r--r-- 1 lxy lxy 0 Jul 12 19:06 statm
-r--r--r-- 1 lxy lxy 0 Jul 12 19:04 status
-r--r--r-- 1 lxy lxy 0 Jul 12 19:06 syscall
dr-xr-xr-x 3 lxy lxy 0 Jul 12 19:06 task
-r--r--r-- 1 lxy lxy 0 Jul 12 19:06 timers
-rw-r--r-- 1 lxy lxy 0 Jul 12 19:06 uid_map
-r--r--r-- 1 lxy lxy 0 Jul 12 19:06 wchan

 这里面我们介绍两个字段

  • exe:我们前面说了,我们的程序就是 PCB + 代码和数据,而我们的 exe就是软连接指向我们的代码和数据,而我们的代码和数据就在后面的那条路劲下
  • cwd:我们的一个程序里面其实有一个默认的路劲,就是当前目录,我们的程序在哪里执行,我们的默认当前目录就在哪个目录下

3. PID

在操作系统中,每一个进程都是由自己独立的编号的,而在PCB中进程的编号就是 PID, PID 可以表示某一个操作系统中的特定的一个进程。

现在我们自己编写一个程序,我们的程序之打印 hello world,下面查看该进程的 PID

#include<stdio.h>
#include<unistd.h>


int main()
{
  while(1)
  {
    printf("hello world\n");
    sleep(1);
  }
  return 0;
}

查看进程的前面已经说过了,下面直接查看一下。

[lxy@hecs-165234 linux3]$ ps axj | head -1 && ps axj | grep proc
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 6468  6552  6552  6468 pts/1     6552 S+    1000   0:00 ./proc

这里使用 head -1 来显示第一行的信息,第一行可以看到 PID 而该进程的 PID 就是 6552

4. PPID

这里先介绍 PPID 是什么,PPID 就是 该进程的父进程。

父进程是什么?

父进程就是创建该进程的进程,就叫做父进程,而在 linux 中,进程的创建时有两种方法的:

  • 第一种就是: ./程序名,然后操作系统会帮助我们将该进程的代码和数据加载到内存中,然后操作系统也会帮我们为该进程创建对应的 tash_struct 
  • 第二种就是:自己创建,操作系统为我们提供一部分接口,而用户就可以使用这一部分接口来手动的创建进程(fork函数)。

所以系统中的进程都是由别的进程创建的,或者是自己手动创建的,而当用户 ./ 执行一个程序的时候其实也是由父进程的,而这个父进程就是 bash 这个之前说过,bash 在 这里就是命令行解释器,当 bash 察觉到我们是要启动一个进程的时候,就是 bash 帮助用户创建进程,而这里的进程的父进程也就是 bash。

下面查看一下刚才的 6468 的进程,这里可以看到就是 bash

[lxy@hecs-165234 linux3]$ ps axj | head -1 && ps axj | grep 6468
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 6467  6468  6468  6468 pts/1     6468 Ss+   1000   0:00 -bash

为什么要有父进程?

这个我们在下一期说。

5. fork 创建子进程

前面说了, fork 可以创建子进程,那么怎么创建呢?

fork 是一个系统接口,所以如果当我们不明白的时候我们可以使用 man 进程查看该函数的信息。

[lxy@hecs-165234 linux3]$ man 2 fork


NAME
       fork - create a child process

SYNOPSIS
       #include <unistd.h>

       pid_t fork(void);

RETURN VALUE
       On  success, the PID of the child process is returned in the parent, and 0 is
       returned in the child.  On failure, -1 is returned in the  parent,  no  child
       process is created, and errno is set appropriately.

这里没有全部显示出来,想了解的可以自己查看。

这里可以看到,该函数的作用就是创建一个子进程,然后下面就是该函数的返回值和头文件,而该函数成功的话有两个返回值, 一个是给父进程返回子进程的 PID, 另一个是给子进程返回0,如果失败的话,就返回 -1.

下面看一下使用。

1) fork 后的现象

#include<stdio.h>
#include<unistd.h>


int main()
{
  printf("begin....\n");
  fork();
  printf("end....\n");
  sleep(1);
  return 0;
}

运行.... 

[lxy@hecs-165234 linux3]$ ./proc 
begin....
end....
end....

这里是运行后的结果,看到 end 打印的两次。

为什么会打印两次?

第一次解释:

因为 fork 后就会创建一个进程,然后这时候就会有两个执行流执行,所以说 end 被打印了两次

2) 的返回值

这里继续编写一段代码。

 

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>

int main()
{
  pid_t id = fork();
  if(id == 0)
  {
    // 子进程
    while(1)
    {
      printf("I am a child... \n");
      sleep(1);
    }
  }
  else if(id > 0)
  {
    // 父进程
    while(1)
    {    
      printf("I am a father... \n");
      sleep(1);
    }
  }
  else{
    //创建失败
  }
  return 0;
}

然后我们运行看一下结果。

[lxy@hecs-165234 linux3]$ ./proc 
I am a father... 
I am a child... 
I am a father... 
I am a child... 
I am a father... 
I am a child... 

结果就是我们该程序不仅在打印 father 还在打印 child ,我们该进程不仅进了 if 还进了 else if,所以为什么会有这个现象呢?

下面在看一下查看到的进程。

[lxy@hecs-165234 linux3]$ ps axj | head -1 && ps axj | grep proc
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 6468  7387  7387  6468 pts/1     7387 S+    1000   0:00 ./proc
 7387  7388  7387  6468 pts/1     7387 S+    1000   0:00 ./proc

这里看到确实是这样,第一个是父进程,第二个是子进程,子进程的 PPID 就是父进程的 PID

在下面开始之前先介绍两个函数。

getpid petppid
NAME
       getpid, getppid - get process identification

SYNOPSIS
       #include <sys/types.h>
       #include <unistd.h>

       pid_t getpid(void);
       pid_t getppid(void);

DESCRIPTION
       getpid()  returns the process ID of the calling process.  (This is often used
       by routines that generate unique temporary filenames.)

这就是两个函数的介绍,我这里在简单介绍一下,这两个函数一个是返回PID 另一个是返回 PPID 其中,谁调用该函数就返回谁的 PID 和 PPID。

这里为什么会有上面的结果:

  • fork 有两个返回值
  • fork  后又两个进程在执行代码

3) fork 如何办到?

概念铺设:进程之间是互相独立的。进程是有自己的代码和数据的,所以进程之间是互不干扰的。

  • fork为何会有两个返回值:首先,我们相信 fork 会帮我们创建好子进程,而fork 是有返回值的,那么当fork执行到 return 的时候主要的逻辑就执行结束了,所以返回之前就已经创建好了,所以就可以返回两个返回值。
  • fork 后为什么一个 id 里面看起来是有两份数据?在 fork 创建进程后,子进程是和父进程共享代码的,因为子进程没有自己独立的代码,而且代码是不会被修改的,所以没有必要拷贝一份代码。
  • 子进程和父进程的数据是“写时拷贝”的,就是当子进程不修改数据的时候,那么和父进程时共用同一份数据,那么当子进程修改数据后,会为子进程单独拷贝该数据到子进程的地址空间里面,所以就实现了由看起来时一份数据,但是存储了两份数值。

三. 总结

  • PCB 就是一个用来描述进程的一个控制块,操作系统通过对 PCB 进行管理来达到对进程进行管理。
  • task_struct 是 linux 中的 PCB,tash_struct 中有很多属性用来描述该PCB中的信息,其中PID 就是该进程控制块的编号,PPID 就是该进程的父进程
  • fork 是用户自己创建进程的一个手段,fork 通过不同的返回值来进行分流达到父进程和子进程可以区分来完成不同的工作。

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

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

相关文章

matplotlib从起点出发(6)_Tutorial_6_Animations

1 在matplotlib中使用动画 基于其绘图功能&#xff0c;matplotlib还提供了一个使用动画模块生成动画animation的接口。动画是一系列帧&#xff0c;其中每个帧对应于图形Figure 上的一个绘图。本教程介绍了有关如何创建此类动画的一般准则以及可用的不同选项。 import matplot…

掌握Python的X篇_16_list的切片、len和in操作

接上篇掌握Python的X篇_15_list容器的基本使用&#xff0c;本篇进行进一步的介绍。 文章目录 1. list的索引下标可以是负数2. 切片&#xff08;slice&#xff09;2.1 切片基础知识2.2 如何“取到尽头”2.3 按照步长取元素2.4 逆序取值 3. len函数获取lis的元素个数4. in操作符…

自动驾驶感知系统--惯性导航定位系统

惯性导航定位 惯性是所有质量体本身的基本属性&#xff0c;所以建立在牛顿定律基础上的惯性导航系统&#xff08;Inertial Navigation System,INS&#xff09;(简称惯导系统)不与外界发生任何光电联系&#xff0c;仅靠系统本身就能对车辆进行连续的三维定位和三维定向。卫星导…

【嵌入式Linux项目】基于Linux的全志H616开发板智能家居项目(语音控制、人脸识别、安卓APP和PC端QT客户端远程操控)有视频功能展示

目录 一、功能需求 二、开发环境 1、硬件&#xff1a; 2、软件&#xff1a; 3、引脚分配&#xff1a; 三、关键点 1、设计模式之工厂模式 2、wiringPi库下的相关硬件操作函数调用 3、语音模块的串口通信 4、线程 5、摄像头的实时监控和拍照功能 6、人脸识别 四、编…

Python web实战 | 使用 Django 搭建 Web 应用程序 【干货】

概要 从社交媒体到在线购物&#xff0c;从在线银行到在线医疗&#xff0c;Web 应用程序为人们提供了方便快捷的服务。Web 应用程序已经成为了人们日常生活中不可或缺的一部分。搭建一个高效、稳定、易用的 Web 应用程序并不是一件容易的事情。本文将介绍如何使用 Django 快速搭…

Python基础入门教程(上)

目录 一、你好Python 1.1、Python安装 win版 Linux版 1.2、第一个Python程序 二、Python基本语法 2.1、字面量 2.2、注释 2.3、变量 2.4、数据类型 type()函数 字符串类型的不同定义方式 2.5、数据类型转换 ​编辑 2.6、标识符 2.7、运算符 2.8、字符串扩展 …

Linux安装kafka3.5.1

要在Ubuntu上安装Apache Kafka&#xff0c;请按照以下步骤操作&#xff1a; 1、安装Java运行时环境(Ubuntu)&#xff1a; 如果已经安装jdk不用执行 sudo apt update sudo apt install default-jre2、下载Kafka&#xff1a; wget https://downloads.apache.org/kafka/3.5.1/…

【【51单片机的红外遥控】】

红外遥控&#xff0c;完全把控 红外遥控 利用红外光进行通信的设备&#xff0c;由红外LED将调制后的信号发出&#xff0c;再由专门的红外接收头进行解调输出 通信方式&#xff1a;单工 异步 红外LED波长&#xff1a;940nm 通信协议标准&#xff1a;NEC标准 用那种一体化红红外…

下级平台级联视频汇聚融合平台EasyCVR,层级显示不正确的原因排查

视频汇聚平台安防监控EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有GB28181、RTSP/Onvif、RTMP等&#xff0c;以及厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等&#xff0c;能对外分发RTSP、RTMP、FLV、HLS、WebRTC等…

如何使用 After Effects 导出摄像机跟踪数据到 3ds Max

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 在本教程中&#xff0c;我将展示如何在After Effects中跟踪实景场景&#xff0c;然后将相机数据导出到3ds Max。 1. 项目设置 步骤 1 打开“后效”。 打开后效果 步骤 2 转到合成>新合成以创建新合…

Rust vs Go:常用语法对比(十二)

题图来自 Rust vs Go in 2023[1] 221. Remove all non-digits characters Create string t from string s, keeping only digit characters 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. 删除所有非数字字符 package mainimport ( "fmt" "regexp")func main() { s : hei…

运行时数据区

点击下方关注我&#xff0c;然后右上角点击...“设为星标”&#xff0c;就能第一时间收到更新推送啦~~~ 类文件被类装载器加载之后&#xff0c;类中的内容&#xff08;比如&#xff1a;变量、常量、方法、对象等&#xff09;这些数据需要存储起来&#xff0c;存储的位置就是在 …

RabbitMQ 教程 | 客户端开发向导

&#x1f468;&#x1f3fb;‍&#x1f4bb; 热爱摄影的程序员 &#x1f468;&#x1f3fb;‍&#x1f3a8; 喜欢编码的设计师 &#x1f9d5;&#x1f3fb; 擅长设计的剪辑师 &#x1f9d1;&#x1f3fb;‍&#x1f3eb; 一位高冷无情的编码爱好者 大家好&#xff0c;我是 DevO…

JMeter常用内置对象:vars、ctx、prev

在前文 Beanshell Sampler 与 Beanshell 断言 中&#xff0c;初步阐述了JMeter beanshell的使用&#xff0c;接下来归集整理了JMeter beanshell 中常用的内置对象及其使用。 注&#xff1a;示例使用JMeter版本为5.1 1. vars 如 API 文档 所言&#xff0c;这是定义变量的类&a…

【点云处理教程】04 Python 中的点云过滤

一、说明 这是我的“点云处理”教程的第 4 篇文章。“点云处理”教程对初学者友好&#xff0c;我们将在其中简单地介绍从数据准备到数据分割和分类的点云处理管道。 在本教程中&#xff0c;我们将学习如何使用 Open3D 在 python 中过滤点云以进行下采样和异常值去除。使用 Open…

Python将COCO格式实例分割数据集转换为YOLO格式实例分割数据集

Python将COCO格式实例分割数据集转换为YOLO格式实例分割数据集 前言相关介绍COCO格式实例分割数据集转换为YOLO格式实例分割数据集coco格式对应的json文件&#xff0c;以test.json为例格式转换代码&#xff0c;内容如下 前言 由于本人水平有限&#xff0c;难免出现错漏&#xf…

【JAVASE】什么是方法

⭐ 作者&#xff1a;小胡_不糊涂 &#x1f331; 作者主页&#xff1a;小胡_不糊涂的个人主页 &#x1f4c0; 收录专栏&#xff1a;浅谈Java &#x1f496; 持续更文&#xff0c;关注博主少走弯路&#xff0c;谢谢大家支持 &#x1f496; 方法 1. 方法概念及使用1.1 什么是方法1…

Vue『卡片拖拽式课程表』

Vue『卡片拖拽式课程表』 概述 在本篇技术博客中&#xff0c;我们将介绍一个使用Vue实现的『卡片拖拽式课程表』。这个课程表允许用户通过拖拽课程卡片来安排不同的课程在时间表上的位置。我们将逐步讲解代码实现&#xff0c;包括课程表的布局、拖拽功能的实现&#xff0c;以…

6G内存运行Llama2-Chinese-7B-chat模型

6G内存运行Llama2-Chinese-7B-chat模型 Llama2-Chinese中文社区 第一步&#xff1a; 从huggingface下载 Llama2-Chinese-7b-Chat-GGML模型放到本地的某一目录。 第二步&#xff1a; 执行python程序 git clone https://github.com/Rayrtfr/llama2-webui.gitcd llama2-web…

QtC++ 技术分析3 - IOStream

目录 iostreamscanf/printfiostream 整体架构流相关类流缓冲区 模板特化后整体结构文件流文件流对象创建常见文件流操作输出格式设定文件流状态 字符串流字符串流内部缓冲区字符串流使用 流缓冲区用户自定义 IO iostream scanf/printf 几种常见的输入输出流函数 scanf 从键盘…