04-Linux系统编程之进程
一、进程的概述
1.什么是进程
进程:即进行中的程序,可执行文件从开始运行到结束运行这段过程就叫进程。
2.程序和进程的区别
-
程序:存储在磁盘上、占磁盘空间、静态的。如:我们编写的C语言代码就是程序,存储在我们电脑磁盘上;
-
进程:运行在系统上、占内存空间,动态的,包括进程的创建、调度、消亡。如:我们的代码经过编译生成了可执行文件,然后将可执行文件运行,这个运行中的程序就是进程。
3.并发和并行的区别
- 并行(parallel):指在同一时刻,有多条指令在多个处理器上同时执行,并行是真正做到了同时进行;
- 并发(concurrency):指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,利用了人眼的暂留现象(余晖效应),因为切换太快,人眼感觉不出来,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行(分时复用)。
4.PCB 进程控制块
4.1 PCB 的概述
PCB:进程控制块,程序运行时,内核为每个进程分配一个 PCB(进程控制块),维护进程相关的信息。Linux 内核的进程控制块是 task_struct 结构体,这个结构体里面存放着运行维护进程需要的所有资源。
- task_struct 结构体:这个结构体里面的内容很多,但很多是涉及到系统内核的一些操作,我们不需要全部了解,后续学习可能用到和需要掌握的内容主要如下:
- 进程 id:系统会为每个进程分配唯一的 id,在 C 语言中用
pid_t
类型表示,其本质是一个非负整数, 进程有就绪、运行、挂起、停止等状态; - 进程切换时需要保存和恢复的一些 CPU 寄存器,因为我们上面讲到了分时复用,进程间快速切换,当前这个进程暂停了以后,下次要接着当前运行,就得保存当前的工作状态;
- 描述虚拟地址空间的信息,描述控制终端的信息,当前工作目录(CurrentWorking Directory),umask 掩码;
- 文件描述符表,包含很多指向 file 结构体的指针;
- 和信号相关的信息,用户 id 和组 id,会话(Session)和进程组,进程可以使用的资源上限(Resource Limit)等。
- 进程 id:系统会为每个进程分配唯一的 id,在 C 语言中用
4.2进程的状态
上面提到了进程包括就绪、运行、挂起、停止等状态,这里就详细介绍一下。
4.2.1进程状态三层模型
-
三层模型包括:
- 等待态:进程还不具备被 CPU 调度的条件,进程正在等待 CPU 能调用的条件成立;
- 就绪态:进程被调度的条件成立,等待 CPU 调度;
- 执行态:进程的正在被 CPU 执行。
-
三层模型示意图
这里还有一个就绪态和执行态之间的一个循环切换,这里就是我们前面提到的分时复用,不同进程来回切换,每个进程只允许执行很短的事件,一个时间片到以后就把 CPU 让出来给其它进程用,该进程就变为就绪态等待被再次调用,如此循环。
4.2.2进程状态五层模型
相比于三层模型,多了僵尸态和停止态,等待态也分为了两种情况,其示意图如下:
- 五层模型介绍:
- 可中断等待态(TASK_INTERRUPTIBLE) :进程被 CPU 调度的条件还不成立,但不一定要条件成立才能被唤醒,也会因为接收到信号而提前被唤醒;
- 不可中断等待态(TASK_UNINTERRUPTIBLE):和可中断相比,这个必须等到条件满足才能被唤醒,不能通过信号提前唤醒;
- 就绪态(TASK_RUNNABLE): 表示己经准备就绪,正等待被调度;
- 执行态(TASK_RUNNING) : 进程正在被 CPU 执行 ;
- 僵尸态(TASK_ZOMBIE):表示该进程已经结束了,但是其父进程还没有调用 wait 或 waitpid 来释放该进程资源(PCB资源);
- 停止态(TASK_STOPPED):进程停止执行,当进程接收到 SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU 等信号的时候会进入停止态。此外,在调试期间接收到任何信号,都会使进程进入这种状态。当接收到 SIGCONT 信号,会重新回到执行态。
4.2.3查看进程状态
通过 ps -aux
命令查看进程状态。
- 命令演示
edu@edu:~$ ps -aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.1 0.1 119968 6004 ? Ss 15:43 0:01 /sbin/init splash
root 2 0.0 0.0 0 0 ? S 15:43 0:00 [kthreadd]
root 3 0.0 0.0 0 0 ? S 15:43 0:00 [ksoftirqd/0]
root 5 0.0 0.0 0 0 ? S< 15:43 0:00 [kworker/0:0H]
root 7 0.0 0.0 0 0 ? S 15:43 0:00 [rcu_sched]
root 8 0.0 0.0 0 0 ? S 15:43 0:00 [rcu_bh]
root 9 0.0 0.0 0 0 ? S 15:43 0:00 [migration/0]
root 10 0.0 0.0 0 0 ? S 15:43 0:00 [watchdog/0]
root 11 0.0 0.0 0 0 ? S 15:43 0:00 [watchdog/1]
root 12 0.0 0.0 0 0 ? S 15:43 0:00 [migration/1]
......
- 上面的 STAT 就是其状态信息列,参数意义如下:
D 不可中断 Uninterruptible(usually IO)
R 正在运行,或在队列中的进程
S 处于休眠状态(大写S)
T 停止或被追踪
Z 僵尸进程
W 进入内存交换(从内核 2.6 开始无效)
X 死掉的进程
< 高优先级
N 低优先级
s 包含子进程(小写s)
+ 位于前台的进程组(即与终端设备有交互的进程)
- ps 命令常用于查看进程相关的信息,其选项如下:
-a 显示终端上的所有进程,包括其他用户的进程
-u 显示进程的详细状态
-x 显示没有控制终端的进程
-w 显示加宽,以便显示更多的信息
-r 只显示正在运行的进程
pstree 树状显示进程关系
啥也不加,显示的是当前进程
二、进程号
1.进程号概述
每个进程都由一个唯一的进程号来标识,其类型为 pid_t。进程号总是唯一的,但进程号可以重用,即当一个进程终止后,其进程号就可以再次被其它使用。
- 常用进程号分为:
- PID:当前进程号;
- PPID:当前进程的父进程号;
- PGID:进程组 ID。
2.获取进程号
2.1获取当前进程号
- 函数介绍
#include <sys/types.h> // 包含的头文件
#include <unistd.h>
pid_t getpid(void);
功能:获取本进程号(PID)
参数:无
返回值:本进程的进程号(PID)
2.2获取当前进程父进程号
- 函数介绍
#include <sys/types.h>
#include <unistd.h>
pid_t getppid(void);
功能:获取调用此函数的进程的父进程号(PPID)
参数:无
返回值:调用此函数的进程的父进程号(PPID)
2.3获取进程组号
- 函数介绍
#include <sys/types.h>
#include <unistd.h>
pid_t getpgid(pid_t pid);
功能:获取进程组号(PGID)
参数:pid:0或指定进程号
返回值:参数为 0 时返回当前进程组号,否则返回参数指定的进程的进程组号
- 代码演示
void test01()
{printf("当前进程号:%d\n", getpid());printf("当前进程父进程号:%d\n", getppid());printf("当前进程组号:%d\n", getpgid(0));// 用于阻塞,防止进程退出getchar();
}
- 运行结果
当前进程号:4618
当前进程父进程号:2791
当前进程组号:4618
- 通过 ps 命令查看当前进程相关进程号
edu@edu:~$ ps -ajx | grep a.outPPID PID PGID SID TTY TPGID STAT UID TIME COMMAND2791 4618 4618 2791 pts/21 4618 S+ 1000 0:00 ./a.out
可以看到,几个进程号是对应的。
查看父进程号对应的哪个进程:
edu@edu:~$ ps -A | grep 27912791 pts/21 00:00:00 bash
可以看到,父进程号对应的进程是 bash 解析器,因此,每个进程都不能独立启动,都必须通过一个父进程间接启动,父进程还有父进程,就这样一层层有秩序地管理进程(创建进程和回收进程资源),一直到最顶层的 1 号进程。
- 可以通过 pstree 命令查看进程间的创建关系
systemd─┬─ManagementAgent───6*[{ManagementAgent}]├─ModemManager─┬─{gdbus}│ └─{gmain}├─NetworkManager─┬─dhclient│ ├─dnsmasq│ ├─{gdbus}│ └─{gmain}├─VGAuthService├─accounts-daemon─┬─{gdbus}│ └─{gmain}
... ...
三、创建子进程
1.子进程引入
- 为什么我们要创建子进程,看下面的例子:
void test02()
{while (1){printf("---------------------------1\n");sleep(1);}while (1){printf("---------------------------2\n");sleep(1);}
}
- 运行结果
---------------------------1
---------------------------1
---------------------------1
---------------------------1
---------------------------1
---------------------------1
... ...
- 说明:可以看到,当程序执行时,永远都只能执行第一个循环,第二个循环被第一个阻塞掉了,就无法执行到,而如果我们想要同时执行两个循环,就需要用到子进程。
2.fork 创建进程
2.1fork 语法
进程是系统进行资源分配的基本单位。
子进程:系统允许一个进程创建新进程,这个新进程即为子进程,子进程还可以创建新的子进程,形成进程树结构模型。
- fork 函数介绍
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
功能:用于从一个已存在的进程中创建一个新进程,新进程称为子进程,原进程称为父进程
参数:无
返回值:成功:在子进程中返回 0,父进程中返回子进程 ID。pid_t,为整型。失败:返回-1。
失败的两个主要原因是:1)当前的进程数已经达到了系统规定的上限,这时 errno 的值被设置为 EAGAIN。2)系统内存不足,这时 errno 的值被设置为 ENOMEM。
- 注意:父进程和子进程都会在 fork 之后运行,创建子进程后,会另外开辟一个空间,将父进程的资源拷贝一份给子进程。
2.2创建一个子进程
- 代码演示
void test03()
{pid_t pid = fork();if (pid > 0) // 父进程执行的代码{printf("父进程ID:%d\n", getpid());getchar(); // 阻塞,防止父进程退出}else if (pid == 0) // 子进程执行的代码{printf("子进程ID:%d\n", getpid());getchar(); // 阻塞,防止子进程退出}
}
- 运行结果
父进程ID:6826
子进程ID:6827
- 命令查看进程号
edu@edu:~$ ps -ajx | grep a.out2791 6826 6826 2791 pts/21 6826 S+ 1000 0:00 ./a.out6826 6827 6826 2791 pts/21 6826 S+ 1000 0:00 ./a.out
- 当我们输入字符解堵塞
// 明明是一个父进程一个子进程,但是只输入了一个字符就退出了
// 难道父子进程都退出了马,通过命令查看进程状态
edu@edu:~$ ps -ajx | grep a.out1 6827 6826 2791 pts/21 2791 S 1000 0:00 ./a.out
-
说明:
- 可以看到还有一个 a.out 进程在运行,对应进程号,可以看到是之前的子进程,说明刚刚只是父进程退出了,然后子进程没了父进程,就没有父进程为其回收资源了,为了防止无法回收子进程资源,系统会通过1号进程来接手,这样的进程叫做孤儿进程;
- 我们上面创建父子进程,然后分别执行了各自的代码,这里有一个误区,就是会误以为,
if (pid > 0)
条件成立里面的部分是父进程,else if (pid == 0)
条件成立里面的是子进程。其实创建子进程的时候,会将父进程的整个资源,包括这里的所有代码都拷贝一份到子进程,所有这里所有的代码在父子进程中都存在,只是从逻辑角度将其划分为父进程执行的代码和子进程执行的代码。
-
上面同时执行两个 while 循环的代码的实现
void test04()
{pid_t pid = fork();if (pid > 0){while (1){printf("---------------------------1\n");sleep(1);}}else if (pid == 0){while (1){printf("---------------------------2\n");sleep(1);}}
}
- 运行结果
edu@edu:~/study/my_code$ ./a.out
---------------------------1
---------------------------2
---------------------------2
---------------------------1
---------------------------1
---------------------------2
---------------------------1
---------------------------2
... ...
3.父进程和子进程的关系
3.1父子进程的关系
使用 fork 函数创建的子进程是父进程的一个复制品,父进程的空间地址的内容拷贝了一份给子进程空间。
地址空间中包括:进程上下文、进程堆栈、打开的文件描述符、信号控制设定、进程优先级、进程组号等。
子进程所独有的只有它的进程号,计时器等。因此,使用 fork函数的代价是很大的。
但是为了尽可能减少空间的消耗,并不是将父进程的资源完完全全拷贝给子进程,对于一些数据在写时是独立的,读时是共享。
3.2 写时独立读时共享
- 代码演示:读时共享
void test05()
{int num = 10;pid_t pid = fork();if (pid > 0){while (1){printf("父进程:num = %d,num_id = %p\n", num, &num);sleep(1);}}else if (pid == 0){while (1){printf("子进程:num = %d,num_id = %p\n", num, &num);sleep(1);}}
}
- 运行结果
父进程:num = 10,num_id = 0x7ffcfcff2ee0
子进程:num = 10,num_id = 0x7ffcfcff2ee0
父进程:num = 10,num_id = 0x7ffcfcff2ee0
子进程:num = 10,num_id = 0x7ffcfcff2ee0
父进程:num = 10,num_id = 0x7ffcfcff2ee0
子进程:num = 10,num_id = 0x7ffcfcff2ee0
父进程:num = 10,num_id = 0x7ffcfcff2ee0
子进程:num = 10,num_id = 0x7ffcfcff2ee0
... ...
- 说明:上面是通过父子进程分别读取 num 变量的数据,同时打印 num 变量数据的地址,发现打印的数据和地址都一样,验证了上面所说的读时共享。也就是创建子进程的时候,只是将变量名拷贝了过去,但是通过变量名访问数据的时候还是访问的同一个内存地址。
- 代码演示:写时独立
void test06()
{int num = 10;pid_t pid = fork();if (pid > 0){while (1){printf("父进程:num = %d,num_id = %p\n", num, &num);sleep(1);}}else if (pid == 0){while (1){num++;printf("子进程:num = %d,num_id = %p\n", num, &num);sleep(1);}}
}
- 运行结果
父进程:num = 10,num_id = 0x7ffc89a37c30
子进程:num = 11,num_id = 0x7ffc89a37c30
父进程:num = 10,num_id = 0x7ffc89a37c30
子进程:num = 12,num_id = 0x7ffc89a37c30
父进程:num = 10,num_id = 0x7ffc89a37c30
子进程:num = 13,num_id = 0x7ffc89a37c30
父进程:num = 10,num_id = 0x7ffc89a37c30
... ...
- 说明:可以看到,父子进程打印的两个 num 的值不一样了,说明子进程修改 num 变量了以后,num 在父子进程中已经是独立的两份了。但是这里看到的 num 的地址还是一样的,那是因为进程里面的内存地址是虚拟地址,并不是实际的物理地址,虽然父进程与子进程中变量虚拟地址是一样的,但是映射到不同的物理地址就得到了不同的数值。
3.3 printf 换行与不换行
- 代码演示
void test07()
{printf("hello world\n"); // 加换行printf("hello friend"); // 不加换行pid_t pid = fork();if (pid > 0){}else if (pid == 0){}
}
- 运行结果
hello world
hello friendhello friendedu@edu:~/study/my_code$
- 说明
- 现象:可以看到,加了换行符的字符串打印了一遍,但没加换行符的字符串打印了两遍;
- 加换行符打印一遍,是因为我们知道 printf 函数是一个库函数,库函数有缓冲区,要将数据显示在终端设备上,需要将输出到缓冲区的数据刷新到终端,换行就是其中的刷新方式之一。在创建子进程之前,字符串就已经刷新到终端了,又因为父子进程是从 fork 之后执行的,因此对有换行的这个打印不会有任何影响,直接打印一次就完事了;
- 但是不加换行符,没有行刷新、满刷新和强制刷新,就只剩下进程结束刷新了,又因为在进程结束前先创建了子进程,子进程会拷贝父进程资源,连同缓冲区一起拷贝了,因此父子进程结束,会分别将字符串刷新到终端设备,就出现了两个 hello friend;
- 如果在其下面添加一个
fflush(stdout)
强制刷新,就只会打印一次。
3.4库函数 write 输出
- 代码演示
void test08()
{write(1, "hello world", 11); // 加换行printf("hello friend"); // 不加换行pid_t pid = fork();if (pid > 0){}else if (pid == 0){}
}
- 运行结果
hello worldhello friendhello friendedu@edu:~/study/my_code$
- 说明:可以看到,如果使用库函数输出,即使不加换行也只会输出一次,因为库函数是直接操作内核资源,可以直接将数据输出到终端设备,根本不需要什么缓冲区,因此也就不存在库函数的缓冲区拷贝和结束刷新。
3.5 exit 和 _exit
- 代码演示1
void test09()
{printf("hello friend");pid_t pid = fork();if (pid > 0){exit(-1);}else if (pid == 0){_exit(-1);}
}
- 运行结果
hello friendedu@edu:~/study/my_code$
- 代码演示2
void test09()
{printf("hello friend");pid_t pid = fork();if (pid > 0){_exit(-1);}else if (pid == 0){_exit(-1);}
}
- 运行结果
edu@edu:~/study/my_code$ // 啥也没有
- 代码演示3
void test09()
{printf("hello friend");pid_t pid = fork();if (pid > 0){exit(-1);}else if (pid == 0){exit(-1);}
}
- 运行结果
hello friendhello friendedu@edu:~/study/my_code$
- 说明:
- 上面演示的三种情况,可以发现,通过
_exit(-1)
退出进程的时候,不打印,通过exit(-1)
会打印; - 因为
_exit(-1)
是系统调用,作用是退出进程,不会刷新缓冲区; exit(-1)
是库函数,作用是退出进程,会刷新缓冲区。
- 上面演示的三种情况,可以发现,通过
4.父子进程运行顺序
- 代码演示
void test10()
{pid_t pid = fork();if (pid > 0){printf("父进程运行了\n");}else if (pid == 0){printf("子进程运行了\n");}
}
- 运行结果
edu@edu:~/study/my_code$ ./a.out
父进程运行了
子进程运行了
edu@edu:~/study/my_code$ ./a.out
父进程运行了
子进程运行了
- 说明:
- 上面运行的结果可以看出,我们多次调用,都是父进程先执行,子进程后执行,那是因为这里只有父进程先执行了才能调用 fork 创建子进程,因此这里演示肯定是父进程先执行,不然哪来的子进程;
- 但是我们站在原理的角度出发,父子进程是分别独立的进程,它们之间谁先运行要看谁先抢占到 CPU 资源,因此谁先运行是不确定的。
相关文章:
04-Linux系统编程之进程
一、进程的概述 1.什么是进程 进程:即进行中的程序,可执行文件从开始运行到结束运行这段过程就叫进程。 2.程序和进程的区别 程序:存储在磁盘上、占磁盘空间、静态的。如:我们编写的C语言代码就是程序,存储在我们电…...
分布式ID生成-雪花算法实现无状态
雪花算法这里不再赘述,其缺点是有状态(多副本隔离时,依赖手动配置workId和datacenterId),代码如下: /*** 雪花算法ID生成器*/ public class SnowflakeIdWorker {/*** 开始时间截 (2017-01-01)*/private st…...
在Linux系统上使用nmcli命令配置各种网络(有线、无线、vlan、vxlan、路由、网桥等)
1、更新于2024/5/13,新增Veth Pair配置 2、更新于2024/5/19,修复NetworkManager接管网络配置无效的错误 3、更新于2024/5/20,新增Ubuntu两种版本下NetworkManager接管网络的配置 目录 一、配置NetworkManager接管网络(选&…...
计算机网络 (25)IPV6
前言 IPv6,全称为“互联网协议第6版”(Internet Protocol Version 6),是由互联网工程任务组(IETF)设计的用于替代IPv4的下一代IP协议。 一、产生背景 IPv4,即互联网协议第4版,是现行…...
Git 安装 操作 命令 远程仓库 多人协作
Git作用 Git诞生史 很多人都知道,Linus在1991年创建了开源的Linux,从此,Linux系统不断发展,已经成为最大的服务器系统软件了。Linus虽然创建了Linux,但Linux的壮大是靠全世界热心的志愿者参与的,这么多人在…...
线性回归的改进-岭回归
2.10 线性回归的改进-岭回归 学习目标 知道岭回归api的具体使用 1 API sklearn.linear_model.Ridge(alpha1.0, fit_interceptTrue,solver"auto", normalizeFalse) 具有l2正则化的线性回归alpha:正则化力度,也叫 λ λ取值:0~1 1~10solver:会根…...
【LC】2469. 温度转换
题目描述: 给你一个四舍五入到两位小数的非负浮点数 celsius 来表示温度,以 摄氏度(Celsius)为单位。 你需要将摄氏度转换为 开氏度(Kelvin)和 华氏度(Fahrenheit),并以…...
【在安卓平台上,Unity与C/C++编写的.so动态库交互的实现】
在安卓平台上,Unity与C/C++编写的.so动态库交互的实现,通常通过JNI(Java Native Interface)和P/Invoke机制来完成。通过这种方式,C#脚本可以调用C/C++代码中的函数,并与本地库进行交互。 以下是一个简单的步骤演示,展示如何在Unity中与安卓平台上的.so动态库交互。 步…...
【llm/ollama/qwen】在本地部署qwen2.5-coder并在vscode中集成使用代码提示功能
说在前面 操作系统:windows11ollama版本:0.5.4vscode版本:1.96.2continue插件版本:0.8.66 ollama安装 访问官网,点击下载安装即可 默认装在了C盘,比较蛋疼;但是可以指定路径安装:Ol…...
C语言练习:求数组的最大值与最小值
文章目录 1. 提出任务2. 完成任务2.1 方法一:通过返回结构体指针来间接返回结果2.1.1 编写程序,实现功能2.1.2 运行程序,查看结果 2.2 方法二:通过参数传递数组,并在函数中修改传入的参数2.2.1 编写程序,实…...
【TCP】SYN、ACK、FIN、RST、PSH、URG的全称
在 TCP 协议中,SYN、ACK、FIN、RST、PSH 和 URG 都是控制标志位(Flags),每个标志位对应不同的功能。它们的全称如下: URG:(URGent)紧急 ACK:(ACKnowledgment)确认 PSH:(PuSH)推送 RS…...
Re77 读论文:LoRA: Low-Rank Adaptation of Large Language Models
诸神缄默不语-个人CSDN博文目录 诸神缄默不语的论文阅读笔记和分类 论文全名:LoRA: Low-Rank Adaptation of Large Language Models ArXiv网址:https://arxiv.org/abs/2106.09685 官方GitHub网站(包含在RoBERTa、DeBERTa、GPT-2上用Lora微调…...
《OpenCV 4.10.0 实例:开启图像处理新世界》
《OpenCV 4.10.0 实例:开启图像处理新世界》 一、OpenCV 4.10.0 初印象二、环境搭建:开启 OpenCV 之旅(一)不同系统安装指南(二)配置细节与技巧 三、基础图像处理实例(一)图像读取、…...
硬件电路(3)-PT100温度采集电路
一、概述 PT100是铂热电阻,它的阻值会随着温度的变化而改变。PT后的100即表示它在0℃时阻值为100欧姆,在100℃时它的阻值约为138.5欧姆。它的工作原理:当PT100在0摄氏度的时候它的阻值为100欧姆,它的阻值会随着温度上升而成匀速增…...
计算机网络——数据链路层-流量控制和可靠传输
一、流量控制 流量控制是指由接收方及时控制发送方发送数据的速率,使接收方来得及接受。 • 停止等待流量控制 • 滑动窗口流量控制 1、停止—等待流量控制 停止-等待流量控制的基本原理是发送方每发出一帧后,就要等待接收方的应答信号ÿ…...
sqlserver数据库备份和还原
一、备份 打开smss,选中数据库-任务-备份 地址选择默认如:C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\Backup\ (远程服务器文件夹,非本机文件夹) PS:备份后编辑下数据 二、还原 PS:如果提示数…...
Python爬虫基础——认识网页结构(各种标签的使用)
1、添加<div>标签的代码定义了两个区块的宽度和高度均为100px,边框的格式也相同,只是区块中显示的内容不同; 2、添加<ul>和<ol>标签分别用于定义无序列表和有序列表。<il>标签位于<ul>标签或<ol>标签之…...
Linux双端口服务器:端口1的文件系统目录挂载到端口2
目录 一、服务器安装NFS服务并配置二、文件挂载三、持久化挂载总结为什么服务器配置多个端口 目前有一台服务器,不过他设置了两个SSH的端口,通过下面方法可以让这两个端口连接的主机能够共享同一个文件系统,原本这两个端口的文件系统是隔离的…...
unity学习11:地图相关的一些基础
目录 1 需要从 unity的 Asset Store 下载资源 1.1 下载资源 1.2 然后可以从 package Manager 里选择下载好的包,import到项目里 2 创建地形 2.1 创建地形 2.2 地形 Terrain大小 2.3 各种网格的尺寸大小 2.4 比较这个地形尺寸和创建的其他物体的大小对比 3 …...
如何在 Spring Cloud Gateway 中创建全局过滤器、局部过滤器和自定义条件过滤器
Spring Cloud Gateway 是一个功能强大的 API 网关,能够处理 HTTP 请求、响应及路由。通过过滤器机制,您可以在请求和响应过程中进行各种处理操作,如记录日志、身份验证、限流等。Spring Cloud Gateway 提供了三种主要类型的过滤器:…...
总分支机构跨网文件交换解决方案几个关键要点
跨国公司和国内大型集团的日常运营中,总分支机构之间的文件交换已成为必不可少的环节。然而,跨网文件交换面临着诸多挑战,比如安全性、效率、合规性、管理便捷性、技术兼容性和成本效益等。 为了解决这些问题,在挑选总分支机构跨网…...
机器人手眼标定
机器人手眼标定 一、机器人手眼标定1. 眼在手上标定基本原理2. 眼在手外标定基本原理 二、眼在手外标定实验三、标定精度分析 一、机器人手眼标定 要实现由图像目标点到实际物体上抓取点之间的坐标转换,就必须拥有准确的相机内外参信息。其中内参是相机内部的基本参…...
将 Docker 数据迁移到新磁盘:详细操作指南
将 Docker 数据迁移到新磁盘:详细操作指南 背景 在容器化应用的部署中,Docker 通常将数据存储在默认的 /var/lib/docker 目录。然而,随着容器数量的增加和镜像的累积,该目录可能会迅速占满系统磁盘空间,从而影响系统…...
AWS re:Invent 的创新技术
本月早些时候,Amazon 于 12 月 1 日至 5 日在内华达州拉斯维加斯举行了为期 5 天的 re:Invent 大会。如果您从未参加过 re:Invent 会议,那么最能描述它的词是“巨大”——不仅从与会者人数(60,000 人)来看&…...
无人机低小慢探测难题!
一、探测难度增加 由于无人机的雷达散射面积小,雷达在探测时接收到的反射信号相对较弱。这使得雷达系统需要更高的灵敏度和更先进的信号处理算法才能有效捕捉到无人机的信号。在复杂环境中,如城市、山区或海洋,背景噪声和干扰源众多…...
第2章:Go语言基础语法
第2章:Go语言基础语法 1. 变量和数据类型 1.1 基本数据类型 Go语言提供了以下基本数据类型: // 整数类型 var intVar1 int // 根据系统位数,32或64位 var intVar2 int8 // -128 到 127 var intVar3 int16 // -32768 到 32767 var i…...
RP2K:一个面向细粒度图像的大规模零售商品数据集
这是一种用于细粒度图像分类的新的大规模零售产品数据集。与以往专注于相对较少产品的数据集不同,我们收集了2000多种不同零售产品的35万张图像,这些图像直接在真实的零售商店的货架上拍摄。我们的数据集旨在推进零售对象识别的研究,该研究具…...
pnpm安装与使用
pnpm官网 全局安装 npm install pnpm -g设置源 // 查看源 pnpm config get registry // 切换淘宝源 pnpm config set registry https://registry.npmmirror.com/ 移除 pnpm remove 包名称 // 移除包 pnpm remove 包名称 --global // 移除全局包 更新 pnpm up // 更新所有…...
c# 服务中启动exe窗体程序
Windows服务默认在会话0(Session 0)中运行,这是一个隔离的环境,旨在防止服务与应用程序和用户会话交互,从而提高系统的稳定性和安全性。由于这个原因,直接从服务启动的GUI应用程序将不会显示,因…...
使用pip-chill清理pip依赖包,清理不使用的包
最近需要精简python项目的依赖,需要清理一下哪些代码里面没有使用的包,查询了一下,记录 pip-chill 是一个用于管理 Python 项目中依赖项的工具,可以帮助您保持所需依赖项的清单简洁明了,并删除不必要的依赖。 1. 安装 …...
【算法题系列】LeetCode 5.最长回文子串|JavaScript 5种思路实现
题目描述 给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。 示例 1: 输入: "babad" 输出: "bab" 注意: "aba" 也是一个有效答案。 示例 2: 输入: "cbbd" 输出: &q…...
QT6编程入门(一)
1、QT6介绍 1.1、QT6概述 Qt 是一个跨平台的 C 应用程序框架,它被广泛用于开发图形用户界面(GUI)应用程序,同时也可以用于开发非GUI程序,如控制台工具和服务器。Qt 被设计成能够在多种操作系统上运行,包括…...
串口通信控制LED、马达、蜂鸣器、风扇
1.STM32U575RITx 设置引脚 编写main.c //对fputc重写,方便调用printf函数 int fputc(int ch, FILE* F){HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 10);return ch; } //main函数的主要循环while (1){char whole_buf[128];memset(whole_buf, 0, siz…...
每日一题:BM2 链表内指定区间反转
文章目录 链表区间反转教学内容1. 任务描述2. 题目分析例子 3. 思路4. 详细步骤4.1 创建虚拟头节点4.2 寻找反转区间的前一个节点4.3 反转区间中的节点4.4 重新连接链表4.5 返回结果 5. 代码实现6. 代码解析6.1 初始化虚拟头节点6.2 寻找反转区间的前一个节点6.3 反转区间中的节…...
Cherno C++学习笔记 P50 C++当中的动态库
在上一篇文章当中我们学习了C当中是如何使用静态库的,这一篇我们会讲一下如何使用动态库,并同样用GLFW这个已有的库来举例子。 有了静态库的经验,其实动态库就好理解和使用多了。这两者的区别是,静态链接发生在编译的时候&#x…...
【Flink CDC】Flink CDC的Schema Evolution表结构演变的源码分析和流程图
Flink CDC版本:3.2.1 说明:本文从SchemaOperator接收到,表结构变更事件开始,表结构变更事件应由source端产生,本文不讨论。 可以先看流程图,研究源码。 参考文章: Flink cdc3.0动态变更表结构—…...
计算机网络 (27)IP多播
前言 IP多播(也称多址广播或组播)技术是一种允许一台或多台主机(多播源)发送单一数据包到多台主机(一次性的、同时的)的TCP/IP网络技术。 一、基本概念 定义:多播作为一点对多点的通信ÿ…...
当算法遇到线性代数(四):奇异值分解(SVD)
SVD分解的理论与应用 线性代数系列相关文章(置顶) 1.当算法遇到线性代数(一):二次型和矩阵正定的意义 2.当算法遇到线性代数(二):矩阵特征值的意义 3.当算法遇到线性代数࿰…...
鸿蒙操作系统(HarmonyOS)
鸿蒙操作系统(HarmonyOS)是华为公司推出的一款面向未来、面向全场景的分布式操作系统。它旨在为用户提供一个更加智能、便捷和安全的操作环境,支持多种终端设备之间的无缝协作。在鸿蒙应用开发中,ArkUI作为官方推荐的用户界面开发…...
通义灵码在跨领域应用拓展之物联网篇
目录 一.引言 二.通义灵码简介 三.通义灵码在物联网领域的设备端应用 1.传感器数据采集 (1).不同类型传感器的数据读取 (2).数据转换与预处理 2.设备控制指令接收和执行 (1).指令解析与处理 (2).设备动作执行 四.通义灵码在物联网领域的云端平台应用 1.数据存储和管…...
CSS语言的数据库交互
CSS语言的数据库交互:一种新潮流的探索 引言 在现代网页开发中,CSS(层叠样式表)无疑是构建优美和响应式网页的重要工具。然而,关于CSS和数据库之间的直接交互,尽管并不是一种常见的做法,却引发…...
[SMARTFORMS] 创建二维码
我们可以使用事务码SE73创建二维码 选择系统条形码,点击"更改"按钮 点击 创建选项 选择"新" 输入二维码名称和简短描述,点击"确认"按钮 选择"QR CODE 2005",点击"确认"按钮 选择"No…...
数据项目相关的AWS云计算架构设计
电商数据平台架构 高性能:使用Amazon EC2的计算优化实例处理业务逻辑和数据计算,搭配Amazon ElastiCache内存缓存,加速数据读取。应用负载均衡器(ALB)在EC2实例间分发流量,实现负载均衡。高可用性…...
智慧农业应用场景|珈和科技高标准农田信息化监管平台解决方案
近年来,珈和科技持续深耕农业领域,深耕农业时空大数据服务。 珈和利用遥感大数据、云计算、移动互联网、物联网、人工智能等先进技术,搭建“天空地一体化”监测体系,并创新建设了150的全球领先算法模型,可为100多种农作…...
C++ operator = 返回void 会发生什么?
1.operator 正常情况 #include <iostream> using namespace std;class Box { public:Box(double L) : length(L) {}Box(const Box& b){}Box& operator (const Box&){return *this;}public:double length; // 长度 };int main() {Box box1(1.0);Box box2(…...
Scala 模式匹配
Scala 模式匹配 引言 Scala 作为一种多范式编程语言,不仅支持面向对象编程,还融合了函数式编程的特性。在其丰富的特性集中,模式匹配(Pattern Matching)尤为引人注目,它是一种在许多编程语言中都有应用的编程范式,但在 Scala 中得到了特别强大的支持。模式匹配允许开发…...
微信小程序中 “页面” 和 “非页面” 的区别
微信小程序中 “页面” 和 “非页面” 的区别,并用表格进行对比。 核心概念: 页面 (Page): 页面是微信小程序中用户可以直接交互的视图层,也是小程序的基本组成部分。每个页面都有自己的 WXML 结构、WXSS 样式和 JavaScript 逻辑…...
解耦Java应用程序的方法和技巧
解耦 Java 应用程序是一项重要的设计原则是减少组件之间的依赖关系,使系统更加模块化、灵活和可维护。通过分离,您可以更轻松地更改、扩展或测试应用程序的各个部分,而不会影响其他部分。 分离 Java 应用程序需要应用减少组件之间直接依赖关系…...
Go语言的 的设计模式(Design Patterns)基础知识
Go语言的设计模式基础知识 引言 设计模式是一种在软件开发中经常使用的解决特定问题的通用方案。它们为开发者提供了一种有效的方式来组织代码、提高代码的可复用性、可维护性和灵活性。在众多编程语言中,Go语言因其独特的特性,如并发支持和简洁的语法…...
【Uniapp-Vue3】常用的表单组件button和input
表单组件中最常用的就是button组件和input组件 一、button组件 常用属性: <template><button>普通按钮</button><button size"mini">小按钮</button><button type"primary">蓝色按钮</button><…...