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

UNIX下C语言编程与实践33-UNIX 僵死进程预防:wait 法、托管法、信号忽略与捕获

从原理到实战,掌握四种核心预防方案,彻底规避僵死进程风险

一、预防僵死进程的核心逻辑

UNIX 系统中僵死进程的根源是“子进程终止后父进程未回收其退出状态”。因此,预防僵死进程的核心逻辑围绕“确保父进程正确处理子进程终止事件”展开,具体可归纳为两类思路:

  • 主动回收:父进程主动调用 wait 或 waitpid 函数,获取子进程的退出状态,触发内核清理进程表项;
  • 被动托管:通过系统机制让其他进程(如 init 进程)接管子进程的回收责任,避免父进程未处理导致僵死。

基于这两类思路,衍生出四种经典的预防方法:wait 法、托管法、信号忽略法、信号捕获法。每种方法适用于不同场景,需根据父进程的并发需求、资源限制等因素选择。

二、方法一:wait 法——父进程主动等待子进程终止

核心原理

父进程在 fork 子进程后,调用 wait 或 waitpid 函数,主动阻塞等待子进程终止。子进程结束后,父进程通过这些函数读取其退出状态,内核随之清除子进程的进程表项,避免僵死。

关键特性:父进程会阻塞直到子进程终止,期间无法执行其他逻辑,适用于“父进程仅需创建单个子进程且无需并发”的场景。

1. wait 函数与 waitpid 函数的区别

在预防僵死进程时,wait 和 waitpid 均用于回收子进程,但功能灵活性差异显著,需根据场景选择:

对比维度wait 函数waitpid 函数在预防僵死中的应用
函数原型pid_t wait(int *status);pid_t waitpid(pid_t pid, int *status, int options);-
等待对象等待任意一个子进程终止,无法指定可指定等待的子进程(pid 参数控制):
pid > 0:等待 PID 为 pid 的子进程;
pid = -1:等待任意子进程(同 wait);
pid = 0:等待同组的任意子进程
单子进程场景用 wait;多子进程场景用 waitpid 指定回收目标,避免遗漏
阻塞行为始终阻塞,直到有子进程终止可通过 options 控制是否阻塞:
0:阻塞等待;
WNOHANG:非阻塞,若无子进程终止则立即返回 0
父进程需并发处理其他任务时,用 waitpid + WNOHANG 实现非阻塞回收
返回值成功:终止子进程的 PID;
失败:-1(无子嗣或被信号中断)
成功:终止子进程的 PID(阻塞模式)或 0(非阻塞模式无子嗣终止);
失败:-1
多子进程场景需循环调用 waitpid,直到返回 -1(无更多子嗣),确保所有子进程均被回收

2. 实战:wait 法预防僵死进程(单子进程场景)

程序逻辑

父进程 fork 子进程 → 子进程执行任务(休眠 3 秒)→ 父进程调用 wait 阻塞等待 → 子进程终止后父进程回收,无僵死进程产生。

代码内容

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>int main() {printf("父进程:PID = %d,创建子进程...\n", getpid());pid_t pid = fork();if (pid == -1) {perror("fork 失败");exit(EXIT_FAILURE);}// 子进程逻辑:执行任务后终止if (pid == 0) {printf("子进程:PID = %d,开始执行任务(休眠 3 秒)...\n", getpid());sleep(3); // 模拟子进程执行耗时任务printf("子进程:PID = %d,任务完成,终止\n", getpid());exit(EXIT_SUCCESS);}// 父进程逻辑:调用 wait 等待子进程终止,主动回收printf("父进程:等待子进程(PID = %d)终止...\n", pid);int status;pid_t ret_pid = wait(&status); // 阻塞等待,直到子进程终止// 解析子进程退出状态if (ret_pid == -1) {perror("wait 失败");exit(EXIT_FAILURE);}if (WIFEXITED(status)) {printf("父进程:成功回收子进程(PID = %d),退出码 = %d\n", ret_pid, WEXITSTATUS(status));} else if (WIFSIGNALED(status)) {printf("父进程:子进程(PID = %d)被信号 %d 终止\n", ret_pid, WTERMSIG(status));}// 验证:此时子进程已被回收,无僵死进程printf("父进程:执行 ps 命令查看进程状态(无僵死进程)\n");system("ps aux | grep -w 'Z' | grep -v grep");return EXIT_SUCCESS;
}

