【Linux笔记】Linux进程控制

发布时间:2026/10/1 9:46:43

【Linux笔记】Linux进程控制 一、进程创建fork函数1. 函数体 pid_t fork(void); ​ ​ 2. 包含在头文件#include unistd.h ​ 3. 函数功能fork() 是 Linux 中创建子进程的唯一系统调用调用一次返回两次。 4. 返回值对于父进程返回子进程的pid对于子进程返回pid0 ​ 5. 执行逻辑 ​ 时刻1父进程调用 fork() │ ↓ 时刻2内核复制进程此时有两个进程了 │ ├── 父进程原来的 └── 子进程新复制的 │ ↓ 时刻3两个进程各自返回 ├── 父进程返回子进程PID └── 子进程返回0完整示例创建子进程#include stdio.h #include unistd.h int main() { int x 10; pid_t id fork(); if(id 0) { perror(fork); return 1; } if (id 0) { // 子进程 printf(子进程: pid%d\n, getpid()); } else if (id 0) { // 父进程 printf(父进程: pid%d\n, getpid()); } return 0; }二、进程退出2.1 什么是进程退出进程退出意味着一个进程结束了它的生命周期。在 Linux 中进程退出后并非直接消失而是经历一系列状态变迁运行态(Running) -- 退出(Exit) -- 僵死态(Zombie/Defunct) -- 彻底销毁2.2 进程退出的分类2.2.1 进程正常退出进程通过显式调用退出函数结束运行自愿退出序号退出方式说明1main函数中的returnC/C 中执行return语句等价于调用exit()2exit(n)C 标准库函数执行完整的用户态清理流程后进入内核3_exit(n)/_Exit(n)系统调用的包装器直接通知内核终止进程不执行用户态清理A. exit 函数 --C库函数退出方式所属标准头文件本质身份exit(n)ISO C 标准stdlib.hC 标准库函数用户态封装exit退出流程详解调用 exit(n) │ ▼ 1. 调用 atexit() 注册的清理函数按 LIFO 顺序 │ ▼ 2. 刷新并关闭所有标准 I/O 流fclose all FILE* │ ▼ 3. 删除由 tmpfile() 创建的临时文件 │ ▼ 4. 调用 _exit(n) 进入内核 │ ▼ 5. 内核回收进程资源B. _exit 函数 --内核函数退出方式所属标准头文件本质身份_exit(n)POSIX 标准unistd.h系统调用的 C 语言包装器直接进内核_exit退出流程详解_exit(status) │ ▼ syscall(SYS_exit, status) --- 直接陷入内核C. main函数的return退出方式所属标准头文件本质身份returnC/C 语言标准无需头文件语言级别的控制流跳转关键字return退出流程详解_start() │ ▼ 1. 调用 __libc_start_main() │ ▼ 2. 执行 main() │ ▼ 3. 返回 return 0; 传回给 __libc_start_main │ ▼ 4. 调用 exit(0) __libc_start_main 拿到返回值后调用 exit(0)2.2.2 进程异常退出进程异常退出非自愿退出方式 说明 1. 接收到信号 如 SIGSEGV、SIGFPE、SIGKILL、SIGTERM 等 2. 运行时错误 除零、段错误、栈溢出等 3. 系统关机 内核强制结束进程2.3 进程退出的关键细节2.3.1 退出状态码进程退出状态码0通常表示成功非 0表示失败不同数值代表不同错误异常通常由 信号 触发在 shell 中可通过$?查看上一条命令的退出码A. 代码运行完毕结果正确直观理解程序按照预期顺利执行完成没有遇到任何错误。日常表现程序正常结束没有任何报错信息执行echo $?查看退出码输出为0$ ls /home/user file1.txt file2.txt $ echo $? 0B. 代码运行完毕结果错误直观理解程序逻辑上走完了没有崩溃但发现业务条件不满足或执行过程中遇到了可预见的错误主动报错退出。日常表现程序可能输出了错误提示信息执行echo $?查看退出码输出为非 0 值如 1, 2, 127 等$ ls /nonexistent_directory ls: cannot access /nonexistent_directory: No such file or directory $ echo $? 2C.代码没有运行完毕异常终止直观理解程序运行到一半被迫中断了可能是自身崩溃也可能是被外部强制干预。日常表现程序可能突然消失或输出 Segmentation fault、Killed 等信息执行echo $?查看退出码输出通常为128 信号编号$ sleep 100 ^C # 用户按下 CtrlC $ echo $? 130 # 128 2 (SIGINT 信号编号为 2)异常终止通常由信号触发分为两类1. 硬件/软件异常自动触发除零错误 →SIGFPE(信号 8)非法内存访问 →SIGSEGV(信号 11段错误)非法指令 →SIGILL(信号 4)2. 人为/外部触发用户按CtrlC→SIGINT(信号 2)kill -9 pid→SIGKILL(信号 9强制杀死)代码示例#include stdio.h int main() { int *p NULL; *p 100; // 触发 SIGSEGV程序异常终止 // 以下代码永远不会执行 printf(This will never be printed\n); return 0; }2.3.2 缓冲区刷新问题代码示例1 exit 函数退出时会进行缓冲区的刷新#includestdio.h int main() { printf(hello); exit(0); return 0; } 运⾏结果:hello代码示例2 _exit 函数退出时不会进行缓冲区的刷新#includestdio.h int main() { printf(hello); _exit(0); return 0; } 运⾏结果:2.3.3 父子进程退出顺序顺序结果子先退出父后 wait正常清理子进程短暂成为僵尸后被回收子先退出父不 wait子进程变成了僵尸进程父先退出子未退出子进程成为孤儿被init(1号进程) 收养父子同时退出子先被init(1号进程) 收养再被回收三、进程等待进程等待本质是父进程通过系统调用回收已退出的子进程。父进程等待子进程并进行回收子进程。3.1 为什么要进程等待A. 回收僵尸进程子进程退出后内核会保留其 PCB进程控制块以存储退出状态成为僵尸进程Z。父进程必须调用 wait 系列函数读取状态内核才会彻底释放 PCB。B. 获取子进程退出状态父进程需要知道子进程的退出消息是正常退出还是被信号杀死退出码是多少是否产生核心转储core dumpC. 同步执行流程父进程可能需要等待子进程完成某项任务后才能继续执行。D. 防止资源泄漏僵尸进程占用 PID 与内核资源长时间累积会导致系统无法创建新进程。3.2 进程等待的系统调用3.2.1wait函数1. 函数原型pid_t wait(int *status); 2. 头文件包含#include sys/wait.h 3. status 参数这是一个输出参数用于传出子进程的退出状态如果不关心状态可以传 NULL。 4. 返回值 pid_t : - 返回值 0 表示已结束子进程的 PID - 返回值 -1 表示出错例如当前进程没有子进程关键特性阻塞调用父进程会一直挂起直到任意一个子进程结束不挑子进程无法指定等待某一个特定的子进程3.2.2waitpid函数1.函数原型pid_t waitpid(pid_t pid, int *status, int options); 2.头文件包含#include sys/wait.h 3.参数一pid_t pid决定等待哪一个子进程 - pid取值 0 表示等待 PID 等于该值的单个子进程 - pid取值 -1表示等待任意子进程等同 wait 4.参数二int *status这是一个输出参数用于传出子进程的退出状态如果不关心状态可以传 NULL。 5.参数三int options行为控制位 - 默认取值为0 表示阻塞等待 - 取值为宏 WNOHANG表示为非阻塞等待即pid指定的子进程没有结束则waitpid()函数立刻返回0不予以等待。 若正常结束则返回该⼦进程的ID。 6.返回值 - 返回值 0 表示已经回收了指定pid的子进程 - 返回值 0 表示option使用了 WNOHANG但没有子进程结束 - 返回值 0 : 出错如 EINTR 被信号中断或无子进程3.2.3 参数Status的理解status不是一个简单的退出码而是一个由内核填充的32 位整数int它通过位编码bit field将子进程的终止原因、退出码、信号编号、是否 core dump等多种信息打包在一个整数里。status 的内部位结构status 的低 16 位承载了所有信息高 16 位保留(通常为0)根据子进程退出的方式不同这低16 位的编码方式也不同。A. 正常退出exit / returnstatus 低16位图如下所示 15 8 7 0 ┌──────────┬──────────┐ │ 退出码 │ 00000000 │ │ (0-255) │ 全为 0 │ └──────────┴──────────┘ bit位解析 -低 8 位bit 0-7全为 0表示不是被信号杀死的 -次低8位bit 8-15退出码0-255代码验证获取status第8~15位得到退出码#include unistd.h #include stdio.h #include stdlib.h #include sys/wait.h void print_status(int status) { printf(第0~7位:- %d\n,(status)0xFF); printf(第8~15位exit code - %d\n,(status 8) 0xFF); return; } void test01() { pid_t id fork(); if(id0) exit(48); int status; waitpid(id, status, 0); printf(test01正常退出\n); printf(status :%d\n,status); print_status(status); } int main() { test01(); return 0; }输出结果为test01正常退出 status :12288 第0~7位:- 0 第8~15位exit code - 48B. 被信号杀死-被信号 SIGTERM(15) 杀死无 core-被信号 SIGSEGV(11) 杀死 产生 core被信号 SIGSEGV(11) 杀死 status 低16位图如下所示 15 8 7 6 0 ┌──────────┬──┬──────────┐ │ 00000000 │C │ 1-126 │ 信号编号 │ │ │ │ └──────────┴──┴──────────┘ ↑ core dump 标志位 0 没有 core dump 1 生成了 core dump bit位解析 -低 7 位bit 0-6表示信号编号 -第 7 位 bit 7 表示core dump 标志位 -次低8位bit 8-15全为03.2.4 获取status信息的宏A. WIFEXITED -- 判断是否正常退出- Linux中提供的宏判断status低8位是否为0即进程是否正常退出WIFEXITED -- 判断是否正常退出 原理 #define WIFEXITED(status) (((status) __WIFEXITED) 0) // __WIFEXITED 0xff // 等价于: ((status) 0xff) 0B. WEXITSTATUS -- 提取退出码-Linux中提供的宏提取status中的第0~7位即获取status中的退出码WEXITSTATUS -- 提取退出码 原理 #define WEXITSTATUS(status) (((status) __WEXITCODE) 8) // __WEXITCODE 0xff00 // 等价于: ( status (0xff00 8)C. WIFSIGNALED -- 判断是否被信号杀死-Linux中提供的宏判断status中的第0~6位判断进程是否被信号杀死WIFSIGNALED -- 判断是否被信号杀死 原理 #define WIFSIGNALED(status) (((unsigned int)(status) - 1) (unsigned int)0xFFu) - 低 7 位不为 0排除正常退出 - 低 7 位不等于 0x7F排除暂停状态D. WTERMSIG -- 提取终止信号编号-Linux中提供的宏提取status中的第0~6位即获取status中的信号编号WTERMSIG -- 提取终止信号编号 原理 #define WTERMSIG(status) ((status) __WTERMSIG) // __WTERMSIG 0x7f // 等价于: (status) 0x7fE. WCOREDUMP -- 判断是否生成了 core dump-Linux中提供的宏提取status中的第7位即判断是否有core dumpWCOREDUMP -- 判断是否生成了 core dump 原理 #define WCOREDUMP(status) ((status) __WCOREDUMP) 1000 0000 // __WCOREDUMP 0x80 // 等价于: (status) 0x803.3 阻塞等待 和 非阻塞等待在进程等待wait/waitpid中“阻塞”与“非阻塞”的核心区别在于当子进程尚未退出时父进程的行为是什么3.3.1 阻塞等待行为父进程调用wait后如果此时没有子进程退出父进程会进入阻塞状态可中断睡眠状态让出 CPU 控制权。直到有子进程退出内核将其唤醒wait才返回。状态流转父进程调用 wait() │ ▼ (否) 有子进程退出 ────────→ 父进程进入睡眠 (TASK_INTERRUPTIBLE) │ │ │ (是) │ 内核调度其他进程运行 │ │ ▼ │ 回收子进程返回 PID ◄─────────────┘ (子进程退出内核唤醒父进程)代码演示子进程未退出父进程阻塞等待#include stdio.h #include stdlib.h #include unistd.h #include sys/wait.h int main() { pid_t pid fork(); if (pid 0) { // 子进程模拟耗时工作 printf(child: working for 3 seconds...\n); sleep(3); printf(child: done, exiting.\n); exit(42); } // 父进程阻塞等待 printf(parent: waiting for child (blocked)...\n); int status; pid_t ret wait(status); // 父进程在这里挂起 3 秒什么都不做 printf(parent: wait returned. child pid%d, exit code%d\n, ret, WEXITSTATUS(status)); return 0; }输出结果如下parent: waiting for child (blocked)... child: working for 3 seconds... child: done, exiting. parent: wait returned. child pid5080, exit code423.3.2 非阻塞等待行为父进程调用带WNOHANG选项的waitpid后如果此时没有子进程退出waitpid立即返回 0此时父进程不进入阻塞状态可中断睡眠状态反而继续执行后续代码。状态流转父进程调用 waitpid(WNOHANG) │ ▼ (否) 有子进程退出 ────────→ 立即返回 0父进程继续执行其他逻辑 │ | (是) │ ▼ 回收子进程返回 PID代码示例非阻塞等待的实现#include stdio.h #include stdlib.h #include unistd.h #include sys/wait.h int main(void) { pid_t pid fork(); //1.子进程 if (pid 0) { printf(child: working for 5 seconds...\n); sleep(5); printf(child: done, exiting.\n); exit(42); } // 2.父进程 printf(parent: doing other work while checking child...\n); int status; while (1) { //父进程进行非阻塞等待 pid_t ret waitpid(pid, status, WNOHANG); if (ret 0) { // 子进程还在运行 printf(parent: child still running, parent doing other work...\n); sleep(1); // 用休眠1s模拟父进程做其他事情 } else if (ret 0) { // 子进程已退出 printf(parent: child exited. pid%d, code%d\n, ret, WEXITSTATUS(status)); break; } else { // 出错 perror(waitpid); break; } } return 0; }代码流程fork() │ ├─ 子进程打印 → sleep(3) → 打印 → exit(42) │ └─ 父进程进入 while 循环每秒检查一次子进程是否退出 如果没退出 → 做其他事情这里是 sleep(1) 如果已退出 → 打印退出码跳出循环程序结束输出结果parent: doing other work while checking child... parent: child still running, doing other work... child: working for 5 seconds... parent: child still running, doing other work... parent: child still running, doing other work... parent: child still running, doing other work... parent: child still running, doing other work... child: done, exiting. parent: child exited. pid7355, code42四、进程程序替换4.1 什么是程序进程替换进程程序替换指在已经存在的进程内部把它的代码段、数据段等内容整体替换为另一个可执行程序的镜像替换之后这个进程的灵魂执行的代码变了但躯体进程ID、进程控制块等没有变。用一个例子来理解一个进程就像一个人坐在办公桌前工作。进程程序替换就是这个人把手头的文件程序A收起来换上一份新的文件程序B继续在这张桌子上工作。人没换桌子没换但干的事情变了。代码验证进程替换不改变进程的PID#include sys/wait.h #include stdio.h #include unistd.h #include stdlib.h int main() { printf(我的程序要运行了\n); pid_t id fork(); if(id 0) { printf(I am child , My Pid is %d\n, getpid()); sleep(1); execl(./other, other, NULL); // 执行对象 执行方式 exit(1); } waitpid(-1, NULL, 0); printf(我的程序执行完毕\n); return 0; }4.2 为什么需要进程程序替换最典型的场景是shell 执行命令的过程中当你在终端输入ls时shell 做了两件事调用fork函数 -- 创建一个子进程。执行exec系列函数 -- 在子进程中进行程序替换执行ls程序。fork exec 父进程(shell) ──────── 子进程(shell的副本) ─────── 子进程(ls程序)如果没有进程程序替换子进程能做的一切都被父进程已经写死在代码中。想执行别的程序除非父进程事先把所有可能性都链接进来否则做不到4.3 进程替换的调用函数函数参数方式路径查找环境变量execl列表完整路径继承当前execlp列表PATH搜索继承当前execle列表完整路径自定义execv数组完整路径继承当前execvp数组PATH搜索继承当前execvpe数组PATH搜索自定义exec 后缀字母 l list 参数一个一个列出来末尾以 (char *)NULL 结束 v vector 参数打包成一个数组指针向量 p path 不用写完整路径系统自动去 PATH 环境变量里找 e env 可以手动给新程序指定环境变量 exec系列函数: execl execlp execle execv execvp execvpeA.execl系列函数#include unistd.h #include stdio.h int main() { printf(--- 替换前 ---\n); // 参数列表程序路径, arg0, arg1, arg2, ..., NULL execl(/bin/ls, ls, -l, /tmp, NULL); // ^ ^ ^ // arg0 arg1 arg2 // // arg0 惯例上写程序名arg1 是第一个参数以此类推 // NULL 是终止标志绝对不能漏 // 如果走到这里说明 exec 失败了 perror(execl 失败); return 1; }B.execv系列函数#include unistd.h #include stdio.h int main() { printf(--- 替换前 ---\n); // 构造参数数组最后必须以 NULL 结尾 char *args[] { ls, // args[0]: 程序名惯例 -l, // args[1]: 第一个参数 /tmp, // args[2]: 第二个参数 NULL // 终止标志 }; execv(/bin/ls, args); perror(execv 失败); return 1; }4.3.1execl函数execl-- 列表方式l list1.函数原型int execl(const char *path, const char *arg, ...); 2.参数 path可执行文件的完整路径 3.参数 arg 传递给新程序的命令行参数argv[0], argv[1]…必须以 NULL 结束代码示例参数像列表一样一个一个写出来最后必须以 NULL 结尾#include sys/wait.h #include stdio.h #include unistd.h #include stdlib.h int main() { printf(我的程序要运行了\n); pid_t id fork(); if(id 0) { printf(I am child , My Pid is %d\n, getpid()); sleep(1); // 参数列表程序路径, arg0, arg1, arg2, ..., NULL execl(/usr/bin/ls, ls, -l, NULL); // ^ ^ ^ ^ // 程序路径 arg0 arg1 arg2 exit(1); } waitpid(-1, NULL, 0); printf(我的程序执行完毕\n); return 0; } //注 // arg0 惯例上写程序名arg1 是第一个参数以此类推 // NULL 是终止标志绝对不能漏A.execlp函数1.函数原型int execlp(const char *file, const char *arg, ...); 2.参数file可执行文件备注会自动在PATH环境目录中搜索可执行文件 3.参数arg传递给新程序的命令行参数argv[0], argv[1]…必须以 NULL 结束#include sys/wait.h #include stdio.h #include unistd.h #include stdlib.h int main() { printf(我的程序要运行了\n); pid_t id fork(); if(id 0) { printf(I am child , My Pid is %d\n, getpid()); sleep(1); // 参数列表程序路径, arg0, arg1, arg2, ..., NULL execl(ls, ls, -l, NULL); // ^ ^ ^ ^ // 可执行文件 arg0 arg1 arg2 exit(1); } waitpid(-1, NULL, 0); printf(我的程序执行完毕\n); return 0; } //注 // arg0 惯例上写程序名arg1 是第一个参数以此类推 // NULL 是终止标志绝对不能漏B.execle函数1.函数原型int execle(const char *path, const char *arg, ..., char *const envp[]); 2.参数 path: 可执行文件的完整路径 3.参数 arg: 传递给新程序的命令行参数argv[0], argv[1]…必须以 NULL 结束 4.参数 envp自定义环境变量会覆盖PATH中的环境变量只保留envp中特有的环境变量#include unistd.h #include sys/wait.h #include stdio.h int main() { pid_t pid fork(); if (pid 0) { // 自定义环境只保留 PATH新增 GREETING char *const envp[] { PATH/usr/bin:/bin, GREETINGhello-from-parent, NULL }; // 用 /usr/bin/env 把当前环境打印出来看看 execle(/usr/bin/env, env, (char *)NULL, envp); perror(execle); exit(1); } waitpid(pid, NULL, 0); return 0; }4.3.4execv函数execv-- 数组方式v vector1.函数原型 int execv(const char *path, char *const argv[]); 2.参数 path : 可执行文件的完整路径 3.参数 argv : 构造参数数组包含命令行参数代码示例#include unistd.h #include stdio.h int main() { printf(--- 替换前 ---\n); // 构造参数数组最后必须以 NULL 结尾 char *args[] { ls, // args[0]: 程序名惯例 -l, // args[1]: 第一个参数 /tmp, // args[2]: 第二个参数 NULL // 终止标志 }; execv(/usr/bin/ls, args); perror(execv 失败); return 1; }A.execvp函数1.函数原型 int execvp(const char *file, char *const argv[]); 2.参数 file : 可执行文件,可以是绝对路径 或 相对路径(会去环境变量PATHz) 3.参数 argv : 构造参数数组包含命令行参数代码示例#include sys/wait.h #include stdio.h #include unistd.h #include stdlib.h int main() { printf(我的程序要运行了\n); pid_t id fork(); if(id 0) { printf(I am child , My Pid is %d\n, getpid()); sleep(1); char *args[]{ ls, -l, NULL }; // 参数列表程序路径, arg0, arg1, arg2, ..., NULL execvp(ls, args); // ^ ^ // 可执行文件 参数数组 exit(1); } waitpid(-1, NULL, 0); printf(我的程序执行完毕\n); return 0; }B.execvpe函数1.函数原型 int execvpe(const char *file, char *const argv[], char *const envp[]); 2.参数 file: 可执行文件 3.参数 argv: 构造参数数组 4.参数 envp: 自定义环境变量会覆盖PATH中的环境变量只保留envp中特有的环境变量#define _GNU_SOURCE #include unistd.h #include sys/wait.h #include stdio.h int main() { pid_t pid fork(); if (pid 0) { char *const argv[] { env, NULL }; char *const envp[] { PATH/usr/bin:/bin, MYMODEproduction, NULL }; execvpe(env, argv, envp); // GNU 扩展 perror(execvpe); exit(1); } waitpid(pid, NULL, 0); return 0; }
延伸阅读

