深入解析:如何通过网络命名空间跟踪单个进程的网络活动(C/C++代码实现)
在 Linux 系统中,网络命名空间(Network Namespaces)是一种强大的功能,它允许系统管理员和开发者隔离网络资源,使得每个命名空间都拥有独立的网络协议栈。这种隔离机制不仅用于容器技术如 Docker,也是网络安全和性能分析的重要工具。是一个利用 Linux 网络命名空间来跟踪单个应用程序网络活动的开源工具,它将跟踪结果保存为 pcap 文件,以便后续使用 Wireshark 或 tshark 等工具进行分析。
网络命名空间的基本概念
在监控和分析网络流的过程中,理解和应用网络命名空间的基本概念是至关重要的。以下是一些关键点,可以帮助你更好地掌握这一技术:
- 基本概念
- 定义:网络命名空间(Network Namespace)是一种内核功能,允许系统创建多个隔离的网络环境,每个网络命名空间都有自己独立的网络栈,包括网络设备、IP地址、路由表等。
- 作用:通过使用网络命名空间,可以在同一台机器上运行多个进程组,每组进程都认为自己拥有独立的网络资源,从而实现网络资源的隔离和管理。
- 创建和管理网络命名空间
- 创建命令:可以使用
ip netns add <namespace_name>
命令来创建一个新的网络命名空间。 - 查看和管理:通过
ip netns list
命令可以列出所有已创建的网络命名空间,而ip netns delete <namespace_name>
则用于删除一个命名空间。
- 将网络接口添加到命名空间
- 虚拟以太网对(veth pair):使用
ip link add veth0 type veth peer name veth1
创建一个虚拟以太网对,然后使用ip link set veth1 netns <namespace_name>
将其中一个接口添加到指定的网络命名空间。 - 配置 IP 地址:在添加接口到命名空间后,需要为这些接口配置 IP 地址并启用它们,例如
ip addr add 192.168.1.1/24 dev veth1
和ip link set dev veth1 up
。
- 跨命名空间通信
- 使用桥接(bridge):为了实现不同网络命名空间之间的通信,可以创建一个桥接设备,并将各个命名空间中的虚拟接口连接到这个桥上。
- 配置路由:确保各个网络命名空间能够正确路由数据包,可以通过配置静态路由或使用动态路由协议来实现这一点。
- 监控和分析
- 工具使用:利用
tcpdump
和Wireshark
等工具可以在特定网络命名空间中捕获和分析流量。 - 脚本自动化:编写脚本定期捕获和保存网络数据,以便后续分析。
将进程关联到特定网络命名空间
要将进程关联到特定的网络命名空间,可以使用以下步骤:
-
首先,确保你已经创建了一个网络命名空间。你可以使用
ip netns add <namespace_name>
命令来创建一个命名空间。例如,要创建一个名为"my_namespace"的命名空间,可以运行以下命令:ip netns add my_namespace
-
接下来,你需要将一个网络接口(如veth对)添加到该命名空间中。首先,创建一个虚拟以太网对,然后将其连接到命名空间。以下是创建和连接虚拟以太网对的命令示例:
ip link add veth0 type veth peer name veth1 ip link set veth1 netns my_namespace
-
在命名空间内部,你需要配置网络接口并启用它。进入命名空间的命令是
ip netns exec <namespace_name> /bin/bash
。一旦你进入了命名空间,你可以执行以下命令来配置网络接口:ip addr add 192.168.1.2/24 dev veth1 ip link set dev veth1 up
-
现在,你可以在命名空间内启动你的进程。你可以使用
ip netns exec <namespace_name> <command>
来执行命令。例如,要在命名空间内启动一个名为"my_process"的进程,可以运行以下命令:ip netns exec my_namespace my_process
-
最后,你可以使用
ps
命令查看命名空间内的进程列表。要查看特定命名空间中的进程,可以使用ip -n <namespace_name> ps
命令。例如,要查看名为"my_namespace"的命名空间中的进程,可以运行以下命令:ip -n my_namespace ps
通过以上步骤,你可以将进程关联到特定的网络命名空间,并在该命名空间内进行监控和分析。
网络命名空间的配置
1.创建虚拟网络接口
为了使被跟踪的进程能够与外界通信,我们需要在根命名空间和新的命名空间之间创建一对虚拟网络接口(veth对)。然后,我们可以将其中一个接口配置为新命名空间的默认网关。
2.使用iptables进行NAT
为了允许命名空间中的进程访问外部网络,我们可以使用iptables设置网络地址转换(NAT)。这样,所有从命名空间发出的流量都会被自动转换到根命名空间的IP地址。
3.处理DNS解析
在网络命名空间中,DNS解析可能会遇到问题,因为默认的DNS服务器可能无法访问。nsntrace提供了一个选项–use-public-dns,可以覆盖命名空间内的resolv.conf文件,使用公共DNS服务器进行查询。
深入解析:如何通过网络命名空间跟踪单个进程的网络活动(C/C++代码实现)
利用Linux的网络命名空间来跟踪单个进程的网络活动。它通过创建一个新的网络命名空间,在这个隔离的环境中启动目标进程,并使用libpcap库来捕获该进程产生的所有网络数据包,然后将这些数据包保存到pcap文件中,以便后续分析
int nsntrace_capture_start(const char *iface,const char *filter,FILE *fp);void nsntrace_capture_stop();unsigned long nsntrace_capture_packet_count();void nsntrace_capture_flush();char *nsntrace_capture_default_device();int nsntrace_capture_check_device(char *iface);int nsntrace_net_init(pid_t ns_pid,const char *device,struct nsntrace_if_info *info);int nsntrace_net_deinit(pid_t ns_pid,const char *device,struct nsntrace_if_info *info);int nsntrace_net_ns_init(int use_public_dns,struct nsntrace_if_info *info);int nsntrace_net_ip_forward_enabled();int nsntrace_net_get_if_info(pid_t pid,struct nsntrace_if_info *info);
...const int nsntrace_signals[] = {SIGHUP,SIGINT,SIGABRT,SIGTERM,SIGQUIT,SIGSEGV,
};static void
_nsntrace_handle_signals(void (*handler)(int))
{struct sigaction action = { 0 };int s;action.sa_handler = handler;for (s = 0; s < sizeof(nsntrace_signals) / sizeof(int); s++) {sigaction(nsntrace_signals[s], &action, NULL);}
}static int _nsntrace_unlink_cb(const char *fpath,const struct stat *sb,int typeflag,struct FTW *ftwbuf)
{int rv = remove(fpath);if (rv)perror(fpath);return rv;
}static void
_nsntrace_remove_rundir(pid_t pid)
{char path[PATH_MAX] = { 0, };snprintf(path, PATH_MAX, "%s/%d", NSNTRACE_RUN_DIR, getppid());nftw(path, _nsntrace_unlink_cb, 64, FTW_DEPTH | FTW_PHYS);
}static void _nsntrace_remove_ns()
{char netns_path[PATH_MAX];snprintf(netns_path, sizeof(netns_path), "%s/nsntrace-%d", NETNS_RUN_DIR, getpid());umount2(netns_path, MNT_DETACH);if (unlink(netns_path) < 0) {perror("unlink");}_nsntrace_remove_rundir(getppid());
}static void
_nsntrace_cleanup_ns()
{kill(child_pid, SIGTERM);waitpid(child_pid, NULL, 0);_nsntrace_remove_ns();fprintf(where, "Finished capturing %lu packets.\n",nsntrace_capture_packet_count());nsntrace_capture_flush();
}static void
_nsntrace_cleanup_ns_signal_callback()
{_nsntrace_cleanup_ns();exit(EXIT_SUCCESS);
}static void
_nsntrace_cleanup() {wait(NULL);
}static void
_nsntrace_set_name()
{
.../* 如果基本netns目录不存在,则创建它 */if ((ret = mkdir(NETNS_RUN_DIR, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)) < 0) {if (errno != EEXIST) {goto out;}}snprintf(netns_path, PATH_MAX, "%s/nsntrace-%d", NETNS_RUN_DIR, getpid());fd = open(netns_path, O_RDONLY|O_CREAT|O_EXCL, 0);if (fd < 0) {ret = fd;goto out;}close(fd);ret = mount(PROC_SELF_NETNS_PATH, netns_path, "none", MS_BIND, NULL);
out:if (ret < 0) {fprintf(stderr, "failed to set namespace name\n");}
}static void
_nsntrace_start_tracer(struct nsntrace_options *options,struct nsntrace_if_info *info)
{int ret;/** 如果outfile是一个单破折号(“-”),用户希望我们输出到STDOUT*/FILE *fp;if (options->outfile[0] == '-' && options->outfile[1] == '\0') {where = stderr;fp = stdout;} else {where = stdout;fp = fopen(options->outfile, "w");if (!fp) {perror("fopen");exit(EXIT_FAILURE);}}fprintf(where, "Starting network trace of '%s' on interface %s.\n""Your IP address in this trace is %s.\n""Use ctrl-c to end at any time.\n\n",options->args[0], options->device, info->ns_ip);ret = nsntrace_capture_start(info->ns_if, options->filter, fp);if (ret != 0) {exit(ret);}
}static void
_nsntrace_start_tracee(struct nsntrace_options *options)
{
...if (sscanf(options->user, "%i", &uid) == 1) {pwd = getpwuid(uid);} else {pwd = getpwnam(options->user);}if (!pwd) {fprintf(stderr,"Cannot find user '%s'\n",options->user);exit(EXIT_FAILURE);}uid = pwd->pw_uid;gid = pwd->pw_gid;setenv("HOME", pwd->pw_dir, 1);setenv("USER", pwd->pw_name, 1);setenv("USERNAME", pwd->pw_name, 1);if (initgroups(options->user, gid) < 0) {fprintf(stderr, "Failed to initialize groups\n");exit(EXIT_FAILURE);}} else {uid = getuid();gid = getgid();}if (setgid(gid) < 0) {fprintf(stderr, "Unable to set process group ID\n");exit(EXIT_FAILURE);}if (setuid(uid) < 0) {fprintf(stderr, "Unable to set process user ID\n");exit(EXIT_FAILURE);}/** 只在STDOUT上输出PCAP数据*/if (options->outfile[0] == '-' && options->outfile[1] == '\0') {dup2(STDERR_FILENO, STDOUT_FILENO);}/* 启动要跟踪的应用程序 */if (execvp(options->args[0], options->args) < 0) {fprintf(stderr, "Unable to start '%s'\n", options->args[0]);exit(EXIT_FAILURE);}
}static int
netns_main(void *arg) {
...if (nsntrace_net_ns_init(options->use_public_dns, &common->if_info) < 0) {fprintf(stderr, "failed to setup network environment\n");return EXIT_FAILURE;}_nsntrace_handle_signals(_nsntrace_cleanup_ns_signal_callback);_nsntrace_start_tracer(common->options, &common->if_info);_nsntrace_set_name();child_pid = fork();if (child_pid < 0) {return EXIT_FAILURE;} else if (child_pid > 0) { /* parent - tracer */struct timespec timeout = { 0 };waitpid(child_pid, &status, 0);if (WIFEXITED(status)) {ret = WEXITSTATUS(status);} else {ret = EXIT_FAILURE;}/** 睡眠,以便处理所有数据包。*/timeout.tv_sec = 2; /* 2 seconds timeout */nanosleep(&timeout, NULL);nsntrace_capture_stop();/* 退出捕获循环,清理 */_nsntrace_cleanup_ns();exit(ret);} else { _nsntrace_start_tracee(common->options);}return ret;
}...
static void
_nsntrace_parse_options(struct nsntrace_options *options,int argc, char **argv)
{int c;opterr = 0;while ((c = getopt_long(argc, argv, short_opt, long_opt, NULL)) > 0) {switch(c) {case -1:case 0:break;case 'o':options->outfile = strdup(optarg);break;case 'd':options->device = strdup(optarg);break;case 'u':options->user = strdup(optarg);break;case 'f':options->filter = strdup(optarg);break;case PUBLIC_DNS:options->use_public_dns = 1;break;case 'h':_nsntrace_usage();exit(EXIT_SUCCESS);break;default:fprintf(stderr, "Invalid option '%c'\n", c);_nsntrace_usage();exit(EXIT_FAILURE);}}if (!options->device) {options->device = nsntrace_capture_default_device();}if (!options->outfile) {options->outfile = strdup(DEFAULT_OUTFILE);}options->args = argv + optind; /* 解析选项后的参数 */if (!options->args[0]) {_nsntrace_usage();exit(EXIT_FAILURE);}
}/*
创建基于pid的运行目录*/
static void
_nsntrace_mkrundir()
{char path[PATH_MAX] = { 0, };if (mkdir(NSNTRACE_RUN_DIR, 0644) < 0) {if (errno != EEXIST) {perror("mkdir");}}snprintf(path, PATH_MAX, "%s/%d", NSNTRACE_RUN_DIR, getpid());if (mkdir(path, 0644) < 0) {if (errno != EEXIST) {perror("mkdir");}}
}int
main(int argc, char **argv)
{
..._nsntrace_parse_options(&options, argc, argv);/* geteuid()返回有效的用户ID,如果是root,则返回0 */if (geteuid() != 0) {fprintf(stderr,"You need root privileges to run this application\n");exit(EXIT_FAILURE);}if (!nsntrace_net_ip_forward_enabled()) {fprintf(stderr, "Please enable IP forwarding:\n");if( NULL == getenv("SUDO_UID") ) {fprintf(stderr, "# sysctl net.ipv4.ip_forward=1\n");} else {fprintf(stderr, "$ sudo sysctl net.ipv4.ip_forward=1\n");}exit(EXIT_FAILURE);}_nsntrace_mkrundir();if (nsntrace_net_get_if_info(getpid(), &common.if_info) < 0) {ret = EXIT_FAILURE;goto out;}/* 在一个新的网络名称空间中创建一个新进程 */pid = clone(netns_main, child_stack + STACK_SIZE,CLONE_NEWNET | SIGCHLD, &common);if (pid < 0) {perror("clone");ret = EXIT_FAILURE;goto out;}.../* 在此处等待,直到跟踪的进程存在或用户中止 */waitpid(pid, &status, 0);if (WIFEXITED(status)) {ret = WEXITSTATUS(status);} else {ret = EXIT_FAILURE;}out:if (pid != 0) {nsntrace_net_deinit(pid, options.device, &common.if_info);}_nsntrace_remove_rundir(getpid());return ret;
}
If you need the complete source code, please add the WeChat number (c17865354792)
$ sudo my_nsntrace -d eth1 wget www.google.com
Starting network trace of 'wget' on interface eth1.
Your IP address in this trace is 172.16.42.255.
Use ctrl-c to end at any time.--2024-07-15 12:12:17-- http://www.google.com/
Location: http://www.google.se/?gfe_rd=cr&ei=AbeIV5zZHcaq8wfTlrjgCA [following]
--2024-07-15 12:12:17-- http://www.google.se/?gfe_rd=cr&ei=AbeIV5zZHcaq8wfTlrjgCA
Length: unspecified [text/html]
Saving to: ‘index.html’index.html [ <=> ] 10.72K --.-KB/s in 0.001s2024-07-15 12:12:17 (15.3 MB/s) - ‘index.html’ saved [10980]Finished capturing 42 packets.$ tshark -r my_nsntrace.pcap -Y 'http.response or http.request'
16 0.998839 172.16.42.255 -> 195.249.146.104 HTTP 229 GET http://www.google.com/ HTTP/1.1
20 1.010671 195.249.146.104 -> 172.16.42.255 HTTP 324 HTTP/1.1 302 Moved Temporarily (text/html)
22 1.010898 172.16.42.255 -> 195.249.146.104 HTTP 263 GET http://www.google.se/?gfe_rd=cr&ei=AbeIV5zZHcaq8wfTlrjgCA HTTP/1.1
31 1.051006 195.249.146.104 -> 172.16.42.255 HTTP 71 HTTP/1.1 200 OK (text/html)
使用tshark进行实时捕捉
$ sudo my_nsntrace -f tcp -o - wget www.google.com 2> /dev/null | tshark -r -
1 0.000000 172.16.42.255 → 142.250.74.36 TCP 74 51088 → 80 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=1362504482 TSecr=0 WS=128
2 0.014010 142.250.74.36 → 172.16.42.255 TCP 74 80 → 51088 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=1430 SACK_PERM=1 TSval=2846449454 Secr=1362504482 WS=256
3 0.014078 172.16.42.255 → 142.250.74.36 TCP 66 51088 → 80 [ACK] Seq=1 Ack=1 Win=64256 Len=0 TSval=1362504496 TSecr=2846449454
4 0.014221 172.16.42.255 → 142.250.74.36 HTTP 207 GET / HTTP/1.15 0.033935 142.250.74.36 → 172.16.42.255 TCP 66 80 → 51088 [ACK] Seq=1 Ack=142 Win=66816 Len=0 TSval=2846449475 TSecr=1362504496
6 0.093989 142.250.74.36 → 172.16.42.255 TCP 1484 HTTP/1.1 200 OK [TCP segment of a reassembled PDU]
7 0.094022 172.16.42.255 → 142.250.74.36 TCP 66 51088 → 80 [ACK] Seq=142 Ack=1419 Win=63360 Len=0 TSval=1362504576 TSecr=2846449532
8 0.096447 142.250.74.36 → 172.16.42.255 TCP 2902 HTTP/1.1 200 OK [TCP segment of a reassembled PDU]
9 0.096478 172.16.42.255 → 142.250.74.36 TCP 66 51088 → 80 [ACK] Seq=142 Ack=4255 Win=62208 Len=0 TSval=1362504578 TSecr=2846449532
10 0.099871 142.250.74.36 → 172.16.42.255 HTTP 9626 Continuation[Packet size limited during capture]
11 0.099936 172.16.42.255 → 142.250.74.36 TCP 66 51088 → 80 [ACK] Seq=142 Ack=13815 Win=56320 Len=0 TSval=1362504582 TSecr=2846449532
12 0.100743 172.16.42.255 → 142.250.74.36 TCP 66 51088 → 80 [FIN, ACK] Seq=142 Ack=13815 Win=64128 Len=0 TSval=1362504583 TSecr=2846449532
13 0.113167 142.250.74.36 → 172.16.42.255 TCP 66 80 → 51088 [FIN, ACK] Seq=13815 Ack=143 Win=66816 Len=0 TSval=2846449554 TSecr=1362504583
14 0.113190 172.16.42.255 → 142.250.74.36 TCP 66 51088 → 80 [ACK] Seq=143 Ack=13816 Win=64128 Len=0 TSval=1362504595 TSecr=2846449554
my_nsntrace 应用程序使用 clone 系统调用创建一个新的网络命名空间(CLONE_NEWNET),并在该命名空间中启动目标进程,同时使用 libpcap 开始跟踪。这样可以确保所有跟踪到的数据包都来自该进程。
由于进程在命名空间中是隔离的,无法直接访问其他网络,因此 my_nsntrace 通过创建虚拟网络接口来解决这个问题。其中一个虚拟网络接口保留在根网络命名空间中,另一个放在新创建的命名空间中。然后,将根命名空间中的虚拟设备设置为跟踪命名空间中虚拟设备的默认网关。
为了确保能够访问目标网络,my_nsntrace 使用 iptables 和 NAT 技术将虚拟设备的所有流量转发到默认的网络接口。这样,就可以在不影响系统其他部分的情况下,捕获单个进程与默认网络通信时的数据包。
总结
通过网络命名空间跟踪单个进程的网络活动是一种高级网络监控技术,它允许系统管理员和网络安全专家深入分析特定应用程序或服务的网络行为。通过创建隔离的网络环境并将目标进程移入该环境,我们可以精确地捕捉到该进程生成的所有网络流量,而不受其他进程的干扰。
在实际操作中,这通常涉及使用ip netns命令来管理网络命名空间,利用nsenter工具将进程附加到特定的网络命名空间,并在该命名空间内部署如tcpdump或iftop等网络嗅探工具以捕获数据包。通过这种方式,我们能够获得关于进程通信模式、数据流大小、连接尝试等详细信息,从而为进一步的分析提供坚实的基础。
We also undertake the development of program requirements here. If necessary, please follow the WeChat official account 【程序猿编码】and contact me
相关文章:
深入解析:如何通过网络命名空间跟踪单个进程的网络活动(C/C++代码实现)
在 Linux 系统中,网络命名空间(Network Namespaces)是一种强大的功能,它允许系统管理员和开发者隔离网络资源,使得每个命名空间都拥有独立的网络协议栈。这种隔离机制不仅用于容器技术如 Docker,也是网络安…...
C++ 科目二 [const_cast]
基础数据类型 const_cast 仅仅是深层拷贝改变,而不是改动之前的值 如果需要使用改动后的值,需要通过指针或者引用来间接使用 const int n 5; const string s "MyString";// cosnt_cast 针对指针,引用,this指针 // co…...