编译与运行

gcc wait_prevent.c -o wait_prevent
./wait_prevent

运行结果示例

父进程:PID = 1234,创建子进程...
父进程:等待子进程(PID = 1235)终止...
子进程:PID = 1235,开始执行任务(休眠 3 秒)...
子进程:PID = 1235,任务完成,终止
父进程:成功回收子进程(PID = 1235),退出码 = 0
父进程:执行 ps 命令查看进程状态(无僵死进程)
# 无任何僵死进程输出,证明预防成功

适用场景:父进程仅需创建单个子进程,且无需在子进程执行期间处理其他任务(如简单的命令执行、单任务处理)。

3. 实战:waitpid 法预防僵死进程(多子进程场景)

程序逻辑

父进程循环 fork 3 个子进程 → 子进程各自执行不同时长的任务 → 父进程用 waitpid(-1, &status, WNOHANG) 非阻塞循环回收 → 所有子进程终止后父进程退出,无僵死。

代码示例

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>#define CHILD_NUM 3 // 子进程数量int main() {printf("父进程:PID = %d,开始创建 %d 个子进程...\n", getpid(), CHILD_NUM);// 1. 创建多个子进程for (int i = 0; i < CHILD_NUM; i++) {pid_t pid = fork();if (pid == -1) {perror("fork 失败");exit(EXIT_FAILURE);}if (pid == 0) {// 子进程:执行任务(休眠时间随序号递增)int sleep_sec = (i + 1) * 2; // 子进程 1 休眠 2 秒,子进程 2 休眠 4 秒...printf("子进程 %d:PID = %d,休眠 %d 秒后终止\n", i+1, getpid(), sleep_sec);sleep(sleep_sec);exit(EXIT_SUCCESS);}}// 2. 父进程:非阻塞循环回收所有子进程(避免僵死)printf("父进程:非阻塞回收子进程...\n");int remaining = CHILD_NUM; // 剩余未回收的子进程数量while (remaining > 0) {int status;pid_t ret_pid = waitpid(-1, &status, WNOHANG); // 非阻塞,等待任意子进程if (ret_pid > 0) {// 成功回收一个子进程remaining--;if (WIFEXITED(status)) {printf("父进程:回收子进程(PID = %d),剩余 %d 个\n", ret_pid, remaining);}} else if (ret_pid == 0) {// 无子嗣终止,父进程可处理其他任务(此处模拟)printf("父进程:暂无子进程终止,处理其他任务...\n");sleep(1); // 避免循环过快占用 CPU} else {// waitpid 失败(无更多子嗣)perror("waitpid 失败");break;}}printf("父进程:所有子进程回收完成,退出\n");return EXIT_SUCCESS;
}

编译与运行

# 1. 编译程序
gcc waitpid_prevent.c -o waitpid_prevent# 2. 运行程序
./waitpid_prevent

输出结果

父进程:PID = 1236,开始创建 3 个子进程...
子进程 1:PID = 1237,休眠 2 秒后终止
子进程 2:PID = 1238,休眠 4 秒后终止
子进程 3:PID = 1239,休眠 6 秒后终止
父进程:非阻塞回收子进程...
父进程:暂无子进程终止,处理其他任务...
父进程:暂无子进程终止,处理其他任务...
父进程:回收子进程(PID = 1237),剩余 2 个
父进程:暂无子进程终止,处理其他任务...
父进程:暂无子进程终止,处理其他任务...
父进程:回收子进程(PID = 1238),剩余 1 个
父进程:暂无子进程终止,处理其他任务...
父进程:暂无子进程终止,处理其他任务...
父进程:回收子进程(PID = 1239),剩余 0 个
父进程:所有子进程回收完成,退出

关键优势:通过 waitpid(-1, &status, WNOHANG),父进程可在回收子进程的同时处理其他任务,兼顾并发与僵死预防,适合多子进程场景。

三、方法二:托管法——父进程先终止,子进程由 init 托管

核心原理

UNIX 系统有一个特殊机制:若父进程先于子进程终止,子进程会被 init 进程(PID = 1)或 systemd 进程收养。init 进程会周期性调用 wait 回收所有被收养的子进程,因此子进程终止后不会变为僵死进程。

