56/14 shell脚本 后台启动 程序1 + “tail -f log“, ctrl +c 导致程序1中断
前言
接上一篇文章, node 程序后台执行加上 tail 命令, 中断 tail 命令, 同时也中断了 node 程序
我们来详细 参照对比一下 这个问题的各种情况
主要的脚本如下类似, 第一条命令 后台启动 程序1, 然后 第二条命令 tail -f 查看日志
然后 ctrl+c 中断 "tail -f" 第一条命令的进程 也收到了 SIGINT 的信号
nohup node nodeProcess.js > ./logs/nodeNohup.log 2>&1 &
tail -f ./logs/nodeNohup.log
c/node/java 程序的 前台/后台 执行的对比
1. c 程序, 测试程序如下
#include <signal.h>
#include <unistd.h>
#include <stdio.h>/*** sign handler* @param dunno*/
void sigHandler(int dunno) {switch (dunno) {case 1:printf("Get a signal-SIGHUP \n");break;case 2:printf("Get a signal - SIGINT \n");break;case 3:printf("Get a signal - SIGQUIT \n");break;}fflush(stdout);
}int main() {printf("press id is %d \n", getpid());fflush(stdout);signal(SIGHUP, sigHandler);signal(SIGINT, sigHandler);signal(SIGQUIT, sigHandler);struct sigaction sigAction;sigAction.sa_sigaction = sigHandler;sigAction.sa_flags = SA_SIGINFO;struct sigaction existsSigHandler;sigaction(SIGINT, &sigAction, &existsSigHandler);int counter = 0;while (1) {counter ++;}}
新建 cStartUp.sh 脚本如下
master:11_singal2Parent jerry$ cat cStartUp.sh /Users/jerry/ClionProjects/HelloWorld/cmake-build-debug/HelloWorld > ./logs/cNohup.log 2>&1
# /Users/jerry/ClionProjects/HelloWorld/cmake-build-debug/HelloWorld > ./logs/cNohup.log 2>&1 &tail -f ./logs/cNohup.log
在 Clion 中直接 运行/调试
// 调试启动, 分别发送 SIGHUP, SIGINT 到进程, 发现 SIGINT 的 handler 没有生效
press id is 11024
Signal: SIGHUP (signal SIGHUP)
Get a signal-SIGHUP
Signal: SIGINT (signal SIGINT)// 正常启动, 分别发送 SIGHUP, SIGINT 到进程, 两个 handler 都生效
press id is 11047
Get a signal-SIGHUP
Get a signal - SIGINT
分别切换 cStartUp.sh 的前台启动 和 后台启动, 查看现象
# 前台启动时, 分别发送 SIGHUP, SIGINT 到进程, 两个 handler 都生效
master:11_singal2Parent jerry$ tail -f logs/cNohup.log
press id is 11137
Get a signal-SIGHUP
Get a signal - SIGINT # 后台启动时, 分别发送 SIGHUP, SIGINT 到进程, 两个 handler 都生效
press id is 11166
Get a signal-SIGHUP
Get a signal - SIGINT
cStartUp.sh 的前台启动 和 后台启动时, 中断 "tail -f" 是否向 c程序 发送 SIGINT
# 前台启动时 cStartUp.sh 的日志信息, "^C" 为 ctrl + c 的操作
master:11_singal2Parent jerry$ ./cStartUp.sh
^C# 读取日志文件, 发现 c程序 收到了 SIGINT
master:11_singal2Parent jerry$ tail -f logs/cNohup.log
press id is 11581
Get a signal-SIGHUP
Get a signal - SIGINT
Get a signal - SIGINT # 后台启动时 cStartUp.sh 的日志信息, 下面为 tail -f 的输出, "^C" 为 ctrl + c 的操作
master:11_singal2Parent jerry$ ./cStartUp.sh
press id is 11166
Get a signal-SIGHUP
Get a signal - SIGINT
^Cmaster:11_singal2Parent jerry$ # 读取日志文件, 发现 c程序 收到了 SIGINT
press id is 11166
Get a signal-SIGHUP
Get a signal - SIGINT
Get a signal - SIGINT
2. node 程序, 测试程序如下
process.on('SIGHUP', function() {console.log('SIGHUP');
});process.on('SIGINT', function() {console.log('SIGINT');
});console.log('PID: ', process.pid);var http = require('http'); // HTTP server to keep the script up long enough
http.createServer(function (req, res) {res.writeHead(200, {'Content-Type': 'text/plain'});res.end('Hello World\
');
}).listen(1337, '127.0.0.1');console.log('Server running at http://127.0.0.1:1337/');
nodeStartUp.sh 脚本如下
master:11_singal2Parent jerry$ cat nodeStartUp.sh
nohup node nodeProcess.js > ./logs/nodeNohup.log 2>&1
# nohup node nodeProcess.js > ./logs/nodeNohup.log 2>&1 &tail -f ./logs/nodeNohup.log
在 Webstorm 中直接 运行/调试
// 调试启动, 分别发送 SIGHUP, SIGINT 到进程, 发现 SIGINT 的 handler 没有生效
press id is 11024
Signal: SIGHUP (signal SIGHUP)
Get a signal-SIGHUP
Signal: SIGINT (signal SIGINT)// 正常启动, 分别发送 SIGHUP, SIGINT 到进程, 两个 handler 都生效
press id is 11047
Get a signal-SIGHUP
Get a signal - SIGINT
分别切换 nodeStartUp.sh 的前台启动 和 后台启动, 查看现象
# 前台启动时, 分别发送 SIGHUP, SIGINT 到进程, 两个 handler 都生效
PID: 11461
Server running at http://127.0.0.1:1337/
SIGHUP
SIGINT# 后台启动时, 分别发送 SIGHUP, SIGINT 到进程, 两个 handler 都生效
PID: 11538
Server running at http://127.0.0.1:1337/
SIGHUP
SIGINT
nodeStartUp.sh 的前台启动 和 后台启动时, 中断 "tail -f" 是否向 node程序 发送 SIGINT
# 前台启动时 nodeStartUp.sh 的日志信息, "^C" 为 ctrl + c 的操作
master:11_singal2Parent jerry$ ./nodeStartUp.sh
^C# 读取日志文件, 发现 node程序 收到了 SIGINT
master:11_singal2Parent jerry$ tail -f logs/nodeNohup.log
PID: 11606
Server running at http://127.0.0.1:1337/
SIGHUP
SIGINT
SIGINT# 后台启动时 nodeStartUp.sh 的日志信息, "^C" 为 ctrl + c 的操作
master:11_singal2Parent jerry$ ./nodeStartUp.sh
PID: 11617
Server running at http://127.0.0.1:1337/
SIGHUP
SIGINT
^Cmaster:11_singal2Parent jerry$ # 读取日志文件, 发现 node程序 收到了 SIGINT
PID: 11617
Server running at http://127.0.0.1:1337/
SIGHUP
SIGINT
SIGINT
3. java 程序, 测试程序如下
/*** Test07Signal2ParentProcess** @author Jerry.X.He <970655147@qq.com>* @version 1.0* @date 2022-09-10 19:18*/
public class Test07Signal2ParentProcess {// Test07Signal2ParentProcesspublic static void main(String[] args) throws Exception {String lines = "HUP\n" +"INT\n" +
// "QUIT\n" +
// "ILL\n" +"TRAP\n" +"ABRT\n" +"EMT\n" +
// "FPE\n" +
// "KILL\n" +"BUS\n" +
// "SEGV\n" +"SYS\n" +"PIPE\n" +"ALRM\n" +"TERM\n" +"URG\n" +
// "STOP\n" +"TSTP\n" +"CONT\n" +"CHLD\n" +"TTIN\n" +"TTOU\n" +"IO\n" +"XCPU\n" +"XFSZ\n" +"VTALRM\n" +"PROF\n" +"WINCH\n" +// todo, not work in hostpostVM9
// "INFO\n" +
// "USR1\n" +"USR2";for (String sigNo : lines.split("\n")) {Signal.handle(new Signal(sigNo), new SignalHandler() {@Overridepublic void handle(Signal signal) {System.out.println(sigNo);}});}Signal.raise(new Signal("ALRM"));Signal.raise(new Signal("INT"));Thread.sleep(300 * 1000);}}
startUp.sh 脚本如下
master:11_singal2Parent jerry$ cat startUp.sh # java -classpath /Users/jerry/IdeaProjects/HelloWorld/target/classes com.hx.test13.Test07Signal2ParentProcess > ./logs/nohup.log 2>&1 &# java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -classpath /Users/jerry/IdeaProjects/HelloWorld/target/classes com.hx.test13.Test07Signal2ParentProcessjava -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -classpath /Users/jerry/IdeaProjects/HelloWorld/target/classes com.hx.test13.Test07Signal2ParentProcess > ./logs/nohup.log 2>&1 &tail -f ./logs/nohup.log
在 Idea 中直接 运行/调试
// 调试启动, 分别发送 SIGHUP, SIGINT 到进程, 发现 SIGINT 的 handler 没有生效
INT
ALRM// 正常启动, 分别发送 SIGHUP, SIGINT 到进程, 两个 handler 都生效
INT
ALRM
分别切换 startUp.sh 的前台启动 和 后台启动, 查看现象
# 前台启动时, 分别发送 SIGHUP, SIGINT 到进程, 两个 handler 都生效
master:11_singal2Parent jerry$ ./startUp.sh
Listening for transport dt_socket at address: 5005
INT
ALRM
^CINT# 后台启动时, 分别发送 SIGHUP, SIGINT 到进程, SIGINT 的 handler 不生效
# 使用 kill -2 $pid 同样不生效
master:11_singal2Parent jerry$ ./startUp.sh
Listening for transport dt_socket at address: 5005
ALRM
startUp.sh 的前台启动 和 后台启动时, 中断 "tail -f" 是否向 java程序 发送 SIGINT
没有 SIGINT 的日志输出 就表示该 进程没有收到 SIGINT 的信号吗? 不一定
# 前台启动时 startUp.sh 的日志信息, "^C" 为 ctrl + c 的操作
master:11_singal2Parent jerry$ ./startUp.sh
Listening for transport dt_socket at address: 5005
INT
ALRM
^CINT# 后台启动时 startUp.sh 的日志信息, "^C" 为 ctrl + c 的操作
master:11_singal2Parent jerry$ ./startUp.sh
Listening for transport dt_socket at address: 5005
ALRM
^Cmaster:11_singal2Parent jerry$ # 读取日志文件, 没有 SIGINT 的日志输出
Listening for transport dt_socket at address: 5005
ALRM
后台启动时, 使用 gdb 连接后台进程, ctrl + c 的时候, 能够接收到 SIGINT 的信号
Thread 1 "java" received signal SIGINT, Interrupt.
0x00007fb7f452098d in pthread_join (threadid=140428059076352, thread_return=0x7fffb6cb9e08) at pthread_join.c:90
90 in pthread_join.c
ubuntu 中基于 openJdk 调试 Test07Signal2ParentProcess
这里是基于 jvm, linux 的一些调试, 因此 我们期望看到更细节的东西
这里 hotspotVM 基于 openjdk8
比如 注册 SignalHandler 的时候, 是否会有一些不同的处理, 是否注册上
另外就是 发送了 SIGINT 之后的处理?
startUp.sh 的前台启动 和 后台启动时, 中断 "tail -f" 是否向 java程序 发送 SIGINT
在 Clion 中直接 运行/调试
这里的现象和 上面 Clion 调试 HelloWord 的现象一致, 应该是 Clion 的相关代理处理, 导致的问题
// 调试启动, 分别发送 SIGHUP, SIGINT 到进程, 发现 SIGINT 的 handler 没有生效
ALRM// 正常启动, 分别发送 SIGHUP, SIGINT 到进程, 两个 handler 都生效
INT
ALRM
Clion 中调试看一下 注册 SignalHandler, 以及 接收到 SIGINT 之后的处理
SIGHUP 注册了 UserHandler 作为 handler
SIGINT 注册了 UserHandler 作为 handler
通过 raise 准备发送 SIGHUP 的信号
这里往 将 0xea 赋值到 eax, 然后执行 syscall, 0xea 对应的系统调用为 tgkill, 类似于 kill 命令
向给定的 进程发送了 SIGHUP 的信号
resultvar 对应于 eax, 值为 0, 表示 tgkill 的系统调用调用成功
如下是收到了 SIGHUP 的信号之后, 当前进程的处理, 注册的函数是 UserHandler
通过 raise 准备发送 SIGINT 的信号
这里往 将 0xea 赋值到 eax, 然后执行 syscall, 0xea 对应的系统调用为 tgkill, 类似于 kill 命令
向给定的 进程发送了 SIGINT 的信号
resultvar 对应于 eax, 值为 0, 表示 tgkill 的系统调用调用成功
但是后面没有 收到 SIGINT 之后的回调处理, 这个就很奇怪?
这也是我没有搞明白的地方
我们再确认一下 raise 的时候, SIGINT 对应的 handler, 执行 "print os::signal(2, os::user_handler())", sigAct 为新的 handler, oldSigAct 为已有的 handler, 可以发现都是 UserHandler
那就说明我们这里的情况是 tgkill 发送了 SIGINT 成功, 但是 后面操作系统有什么其他的处理?
Clion 远程调试 gdbServer 前台启动的服务, startGdbServer.sh 如下
root@ubuntu:~/Desktop/openJdk/HelloWorld# cat startGdbServer.sh mainClazz=Test07Signal2ParentProcess
classpath=.:./lib/jol-core-0.8.jar:EXISTS_PID=`jps -lvm | grep $mainClazz | grep -v grep | awk '{print $1}'`
if [ "$EXISTS_PID" = "" ]; thenecho " the main class $mainClazz does not startup "
elsekill -9 $EXISTS_PIDecho " the main class $mainClazz shutdown succeed, kill -9 $EXISTS_PID "
figdbserver localhost:1234 /root/Desktop/openJdk/openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/bin/java -XX:+AllowUserSignalHandlers -cp $classpath $mainClazz > ./logs/nohup.log 2>&1
# gdbserver localhost:1234 /root/Desktop/openJdk/openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/bin/java -XX:+AllowUserSignalHandlers -cp $classpath $mainClazz > ./logs/nohup.log 2>&1 & tail -f ./logs/nohup.log
注册 SIGHUP, SIGINT 的 handler, 均为 UserHandler
raise 发送信号之后, 和上面一样 SIGHUP 进入了 UserHandler 的处理, SIGINT 没有进入 UserHandler 的处理
Clion 远程调试 gdbServer 后台启动的服务, startGdbServer.sh 如下
注册 SIGHUP 的 handler, 为 UserHandler, 但是 SIGHUP 的 handler 的注册, 我们发现 居然没有了?
另外就是 raise 发送信号的处理, 也是一样, SIGHUP 的正常发送, 然后 SIGINT 的没有了?
呵呵 这是本文的另外的一个 细节的地方了, 如果 没有调试, 这种现场 估计你想都想不到
看第一个 RegisterSignal, 可以看到 SIGINT 的 handler 为 SIG_IGN
这里对于 SIGHUP, SIGINT, SIGTERM 如果 已有的 handler 是 SIG_IGN 的话, 会直接返回, 返回 1, 表示 handler 为 SIG_IGN
raise 的时候处理如下, SIGINT 的 handler 为 SIG_IGN, 因此 直接 返回了
Clion 远程调试 gdbServer 后台启动的服务, 中断 "tail -f" 是否向 java程序 发送 SIGINT
手动执行 "kill -2 $pid" 是可以看到一个 SIGINT 触发了断点中断
但是当我 ctrl + c 中断 "tail -f " 的时候
在 gdb server 上面接收到了这个 SIGINT 的中断
mac 中基于 openJdk 调试 Test07Signal2ParentProcess
和上述 ubuntu 的相关结论基本一致
是那个进程发送的 SIG_INT?
基于 siginfo_t.si_pid 来获取发送信号的进程的信息
#include "stdio.h"
#include "stdlib.h"
#include "unistd.h"
#include "signal.h"void signalHandler(int sigNumber, siginfo_t *info, void *context) {int senderPid = info->si_pid;printf(" process %d, sender process pid %d, got signal %d \r\n", getpid(), senderPid, sigNumber);fflush(stdout);
}void processHandler(int pid, char *processName) {while(1) {printf(" I'm %s, and pid is %d \r\n", processName, pid);fflush(stdout);sleep(2);}
}int main(int argc, char** argv) {// echo -e "nohup /Users/jerry/ClionProjects/HelloWorld/cmake-build-debug/HelloWorld >> logs/nohup.log 2>&1 &\n tail -f logs/nohup.log" >> start.sh// chmod +x start.shstruct sigaction sa;sa.sa_flags = SA_SIGINFO;sa.sa_sigaction = signalHandler;sigaction(2, &sa, NULL);sigaction(3, &sa, NULL);sigaction(4, &sa, NULL);int childPid = fork();if(childPid == 0) {processHandler(getpid(), "child1");} else {int child2Pid = fork();if(child2Pid == 0) {processHandler(getpid(), "child2");} else {processHandler(getpid(), "parent");}}}
再 mac 上面, 无论是 前台启动进程, 还是 后台启动进程 + "tail -f logs/nohup.log", 得到的日志 均是类似于如下
I'm child2, and pid is 2246
I'm child1, and pid is 2245
I'm parent, and pid is 2243
process 2243, sender process pid 488, got signal 2
process 2246, sender process pid 488, got signal 2
process 2245, sender process pid 488, got signal 2
I'm child2, and pid is 2246
I'm child1, and pid is 2245
I'm parent, and pid is 2243
进程树 大致如下 , 可以看到的是 signal 是由 Terminal 这个进程发出的
488 1 0 10:37AM ?? 0:27.76 /Applications/Utilities/Terminal.app/Contents/MacOS/Terminal489 488 0 10:37AM ttys000 0:00.02 login -pf jerry// bash 490 489 0 10:37AM ttys000 0:00.19 -bash// start.sh2242 490 0 3:51PM ttys000 0:00.00 -bash2243 2242 0 3:51PM ttys000 0:00.01 /Users/jerry/ClionProjects/HelloWorld/cmake-build-debug/HelloWorld2245 2243 0 3:51PM ttys000 0:00.00 /Users/jerry/ClionProjects/HelloWorld/cmake-build-debug/HelloWorld2246 2243 0 3:51PM ttys000 0:00.00 /Users/jerry/ClionProjects/HelloWorld/cmake-build-debug/HelloWorld2244 2242 0 3:51PM ttys000 0:00.00 tail -f logs/nohup.log
再 linux 上面, 若果是直接 中断 "tail -f", 似乎是拿不到的发送 SIGINT 的进程的进程号的
I'm child1, and pid is 7521I'm parent, and pid is 7519I'm child2, and pid is 7522process 7522, sender process pid 0, got signal 2I'm child2, and pid is 7522process 7519, sender process pid 0, got signal 2I'm parent, and pid is 7519process 7521, sender process pid 0, got signal 2I'm child1, and pid is 7521I'm child2, and pid is 7522
但是手动 kill 发送信号, 是可以拿到 发送 SIGINT 的进程的进程号的
I'm child2, and pid is 7522I'm child1, and pid is 7521I'm parent, and pid is 7519process 7521, sender process pid 7336, got signal 2I'm child1, and pid is 7521I'm child2, and pid is 7522I'm parent, and pid is 7519I'm child1, and pid is 7521process 7519, sender process pid 7336, got signal 2I'm parent, and pid is 7519I'm child2, and pid is 7522I'm child1, and pid is 7521process 7522, sender process pid 7336, got signal 2I'm child2, and pid is 7522I'm parent, and pid is 7519I'm child1, and pid is 7521
结论
1. 在后台启动了 c/node/java 程序之后, 添加了一个 "tail -f", 当 ctrl + c 中断 "tail -f" 的时候, 会发送 SIGINT 到对应的 c/node/java 进程
2. 在后台启动 java 进程的场景中, 注册 SIGINT 实际上最终是没有注册成功, 会直接被忽略掉, 发送 SIGINT 信号到给定的进程, 也会被 忽略掉
3. 在后台启动 java 进程的场景中, SIGINT 的 handler 在 java 层面 和 jvm 层面存放的逻辑 handler 不匹配, java 层面拿到的是注册的自定义的 handler, jvm 拿到的的是 SIG_IGN
4. Clion 调试启动程序时候, 对于 SIGINT 信号的支持存在问题 ?
这里大概可以解释 node 程序后台执行加上 tail 命令, 中断 tail 命令, 同时也中断了 node 程序 中的第三个, 第四个问题
3. java 程序这边通过 "startUp.sh" 启动, 进程到底收到 SIGINT 没有?
4. java 程序这边程序中注册的 SIGINT 的 handler 为什么没有生效?, 替换成了什么?
java 程序这边通过 "startUp.sh" 启动, 然后 ctrl + c 中断了 "tail -f" 是收到了 SIGINT 的信号了的
java 程序这边程序中注册的 SIGINT 的 handler
如果是前台启动, 则会为 SIGINT 注册正确的 UserHandler/Test07Signal2ParentProcess$1, raise, kill 也能正确的发送信号, 但是因为神秘的原因, 没有处理这个信号
如果是后台启动, 则 SIGINT 默认注册的 handler 是 SIG_IGN, 因此后面用户手动注册的 handler 是没有注册上的, 然后 raise 的时候, 直接忽略了这个信号请求, 甚至还没有发送信号
这里大概可以解释 node 程序后台执行加上 tail 命令, 中断 tail 命令, 同时也中断了 node 程序 中的第一个问题
1. 但是是 哪一个进程呢 向 node进程 发送的 SIGINT ?
2. 这个进程 为什么要发送 SIGINT 到 node 进程呢 ?
在 mac 中就是 bash 对应的 父进程 发送的 SIGINT 信号
在 linux 中不确定
完
56/14 shell脚本 后台启动 程序1 + “tail -f log“, ctrl +c 导致程序1中断相关推荐
- java源码如何启动脚本_使用Shell脚本如何启动/停止Java的jar程序
本文介绍如何使用Shell脚本来开启和停止jar程序的后台运行,以及如何实现out大文件的切分.另外,补充一些后台运行的小知识. 启动脚本:start_upload.sh #!/bin/sh nohu ...
- nohup执行的jar 怎么kill_使用Shell脚本如何启动/停止Java的jar程序
本文介绍如何使用Shell脚本来开启和停止jar程序的后台运行,以及如何实现out大文件的切分.另外,补充一些后台运行的小知识. 启动脚本:start_upload.sh #!/bin/sh nohu ...
- linux打开pythonshall,linux系统shell脚本后台运行python程序
python开发的同学们应该都知道怎么样去启动一个python应用,但是一旦我们把python应用部署到linux服务器上该如何启动呢? 方式①:就是使用进程管理工具,比如supervisor.想了解 ...
- GDB怎么调试使用.sh(shell脚本)启动的程序?(未完成,待测试)
1 )直接在shell脚本中调用GDB.这意味着你没有标准的和标准的重定向.(不知道怎么操作..反正没成功) 2)运行shell脚本,然后将调试器附加到已经运行的c ++进程,像这样:gdb prog ...
- Jenkins执行shell脚本无法启动子进程解决
例子:shell脚本 cd /home #test.jar目录 nohup java -jar test.jar --server.port=8081 >test.log 2>&1 ...
- Win7运行程序总提示出现一个问题,导致程序停止正常工作。请关闭该程序?原来GreenBrowser下载有问题……
前段时间借了一位朋友的电脑来用.那台电脑里原有的浏览器不顺手,就先下载安装GreenBrowser,再运行GreenBrowser下载其它的应用程序. 首先下载EditPlus,结果运行时提示: 出现 ...
- Win7运行程序总提示出现一个问题,导致程序停止正常工作 请关闭该程序 原来GreenBrowser下载有问题
前段时间借了一位朋友的电脑来用.那台电脑里原有的浏览器不顺手,就先下载安装GreenBrowser,再运行GreenBrowser下载其它的应用程序. 首先下载EditPlus,结果运行时提示: 出现 ...
- php fastcgi进程启动,Shell脚本实现启动PHP内置FastCGI Server
前几天把工作平台从 Ubuntu 9.10 Karmic 更新到了 10.04 Lucid,由于 Lucid 官方源自带了 PHP5.3.2,以前使用的 dotdeb的源就没法用了,一直很喜欢这个源的 ...
- linux 关闭java进程后重启有用吗_linux启动java进程的shell脚本(包括启动,停止,重启)...
近来使用spring boot写了一个短信服务组件,并配上此段脚本,实现对服务进程的管理 #!/bin/bash # chkconfig: 2345 85 85 # description: sms- ...
最新文章
- 【每日一算法】搜索插入位置
- linux下使用ffmpeg命令录屏桌面
- 《Effective STL》学习笔记(第四部分)
- 鸟哥的Linux私房菜(基础篇)- 第八章、Linux 磁盘与文件系统管理
- 虚拟机安装以及PCL的配置(1)
- 《前端JavaScript重点》学习笔记 1-5
- 高精度加法(简明版C语言),高精度加法(简明版C语言)
- php如何在微信跳转支付宝支付,微信支付成功了怎么跳转到我指定的信息提示页? - 码支付...
- 中国水平板式过滤器行业市场供需与战略研究报告
- 删除误添加的本地github检查库文件
- 6.Linux/Unix 系统编程手册(上) -- 进程
- python java爬虫_java爬虫与python爬虫对比
- aqua datastudio配置
- 数字电子技术基础笔记
- 多功能的Silverlight控件User Interface Edition for Silverlight下载及详细介绍
- 540s inter 固件_Intel SSD Firmware Update Tool(英特尔ssd固件更新工具)下载 v2.1.6官方版...
- 《下学梯航》(全文)
- 阿里云网盘内测申请_最新阿里云网盘官方申请地址,哪里可以获得阿里网盘内测码?9月23日截至...
- 腾讯web引用skey g_tk bkn和日期显示分析
- Stable Diffsuion还能用来压缩图像?压缩率更高,清晰度超越JPEG等算法