56/14 shell脚本 后台启动 程序1 + “tail -f log“, ctrl +c 导致程序1中断

news2024/11/19 22:36:18

前言

接上一篇文章, 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 {

    // Test07Signal2ParentProcess
    public 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() {
                @Override
                public 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.Test07Signal2ParentProcess

java -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" = "" ]; then
    echo " the main class $mainClazz does not startup "
else
    kill -9 $EXISTS_PID
    echo " the main class $mainClazz shutdown succeed, kill -9 $EXISTS_PID "
fi


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
# 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.sh
    struct 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/Terminal
    489    488   0  10:37AM ttys000    0:00.02 login -pf jerry
        // bash 
        490    489   0  10:37AM ttys000    0:00.19 -bash
            // start.sh
            2242   490   0  3:51PM ttys000    0:00.00 -bash
                2243  2242   0  3:51PM ttys000    0:00.01 /Users/jerry/ClionProjects/HelloWorld/cmake-build-debug/HelloWorld
                    2245  2243   0  3:51PM ttys000    0:00.00 /Users/jerry/ClionProjects/HelloWorld/cmake-build-debug/HelloWorld
                    2246  2243   0  3:51PM ttys000    0:00.00 /Users/jerry/ClionProjects/HelloWorld/cmake-build-debug/HelloWorld
                2244  2242   0  3:51PM ttys000    0:00.00 tail -f logs/nohup.log

再 linux 上面, 若果是直接 中断 "tail -f", 似乎是拿不到的发送 SIGINT 的进程的进程号的 

 I'm child1, and pid is 7521
 I'm parent, and pid is 7519
 I'm child2, and pid is 7522
 process 7522, sender process pid 0, got signal 2
 I'm child2, and pid is 7522
 process 7519, sender process pid 0, got signal 2
 I'm parent, and pid is 7519
 process 7521, sender process pid 0, got signal 2
 I'm child1, and pid is 7521
 I'm child2, and pid is 7522

但是手动 kill 发送信号, 是可以拿到 发送 SIGINT 的进程的进程号的

 I'm child2, and pid is 7522
 I'm child1, and pid is 7521
 I'm parent, and pid is 7519
 process 7521, sender process pid 7336, got signal 2
 I'm child1, and pid is 7521
 I'm child2, and pid is 7522
 I'm parent, and pid is 7519
 I'm child1, and pid is 7521
 process 7519, sender process pid 7336, got signal 2
 I'm parent, and pid is 7519
 I'm child2, and pid is 7522
 I'm child1, and pid is 7521
 process 7522, sender process pid 7336, got signal 2
 I'm child2, and pid is 7522
 I'm parent, and pid is 7519
 I'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 中不确定

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/148874.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

实现自己的打印函数

文章目录前言前置知识代码说明实验操作单字符打印字符串打印整形字符串打印前言 本博客记录《操作系统真象还原》第六章实验的操作~ 实验环境&#xff1a;ubuntu18.04VMware &#xff0c; Bochs下载安装 实验内容&#xff1a; 实现 put_char 单字符打印输出函数。实现 put_…

TEE 背景知识

TEE 背景 计算机世界的安全&#xff0c;是保护计算机系统和网络免受攻击者的攻击&#xff0c;这些攻击可能导致未经授权的信息泄露、窃取或损坏硬件、软件或数据&#xff0c;以及它们所提供的服务的中断或误导。更多参考 Computer_security 1 安全是什么 谈安全&#xff0c;…

28.0:Combit LIST LABEL Reporting for Delphi Crack

作为 Delphi 开发人员的优势&#xff1a; 将 VCL 组件直接集成到 Embarcadero IDE 中 免版税报表设计器 交互式预览等 使用综合报表对象 Delphi 和所有其他相关语言和开发环境的报告工具。 将列表和标签集成到 Embarcadero RAD Studio List & Label 的 VCL 组件编译并直接…

HTML与CSS基础(八)—— CSS布局(定位、装饰、选择器拓展)

目标能够说出 定位 的常见应用场景 能够说出 不同定位方式 的特点 能够使用 子绝父相 完成元素水平垂直案例 能够写出三种常见的 光标类型&#xff08;cursor&#xff09; 能够使用 圆角边框 属性完成 正圆 和 胶囊按钮 效果 能够说出 display 和 visibility 让 元素本身隐藏 的…

【java查漏补缺】File类的使用

File类可以用来处理文件数据&#xff0c;比如使用list()方法可以获得某个文件夹下面的所有文件名。 File类的list()方法有一个含有一个参数的重载&#xff0c;在调用该方法的时候需要传入一个FilenameFilter对象&#xff0c;这样便可以进行文件名过滤。 我们先来看一下list()…

GJB 5000B二级-PP项目策划

一、主要变化情况 合并10项(绿色)、修订3项(蓝色)、删除1项(红色) 新增的主要内容 基于A版标准“项目策划”、“集成项目管理”、“定量项目管理”三个过程域中有关项目策划实践的内容来识别,没有新增实践,只增加了以下两个要求: 1、增加软件项目和设备/系统生存周…

Java线程之中断方法

interrupt()方法介绍interrupt() 给目标线程发送一个中断信号&#xff0c;同时将目标线程的中断标志设置为true。至于目标线程是否做出响应&#xff0c;需要看目标线程是否有对应的中断业务逻辑。场景1&#xff1a;假设&#xff0c;你是一个工厂的老板&#xff08;main线程&…

C++类和对象(下)

目录 初始化列表 explicit关键字 Static成员 友元 友元函数 友元类 匿名对象 内部类 初始化列表 初始化列表是以冒号开始&#xff0c;以逗号分割的成员列表&#xff0c;每一个成员变量后面跟一个放在括号中的初始值或表达式。&#xff08;代码演示以日期类为例&#xff…

【实现QQ登录界面 Objective-C语言】

一、实现QQ登录界面 1.实现这样的QQ登录界面 1.实现这样的QQ登录界面 2.首先,分析一下,这个界面里,都有哪些控件 是不是两个Label,两个TextField文本框,1个Button吧 3.先拖1个Label上来 再拖1个TextField文本框上来,在这个Label右边, 然后选中这两个控件, 按住o…

Altium Designer 超详细学习教程——印制电路板基础知识

在介绍Altium Designer软件使用办法之前先介绍下印制电路板的基础知识&#xff0c;不管是画图还是绘制PCB最终目的都是为了制作电路板&#xff0c;因此了解PCB的基础知识很有必要。 1.1印制电路板概述 1.1.1印制电路板结构 在进行PCB设计时&#xff0c;需要先对印制电路板的…

【Linux】对于make/Makefile的使用

本文目录 背景简介 细说关于make命令和makefile文件&#xff1a; 使用方法 为什么执行的指令是make和make clean呢&#xff1f; gcc如何判断文件是否需要重新执行&#xff1f; 背景简介 一个工程中的源文件不计数&#xff0c;其按类型、功能、模块分别放在若干个目录中&…

064-Tcp/IP通讯使用ServerSocket及Server实现多人在线聊天项目实训

【上一讲】063-Tcp/IP通讯使用ServerSocket及Server实现多人在线聊天_CSDN专家-赖老师(软件之家)的博客-CSDN博客 本文主要讲解使用ServerSocket,Socket类实现多人同时在线聊天的网络通讯程序,主要功能及技术如下: 1.使用ServerSocket,Socket类实现网络通讯功能; 2.使用…

【go语言之websocket】

go语言之websocket写在前面服务端msggetConnUpgradeNewWsConnectionNextWriterwriteread客户端抓包表现总结写在前面 之前的文章都是介绍的是http的使用&#xff0c;这里主要介绍的是websocket,主要是解决长连接场景下的使用。这里概念不多说&#xff0c;网上很多&#xff0c;…

Ajax篇-前后端交互, 接口文档, ajax, axios, fetch,Postman工具

前后端交互接口文档请求方法区分方式GETPOST从语义上区分偏向于查询&#xff08;获取数据....&#xff09;偏向于提交数据(注册,修改,删除...)安全性显示的携带参数,参数是直接拼接在请求地址之后,安全性较差,隐私性差隐式的携带参数,不会在请求地址上显示,安全性好,以JSON格式…

网络虚拟化基本架构

文章目录架构概述架构图核心组件OpenFlow SwitchPipelineFlow Table EntryInstructionOpenFlow Switch Protocol测试验证Pipeline流表项间流程流表间流程Flow Table EntryInstructionsSwitch ProtocolFaucet架构概述 我们知道网络虚拟化的主要目标就是让报文可以在虚拟机之间进…

Windows Anaconda YOLOv3环境部署--2023年1月8日

时效性&#xff1a; 2023年1月8日 目录摘要1 使用 Anaconda 创建虚拟环境2 安装官方要求的依赖库3 验证安装 | 执行 detect.py 示例代码Key already registered with the same priority摘要 网好的可以直接参考官方文档安装&#xff0c;遇到安装报错和网络问题可以参考本文 本地…

网络安全等级保护定级指南 范围

声明 本文是学习github5.com 网站的报告而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 网络安全等级保护 为了配合《中华人民共和国网络安全法》的实施&#xff0c;适应云计算、移动互联、物联网、工业控制和大数据等新技术、新应用情况下网络安全等…

Spring Cloud OpenFeign入门示例

简介 Feign Feign是一个声明性web服务客户端。让编写Web服务客户端变得非常容易&#xff0c;只需创建一个接口并在接口上添加注解即可。让http远程调用就像接口调用一样简单。&#xff08;远程http调用的工具有很多&#xff0c;HttpClient、OKHttp、Spring Boot中的RestTempl…

2023新年红包,兔年HTML红包页面代码【2023新年快乐_附源码】

文章目录一.新年红包&#xff0c;兔年HTML红包页面1.1 资源获取和效果预览二.代码讲解&#xff08;Html文件&#xff09;三.Html文件代码展示&#xff08;需要全部源码请到文章开头链接处下载&#xff09;一.新年红包&#xff0c;兔年HTML红包页面 1.1 资源获取和效果预览 1.…

【Nacos】- Mac-M1下Nacos安装及Nacos启动报错“have ‘x86_64’,need ‘arm64e‘”

Nacos安装及Nacos启动报错“have ‘x86_64’,need arm64e”nacos下载启动nacos问题描述解决方案1、下载安装x86架构的jdk2、更换nacos版本&#xff1a;我这是更换为1.4.2的nacos下载 下载地址&#xff1a;https://github.com/alibaba/nacos/releases 根据自己的工具及需要版本…