更多相关文章

2026/10/1 9:46:43

【Linux笔记】Linux自定义Shell

一、自定义shell1.1 myshell.h#ifndef __MYSHELL_H__ #define __MYSHELL_H__ ​ //在所有系统头文件之前定义,暴露 getenv/setenv 等的正确声明 #define _DEFAULT_SOURCE ​ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unist…

2026/10/1 9:46:43

OpenAI智能体越界事件复盘:Agent安全控制与权限边界设计

1. 一次“越界”事件的技术复盘价值OpenAI的智能体在测试中闯进了政府网站&#xff0c;53张用户图片被意外抓取外泄——这条消息在圈子里传开的时候&#xff0c;我正在调试自己搭的一个Agent工作流。说实话&#xff0c;第一反应不是震惊&#xff0c;而是“终于来了”。任何做过…

2026/10/1 16:07:02

RL训练规模放大为何总翻车?mimo-v2.6强化学习scaling实验全记录

1. 为什么RL scaling值得单独拿出来聊 做强化学习训练的人大概都有过这种体验&#xff1a;小规模实验跑得挺漂亮&#xff0c;reward曲线稳步上升&#xff0c;评估指标也好看&#xff0c;可一旦把模型参数、并行环境数、batch size往上翻几倍&#xff0c;整个训练就开始"抽…