关键特性:无需父进程主动调用回收函数,依赖系统托管机制,适用于“父进程无需等待子进程完成,且子进程可独立运行”的场景(如后台任务)。

实战:托管法预防僵死进程

程序逻辑

父进程 fork 子进程 → 父进程立即终止(先于子进程)→ 子进程被 init 收养 → 子进程执行任务后终止,init 自动回收,无僵死。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>int main() {printf("父进程:PID = %d,创建子进程...\n", getpid());pid_t pid = fork();if (pid == -1) {perror("fork 失败");exit(EXIT_FAILURE);}// 子进程逻辑:父进程终止后,由 init 托管if (pid == 0) {printf("子进程:PID = %d,父进程 PID = %d\n", getpid(), getppid());// 等待父进程终止(确保被 init 收养)sleep(1);printf("子进程:父进程已终止,当前父进程 PID = %d(init 进程)\n", getppid());// 执行任务(休眠 5 秒,模拟后台运行)printf("子进程:执行后台任务(休眠 5 秒)...\n");sleep(5);printf("子进程:任务完成,终止(由 init 回收)\n");exit(EXIT_SUCCESS);}// 父进程逻辑:创建子进程后立即终止(先于子进程)printf("父进程:PID = %d,创建子进程(PID = %d)后立即终止\n", getpid(), pid);exit(EXIT_SUCCESS); // 父进程终止,子进程被 init 收养
}

# 1. 编译程序
gcc adopt_prevent.c -o adopt_prevent# 2. 运行程序(后台运行,避免终端阻塞)
./adopt_prevent &# 3. 查看子进程状态(确认被 init 收养且无僵死)
sleep 3  # 等待父进程终止,子进程被收养
ps aux | grep -E 'adopt_prevent|PID' | grep -v grep

输出示例

[1] 1240
父进程:PID = 1240,创建子进程...
父进程:PID = 1240,创建子进程(PID = 1241)后立即终止
子进程:PID = 1241,父进程 PID = 1240
子进程:父进程已终止,当前父进程 PID = 1(init 进程)
子进程:执行后台任务(休眠 5 秒)...USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0 168720 11648 ?        Ss   08:00   0:02 /sbin/init
bill      1241  0.0  0.0   4168   728 pts/0    S    10:00   0:00 ./adopt_prevent# 子进程 1241 的父进程 PID 为 1(init),状态为 S(休眠),无僵死
子进程:任务完成,终止(由 init 回收)
[1]+  Done                    ./adopt_prevent

适用场景:父进程无需等待子进程完成的场景,如启动后台服务(如 nginx -d)、执行异步任务,子进程由系统托管回收,无需父进程干预。

注意事项

  • 子进程被 init 收养后,父进程无法再获取其退出状态(如执行结果、错误码),若需监控子进程执行结果,不适合使用托管法;
  • 若子进程为“长期运行服务”(如数据库、Web 服务器),托管法是合理选择;若子进程为“短期任务”且需父进程处理结果,需优先使用 wait 法或信号捕获法。

四、方法三:信号忽略法——忽略 SIGCHLD 信号,系统自动回收

核心原理

子进程终止时,内核会向父进程发送 SIGCHLD 信号。若父进程通过 signal(SIGCHLD, SIG_IGN) 明确忽略该信号,部分 UNIX 系统(如 Linux 2.6+)会自动回收子进程,不产生僵死进程

关键特性:无需父进程调用 wait 函数,仅需设置信号处理方式,代码简洁;但兼容性依赖系统实现,且无法获取子进程的退出状态。

实战:信号忽略法预防僵死进程

程序逻辑

父进程先设置 signal(SIGCHLD, SIG_IGN) 忽略信号 → fork 子进程 → 子进程终止后,系统自动回收,无僵死进程。

以下是规范格式后的代码和说明,已按要求调整缩进和结构:

