当前位置: 首页 > news >正文

Linux 终端、进程组、会话、守护进程

文章目录

    • 一、终端概念
      • 终端概念
      • 控制终端
    • 二、进程组概念
      • 进程组概述
      • 进程组相关 API
    • 会话
      • 会话概念
      • 会话相关 API
      • 创建会话注意事项
    • 守护进程
      • 守护进程介绍
      • 守护进程模型
      • 守护进程参考代码
      • 守护进程相关 API
      • 参考文章

一、终端概念

终端概念

1、终端(Terminal)

终端是物理设备,只用于输入输出,本身没有强大的计算能力。在计算资源紧张的时代,人们想共享一台计算机,可以通过终端连接到计算机上,将指令输入终端,终端传送给计算机,计算机完成指令后,将输出传送给终端,终端将结果显示给用户,所以有显示器和键盘能够通过串口连接到计算机的设备就叫终端

2、控制台(Console)

控制台也是物理设备,也用于输入输出,但它直接连接在计算机上,是计算机系统的一部分,所以直接连接在电脑上的键盘和显示器就叫做控制台。计算机启动的时候,所有的信息都会显示到控制台上,而不会显示到终端上。简单的说,能直接显示系统消息的那个终端称为控制台,其他的则称为终端

3、控制台与终端的区别

终端是通过串口连接上的,不是计算机本身就有的设备,而控制台是计算机本身就有的设备,一个计算机只有一个控制台。也就是说,控制台是计算机的基本设备,而终端是附加设备。

简言之,终端为主机提供了人机接口,每个人都通过终端使用主机的资源。终端有字符终端和图形终端两种。一台主机可以连很多终端。
控制台是一种特殊的人机接口(控制台也是终端的一种), 是人控制主机的第一人机接口,而主机对于控制台的信任度高于其他终端

4、TTY

TTY 是 Teletype 或 Teletypewriter 的缩写,原来是指电传打字机,后来这种设备逐渐键盘和显示器取代。不管是电传打字机还是键盘显示器,都是作为计算机的终端设备存在的,所以 TTY 也泛指计算机的终端设备

5、虚拟控制台(Virtual Console)与虚拟终端(Virtual Terminal)

虚拟控制台和虚拟终端是一样的:如果我们只有一台终端,这是我们与计算机之间的用户接口,假如有一天,我们想拥有多个用户接口,那么,一方面我们可以增加终端数目,另一方面,还可以在同一台终端上虚拟出多个终端,它们之间互相不影响,至少看起来互相不影响。这些终端就是虚拟终端。Linux 默认所有虚拟终端都是控制台,都能显示系统消息,所以我们在平时的使用中压根就不区分 Linux 中的终端与控制台。

  • 在 Ubuntu 桌面版中,一共有七个虚拟终端:tty1-7(服务器版一般有六个虚拟终端:tty1-6),其中第七个虚拟终端是图形用户界面。
  • 我们可以使用 CTRL+ALT+fn 切换虚拟终端,例如: 我们按下 Ctrl+Alt+F1 时,会进入 tty1。

6、模拟终端器

如今,终端不再是一个需要通过 UART 连接到计算机上物理设备。终端成为内核的一个模块,它可以直接向 TTY 驱动发送字符,并从 TTY 驱动读取响应然后打印到屏幕上。也就是说,用内核模块模拟物理终端设备,因此被称为终端模拟器,所以终端是一种物理设备,而终端模拟器,是一个程序,这些程序用来模拟物理终端

7、shell

和之前说的几个概念截然不同,之前的几个概念都是与计算机的输入输出相关的,而shell是和内核相关的。内核为上层的应用提供了很多服务,shell在内核的上层,在应用程序的下层。例如,你写了一个 hello world 程序,你并不用显式地创建一个进程来运行你的程序,你把写好的程序交给 shell 就行了,由 shell 负责为你的程序创建进程。

我们在终端模拟器中输入命令时,终端模拟器本身并不解释执行这些命令,它只负责输入输出,真正解释执行这些命令的,是 shell。我们平时使用的 sh, bash, csh 是shell 的不实现。