【电脑组装】✈️从配置拼装到安装系统组装自己的台式电脑
目录 🍸前言 🍻一、台式电脑基本组成 🍺二、组装 🍹三、安装系统 👋四、系统设置 👀五、章末 🍸前言 小伙伴们大家好,上篇文章分享了在平时开发的时候遇到的一种项目整合情况&…...

Hadoop生态圈拓展内容(一)
1. Hadoop的主要部分及其作用 HDFS(Hadoop分布式文件系统) HDFS是一个高容错、高可靠性、高可扩展性、高吞吐率的分布式文件存储系统,负责海量数据的存储。 YARN(资源管理调度系统) YARN是Hadoop的资源管理调度系统…...

使用随机森林模型在digits数据集上执行分类任务
程序功能 使用随机森林模型对digits数据集进行手写数字分类任务。具体步骤如下: 加载数据:从digits数据集中获取手写数字图片的特征和对应的标签。 划分数据:将数据集分为训练集和测试集,测试集占30%。 训练模型:使用…...

后端开发刷题 | 打家劫舍
描述 你是一个经验丰富的小偷,准备偷沿街的一排房间,每个房间都存有一定的现金,为了防止被发现,你不能偷相邻的两家,即,如果偷了第一家,就不能再偷第二家;如果偷了第二家࿰…...

