【Linux】Linux的进程控制
目录
1. 学习思维导图
2.进程创建(fork)
2.1 fork创建进程失败
3.进程终止
3.1 进程退出情况
3.1.1main函数
3.1.2 退出码
3.2 exit/_exit函数
1. exit() 函数
2. _exit() 函数
4.进程等待
4.1 实现进程等待的方法
wait/waitpid方法
区别:
4.2 status参数
5.进程替换
5.1 认识exec*系列函数
6.自定义Shell
1. 学习思维导图
2.进程创建(fork)
-
目标:理解进程创建的原理及
fork
函数的使用。
步骤:
创建成功会返回给父进程子进程的PID, 返回给子进程0,创建失败返回给父进-1。
实例代码:
- 阅读文档中
fork
函数的定义和返回值逻辑。 - 理解父子进程的执行流程及输出结果
-
实例代码:
#include <stdio.h>
#include <unistd.h>int main()
{pid_t id = fork();if(id == 0){// 子进程printf("我是一个子进程,我的pid:%d,ppid:%d\n", getpid(), getppid());}else if(id >= 1){// 父进程printf("我是一个父进程,我的pid:%d\n", getpid());}return 0;
}
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2025_2_9]$ ./process
我是一个父进程,我的pid:1297
我是一个子进程,我的pid:1298,ppid:1297
三个问题:
-
为什么fork()函数有多个返回值?
父进程创建子进程后,子进程的PCB指向的是父进程的代码和数据,由于进程是独立的,所以fork()函数被调用了两次(父和子),因此返回值有多个!
-
为什么返回给父进程子进程的pid,而返回子进程0
因为子进程归父进程管理,为了方便管理和找到子进程所以父进程会被返回子进程的pid。
子进程 : 父进程 ----> n : 1 ,一个父进程可以拥有多个子进程
-
为什么id可以接受多个返回值,并且使得if语句同时成立?
我们先打印父子进程的变量id地址观察:
我是一个父进程,我的pid:2198
id:0x7ffd0f3e500c
我是一个子进程,我的pid:2199,ppid:2198
id:0x7ffd0f3e500c
我们会发现地址是一样的,那么id的多值行为怎么解释呢? ---> 写时拷贝,而计算机给我们的id地址是一份虚拟地址。
2.1 fork创建进程失败
-
创建进程数超过最大数
-
空间资源不够创建进程
3.进程终止
3.1 进程退出情况
-
代码运行完毕,结果正确
-
代码运行完毕,结果不正确
-
代码异常终止
3.1.1main函数
-
main函数我们常常写成这样:
int main()
{return 0;
}
-
main函数的返回值返回给谁了?
-
为什么通常返回0,有什么特殊含义吗?
main函数作为程序的入口函数,通常代表一个程序的结果,如果一个程序正常运行完毕,结果正确返回值为0,如果结果错误返回值为!0,如果代码程序异常终止那么返回值无意义!返回值通常存储在寄存器中进行返回!
-
返回结果正确返回值为0不用管。
-
返回值不正确非0,不同的值代表不同的问题!
案例:打开一个不存在的文件:
查看退出码(返回值):
命令: echo $? #打印最近一个程序的退出码[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2025_2_11]$ ./process
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2025_2_11]$ echo $?
1
# Bash进程会收集子进程process的退出码
# 这个退出码会写进进程的task_struct(PCB)
查看所有错误码的信息:
int main()
{for(int i = 0; i < 150; ++i)printf("strerror[%d]->%s\n", i, strerror[i]);return 0;
}
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2025_2_11]$ ./process
strerror[0]->Success
strerror[1]->Operation not permitted
strerror[2]->No such file or directory
strerror[3]->No such process
strerror[4]->Interrupted system call
strerror[5]->Input/output error
strerror[6]->No such device or address
strerror[7]->Argument list too long
strerror[8]->Exec format error
strerror[9]->Bad file descriptor
strerror[10]->No child processes
strerror[11]->Resource temporarily unavailable
strerror[12]->Cannot allocate memory
strerror[13]->Permission denied
strerror[14]->Bad address
strerror[15]->Block device required
strerror[16]->Device or resource busy
strerror[17]->File exists
strerror[18]->Invalid cross-device link
strerror[19]->No such device
strerror[20]->Not a directory
strerror[21]->Is a directory
........................................................
返回一个错误码,直接使用return errno;
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2025_2_11]$ ./process
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2025_2_11]$ echo $?
2
# strerror[1]->Operation not permitted
3.1.2 退出码
退出码(退出状态)可以告诉我们最后⼀次执⾏的命令的状态。在命令结束以后,我们可以知道命令是成功完成的还是以错误结束的。其基本思想是,程序返回退出代码 0 时表⽰执⾏成功,没有问题。代码 1 或 0 以外的任何代码都被视为不成功。
Linux Shell中的主要退出码:
3.2 exit/_exit函数
1. exit()
函数
-
定义:属于C标准库(
stdlib.h
),是高层级的进程终止函数。 -
行为:
-
执行清理操作:刷新所有标准I/O缓冲区(如
printf
的输出),关闭已打开的文件流。 -
调用通过
atexit()
注册的退出处理函数。 -
最后调用
_exit()
终止进程,向操作系统传递退出状态。
-
-
使用场景:通常用于正常终止程序,确保资源正确释放。
#include <stdlib.h>
int main() {printf("Hello"); // 无换行,但exit会刷新缓冲区exit(0); // 输出"Hello"
}
2. _exit()
函数
-
定义:属于系统调用(
unistd.h
),是低层级的进程终止函数。 -
行为:
-
直接终止进程,不刷新I/O缓冲区,不调用退出处理函数。
-
立即将控制权交还给操作系统。
-
-
使用场景:多用于子进程(如
fork()
后),避免干扰父进程的I/O状态。
示例:
#include <unistd.h>
int main() {printf("Hello"); // 无换行_exit(0); // 直接终止,不输出"Hello"
}
典型应用场景
-
子进程终止:在
fork()
后的子进程中,若直接调用exit()
可能导致父进程的I/O缓冲区被重复刷新(如输出两次)。此时应使用_exit()
:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int main() {if (fork() == 0) { // 子进程printf("Child");_exit(0); // 避免刷新父进程的缓冲区} else { // 父进程wait(NULL);printf("Parent");exit(0);}
}
-
立即终止:当程序遇到致命错误且无需清理时(如
exec
失败后的子进程)。
总结
-
需要资源清理(如保存数据)时用
exit()
。 -
需要立即终止(如避免缓冲区冲突)时用
_exit()
。 -
库函数
exit()
是对_exit()
的封装! --> 通过这两个调用我们发现一件事情关于我们谈论的缓冲区的位置,我们知道一定不是系统缓冲区之中,我们使用的是库提供的缓冲区!
4.进程等待
前言:我们之前了解了僵尸进程,而进程等待就是为了解决僵尸进程的问题的!
进程等待的必要性:
-
子进程接受父进程的任务安排,是否完成任务都会有一个返回值,父进程接受这个返回值的过程可以称作为进程等待
-
进程等待是父进程的一种手段,是为了实现对子进程的资源回收(最主要的),和对子进程的进程退出信息的捕获(可以选择的,我们知道一个函数不是必须要有一个返回值,这边也是这种意思)。
4.1 实现进程等待的方法
wait/waitpid方法
WAIT(2) Linux Programmer's Manual WAIT(2)NAMEwait, waitpid, waitid - wait for process to change stateSYNOPSIS#include <sys/types.h>#include <sys/wait.h>pid_t wait(int *status);pid_t waitpid(pid_t pid, int *status, int options);
-
wait方法
-
作用:会阻塞父进程(只有接收到子进程退出信息父进程后面的代码才会执行),直到任意一个子进程退出。
status
参数是用于获取子进程的退出状态的。 -
返回值:成功返回被获取的子进程的PID,失败返回-1。
-
waitpid方法
-
作用:相较于wait方法waitpid方法更加精准高效
-
PID > 0
: 它可以设置传入参数pid用于指定等待哪个子进程 -
pid = -1
:等待任意子进程(类似wait
)。 -
pid = 0
:等待与父进程同进程组的所有子进程。 -
pid < -1
:等待进程组 ID 为|pid|
的所有子进程。
-
-
options
参数(常用选项)-
0
:默认阻塞模式。 -
WNOHANG
:非阻塞模式,立即返回。若无子进程退出,返回0
-
-
返回值
-
成功:返回被回收的子进程 PID。
-
失败:返回
-1
(如无子进程)。 -
WNOHANG
且无退出子进程:waitpid
返回0
。
-
区别:
4.2 status参数
这个参数由子进程的退出码和退出信号决定,如果在该参数处传入NULL/nullptr,表示不关心子进程退出状态信息,这个status参数由OS输出填充。
我们要通过status获取退出码可以使用位运算:
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2025_2_13]$ ./process
我是一个子进程!我是父进程,我接收到退出码:8
或者可以使用宏解析:
-
WIFEXITED(status)
:子进程是否正常退出。 -
WEXITSTATUS(status)
:获取子进程退出码。 -
WIFSIGNALED(status)
:子进程是否被信号终止。 -
WTERMSIG(status)
:获取终止子进程的信号。
5.进程替换
-
什么是进程替换:将一个进程的代码和数据替换成另一个进程!
-
进程替换一旦成功,后续就执行新的代码和数据。
RETURN VALUEThe exec() functions return only if an error has occurred. The return value is -1, and errno is set toindicate the error.
-
进程替换不会生成新的进程只是将老进程的代码和数据覆盖式的替换了。
-
进程替换使用的是exec*系列函数,这类函数无需关系返回值,因为一旦返回代表失败!
进程替换案例:
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2025_2_13]$ ./process
代码程序开始了!
total 24
drwxrwxr-x 2 ouyang ouyang 4096 Feb 13 14:45 .
drwxrwxr-x 14 ouyang ouyang 4096 Feb 13 13:56 ..
-rwxrwxr-x 1 ouyang ouyang 8496 Feb 13 14:45 process
-rw-rw-r-- 1 ouyang ouyang 824 Feb 13 14:45 process.c
5.1 认识exec*系列函数
-
l(list):表示参数采用列表
-
v(vector):参数用数组
-
p(path):有p自动搜索环境变量PATH
-
e(env):表示自己维护环境变量
-
传参方面:1. 路径+程序名 2. 命令怎么写就怎么输入
// 库函数
#include <unistd.h>
extern char **environ; // 全局环境变量指针// 需要传入程序的具体地址,执行参数,NULL表示参数结尾
int execl(const char *path, const char *arg, ...);
// 只需要传入程序名字,根据环境变量查找
int execlp(const char *file, const char *arg, ...);
// 需要传入程序的具体地址,环境变量可以不使用默认全局的,可以自己配置
int execle(const char *path, const char *arg, ..., 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[]);
// 系统调用
#include <unistd.h>
int execve(const char *filename, char *const argv[], char *const envp[]);
库函数是对系统调用进行的函数封装!
-
execl
int main()
{printf("Start\n");execl("/bin/ls", "-a", "-l", NULL); // NULL表示传参结束printf("End\n");return 0;
}
[ouyang@iZ2ze0j6dd76e0o9qypo2rZ dir_2025_2_13]$ ./process
Start
total 16
-rwxrwxr-x 1 ouyang ouyang 8496 Feb 13 14:56 process
-rw-rw-r-- 1 ouyang ouyang 984 Feb 13 14:56 process.c
-
execlp(提供环境变量)
int main()
{printf("Start\n");execlp("ls", "-a", "-l", NULL); // NULL表示传参结束printf("End\n");return 0;
}
-
execv (自己提供argv[])
char* const argv[]{"ls","-a","-l",NULL
};int main()
{printf("Start\n");execv("ls", argv); // NULL表示传参结束printf("End\n");return 0;
}
-
如果需要在原本的基础上新增环境变量使用putevn() 环境变量
SYNOPSIS
#include <stdlib.h>
int putenv(char *string);
6.自定义Shell
设计图:重定向方面的实现(参考I/O操作)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>// 基本信息PutName大小
#define PUTSIZE 1024
// 获取命令行变量Command大小
#define COMMANDSIZE 1024
// 打印格式
#define PUTFOEM "[%s@%s %s] # "// 我们的环境变量表
#define MAX_ENVS 120
char* g_env[MAX_ENVS];
int g_envs = 0;// 我们的命令行参数表
#define MAX_ARGV 128
char* g_argv[MAX_ARGV];
int g_argc = 0;// 实时获取cwd
char cwd[1024];
// 更新env的cwd
char cwdenv[1024];// 上一次的退出码
int lastcode = 0;// 4. 关于重定向,我们关心的内容
#define NONE_REDIR 0 // 无
#define INPUT_REDIR 1 // <
#define OUTPUT_REDIR 2 // >
#define APPEND_REDIR 3 // >>
int redir = NONE_REDIR;
std::string filename;/* 获取父进程的环境变量表 */
void Init_Env()
{extern char** environ; // 全局环境变量表memset(g_env, 0, sizeof(g_env)); // 初始化我们的环境变量表g_envs = 0;/* 获取父进程环境变量表的内容 */for(int i = 0; environ[i]; i++){// 1. 申请空间g_env[i] = (char*)malloc(strlen(environ[i]) + 1);// 2. 拷贝内容strcpy(g_env[i], environ[i]);g_envs++;}// 标志g_env[g_envs++] = (char*)"MyShell = Ouyang Test";// 最后结尾以NULLg_env[g_envs] = NULL;/* 导入环境变量 */for(int i = 0; g_env[i]; ++i)putenv(g_env[i]);// 更改指向environ = g_env;
}/* 测试环境变量是否导入成功 */
void Print_env()
{extern char** environ;for(int i = 0; environ[i]; ++i)printf("%d->%s\n", i, environ[i]);
}/* 用户名 / 主机名 / 绝对路径 / 家目录 */
const char* Get_User()
{const char* GU = getenv("USER");return GU == NULL ? "None" : GU;
}const char* Get_Hostname()
{const char* GH = getenv("HOSTNAME");return GH == NULL ? "None" : GH;
}const char* Get_PWD()
{// const char* GP = getenv("PWD");const char* GP = getcwd(cwd, sizeof(cwd));if(GP != NULL){snprintf(cwdenv, sizeof(cwdenv), "PWD=%s", cwd);putenv(cwdenv);}return GP == NULL ? "None" : GP;
}const char* Get_Home()
{const char* Home = getenv("HOME"); return Home == NULL ? "None" : Home;
}const char* Get_Oldpwd()
{const char* old_pwd = getenv("OLDPWD");return old_pwd == NULL ? "None" : old_pwd;
}/* 获取相对路径 */
std::string DirName(const char* pwd)
{
#define SLASHE "/"std::string rp = pwd;if(rp == SLASHE) return SLASHE;/* 找到最后的"/"位置 */auto pos = rp.rfind(SLASHE);if(pos == std::string::npos) return "Get P_Path Fail!";return rp.substr(pos + 1);
}/* 基本信息的打印 */
void PutBaseInformation()
{fflush(stdout);char PutName[PUTSIZE];const char* pwd = Get_PWD();snprintf(PutName, sizeof(PutName), PUTFOEM, Get_User(), Get_Hostname(), DirName(pwd).c_str());printf("%s", PutName);fflush(stdout);
}/* 获取命令行 */
bool GetCommandLine(char* out, int size)
{// "ls -a -l \n" -> "ls -a -l"if(fgets(out, size, stdin) == nullptr) return false;out[strlen(out) - 1] = 0; // 清理"/n"return strlen(out) > 0; // 没有命令输入不做处理
}/* 解析命令 */
bool CommandPrase(char* out)
{// "ls -a -l" -> "ls" "-a" "-l"// 以" "为分隔符
#define SPACE " "g_argc = 0; // 重置g_argv[g_argc++] = strtok(out, SPACE);while((bool)(g_argv[g_argc++] = strtok(NULL, SPACE)));g_argc--; // 减去多余的++return g_argc > 0;
}/* 打印测试g_argv */
void Print_Argv()
{for(int i = 0; g_argv[i]; ++i)printf("g_argv[%d]->%s", i, g_argv[i]);
}/* Cd */
void Cd()
{// Cd / Cd - / Cd ~if(g_argc == 1){// Cdstd::string home = Get_Home();if(home.empty()) return;chdir(home.c_str());}else{// Cd - / Cd ~std::string op = g_argv[1];if(op == "~"){// 家目录chdir(Get_Home());}else if(op == "-"){// 之前目录chdir(Get_Oldpwd());}else{chdir(op.c_str());}}
}/* Echo */
void Echo()
{if(g_argc == 2 && !(g_argc > 2)){// echo $?// echo "Hello"// echo $PATHstd::string opt = g_argv[1];if(opt == "$?"){std::cout << lastcode << std::endl;lastcode = 0;}else if(opt[0] == '$'){std::string env_name = opt.substr(1);char* env = getenv(env_name.c_str());if(env)std::cout << env << std::endl;}else{std::cout << opt << std::endl;}}
}/* 特殊处理内建命令 */
bool CheckAndExecBuiltin()
{std::string cmd = g_argv[0];if(cmd == "cd"){Cd();return true;}else if(cmd == "echo"){Echo();return true;}// ...return false;
}void TrimSpace(char cmd[], int &end)
{while(isspace(cmd[end])){end++;}
}bool CheckRedirect()
{redir = NONE_REDIR;filename.clear();for (int i = 0; g_argv[i] != NULL; ++i){if (strcmp(g_argv[i], "<") == 0){redir = INPUT_REDIR;if (g_argv[i + 1] != NULL){filename = g_argv[i + 1];g_argv[i] = NULL; // 截断参数列表return true;}return false;}else if (strcmp(g_argv[i], ">") == 0){redir = OUTPUT_REDIR;if (g_argv[i + 1] != NULL){filename = g_argv[i + 1];g_argv[i] = NULL; // 截断参数列表return true;}return false;}else if (strcmp(g_argv[i], ">>") == 0){redir = APPEND_REDIR;if (g_argv[i + 1] != NULL){filename = g_argv[i + 1];g_argv[i] = NULL; // 截断参数列表return true;}return false;}}return false;
}/* 命令执行 */
int Execute()
{// 进程创建 + 进程替换pid_t id = fork();if(id == 0){int fd = -1;// 子进程检测重定向情况if(redir == INPUT_REDIR){fd = open(filename.c_str(), O_RDONLY);if(fd < 0) exit(1);dup2(fd, 0);close(fd);}else if(redir == OUTPUT_REDIR){ umask(0);fd = open(filename.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);if(fd < 0) exit(2);dup2(fd, 1);close(fd);}else if(redir == APPEND_REDIR){umask(0);fd = open(filename.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);if(fd < 0) exit(2);dup2(fd, 1);close(fd);}// childexecvp(g_argv[0], g_argv);exit(EXIT_FAILURE);}// father// 进程等待int st = 0;pid_t pid = waitpid(id, &st, 0);lastcode = WEXITSTATUS(st);return pid;
}int main()
{/* 初始化我们的环境变量 */Init_Env();// Print_env(); /* 测试Env */while(true){/* 基本信息的打印 */PutBaseInformation();/* 命令行的获取 */char CommandLine[COMMANDSIZE];if(!GetCommandLine(CommandLine, sizeof(CommandLine)))continue;/* 解析命令 */if(!CommandPrase(CommandLine))continue; /* 重定向分析 */if(CheckRedirect()){Execute();continue;}/* 特殊处理内建命令 */if(CheckAndExecBuiltin())continue;// 命令执行Execute();}return 0;
}
相关文章:
【Linux】Linux的进程控制
目录 1. 学习思维导图 2.进程创建(fork) 2.1 fork创建进程失败 3.进程终止 3.1 进程退出情况 3.1.1main函数 3.1.2 退出码 3.2 exit/_exit函数 1. exit() 函数 2. _exit() 函数 4.进程等待 4.1 实现进程等待的方法 wait/waitpid方法 区别&a…...
金融支付行业技术侧重点
1. 合规问题 第三方支付系统的平稳运营,严格遵循《非银行支付机构监督管理条例》的各项条款是基础与前提,其中第十八条的规定堪称重中之重,是支付机构必须牢牢把握的关键准则。 第十八条明确指出,非银行支付机构需构建起必要且独…...
Django模型管理器/QuerySet 常见的方法
模型管理器/QuerySet 常见的方法 get([**kwargs]) 方法 用途:获取满足条件的唯一对象。参数:关键字参数,指定查询条件。返回值:模型对象。异常:如果找到多个对象或未找到对象,将分别抛出 MultipleObjects…...
QT播放视频保持视频宽高比消除黑边
QT播放视频保持视频宽高比消除黑边 1、问题 在播放视频的时候,由于框架的大小发生变化,导致视频出现黑边很不好看。 因此需要像一种方法消除黑边 2、处理 1、读取视频的宽高比 2、设置视频的Widget的大小固定,Widget的宽高比和视频宽高比…...
在Ubuntu中,某个文件的右下角有一把锁的标志是什么意思?
在Ubuntu中,某个文件的右下角有一把锁的标志是什么意思? 在 Ubuntu(或其他基于 GNOME 文件管理器的 Linux 发行版)中,文件或文件夹的右下角出现一把“锁”标志,通常表示 你当前的用户没有该文件/文件夹的写…...
聊聊Java的SPI机制
个人自建博客地址 什么是SPI呢? SPI全称Service Provider Interface,翻译过来就是服务提供者接口。调用方提供接口声明,服务提供方对接口进行实现,提供服务的一种机制,服务提供方往往是第三方或者是外部扩展。 下面…...
【 实战案例篇三】【某金融信息系统项目管理案例分析】
大家好,今天咱们来聊聊金融行业的信息系统项目管理。这个话题听起来可能有点专业,但别担心,我会尽量用大白话给大家讲清楚。金融行业的信息系统项目管理,说白了就是如何高效地管理那些复杂的IT项目,确保它们按时、按预算、按质量完成。咱们今天不仅会聊到一些理论,还会通…...
go并发编程
https://www.bilibili.com/video/BV16A4y1f7EX sync包工具 Mutex:同一时间只有一个goroutine持有锁,其它申请该锁的goroutine会被阻塞。RWMutex:允许多个协程同时读取共享数据,但写入时需要独占锁。WaitGroup:等待一组…...
使用Python开发以太坊智能合约:轻松入门与深度探索
使用Python开发以太坊智能合约:轻松入门与深度探索 随着区块链技术的快速发展,以太坊作为最为成熟和广泛使用的智能合约平台,成为了开发去中心化应用(DApp)的核心工具。智能合约不仅是区块链技术的基础,更…...
Python从0到100(八十九):Resnet、LSTM、Shufflenet、CNN四种网络分析及对比
前言: 零基础学Python:Python从0到100最新最全教程。 想做这件事情很久了,这次我更新了自己所写过的所有博客,汇集成了Python从0到100,共一百节课,帮助大家一个月时间里从零基础到学习Python基础语法、Pyth…...
密码学(哈希函数)
4.1 Hash函数与数据完整性 数据完整性: 检测传输消息(加密或未加密)的修改。 密码学Hash函数: 构建某些数据的简短“指纹”;如果数据被篡改,则该指纹(以高概率)不再有效。Hash函数…...
设计模式Python版 备忘录模式
文章目录 前言一、备忘录模式二、备忘录模式示例1三、备忘录模式示例2 前言 GOF设计模式分三大类: 创建型模式:关注对象的创建过程,包括单例模式、简单工厂模式、工厂方法模式、抽象工厂模式、原型模式和建造者模式。结构型模式:…...
CES Asia 2025聚焦量子计算,多领域进展引关注
作为亚洲地区极具影响力的科技盛会,CES Asia 2025第七届亚洲消费电子技术贸易展(赛逸展)将在首都北京举办。本届展会以“创新、智能、互联”为主题,将全方位展示全球消费科技领域的最新成果与发展趋势。其中,量子计算作…...
MySQL索引深度剖析:从数据结构到实际应用
引言 在数据库系统中,索引是提高查询效率的关键技术之一。MySQL作为最流行的关系型数据库之一,其索引机制尤为重要。本文将剖析MySQL索引的数据结构、分类、创建方式以及实际应用场景,帮助读者更好地理解和应用索引技术。 主体部分 1. MyS…...
【deepseek】本地部署+RAG知识库挂载+对话测试
文章目录 前言一、Deepseek模型下载(以7B为例)二、RAG本地知识库挂载三、创建本地对话脚本四、结果展示 前言 本文主要涵盖Deepseek在ubuntu系统中的部署全流程,包括模型的下载、系统部署、本地文档向量化、向量列表存储、RAG知识库挂载、对话测试等内容 一、Deeps…...
Vue.js 组件开发全面详解及应用案例
Vue.js 的组件化开发是其核心特性之一,使得代码复用、维护和扩展变得更加容易。以下是关于 Vue.js 组件开发的全面解析,并附带一个实际应用案例。 一、组件基础概念 1. 什么是组件? 组件是 Vue 应用的基本构建块,封装了 HTML、C…...
java面试场景问题
还在补充,这几天工作忙,闲了会把答案附上去,也欢迎各位大佬评论区讨论 1.不用分布式锁如何防重复提交 方法 1:基于唯一请求 ID(幂等 Token) 思路:前端生成 一个唯一的 requestId(…...
MySQL数据库基本概念
目录 什么是数据库 从软件角度出发 从网络角度出发 MySQL数据库的client端和sever端进程 mysql的client端进程连接sever端进程 mysql配置文件 MySql存储引擎 MySQL的sql语句的分类 数据库 库的操作 创建数据库 不同校验规则对查询的数据的影响 不区分大小写 区…...
【wiki知识库】07.用户管理后端SpringBoot部分
目录 一、今日目标 二、??SpringBoot部分类的添加 2.1 使用逆向工程新增User模块 2.2 UserQueryParam添加 2.3 UserSaveParam添加 2.4 UserResetPasswordParam添加 2.5 UserQueryVo添加 2.6 SnowFlake工具类 三、??后端新增接口? 3.1 /user/list接口添加 3.2 /…...
千峰React:案例二
完成对html文档还有css的引入,引入一下数据: import { func } from prop-types import ./购物车样式.css import axios from axios import { useImmer } from use-immer import { useEffect } from reactfunction Item() {return (<li classNameacti…...
Junit框架缺点
JUnit 是 Java 生态中最流行的单元测试框架,广泛应用于单元测试和集成测试中。尽管它功能强大且易于使用,但也存在一些缺陷和局限性。以下是 JUnit 的主要缺点: 1. 功能相对固定 问题:JUnit 的核心功能相对固定,缺乏灵…...
计算机毕业设计SpringBoot+Vue.js公司日常考勤系统(源码+文档+PPT+讲解)
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
Python线程池知多少
目录 目标 Python版本 官方文档 概述 线程池 实战 创建线程池的基本语法 批量提交任务 生产者&消费者模型 目标 掌握线程池的基本概念和使用方法。 Python版本 Python 3.9.18 官方文档 concurrent.futures — Launching parallel taskshttps://docs.python.org/3…...
MySQL数据库入门到大蛇尚硅谷宋红康老师笔记 高级篇 part 6
从6到12章将会是重中之重,请一定好好看 第06章_索引的数据结构 1.为什么使用索引 索引是存储引擎用于快速找到数据记录的一种数据结构,就好比一本教课书的目录部分,通过目录中找到对应文章的页码,便可快速定位到需要的文章。MySQL中也是一…...
C++动态与静态转换区别详解
文章目录 前言一、 类型检查的时机二、安全性三、适用场景四、代码示例对比总结 前言 在 C 中,dynamic_cast 和 static_cast 是两种不同的类型转换操作符,主要区别体现在类型检查的时机、安全性和适用场景上。以下是它们的核心区别: 一、 类…...
面向AI 的前端发展及初识大模型
AI带来的开发范式迁移 随着AI的涌现,对前端的发展也有着非常大的影响,总结过去前端的发展路径,目前应该属于又一次的大规模的开发范式迁移阶段。上一个阶段是从jquery到React/Vue/Angular迁移(jquery之前的就不讨论了)…...
Java入门的基础学习
Java的基础语法知识 一 初始Java二 Java数据类型和变量1.字面常量2.数据类型基本数据类型引用数据类型 3.变量整型变量浮点型变量字符型变量布尔型变量 4.类型转化和提升类型转化类型提升 三 运算符1.算数运算符2.关系操作符3.逻辑运算符:&&,||&…...
万字详解 MySQL MGR 高可用集群搭建
文章目录 1、MGR 前置介绍 1.1、什么是 MGR1.2、MGR 优点1.3、MGR 缺点1.4、MGR 适用场景 2、MySQL MGR 搭建流程 2.1、环境准备2.2、搭建流程 2.2.1、配置系统环境2.2.2、安装 MySQL2.2.3、配置启动 MySQL2.2.4、修改密码、设置主从同步2.2.5、安装 MGR 插件 3、MySQL MGR 故…...
脚本无法获取响应主体(原因:CORS Missing Allow Credentials)
背景: 前端的端口号8080,后端8000。需在前端向后端传一个参数,让后端访问数据库去检测此参数是否出现过。涉及跨域请求,一直有这个bug是404文件找不到。 在修改过程当中不小心删除了一段代码,出现了这个bug࿰…...
GD32F450 使用
GB32F450使用 1. 相关知识2. 烧写程序3. SPI3.1 spi基础3.2 spi代码 4. 串口4.1 串口引脚4.2 串口通信代码 问题记录1. 修改晶振频率 注意:GD32F450 总共有三种封装形式,本文所述的相关代码和知识,均为 GD32F450IX 系列。 1. 相关知识 参数配…...
神经网络代码入门解析
神经网络代码入门解析 import torch import matplotlib.pyplot as pltimport randomdef create_data(w, b, data_num): # 数据生成x torch.normal(0, 1, (data_num, len(w)))y torch.matmul(x, w) b # 矩阵相乘再加bnoise torch.normal(0, 0.01, y.shape) # 为y添加噪声…...
Android 数据库查询对比(APN案例)
功能背景 APN 数据通常存储在数据库中,由TelephonyProvider提供。当用户进入APN设置界面时,Activity会启动,AOSP源码通过ContentResolver查询APN数据。关键分析点在于这个查询操作是否在主线程执行,因为主线程上的耗时操作会导致…...
神卓 S500 异地组网设备实现监控视频异地组网的详细步骤
一、设备与环境准备 硬件清单 主设备:神卓 S500 异地组网路由器 1子设备:神卓 S500 或兼容设备 N(需通过官网认证)监控设备:支持 RTSP/ONVIF 协议的 NVR、摄像头网络要求:各网点需稳定联网(推荐…...
golang安装(1.23.6)
1.切换到安装目录 cd /usr/local 2.下载安装包 wget https://go.dev/dl/go1.23.6.linux-amd64.tar.gz 3.解压安装包 sudo tar -C /usr/local -xzf go1.23.6.linux-amd64.tar.gz 4.配置环境变量 vi /etc/profile export PATH$…...
leetcode35.搜索插入位置
题目: 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例 1: 输入: nums [1,3,5,6], target 5 输出…...
LeetCode第57题_插入区间
LeetCode 第57题:插入区间 题目描述 给你一个 无重叠的 ,按照区间起始端点排序的区间列表。在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。 难度 中…...
人工智能之数学基础:线性代数中矩阵的运算
本文重点 矩阵的运算在解决线性方程组、描述线性变换等方面发挥着至关重要的作用。通过对矩阵进行各种运算,可以简化问题、揭示问题的本质特征。在实际应用中,我们可以利用矩阵运算来处理图像变换、数据分析、电路网络等问题。深入理解和掌握矩阵的运算,对于学习线性代数以…...
SQL Server 创建用户并授权
创建用户前需要有一个数据库,创建数据库命令如下: CREATE DATABASE [数据库名称]; CREATE DATABASE database1;一、创建登录用户 方式1:SQL命令 命令格式:CREATE LOGIN [用户名] WITH PASSWORD ‘密码’; 例如,创…...
MySQL双主搭建-5.7.35
文章目录 上传并安装MySQL 5.7.35双主复制的配置实例一:172.25.0.19:实例二:172.25.0.20: 配置复制用户在实例 1 (172.25.0.19)上执行:在实例 2 (172.25.0.20)上执行&…...
RNN实现精神分裂症患者诊断(pytorch)
RNN理论知识 RNN(Recurrent Neural Network,循环神经网络) 是一种 专门用于处理序列数据(如时间序列、文本、语音、视频等)的神经网络。与普通的前馈神经网络(如 MLP、CNN)不同,RNN…...
Python中字符串的常用操作
一、r原样输出 在 Python 中,字符串前加 r(即 r"string" 或 rstring)表示创建一个原始字符串(raw string)。下面详细介绍原始字符串的特点、使用场景及与普通字符串的对比。 特点 忽略转义字符࿱…...
uniapp 本地数据库多端适配实例(根据运行环境自动选择适配器)
项目有个需求,需要生成app和小程序,app支持离线数据库,如果当前没有网络提醒用户开启离线模式,所以就随便搞了下,具体的思路就是: 一个接口和多个实现类(类似后端的模板设计模式)&am…...
Spring Cloud Gateway 整合Spring Security
做了一个Spring Cloud项目,网关采用 Spring Cloud Gateway,想要用 Spring Security 进行权限校验,由于 Spring Cloud Gateway 采用 webflux ,所以平时用的 mvc 配置是无效的,本文实现了 webflu 下的登陆校验。 1. Sec…...
【异地访问本地DeepSeek】Flask+内网穿透,轻松实现本地DeepSeek的远程访问
写在前面:本博客仅作记录学习之用,部分图片来自网络,如需引用请注明出处,同时如有侵犯您的权益,请联系删除! 文章目录 前言依赖Flask构建本地网页访问LM Studio 开启网址访问DeepSeek 调用模板Flask 访问本…...
Windows对比MacOS
Windows对比MacOS 文章目录 Windows对比MacOS1-环境变量1-Windows添加环境变量示例步骤 1:打开环境变量设置窗口步骤 2:添加系统环境变量 2-Mac 系统添加环境变量示例步骤 1:打开终端步骤 2:编辑环境变量配置文件步骤 3࿱…...
React实现无缝滚动轮播图
实现效果: 由于是演示代码,我是直接写在了App.tsx里面在 文件位置如下: App.tsx代码如下: import { useState, useEffect, useCallback, useRef } from "react"; import { ImageContainer } from "./view/ImageC…...
Ubuntu20.04确认cuda和cudnn已经安装成功
当我们通过官网安装cuda和cudnn时,终端执行完命令后我们仍不能确定是否已经安装成功。接下来教大家用几句命令测试。 cuda 检测版本号 nvcc -V如果输出如下,则安装成功。 可以看到版本号是11.2 cudnn检测版本号 有两种命令:如果你的cudn…...
sqlilab 46 关(布尔、时间盲注)
sqlilabs 46关(布尔、时间盲注) 46关有变化了,需要我们输入sort,那我们就从sort1开始 递增测试: 发现测试到sort4就出现报错: 我们查看源码: 从图中可看出:用户输入的sort值被用于查…...
AI时代保护自己的隐私
人工智能最重要的就是数据,让我们面对现实,大多数人都不知道他们每天要向人工智能提供多少数据。你输入的每条聊天记录,你发出的每条语音命令,人工智能生成的每张图片、电子邮件和文本。我建设了一个网站(haptool.com),…...
模型优化之强化学习(RL)与监督微调(SFT)的区别和联系
强化学习(RL)与监督微调(SFT)是机器学习中两种重要的模型优化方法,它们在目标、数据依赖、应用场景及实现方式上既有联系又有区别。 想了解有关deepseek本地训练的内容可以看我的文章: 本地基于GGUF部署的…...