代码内容

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <signal.h>int main() {// 关键步骤:忽略 SIGCHLD 信号,触发系统自动回收if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) {perror("signal 设置失败");exit(EXIT_FAILURE);}printf("父进程:已忽略 SIGCHLD 信号,子进程将自动回收\n");// 创建子进程printf("父进程:PID = %d,创建子进程...\n", getpid());pid_t pid = fork();if (pid == -1) {perror("fork 失败");exit(EXIT_FAILURE);}// 子进程逻辑:执行任务后终止if (pid == 0) {printf("子进程:PID = %d,执行任务(休眠 3 秒)...\n", getpid());sleep(3);printf("子进程:PID = %d,终止(系统自动回收)\n", getpid());exit(EXIT_SUCCESS);}// 父进程逻辑:无需调用 wait,继续执行其他任务printf("父进程:子进程 PID = %d,父进程继续处理其他任务...\n", pid);sleep(5); // 确保子进程已终止printf("父进程:执行 ps 命令查看(无僵死进程)\n");system("ps aux | grep -w 'Z' | grep -v grep");printf("父进程:退出\n");return EXIT_SUCCESS;
}

编译与运行

gcc sigign_prevent.c -o sigign_prevent
./sigign_prevent

预期输出

父进程:已忽略 SIGCHLD 信号,子进程将自动回收
父进程:PID = 1242,创建子进程...
父进程:子进程 PID = 1243,父进程继续处理其他任务...
子进程:PID = 1243,执行任务(休眠 3 秒)...
子进程:PID = 1243,终止(系统自动回收)
父进程:执行 ps 命令查看(无僵死进程)
# 无僵死进程输出
父进程:退出

兼容性与局限性

  • 兼容性问题:Linux 2.6+、FreeBSD 8.0+ 支持该特性,但早期 UNIX 系统(如 Solaris 10)和部分嵌入式系统不支持,忽略信号后仍会产生僵死进程;
  • 无法获取退出状态:忽略 SIGCHLD 后,父进程无法通过 wait 函数获取子进程的退出码、终止信号等信息,若需监控子进程执行结果,不适合使用;
  • 谨慎用于多子进程:虽支持多子进程自动回收,但无法确保所有系统均能稳定处理,多子进程场景优先选择信号捕获法。

五、方法四:信号捕获法——捕获 SIGCHLD 信号,在信号处理函数中回收

核心原理

父进程注册 SIGCHLD 信号的处理函数,当子进程终止时,内核发送 SIGCHLD 信号触发处理函数执行,父进程在处理函数中调用 waitpid 回收子进程。该方法兼顾“非阻塞”与“结果获取”,是多子进程并发场景的最优选择。

关键特性:父进程无需主动阻塞,子进程终止时自动触发回收;可获取子进程退出状态;支持多子进程并发回收,兼容性好(所有 UNIX 系统支持)。

实战:信号捕获法预防僵死进程(多子进程并发场景)

程序逻辑

父进程注册 SIGCHLD 信号处理函数 → 循环 fork 3 个子进程 → 子进程各自执行任务后终止 → 触发信号处理函数,调用 waitpid(-1, &status, WNOHANG) 回收 → 所有子进程终止后父进程退出,无僵死。

代码示例

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <signal.h>#define CHILD_NUM 3// SIGCHLD 信号处理函数:回收所有终止的子进程
void sigchld_handler(int sig) {int status;pid_t pid;// 循环回收所有终止的子进程(WNOHANG 非阻塞)while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {if (WIFEXITED(status)) {printf("信号处理函数:回收子进程(PID = %d),退出码 = %d\n", pid, WEXITSTATUS(status));} else if (WIFSIGNALED(status)) {printf("信号处理函数:子进程(PID = %d)被信号 %d 终止\n", pid, WTERMSIG(status));}}
}int main() {// 关键步骤:注册 SIGCHLD 信号处理函数struct sigaction sa;sa.sa_handler = sigchld_handler;sa.sa_flags = 0;sigemptyset(&sa.sa_mask); // 清空信号掩码if (sigaction(SIGCHLD, &sa, NULL) == -1) {perror("sigaction 设置失败");exit(EXIT_FAILURE);}printf("父进程:已注册 SIGCHLD 信号处理函数\n");// 创建多个子进程printf("父进程:PID = %d,创建 %d 个子进程...\n", getpid(), CHILD_NUM);for (int i = 0; i < CHILD_NUM; i++) {pid_t pid = fork();if (pid == -1) {perror("fork 失败");exit(EXIT_FAILURE);}if (pid == 0) {int sleep_sec = (i + 1) * 2;printf("子进程 %d:PID = %d,休眠 %d 秒后终止\n", i+1, getpid(), sleep_sec);sleep(sleep_sec);exit(EXIT_SUCCESS);}}// 父进程:无需阻塞,正常处理其他任务printf("父进程:继续处理其他任务(休眠 10 秒)...\n");sleep(10);// 确保所有子进程均被回收printf("父进程:所有子进程已回收,退出\n");return EXIT_SUCCESS;
}