欧美游戏市场的差异
欧洲和美国的游戏市场虽然高度发达且利润丰厚,但表现出由文化偏好、消费者行为、监管环境和平台受欢迎程度塑造的独特特征。这些差异对于寻求为每个地区量身定制策略的游戏开发商和发行商来说非常重要。 文化偏好和游戏类型 美国:美国游戏市场倾向于青…...

DeDeCMS靶场漏洞复现
打开靶场地址 姿势一:通过文件管理器上传webshell 1.登录后台 dedecms默认的后台登录地址为/dede 2.在附加管理里的文件式管理器中有文件上传 3.上传木马文件 4.访问木马文件 并连接 姿势二:修改模板文件获取webshell 1.点击模板里面的默认模板管理 …...

Transformer模型详细步骤
Transformer模型是nlp任务中不能绕开的学习任务,我将从数据开始,每一步骤都列举出来,然后对应重点的代码进行讲解 ------------------------------------------------------------------------------------------------------------- Trans…...

LC并联电路在正弦稳态下的传递函数推导(LC并联谐振选频电路)
LC并联电路在正弦稳态下的传递函数推导(LC并联谐振选频电路) 本文通过 1.解微分方程、2.阻抗模型两种方法推导 LC 并联选频电路在正弦稳态条件下的传递函数,并通过仿真验证不同频率时 vo(t) 与 vi(t) 的幅值相角的关系。 电路介绍 已知条件…...
【前后端】大文件切片上传
Ruoyi框架上传文件_若依微服务框架 文件上传-CSDN博客 原理介绍 大文件上传时,如果直接上传整个文件,可能会因为文件过大导致上传失败、服务器超时或内存溢出等问题。因此,通常采用文件切片(Chunking)的方式来解决这些…...
图像处理 -- ISP功能之局部对比度增强 LCE
局部对比度增强(LCE) 局部对比度增强(Local Contrast Enhancement, LCE)是一种图像处理技术,旨在通过调整图像的局部区域对比度,增强图像细节和视觉效果。LCE 的实现方式多种多样,以下是几种常…...