2026/10/1 16:07:02

ADB工具与驱动安装全攻略:从零基础到实战排查

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 16:07:02

聚合增长GEO市场口碑如何,合作反馈怎么样

当一位制造业企业主在深夜打开豆包&#xff0c;输入工业撕碎机哪个厂家可靠&#xff0c;得到的答案里却没有自己的品牌时&#xff0c;那种失落感&#xff0c;或许只有身处其中的人才能真正体会。这几年&#xff0c;AI搜索正在悄悄改写企业获客的逻辑——客户不再翻十几页搜索结…

2026/10/1 16:07:02

聚合增长GEO服务性价比好不好,专业吗值得信赖吗

AI搜索技术的迭代&#xff0c;正在重构整个ToB营销的底层逻辑&#xff0c;从传统搜索引擎到短视频流量&#xff0c;再到如今大模型驱动的AI搜索时代&#xff0c;无数企业在流量变革中寻找稳定的获客出口&#xff0c;苏州聚合增长信息科技有限公司(简称聚合AI GEO)自诞生起&…

2026/10/1 16:07:02

聚合AI GEO性价比怎么样 评价好吗

从百度搜索到短视频直播&#xff0c;从传统搜索引擎营销到AI搜索重构获客逻辑&#xff0c;互联网营销行业每五年就会迎来一次深刻的规则重构。当大模型技术快速落地&#xff0c;AI搜索成为越来越多用户获取信息、做出决策的入口&#xff0c;制造业企业的营销体系也必须适配全新…

2026/10/1 16:02:02

达梦事物特性及MVCC

一 支持的事物隔离达梦几种隔离级别都支持&#xff0c;默认的隔离级别是读已提交。隔离级别\数据库达梦未提交读支持已提交读支持&#xff08;默认&#xff09;可重复读支持可串行化支持隔离级别 \ 解决脏读不可重复读幻读未提交读可能可能可能已提交读不可能可能可能可重复读不…

2026/10/1 5:21:14

东莞市品牌网站建设报价常见报错与解决

东莞品牌网站建设报价单背后:一份保姆级建站教程避坑实录 网站做好了没人访问,这大概是很多老板最头疼的事。花了大几万做的品牌站,上线后流量惨淡,比路边摊还冷清。别急着骂外包公司,很多“东莞品牌网站建设报价”里藏着不少猫腻,比如用模板站冒充定制…

2026/9/29 21:48:03

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集&#xff1a;Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/10/1 10:48:55

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