编译与运行

gcc sigcatch_prevent.c -o sigcatch_prevent
./sigcatch_prevent

预期输出

父进程:已注册 SIGCHLD 信号处理函数
父进程:PID = 1244,创建 3 个子进程...
子进程 1:PID = 1245,休眠 2 秒后终止
子进程 2:PID = 1246,休眠 4 秒后终止
子进程 3:PID = 1247,休眠 6 秒后终止
父进程:继续处理其他任务(休眠 10 秒)...
信号处理函数:回收子进程(PID = 1245),退出码 = 0
信号处理函数:回收子进程(PID = 1246),退出码 = 0
信号处理函数:回收子进程(PID = 1247),退出码 = 0
父进程:所有子进程已回收,退出

关键优势

  • 非阻塞并发:父进程可正常处理其他任务,子进程终止时自动触发回收,不影响主逻辑;
  • 多子进程兼容:通过 while (waitpid(...) > 0) 循环回收,确保所有子进程均被处理,无遗漏;
  • 结果可监控:可在信号处理函数中解析子进程的退出状态,满足“需监控子进程执行结果”的场景。

六、常见错误与解决方法

常见错误问题现象原因分析解决方法
wait 函数调用时机不当父进程先执行 wait 后 fork 子进程,导致 wait 无子嗣可回收,返回 -1,子进程终止后变为僵死wait 函数需在 fork 子进程后调用,若先调用,父进程此时无子嗣,wait 立即返回失败,后续子进程终止后无人回收严格遵循“fork → wait”的顺序,确保 wait 调用时子进程已创建;多子进程场景用 waitpid 循环回收,避免依赖单次调用
信号处理函数中未循环调用 waitpid多子进程同时终止时,仅部分子进程被回收,剩余子进程变为僵死内核在发送 SIGCHLD 信号时,若多个子进程同时终止,仅会发送一次信号;信号处理函数中若仅调用一次 waitpid,只能回收一个子进程,剩余子进程无人处理在信号处理函数中用 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) 循环回收,直到 waitpid 返回 <= 0,确保所有终止的子进程均被回收
忽略 SIGCHLD 信号后仍调用 waitwait 函数返回 -1,errno 设为 ECHILD(无子嗣),但子进程已被系统自动回收,无僵死部分系统(如 Linux)忽略 SIGCHLD 后会自动回收子进程,此时子进程已不存在,wait 无子嗣可回收,返回失败忽略 SIGCHLD 信号后,无需再调用 wait/waitpid;若需兼容多系统,避免同时使用“信号忽略”和“主动回收”,选择一种方案即可
托管法中子进程依赖父进程资源父进程终止后,子进程因依赖父进程的文件描述符、共享内存等资源,执行失败父进程终止时会关闭其持有的文件描述符、释放共享内存等资源,若子进程依赖这些资源(如父进程打开的文件),会导致子进程执行异常若子进程需依赖父进程资源,不适合使用托管法;改用 wait 法或信号捕获法,确保父进程在子进程终止后再释放资源;或子进程独立打开/创建所需资源
多线程环境中信号处理函数不安全多线程程序中,信号处理函数调用 waitpid 导致其他线程的系统调用被中断,或数据竞争信号处理函数在多线程环境中属于“全局事件”,会中断任意线程的执行;若信号处理函数中调用非线程安全函数(如 printf)或操作共享数据,会引发数据竞争多线程环境中预防僵死进程:
1. 仅在主线程注册信号处理函数;
2. 信号处理函数中仅执行简单的“标记子进程终止”操作,不直接调用 waitpid;
3. 单独创建一个线程,循环调用 waitpid(-1, &status, WNOHANG) 回收子进程,避免信号处理函数的线程安全问题

七、方法对比与场景选择指南

四种预防方法各有优劣,需根据父进程的并发需求、结果监控需求、系统兼容性等因素选择,以下是详细对比与选择建议:

1. 四种方法核心对比

对比维度wait 法托管法信号忽略法信号捕获法
父进程阻塞是(阻塞等待子进程)否(父进程先终止)否(父进程正常执行)否(信号触发回收,非阻塞)
支持多子进程需循环调用 waitpid,支持支持(均由 init 托管)支持(系统自动回收)支持(信号处理函数循环回收)
获取子进程退出状态能(通过 status 参数)不能(父进程先终止,无法获取)不能(系统自动回收,无接口获取)能(信号处理函数中解析 status)
系统兼容性所有 UNIX 系统支持所有 UNIX 系统支持依赖系统(Linux 2.6+ 支持,部分系统不支持)所有 UNIX 系统支持
代码复杂度低(单进程)→ 中(多进程)低(父进程仅需 fork 后终止)极低(仅需设置信号忽略)中(需注册信号处理函数,循环回收)

2. 场景选择指南

  • 单子进程 + 父进程需等待结果:选择 wait 法,代码简洁,能获取退出状态;
  • 子进程为后台任务 + 父进程无需等待:选择 托管法,依赖系统回收,无需父进程干预;
  • 单/多子进程 + 无需获取结果 + 仅 Linux 环境:选择 信号忽略法,代码最简单,系统自动回收;
  • 多子进程 + 父进程需并发 + 需获取结果 + 跨系统兼容:选择 信号捕获法,兼顾并发、结果监控与兼容性,是最通用的方案;
  • 多线程环境:选择“单独线程 + waitpid 循环回收”,避免信号处理函数的线程安全问题。

UNIX 僵死进程的四种核心预防方法,从原理、实战到场景选择,覆盖了单/多子进程、阻塞/非阻塞、结果监控等不同需求。预防僵死进程的核心是“确保子进程终止后有进程负责回收”,无论是父进程主动回收,还是系统托管回收,本质都是履行这一责任。

在实际开发中,需根据具体场景选择合适的方法:简单场景用 wait 法或信号忽略法,复杂并发场景用信号捕获法,后台任务用托管法。同时,需规避“wait 调用时机不当”“信号处理函数未循环回收”等常见错误,确保预防方案稳定可靠。

http://www.dtcms.com/a/450489.html

相关文章:

  • 手机如何建免费网站做外贸网站信息
  • 深圳做电子工厂的网站在网站留外链怎么做
  • 贵州中航建设集团网站广告传媒公司的网站应该怎么做
  • 汉中微信网站建设推广手机网站开发存储数据
  • 【1000】A+B
  • 网站建设的阶段建设企业网站开发公司
  • wolfSSL已经支持的所有硬件加密方案汇总,涵盖大部分芯片厂商
  • 电视剧怎么做原创视频网站北京网站建设找降龙
  • 网站怎么挂广告那有网页设计培训机构
  • 中秋节与 Spring Boot 的思考:一场开箱即用的团圆盛宴
  • 免费的海报设计网站本溪网站制作
  • 为什么RocketMQ选择mmap+write?RocketMQ零拷贝技术深度解析
  • 淘宝是什么语言做的网站牡丹江建设行业协会网站
  • 【LeetCode - 每日1题】水位上升的泳池中游泳问题
  • UNIX下C语言编程与实践37-UNIX 信号:概念、产生原因与处理方式
  • 全面检测Linux系统健康情况
  • 成都中小企业网站建设北京朝阳区租房价格
  • 装饰公司营销网站模板北京海淀建设中路哪打疫苗
  • 《网络爬虫技术规范与应用指南系列》(xc—5)完
  • seo网站外链专发制作网络网站
  • 河南住房和城乡建设厅网站首页海飞丝网站建设中面临的技术问题_并提出可行的技术解决方案
  • Product Hunt 每日热榜 | 2025-10-06
  • 在电脑新建网站站点wordpress菜单调用
  • 广州个人网站制作公司男生用的浏览器
  • 网站二级页面设计要求怎么做网站代销
  • 关于举办第十九届iCAN大学生创新创业大赛创业赛道复赛的通知
  • 什么是网站关键词创意餐厅网站建设文案书
  • 增量同步 + 双库写入 + 时间游标更新
  • python爬虫爬小说来做网站wordpress分类设置主题
  • 太原网站定制python django做的网站