C++速通LeetCode简单第5题-回文链表
解法1,堆栈O(n)简单法: /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(int x, ListN…...

【Java 优选算法】双指针(下)
欢迎关注个人主页:逸狼 创造不易,可以点点赞吗~ 如有错误,欢迎指出~ 有效三角形的个数 题目链接 解法 解法1:暴力枚举--->O(n^3) 解法2:利用单调性,使用双指针来解决---->O(n^2) 优化:对整个数组进行排序先固定最大数在最大数的左…...

动态规划:07.路径问题_珠宝的最大价值_C++
题目链接:LCR 166. 珠宝的最高价值 - 力扣(LeetCode)https://leetcode.cn/problems/li-wu-de-zui-da-jie-zhi-lcof/description/ 一、题目解析 题目: 解析: 有过做前几道题的经验,我们会发现这道题其实就…...

COMDEL电源CX2500S RF13.56MHZ RF GENERATOR手侧
COMDEL电源CX2500S RF13.56MHZ RF GENERATOR手侧...

GPU加速生物信息分析的尝试
GPU工具分类 实话实说,暂时只有英伟达的GPU才能实现比较方便的基因组分析集成化解决方案,其他卡还需要努力呀,或者需要商业公司或学术团体的努力开发呀!FPGA等这种专用卡的解决方案也是有的,比如某测序仪厂家…...

【零散技术】详解Odoo17邮件发送(一)
序言:时间是我们最宝贵的财富,珍惜手上的每个时分 Odoo的邮件功能十分强大,在非常多的场景中可以看见其应用,例如原生的用户邀请,报价单发送,询价单发送等等.... 那么抛开原生自带的功能,我们如何巧妙的通过代码进行自…...
函数题 6-5 求自定类型元素的最大值【PAT】
文章目录 题目函数接口定义裁判测试程序样例输入样例输出样例 题解解题思路完整代码AC代码 编程练习题目集目录 题目 要求实现一个函数,求N个集合元素S[]中的最大值,其中集合元素的类型为自定义的ElementType。 函数接口定义 ElementType Max( Element…...

Python---爬虫
文章目录 目录 前言 一.Http请求/响应模块 requests模块 二.文本筛选模块 re模块 XPath模块 XPath 路径表达式 XPath 语法元素 三. 爬虫模板 爬虫案例 前言 Python爬虫是一种通过自动化程序爬取互联网上的信息的技术。爬虫可以自动访问网页并提取所需的数据,比…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...

c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
Java求职者面试指南:计算机基础与源码原理深度解析
Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...

HubSpot推出与ChatGPT的深度集成引发兴奋与担忧
上周三,HubSpot宣布已构建与ChatGPT的深度集成,这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋,但同时也存在一些关于数据安全的担忧。 许多网络声音声称,这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...
go 里面的指针
指针 在 Go 中,指针(pointer)是一个变量的内存地址,就像 C 语言那样: a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10,通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...
Python网页自动化Selenium中文文档
1. 安装 1.1. 安装 Selenium Python bindings 提供了一个简单的API,让你使用Selenium WebDriver来编写功能/校验测试。 通过Selenium Python的API,你可以非常直观的使用Selenium WebDriver的所有功能。 Selenium Python bindings 使用非常简洁方便的A…...

五、jmeter脚本参数化
目录 1、脚本参数化 1.1 用户定义的变量 1.1.1 添加及引用方式 1.1.2 测试得出用户定义变量的特点 1.2 用户参数 1.2.1 概念 1.2.2 位置不同效果不同 1.2.3、用户参数的勾选框 - 每次迭代更新一次 总结用户定义的变量、用户参数 1.3 csv数据文件参数化 1、脚本参数化 …...

生产管理系统开发:专业软件开发公司的实践与思考
生产管理系统开发的关键点 在当前制造业智能化升级的转型背景下,生产管理系统开发正逐步成为企业优化生产流程的重要技术手段。不同行业、不同规模的企业在推进生产管理数字化转型过程中,面临的挑战存在显著差异。本文结合具体实践案例,分析…...

第2课 SiC MOSFET与 Si IGBT 静态特性对比
2.1 输出特性对比 2.2 转移特性对比 2.1 输出特性对比 器件的输出特性描述了当温度和栅源电压(栅射电压)为某一具体数值时,漏极电流(集电极电流...