控制终端

由于在 linux 中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端(Controlling Terminal),当控制终端被关闭时,相应的进程都会自动关闭:用户通过终端登录系统后得到一个 Shell 进程,这个终端成为 Shell 进程的控制终端,进程中,控制终端是保存在 PCB 的信息中,而 fork 会复制 PCB 中的信息,因此由 Shell 进程启动的其它进程的控制终端也是这个终端。

默认情况下(没有重定向),每个进程的标准输入、标准输出和标准错误输出都指向控制终端,进程从标准输入读也就是读用户的键盘输入,进程往标准输出或标准错误输出写也就是输出到显示器上。

[[10_ Linux 进程通讯#四、进程间通讯之信号|信号]]一节中还讲过,在控制终端输入一些特殊的控制键可以给前台进程发信号,例如 Ctrl+C 表示 SIGINT,Ctrl+\ 表示 SIGQUIT。

Linux 中所讲的终端,除非特别说明,否则一般都是指控制终端。
(1)tty 命令可以查看当前终端的名字。

yxm@192:~$ tty
/dev/pts/1

(2)也可以通过函数查看终端的名字,函数说明如下:

#include <unistd.h>
char *ttyname(int fd);
功能:由文件描述符查出对应的文件名
参数:fd:文件描述符
返回值:成功:终端名失败:NULL

下面我们借助 ttyname 函数,通过实验看一下各种不同的终端所对应的设备文件名:

#include <stdio.h>
#include <unistd.h>
int main() {printf("fd 0: %s\n", ttyname(0));printf("fd 1: %s\n", ttyname(1));printf("fd 2: %s\n", ttyname(2));return 0;
}
yxm@192:~$ gcc test.c -o test
yxm@192:~$ ./test 
fd 0: /dev/pts/0
fd 1: /dev/pts/0
fd 2: /dev/pts/0

(3)ps -aux 中 TTY 字段:

  1. TTY 字段显示的是 tty1-tty6 说明是在本地机器的命令行下登录的。 tty7 说明是在本地机器的图形界面下登录的
  2. TTY 字段显示的是 pts 说明是用远程工具连接的,比如 xshell,后面的数字代表登录的时间顺序,越小证明登录的越早。

(4)一般情况下,当前终端也是一个进程,在当前终端开启的程序,它的父进程一般也是当前终端

二、进程组概念

进程组概述

进程组,也称之为作业。BSD 于 1980 年前后向 Unix 中增加的一个新特性。代表一个或多个进程的集合。所以进行组由一个或多个共享同一进程组的进程组成。操作系统设计的进程组的概念,是为了简化对多个进程的管理

每个进程都属于一个进程组。当父进程,创建子进程的时候,默认子进程与父进程属于同一进程组。进程组 ID 为第一个进程的 ID,进程组的第一个进程称为组长进程(也叫首进程)。所以,对于组长进程标识而言,其进程组 ID 为其本身进程 ID。进程组的标识符为 PGID:

进程组拥有一个生命周期,其开始时间为首进程创建组的时刻,结束时间为最后一个成员进程离开(终止或转移到另一个进程组)组的时刻。一个进程可能会因为终止而退出进程组,也可能会因为加入了另外一个进程组而退出进程组。进程组首进程无需是最后一个离开进程组的成员:只要进程组中有一个进程存在,进程组就存在,与组长进程是否终止无关。

进程组的概念在 [[09_Linux 进程基础#4、进程回收|waitpid 函数]] 和 [[09_Linux 进程基础#2、进程控制命令|kill ]]中都曾使用到。例如可以使用 kill -SIGKILL -进程组ID (负的)来将整个进程组内的进程全部杀死。

前台后台进程

Linux是一种用户控制的多作业操作系统, 系统允许多个系统用户同时提交作业,而一个系统用户可以用多个 shell 登录,每个系统用户可以用一个 shell 提交多个作业。作业有两种运行方式:前台运行和后台运行。

  • 前台运行:能够控制当前终端或窗口,并且接受用户输入;
  • 后台运行:不在当前激活的终端或窗口中运行,而是在用户“看不到”的情况下运行。
    以前台运行方式运行的进程称作前台进程;以后台运行方式运行的进程称作后台进程。

前台进程与后台进程特点如下:

  • 前台进程:
    • 在终端中启动运行的程序,那么该终端就为进程的控制终端,一旦这个终端关闭,这个进程也随之消失。
    • 只有前台进程才能从控制终端中读取与输入。
    • 一般情况下,前台进程的父进程是当前终端。
    • 使用 CTRL+C 可以强行终止前台进程。
  • 后台进程:
    • 后台进程与控制终端脱离(【注意】后台程序并未完全脱离终端,关闭当前终端也导致该后台进程退出,此处与守护进程存在区别,详细可以看下文“守护进程”一节内容)。
    • 后台进程无法控制读取与输入:后台进程并未完全脱离终端,所以在终端未关闭前还是会往终端输出结果,只是输出并不受控制终端控制,具体表现是程序王终端输出结果,同时终端还会出现命令提示符,可以在终端输入命令。
    • 后台进程的父进程为系统进程(1号进程)
    • 使用 CTRL+C 无法终止后台进程,需要使用 kill -9 NNN 才能强行终止后台进程

进程组相关 API

一个进程可以为自己或子进程设置进程组 ID。

#include <unistd.h>pid_t getpgrp(void);                 /* POSIX.1 version */
功能:获取当前进程的进程组ID
参数:无
返回值:总是返回调用者的进程组IDpid_t getpgid(pid_t pid);
功能:获取指定进程的进程组ID
参数:pid:进程号,如果pid = 0,那么该函数作用和getpgrp一样
返回值:成功:进程组ID失败:-1int setpgid(pid_t pid, pid_t pgid);
功能:改变进程默认所属的进程组。通常可用来加入一个现有的进程组或创建一个新进程组。
参数:将参1对应的进程,加入参2对应的进程组中
返回值:成功:0失败:-1

会话

会话概念

会话是一个或多个进程组的集合。会话首进程是创建该新会话的进程,其进程 ID 会成为会话 ID。新进程会继承其父进程的会话 ID。 一个会话中的所有进程共享单个控制终端。控制终端会在会话首进程首次打开一个终端设备时被建立。一个终端最多可能会成为一个会话的控制终端。

  • 一个会话可以有一个控制终端。这通常是终端设备或伪终端设备;
  • 建立与控制终端连接的会话首进程被称为控制进程;当控制终端的连接建立起来之后,会话首进程会成为该终端的控制进程。
  • 一个会话中的几个进程组可被分为一个前台进程组以及一个或多个后台进程组(前台进程组中的进程以前台运行方式运行;后台进程组中的进程以后台运行方式运行);
  • 如果一个会话有一个控制终端,则它有一个前台进程组,其它进程组为后台进程组;
  • 在任一时刻,会话中的其中一个进程组会成为终端的前台进程组,其他进程组会成为 后台进程组。只有前台进程组中的进程才能从控制终端中读取输入。当用户在控制终 端中输入终端字符生成信号后,该信号会被发送到前台进程组中的所有成员。
  • 如果终端接口检测到断开连接,则将挂断信号发送至控制进程(会话首进程)。

进程组和会话:进程组和会话在进程之间形成了一种两级层次关系,进程组是一组相关进程的集合,会话是一组相关进程组的集合。进程组和会话是为支持 shell 作业控制而定义的抽象概念,用户通过 shell 能够交互式地在前台或后台运行命令示例

在某个终端上执行上面两个命令:find / 2 > /dev/null | wc -l &sort < longlist | uniq -c

  • 一开始,打开一个终端,会话的首进程默认是bash(即shell进程),id 为 400。
  • 接着,执行 find / 2 > /dev/null | wc -l & 命令后,创建一个新的进程组,该进程组 id 是 658,进程组中包括 find 进程和 wc 进程,它们的父进程 id 是 400,会话 id 也是 400,且该进程组以后台方式运行,所以是后台进程。
  • 接着,执行 sort < longlist | uniq -c 命令后,创建一个新的进程组,该进程组 id 是 660,进程组中包括 sort 进程和 uniq 进程,它们的父进程和会话 id 也是 400,默认情况下都是运行在前台,前台进程组才享有控制终端的操作权利。

会话相关 API

#include <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

创建会话注意事项

  1. 调用进程不能是进程组组长,该进程变成新会话首进程(session header),如果该调用进程是组长进程,则出错返回;
  2. 该进程成为一个新进程组的组长进程;
  3. 需有root权限(ubuntu不需要);
  4. 新会话丢弃原有的控制终端,该会话没有控制终端;
  5. 建立新会话时,先调用 fork, 父进程终止,子进程调用 setsid;
  6. 组长进程不能成为新会话首进程,新会话首进程必定会成为组长进程。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>int main(void) {pid  pid = -1;pid = getsid(0);if (-1 == pid) {perror("getsid");return 1;}printf("sid:%d\n", pid);// 创建一个新的回话pid = setsid();    // 创建会失败,因为:调用进程不能是进程组组长,该进程变成新会话首进程if (-1 == pid) {perror("setsid");return 1;}return 0;   
}

守护进程

守护进程介绍

守护进程(Daemon Process),也就是通常说的 Daemon 进程(精灵进程),是 Linux 中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。**文件名一般采用以 d 结尾的名字。**守护进程具备下列特征:

  • 生命周期很长,守护进程会在系统启动的时候被创建并一直运行直至系统被关闭。
  • 守护进程是个特殊的孤儿进程,它在后台运行并且不拥有控制终端。没有控制终端确保了内核永远不会为守护进程自动生成任何控制信号以及终端相关的信号(如 SIGINT、SIGQUIT),以避免进程被任何终端所产生的信息所打断,其在执行过程中的信息也不在任何终端上显示。。
  • Linux 的大多数服务器就是用守护进程实现的。比如,Internet 服务器 inetd,Web 服务器 httpd 等。

守护进程是一种特殊的后台进程,两者并不完全等价(前置知识:[[11_Linux 终端、进程组、会话、守护进程#二、进程组概念|前台与后台进程]]):

  1. 守护进程已经完全脱离终端控制台了,而后台程序并未完全脱离终端(在终端未关闭前还是会往终端输出结果,需要在以 nohup command & 格式运行才能避免影响);
  2. 守护进程在关闭终端控制台时不会受影响,而后台程序会随用户退出而停止,也会随着控制终端的关闭而退出,需要在以 nohup command & 格式运行才能避免影响。
  3. 守护进程的会话组、当前目录、文件描述符都是独立的。后台运行只是终端进行了一次 fork,让程序在后台执行,这些都没改变;

守护进程模型

守护进程的创建步骤如下:

  1. 创建子进程,父进程退出,子进程继续执行(本步骤是必须的步骤)。
    • 目的:子进程行形式上脱离了原有控制终端
    • 为什么fork之后,父进程需要退出:如果父进程不退出,父进程死亡后,终端会显示一个 shell 提示符,想象一下启动一个守护进程之后,在未来的某个时间,突然出现一个shell 提示符会显得非常诡异。
    • 为什么需要在子进程中继续执行,退出父进程:使用fork 会确保子进程不变成一个进程组的首进程(父进程会是进程组的首进程),这样才能子进程才能调用 setsid() 函数(调用进程不能是进程组组长)。
  2. 在子进程中创建新会话(本步骤是必须的步骤)。
    • 使用 setsid() 函数创建新的会话
    • 目的:使子进程完全独立出来,脱离原有控制终端。
      • 子进程创建一个新的会话会产生两个效果:新会话的首进程组,首进程组的进程 id 与子进程的 id 相同 ;对于新创建出的会话,默认情况下没有新的控制终端来连接该会话,那么该会话就没有控制终端。而守护进程就是在后台运行并且不拥有控制终端的进程。创建一个新的会话的目的就是脱离控制终端。新的会话会脱离原先的控制终端,没有控制终端,我们就不能通过键盘、终端产生信号杀死或影响守护进程
      • 为什么需要在子进程中创建会话,因为如果用父进程创建会话,那么新会话的首进程组,首进程组的进程 id 和父进程 id 相同,而父进程与原有会话的首进程组和首进程的id可能相同,这样两个会话id 相同,产生了冲突。
      • 创建新会话后,只是脱离了控制终端,但是该会话依旧有终端。
  3. 重设文件权限掩码(清除进程的 umask 以确保当守护进程创建文件和目录时拥有所需的权限)。
    • 使用 umask() 函数
    • 目的:防止继承的文件创建屏蔽字拒绝某些权限,以增加守护进程灵活性
  4. 修改进程的当前工作目录,通常会改为根目录(\)。
    • 使用 chdir() 函数
    • 目的:防止占用可卸载的文件系统: 因为守护进程会一直运行到操作系统关闭,但是如果当前目录是u盘,那就导致u盘一直无法卸载,所以一般改为根目录,当然也可以换成其它路径。
  5. 关闭文件描述符,即关闭守护进程从其父进程继承而来的所有打开着的文件描述符。
    • 创建新会话后,只是脱离了控制终端,但是该会话依旧有终端。如果不关闭文件描述符,这些文件描述符还是可能往终端读写数据,这样守护进程就无法操作终端。
    • 继承的打开文件不会用到,浪费系统资源,无法卸载:关闭打开的文件,因为如果不关闭,那将无法卸载当前未关闭的文件所在的磁盘。
  6. 关闭文件描述符0、1、2之后,守护进程通常会打开 /dev/null 并使用 dup2() 使所有这些描述符指向这个设备:关闭文件描述符0、1、2之后,如果守护进程依旧有可能有想这三个文件描述符输出数据,但是文件描述符已经关闭,此时就可能出错,所以要将这三个文件描述符输出的数据重定向到 /dev/null 中,/dev/null 中的数据都会被操作系统自动弃掉。
  7. 开始执行守护进程核心工作(本步骤是必须的步骤)

守护进程参考代码

写一个守护进程, 每隔 2s 获取一次系统时间, 将这个时间写入到磁盘文件:

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <signal.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>void work(int num) {// 捕捉到信号之后,获取系统时间,写入磁盘文件time_t tm = time(NULL);struct tm * loc = localtime(&tm);// char buf[1024];// sprintf(buf, "%d-%d-%d %d:%d:%d\n",loc->tm_year,loc->tm_mon// ,loc->tm_mday, loc->tm_hour, loc->tm_min, loc->tm_sec);// printf("%s\n", buf);char * str = asctime(loc);int fd = open("time.txt", O_RDWR | O_CREAT | O_APPEND, 0664);write(fd ,str, strlen(str));close(fd);
}int main() {// 1.创建子进程,退出父进程pid_t pid = fork();if(pid > 0) {exit(0);}// 2.将子进程重新创建一个会话setsid();// 3.设置掩码umask(022);// 4.更改工作目录chdir("/home/nowcoder/");// 5. 关闭、重定向文件描述符int fd = open("/dev/null", O_RDWR);dup2(fd, STDIN_FILENO);dup2(fd, STDOUT_FILENO);dup2(fd, STDERR_FILENO);// 重定向如果不关闭关闭,就会输出到终端,但是终端提示符依旧可以出现并操作(新的会话,脱离了终端)。重定向关闭,就不会输出到终端。// 6.业务逻辑// 捕捉定时信号struct sigaction act;act.sa_flags = 0;act.sa_handler = work;sigemptyset(&act.sa_mask);sigaction(SIGALRM, &act, NULL);struct itimerval val;val.it_value.tv_sec = 2;val.it_value.tv_usec = 0;val.it_interval.tv_sec = 2;val.it_interval.tv_usec = 0;// 创建定时器setitimer(ITIMER_REAL, &val, NULL);// 不让进程结束while(1) {sleep(10);}return 0;
}

守护进程相关 API

#include <unistd.h>
int daemon(int nochdir, int noclose);
功能:创建一个守护进程参数:nochdir:=0 将当前目录更改至“/”noclose:=0 将标准输入、标准输出、标准错误重定向至“/dev/null”返回值:成功:0失败:-1

参考文章

参考文章

相关文章:

Linux 终端、进程组、会话、守护进程

文章目录一、终端概念终端概念控制终端二、进程组概念进程组概述进程组相关 API会话会话概念会话相关 API创建会话注意事项守护进程守护进程介绍守护进程模型守护进程参考代码守护进程相关 API参考文章一、终端概念 终端概念 1、终端&#xff08;Terminal&#xff09; 终端是…...

你是否有潜质成为谷歌开发者专家?加入 GDE 成长计划,释放潜力!

谷歌开发者专家 (Google Developer Experts&#xff0c;GDE)&#xff0c;又称谷歌开发者专家项目&#xff0c;是由一群经验丰富的技术专家、具有社交影响力的开发者和思想领袖组成的全球性社区。通过在各项活动演讲以及各个平台上发布优质内容来积极助力开发者、企业和技术社区…...

安全防御之防火墙篇(二)

目录 1.防火墙如何处理双通道协议&#xff1f; 2.防火墙如何处理NAT&#xff1f; 3.防火墙支持哪些NAT技术&#xff0c;主要应用的场景是什么&#xff1f; 4.当内网PC通过公网域名解析访问内网服务器的时候&#xff0c;会存在什么问题&#xff0c;如何解决&#xff1f;请详细…...

设计必备,5个png免抠素材网站,建议收藏

做设计、PPT都需要用到大量的免抠素材&#xff0c;职场中熟练使用Photoshop的人毕竟是少数&#xff0c;也很少有人愿意花费时间去精细抠图。那这5个免抠素材网站一定要收藏好&#xff0c;可以有效帮你节省时间&#xff0c;提高工作效率。 1、菜鸟图库 https://www.sucai999.co…...

shell 脚本expect

expect 是什么 expect - programmed dialogue with interactive programs&#xff08;与互动程序进行程序对话&#xff09; 定义脚本执行的 shell #!/usr/bin/expect -f 定义的是执行 expect 可执行文件的链接路径&#xff08;或真实路径&#xff09;&#xff0c;功能类似于bas…...

第十九天 Maven总结

目录 Maven 1. 前言 2. 概述 2.1 介绍 2.2 安装 3. IDEA集成Maven 3.1 集成Maven环境 3.2 创建Maven项目 3.3 Maven坐标详解 3.4 导入maven项目 4. 依赖管理 4.1 依赖配置 4.2 依赖传递 4.3 依赖范围 4.4 生命周期 4.5 插件 Maven 1. 前言 1). 什么是Maven? …...

ESP8266-NodeMCU开发板-------开发板介绍(1)

目录 认识ESP8266-NodeMCU开发板​编辑 GPIO编号与NodeMCU开发板引脚名的区别&#xff1a; ESP8266 GPIO编号与NodeMCU开发板引脚名的对应关系 可用引脚 电压电流限制 特殊引脚情况说明 上拉电阻/下拉电阻 模拟输入 通讯 认识ESP8266-NodeMCU开发板 初识NodeMCU开发板 (第1章-第…...

【测试开发篇3】软件测试的常用概念

目录 一、软件测试的生命周期(5个步骤) ①需求分析(两个角度) 用户角度&#xff1a; 开发人员的角度&#xff1a; ②测试计划 ③测试设计、测试开发 ④执行测试 ⑤测试评估 二、软件测试贯穿项目的整个生命周期的体现 需求分析阶段 计划阶段 设计阶段 编码阶段 …...

javaEE初阶 — JavaScript WebAPI

文章目录什么是 DOMDOM 树获取元素1. querySelector2. querySelectorAll事件1. 事件三要素2. 代码案例获取 / 修改元素内容1. innerHTML获取 / 修改元素属性获取 / 修改表单元素属性获取 / 修改样式属性1. 修改内联样式&#xff08;修改 style 属性的值&#xff09;2. 修改元素…...

UE实现地面动态交互效果

文章目录 1.实现目标2.实现过程2.1 SphereMask2.2 材质实现2.3 位置更新3.参考资料1.实现目标 基于SphereMask材质节点实现人物在地面一定范围内的颜色高亮效果。 2.实现过程 实现原理是首先通过,SphereMask材质节点更具计算输出Mask值,其中在球体半径内的输入1,在外部的则…...

如何用自己的数据训练YOLOv5

如何训练YOLOv5 1. Clone the YOLOv5 repository and install dependencies: git clone https://github.com/ultralytics/yolov5.git cd yolov5 pip install -r requirements.txt2. 整理数据&#xff0c;使其适配YOLO训练 Step1&#xff1a;Organize your dataset in the fo…...

【基础算法】数组相关题目

系列综述&#xff1a; &#x1f49e;目的&#xff1a;本系列是个人整理为了秋招算法的&#xff0c;整理期间苛求每个知识点&#xff0c;平衡理解简易度与深入程度。 &#x1f970;来源&#xff1a;材料主要源于代码随想录进行的&#xff0c;每个算法代码参考leetcode高赞回答和…...

MatBox—基于PyQt快速入门matplotlib的教程库

MatBox—基于PyQt快速入门matplotlib的教程库 __ __ _ _ _ _ _ _ _______ _ _ _ | \/ | | | | | | | | |(_)| | |__ __| | | (_) | || \ / | __ _ |…...

go channel使用

go语言中有一句名言&#xff1a; 不要通过共享内存来通信&#xff0c;而应该通过通信来共享内存。 channel实现了协程间的互相通信。 目录 一、channel声明 二、向channel发送数据 三、从channel读取数据 1. i, ok : <-c 2. for i : range c&#xff08;常用&#xff09…...

5. QtDesignStudio中的3D场景

1. 说明&#xff1a; 三维渲染开发是Design Studio的重要功能&#xff0c;且操作方便&#xff0c;设计效率非常高&#xff0c;主要用到的控件是 View3D ,可以在3D窗口中用鼠标对模型直接进行旋转/移动/缩放等操作&#xff0c;也可以为模型设置各种动画&#xff0c;执行一系列的…...

人工智能的几个研究方向

人工智能主要研究内容是&#xff1a;分布式人工智能与多智能主体系统、人工思维模型、知识系统、知识发现与数据挖掘、遗传与演化计算、人工生命、人工智能应用等等。 其中热门研究有以下几种。 一、计算机视觉 就包括图像识别&#xff0c;视频识别&#xff0c;具体应用有人…...

软件测试 - 常见的开发模型和测试模型

1.瀑布模型优点强调开发的阶段性, 强调早期计划及需求调查, 强调产品测试;缺点1. 由于瀑布模型是一种线型结构的模型, 也就意味着前一个阶段结束, 后一个阶段才能开始, 这就导致了风险往往会迟至后期的测试阶段才显露, 因而失去了及早纠正的机会.2. 瀑布模型中测试被后置, 导致…...

从零开始的机械臂yolov5抓取gazebo仿真(四)

Moveit与Gazebo联合仿真 上一篇博客已经将moveit!配置完毕&#xff0c;然而想要让moveit!控制gazebo中的机械臂&#xff0c;还需要进行一些接口的配置。现在我们有的功能包为sunday_description、sunday_moveit_config这两个功能包。且已经配置好xacro文件&#xff0c;本篇内容…...

C++修炼之筑基期第一层——认识类与对象

文章目录&#x1f337;专栏导读&#x1f337;什么是面向对象&#xff1f;&#x1f337;类的引入&#x1f337;什么是类&#x1f337;类的定义方式&#x1f337;类的访问限定符与封装&#x1f33a;访问限定符&#x1f33a;封装&#x1f337;类的作用域&#x1f337;类的实例化&a…...

IT 运营监控工具

在技术复杂性日益增加、业务竞争激烈的挑战以及消费者对服务中断接受度降低的世界中&#xff0c;IT 运营效率已成为增长、利润和成功的关键。IT 宕机的影响在几十年前威胁较小&#xff0c;现在意味着价值数百万美元的损失&#xff0c;有时甚至会损失各种规模的组织的业务和声誉…...

java线程之Thread类的基本用法

Thread类的基本用法1. Thread类的构造方法2. Thread的几个常见属性常见属性线程中断等待一个线程小鱼在上一篇博客详细的讲解了如何创建线程,java使用Thread类来创建多线程,但是对于好多没有相关经验的人来说,比较不容易理解的地方在于操作系统调度的执行过程. 我们通过下面代码…...

【js】多分支语句练习(2)

个人名片&#xff1a; &#x1f60a;作者简介&#xff1a;一名大一在校生&#xff0c;web前端开发专业 &#x1f921; 个人主页&#xff1a;python学不会123 &#x1f43c;座右铭&#xff1a;懒惰受到的惩罚不仅仅是自己的失败&#xff0c;还有别人的成功。 &#x1f385;**学习…...

Redis与MySQL的双写一致性问题

Redis与MySQL的双写一致性问题更新缓存&#xff1f; 删除缓存&#xff1f;先更新缓存再更新数据库先更新数据库&#xff0c;再更新缓存先删除缓存再更新数据库先更新数据库&#xff0c;再删除缓存解决方案1. 重试2. 异步重试2.1 使用消息队列实现重试2.2 Binlog实现异步重试删除…...

Java基础:笔试题

文章目录Java 基础题目1. 如下代码输出什么&#xff1f;2. 当输入为2的时候返回值是多少?3. 如下代码输出值为多少?4. 给出一个排序好的数组&#xff1a;{1,2,2,3,4,5,6,7,8,9} 和一个数&#xff0c;求数组中连续元素的和等于所给数的子数组解析第一题第二题第三题第四题方案…...

spring三级缓存以及@Async产生循环引用

spring三级缓存以及Async产生循环引用spring三级缓存介绍三级缓存解除循环引用原理源码对应1、获取A&#xff0c;从三级缓存中获取&#xff0c;没有获取到2、构造A&#xff0c;将A置入三级缓存构造A(创建A实例)置入缓存3、注入属性&#xff0c;构造B扫描缓存实例的相关信息注入…...

【洛谷刷题】蓝桥杯专题突破-深度优先搜索-dfs(5)

目录 写在前面&#xff1a; 题目&#xff1a;P2036 [COCI2008-2009#2] PERKET - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目描述&#xff1a; 输入格式&#xff1a; 输出格式&#xff1a; 输入样例&#xff1a; 输出样例&#xff1a; 解题思路&#xff1a; 代码…...

【Unity3D】Unity3D中在创建完项目后自动创建文件夹列表

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 随着项目开发的体量增大&#xff0c;要导入大量的素材、UI、模…...

如何设计一个锂电池充电电路(TP4056)

这个是个单节18650锂电池的充电模块&#xff0c;这个是个18650的锂电池&#xff0c;18指的是它的直径是18mm&#xff0c;65指的是它的高度为65mm。这个18650电池的标称电压是3.7V&#xff0c;电池充满时电压为4.2V&#xff0c;一般电池电压越高也就代表它所剩的电量越大。这种锂…...

Spark了解

目录 1 概述 2 发展 3 Spark和Hadoop 4 Spark核心模块 1 概述 Apache Spark是一个快速、通用、可扩展的分布式计算系统&#xff0c;最初由加州大学伯克利分校的AMPLab开发。 Spark可以处理大规模数据处理任务&#xff0c;包括批处理、迭代式算法、交互式查询和流处理等。Spa…...

c++STL急急急

文章目录cSTL急急急vector头文件扩容过程用法&#xff1a;size/emptyclear迭代器begin/endfront/backpush_back() 和 pop_back()queue头文件用法循环队列 queue用法优先队列 priority_queue用法stack头文件deque头文件deque中控器&#xff1a;用法set头文件用法迭代器begin/end…...