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

C++ TinyWebServer项目总结(7. Linux服务器程序规范)

进程 PID

进程的PID(Process ID)是操作系统中用于唯一标识一个进程的整数值。每个进程在创建时,操作系统都会分配一个唯一的PID,用来区分不同的进程。

PID的特点

  1. 唯一性

在操作系统运行的某一时刻,每个进程的PID都是唯一的。不同进程不会共享同一个PID。即使一个进程终止后,该PID可以被回收并分配给新创建的进程,但在同一时刻不会有两个进程拥有相同的PID。

  1. 进程生命周期

PID 的生命周期与进程的生命周期相对应。当一个进程被创建时,操作系统为它分配一个PID;当进程终止时,该PID被释放,并可能被分配给后续的新进程。

  1. 系统管理和调试

PID 在进程管理和调试中起着重要作用。系统管理员和开发者可以通过PID来监视、控制和调试进程。例如,使用 ps 命令可以查看系统中所有进程的PID,以及它们的状态、资源使用等信息。使用 kill 命令可以通过PID来终止指定的进程。

PID的分配

PID 是由操作系统内核管理和分配的,通常是一个非负整数。PID 通常从一个最小值(通常是1)开始,逐渐递增。当系统运行的进程数达到最大PID值时,PID 会回绕到最小值并重新开始分配。

特殊的PID

  • PID 1:在大多数类UNIX操作系统中,PID 1 通常分配给初始化进程(initsystemd),这是系统启动时创建的第一个进程。init 是所有其他进程的祖先,它负责启动系统的其余部分,并在系统运行期间维持各种系统服务。
  • PID 0:PID 0 通常被保留给调度进程或空闲进程,这个进程在大多数情况下不会被普通用户或程序直接操作。5

日志

Linux系统日志

Linux提供一个守护进程(后台进程)来处理系统日志:rsyslogdrsyslogd 守护进程既能接收用户进程输出的日志,又能接收内核日志。

syslog 函数

应用程序使用syslog函数和守护进程rsyslog通信。

#include <syslog.h>
void syslog(int priority, const char* message, ...);

openlog用来改变syslog的默认输出方式,进一步结构化日志内容。

#include <syslog.h>
void openlog(const char* ident, int logopt, int facility);

setlogmask 用于设置日志掩码,使得日志级别大于日志掩码的信息被系统忽略。用于在程序发布之后将程序的调试信息关闭。

#include <syslog.h>
int setlogmask(int maskpri);

closelog函数用于关闭日志功能:

#include <syslog.h>
void closelog();

用户信息

UID、EUID、GID、EGID

在操作系统中,尤其是类 UNIX 系统中,用户和组的标识符用于控制对系统资源的访问权限。以下是对 UID(User ID)、EUID(Effective User ID)、GID(Group ID)和 EGID(Effective Group ID)的解释:

1. UID(User ID)

UID 是用于唯一标识系统中每个用户的整数值。当用户在系统中创建时,系统会为该用户分配一个唯一的 UID。UID 用于控制用户对系统资源的访问权限。

  • UID 0:通常保留给 root 用户(超级用户),它拥有系统的所有权限,能够执行任何操作。
  • 普通用户的 UID:通常从 1000 或 500 开始(取决于操作系统的配置),用于普通用户。

2. EUID(Effective User ID)

EUID 是用于实际控制用户对文件和系统资源访问权限的用户标识符。它可能与 UID 相同,但在某些情况下可以不同。例如,通过 setuid 程序,普通用户可以临时获得文件所有者的权限。EUID 通常用于判断用户是否有权执行某些操作。

  • 典型用法:当一个用户执行 setuid 程序时,该程序的 EUID 会被设置为程序文件的所有者的 UID,而不是当前用户的 UID,从而赋予执行该程序的用户临时的更高权限。

3. GID(Group ID)

GID 是用于标识系统中每个用户组的整数值。与 UID 类似,GID 用于控制用户组对系统资源的访问权限。每个用户在系统中都有一个与之关联的主组(primary group),该组由 GID 表示。

  • GID 0:通常属于 root 组,具有最高权限。
  • 普通用户的 GID:通常与其主组的 GID 相同。

4. EGID(Effective Group ID)

EGID 是用于实际控制用户对文件和系统资源访问权限的组标识符。类似于 EUID,EGID 可以通过 setgid 程序来修改,以便临时提升执行程序的用户的组权限。

  • 典型用法:如果一个文件设置了 setgid 位,那么当任何用户执行该文件时,该进程的 EGID 将被设置为文件所属组的 GID,从而赋予该用户临时的组权限。

总结

  • UID:标识用户身份,决定用户本身的所有权限。
  • EUID:用于实际判断用户的权限,可能与 UID 不同。
  • GID:标识用户所属的组,决定用户组的权限。
  • EGID:用于实际判断用户的组权限,可能与 GID 不同。

在权限管理中,UID 和 GID 决定了用户和组的基本身份和权限,而 EUID 和 EGID 决定了用户在特定情境下(如执行带有 setuidsetgid 标志的程序时)所拥有的实际权限。这些标识符是 UNIX 权限模型的重要组成部分,用于确保系统资源的安全访问和管理。

有效用户为 root 的进程称为特权进程(privileged processes)

下边一组函数可以获取和设置当前进程的UID、EUID、GID、EGID:

#include <sys/types.h>
#include <unistd.h>
uid_t getuid();		//获取真实用户ID
uid_t geteuid();	//获取有效用户ID
gid_t getgid();		//获取真实组ID
gid_t getegid();	//获取有效组IDint setuid(uid_t uid);		//设置真实用户ID
int seteuid(uid_t uid);		//设置有效用户ID
int setgid(gid_t gid);		//设置真实组ID
int setegid(gid_t gid);		//设置有效组ID

测试 UID 和 EUID 的区别

新建一个 test_uid.cpp 文件,并写入下面的代码:

#include <unistd.h> // <unistd.h>(Unix Standard Definitions)头文件提供对POSIX操作系统API的访问,主要用于提供对POSIX操作系统API的函数原型、符号常量等。
#include <stdio.h>  // <stdio.h>(Standard Input Output Header)头文件提供了进行输入和输出操作的函数。int main(){uid_t uid = getuid();uid_t euid = geteuid();printf("userid :%d , euid: %d \n", uid, euid);return 0;
}

编译该文件:

gcc test_uid.cpp -o test_uid

运行可执行文件:

./test_uid

修改目标文件的所有者为 root:

sudo chown root:root test_uid

设置目标文件的 set-user-id 标志:

sudo chmod +s test_uid

重新执行该文件(无需再次编译):

修改后,进程的用户 ID 是启动程序的用户 ID:1000,而有效用户 ID 是文件所有者的 ID:0(这里为 root 账户)。

进程间关系

进程组

Linux下的每一个进程都隶属于一个进程组,因此他们除了 PID 信息外,还有进程组ID:PGID。

获取指定进程的 PGID:

#include <unistd.h>
pid_t getpgid(pid_t pid);

设置 PGID:

int setpgid(pid_t pid, pid_t pgid);

每个进程组都有一个首领进程,其 PGID 和 PID 相同。一个进程只能设置自己或其子进程的 PGID。

会话

一些有关联的进程组将形成一个会话(session)。

创建一个会话:

#include <unistd.h>
pid_t setsid(void);

注意,该函数不能由进程组的首领进程调用,否则会出错。对于非组首领的进程来说,调用该函数会生成一个新的会话,并且:

  • 调用进程成为会话的首领,此时进程是新会话的唯一成员
  • 新建一个进程组,其 PGID 就是调用该函数的进程PID,调用进程成为首领
  • 调用进程甩开终端(若有)。

新的会话不会有控制终端(controlling terminal)。如果调用进程原本有控制终端,那么它会被与控制终端分离。

Linux并没有提供会话ID(SID)的概念,但 Linux 系统认为会话ID等同于会话首领所在进程组的 PGID,并提供如下函数来读取 SID:

#include <unistd.h>
pid_t getsid(pid_t pid);

使用 setsid() 创建一个新的会话和进程组:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int main() {pid_t pid;// 创建一个子进程pid = fork();printf("Child Process ID: %d\n", pid);if (pid < 0) {perror("fork failed");exit(EXIT_FAILURE);} else if (pid > 0) {// 父进程退出,使子进程成为孤儿进程exit(EXIT_SUCCESS);}// 子进程开始执行,创建新的会话pid_t sid = setsid();if (sid < 0) {perror("setsid failed");exit(EXIT_FAILURE);}// 此时,进程已经成为新的会话和进程组的首领printf("New session ID: %d\n", sid);// 继续执行其他代码...return 0;
}

编译运行:

gcc create_sid.cpp -o create_sid
./create_sid

fork() 是一个在 UNIX 和类 UNIX 操作系统中用于创建新进程的系统调用。调用 fork() 后,操作系统会创建一个新的进程(称为子进程),这个子进程是调用进程(父进程)的副本,除了一些特定的区别外,子进程几乎完全继承了父进程的上下文。

create_sid.cpp 的执行逻辑:

  1. 在调用 fork() 之前,只有一个进程在运行,这个进程是你的程序的父进程。当 fork() 被调用时,操作系统会复制当前进程的全部内容,从而创建一个几乎完全相同的子进程。
  2. fork() 成功时,父进程会收到子进程的 PID,而子进程会收到 0。此时,printf("Child Process ID: %d\n", pid); 会在父进程和子进程中都执行,打印不同的 PID 值。
  3. 在代码中,父进程收到这个返回值后,执行 exit(EXIT_SUCCESS);,表示父进程正常退出。
  4. 父进程退出后,子进程就成为了一个孤儿进程(因为它的父进程不再存在),但操作系统会将孤儿进程重新分配给 initsystemd 进程来管理。
  5. 子进程在父进程退出后继续运行,并调用 setsid() 来创建一个新的会话。
  6. 调用 setsid() 后,子进程将成为新会话的会话首领(session leader),并创建一个新的进程组,其中该子进程是进程组的组首领(group leader)。

用ps命令查看进程关系

用 ps 命令可以查看进程、进程组和会话之间的关系:

ps -o pid,ppid,pgid,sid,comm | less

我们在 bash 下执行 psless 命令,所以 psless 的父进程是 bashbash 的 PID 为 264971,而 psless 的 PPID 也为 264971。

这三条命令创建了一个会话:SID = 264971,两个进程组:PGID = 264971, 266761

bash 既是会话首领,也是进程组 264971 的首领。

进程组 266761 的首领是 ps。

| less: 这是一个分页工具,允许你逐页查看命令输出的内容。使用 less 可以方便地查看长输出内容而不会直接在终端上滚动过去。

系统资源限制

Linux上运行的程序会受到资源限制的影响。如物理设备限制:CPU数量,内存数量;系统策略限制:CPU时间;具体实现限制:文件名的最大长度等。

Linux 系统资源限制可以通过如下一对函数读取和设置:

#include <sys/resource.h>
//成功返回0,失败返回-1并设置errno
int getrlimit(int resurce, struct rlimit *rlim);
int setrlimit(int resurce, const struct rlimit *rlim);

rlimit 结构体:

struct rlimit {//rlim_t 描述资源级别rlim_t rlim_ur;		//软限制rlim_t rlim_max;	//硬限制
}

改变工作目录和根目录

工作目录和根目录概念

在操作系统中,尤其是 UNIX 和类 UNIX 系统中,工作目录根目录是两个非常重要的概念,它们与文件系统的组织和进程的操作密切相关。

工作目录(Working Directory)

工作目录,有时也称为当前目录,是指一个进程当前所在的目录。所有相对路径的文件操作(如打开文件、读取文件等)都是相对于工作目录进行的。

  • 特性:
    • 每个进程都有一个工作目录。进程可以通过系统调用 chdir() 或命令 cd 来改变其工作目录。
    • 当你在终端中打开一个 shell 时,通常 shell 的工作目录最初是用户的主目录(如 /home/username)。
    • 在编写程序时,如果使用相对路径(如 ./file.txt),系统会从当前工作目录开始查找文件。
  • 查看和更改:
    • 使用 pwd 命令可以查看当前工作目录。
    • 使用 cd 命令可以更改当前工作目录。例如,cd /var/log 会将工作目录更改为 /var/log

示例:

根目录(Root Directory)

根目录是文件系统的最顶层目录,用 / 表示。在 UNIX 和类 UNIX 系统中,根目录是文件系统的起点,所有文件和目录都位于根目录之下。

  • 特性:
    • 根目录是文件系统层级结构的起点,没有父目录。
    • 所有其他目录(如 /home/etc/usr)都是从根目录派生出来的。
    • 在系统启动时,操作系统会挂载根文件系统,根目录是整个文件系统的基础。
  • 根目录与工作目录的区别:
    • 根目录是文件系统的最顶层,是绝对路径的起点。
    • 工作目录是进程当前正在操作的目录,可以在文件系统的任何位置。

示例:

cd /
ls

在这个示例中,使用 cd / 切换到根目录,并使用 ls 列出了根目录下的文件和子目录。

总结

  • 工作目录是当前进程正在操作的目录,所有相对路径的操作都是基于工作目录。
  • 根目录是文件系统的最顶层目录,用 / 表示,是绝对路径的起点。

有些服务器程序还需要改变工作目录和根目录,比如web服务器的逻辑根目录不是文件系统的根目录/,而是站点的根目录,对于 Linux 上的 Web 服务来说,该目录一般是/var/www/

获取进程当前工作目录,改变进程工作目录的函数分别是:

#include <unistd.h>
char* getcwd(char* buf, size_t size);
int chdir(const char* path);

改变进程根目录的函数:

#include <unistd.h>//成功返回0,失败返回-1并设置errno
int chroot(const char* path);

只有特权进程才能改变根目录。

服务器程序后台化

下面代码实现了如何让一个进程以守护进程的方式运行:

bool daemonize() {// 创建子进程,关闭父进程,这样子进程就不是进程组首进程,就可以调用setsid了pid_t pid = fork();if (pid < 0) {return false;} else if (pid > 0) {exit(0);}/* 设置文件权限掩码。当进程创建新文件(使用open()系统调用),文件的权限将是mode & 0777 */umask(0);// 创建新会话,本进程将成为进程组的首领pid_t sid = setsid();if (sid < 0) {return false;}// 切换工作目录,防止当前工作目录所在文件系统不能卸载if ((chdir("/")) < 0) {return false;}// 关闭所有文件描述符close(STDIN_FILENO);close(STDOUT_FILENO);close(STDERR_FILENO);// 此处省略了关闭其他已打开的文件描述符的代码// 将标准输入、标准输出、标准错误重定向到/dev/null文件open("/dev/null", O_RDONLY);open("/dev/null", O_RDWR);open("/dev/null", O_RDWR);return true;
}

Linux 提供了完成同样功能的库函数:

#include <unistd.h>
int daemon(int nochdir, int noclose);

这个 daemonize() 函数执行了创建守护进程的标准步骤:

  1. 通过 fork() 创建一个子进程,并让父进程退出。
  2. 通过 setsid() 创建一个新的会话,脱离控制终端,并成为会话首领。
  3. 设置文件权限掩码和切换工作目录。
  4. 关闭标准文件描述符并将它们重定向到 /dev/null

如果所有步骤成功,则返回 true 表示守护进程创建成功。否则返回 false。这段代码展示了将一个普通进程转化为守护进程的标准方法,是在后台运行长时间任务的基础方法之一。

接下来对上面代码进行解释:

代码解释

创建子进程并关闭父进程

pid_t pid = fork();
if (pid < 0) {return false;
} else if (pid > 0) {exit(0);
}

fork() 创建一个子进程。如果 fork() 失败(返回值为负),则返回 false 表示守护进程创建失败。

如果 fork() 成功,父进程收到子进程的 PID 并退出 (exit(0)),子进程继续执行。

这样做的目的是让子进程成为孤儿进程,由 initsystemd 进程接管,从而保证守护进程在父进程结束后仍然继续运行。

设置文件权限掩码

umask(0) 清除文件模式创建掩码,确保进程创建的文件权限不受父进程的文件权限掩码影响。这样,进程创建的文件将具有最大的权限(取决于创建文件时指定的权限)。

创建新会话

pid_t sid = setsid();
if (sid < 0) {return false;
}

setsid() 创建一个新的会话,使当前进程成为会话的首领,并与原来的控制终端分离。该进程成为新会话的会话首领和进程组的首领,并且没有控制终端。

切换工作目录

if ((chdir("/")) < 0) {return false;
}

chdir("/") 将工作目录切换到根目录 /。这样做的目的是避免当前工作目录所在的文件系统不能卸载。

关闭所有文件描述符

close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);

关闭标准输入(STDIN_FILENO)、标准输出(STDOUT_FILENO)和标准错误(STDERR_FILENO)的文件描述符,以避免守护进程不小心使用这些文件描述符。

重定向标准输入、输出和错误

open("/dev/null", O_RDONLY);
open("/dev/null", O_RDWR);
open("/dev/null", O_RDWR);

将标准输入重定向到 /dev/null,并将标准输出和标准错误也重定向到 /dev/null/dev/null 是一个特殊的文件,读取它会返回 EOF,写入它的数据将被丢弃。这样可以确保守护进程不向任何终端输出信息,也不会从任何终端读取输入。

参考文章

  1. Linux高性能服务器编程-游双——第七章 Linux服务器程序规范_linux高性能服务器编程 pdf-CSDN博客
  2. Linux高性能服务器编程 学习笔记 第七章 Linux服务器程序规范-CSDN博客

相关文章:

C++ TinyWebServer项目总结(7. Linux服务器程序规范)

进程 PID 进程的PID&#xff08;Process ID&#xff09;是操作系统中用于唯一标识一个进程的整数值。每个进程在创建时&#xff0c;操作系统都会分配一个唯一的PID&#xff0c;用来区分不同的进程。 PID的特点 唯一性&#xff1a; 在操作系统运行的某一时刻&#xff0c;每个…...

基于STM32单片机设计的秒表时钟计时器仿真系统——程序源码proteus仿真图设计文档演示视频等(文末工程资料下载)

基于STM32单片机设计的秒表时钟计时器仿真系统 演示视频 基于STM32单片机设计的秒表时钟计时器仿真系统 摘要 本设计基于STM32单片机&#xff0c;设计并实现了一个秒表时钟计时器仿真系统。系统通过显示器实时显示当前时间&#xff0c;并通过定时器实现秒表计时功能。显示小时…...

人才流失预测项目

在本项目中&#xff0c;通过数据科学和AI的方法&#xff0c;分析挖掘人力资源流失问题&#xff0c;并基于机器学习构建解决问题的方法&#xff0c;并且&#xff0c;我们通过对AI模型的反向解释&#xff0c;可以深入理解导致人员流失的主要因素&#xff0c;HR部门也可以根据分析…...

BUG——imx6u开发_结构体导致的死机问题(未解决)

简介&#xff1a; 最近在做imx6u的linux下裸机驱动开发&#xff0c;由于是学习的初级阶段&#xff0c;既没有现成的IDE可以使用&#xff0c;也没有GDB等在线调试工具&#xff0c;只能把代码烧写在SD卡上再反复插拔&#xff0c;仅靠卑微的亮灯来判断程序死在哪一步。 至于没有使…...

问答:什么是对称密钥、非对称密钥,http怎样变成https的?

文章目录 对称密钥 vs 非对称密钥HTTP 变成 HTTPS 的过程 对称密钥 vs 非对称密钥 1. 对称密钥加密 定义: 对称密钥加密是一种加密算法&#xff0c;其中加密和解密使用的是同一个密钥。特点: 速度快: 因为只使用一个密钥&#xff0c;所以加密和解密速度较快。密钥分发问题: 双…...

虚拟滚动列表组件ReVirtualList

虚拟滚动列表组件ReVirtualList 组件实现基于 Vue3 Element Plus Typescript&#xff0c;同时引用 vueUse lodash-es tailwindCss (不影响功能&#xff0c;可忽略) 在 ReList 的基础上&#xff0c;增加虚拟列表功能&#xff0c;在固定高度的基础上&#xff0c;可以优化大数…...

稳定、耐用、美观 一探究竟六角头螺钉螺栓如何选择

在机器与技术未被发现的过去&#xff0c;紧固件设计和品质并不稳定。但是&#xff0c;他们已成为当今许多行业无处不在的构成部分。六角头标准件或六角头标准件是紧固件中持续的头部设计之一&#xff0c;它有六个面&#xff0c;对广泛工业应用大有益处。六角头标准件或常分成六…...

数据库Mybatis基础操作

目录 基础操作 删除 预编译SQL 增、改、查 自动封装 基础操作 环境准备 删除 根据主键动态删除数据&#xff1a;使用了mybatis中的参数占位符#{ }&#xff0c;里面是传进去的参数。 单元测试&#xff1a; 另外&#xff0c;这个方法是有返回值的&#xff0c;返回这次操作…...

人物形象设计:塑造独特角色的指南

引言 人物形象设计是一种创意过程&#xff0c;它利用强大的设计工具&#xff0c;通过视觉和叙述元素塑造角色的外在特征和内在性格。这种设计不仅赋予角色以生命&#xff0c;还帮助观众或读者在心理层面上与角色建立联系。人物形象设计的重要性在于它能够增强故事的吸引力和说…...

网络安全-安全策略初认识

文章目录 前言理论介绍1. 安全策略1.1 定义&#xff1a;1.2 关键术语&#xff1a; 2. 防火墙状态监测 实战步骤1&#xff1a;实验环境搭建步骤2&#xff1a;配置实现 总结1. 默认安全策略2. 自定义安全策略3. 防火墙状态会话表 前言 who&#xff1a;本文主要写给入门防火墙的技…...

python import相对导入与绝对导入

文章目录 相对导入与绝对导入绝对导入相对导入何时使用相对导入何时使用绝对导入示例 相对导入与绝对导入 在Python中&#xff0c;from .file_manager import SomeFunction 和 from file_manager import SomeFunction 两种导入方式看似相似&#xff0c;但在模块寻找机制上存在…...

深入理解 Go 语言原子内存操作

原子内存操作提供了实现其他同步原语所需的低级基础。一般来说,你可以用互斥体和通道替换并发算法的所有原子操作。然而,它们是有趣且有时令人困惑的结构,应该深入了解它们是如何工作的。如果你能够谨慎地使用它们,那么它们完全可以成为代码优化的好工具,而不会增加复杂性…...

PostgreSQL几个扩展可以帮助实现数据的分词和快速查询

在 PostgreSQL 数据库中,有几个扩展可以帮助实现数据的分词和快速查询,特别是在处理全文搜索和文本分析时。以下是几个常用的扩展: 1. pg_trgm pg_trgm(Trigram)扩展是 PostgreSQL 中的一个强大的工具,它可以通过计算字符串之间的相似度来实现快速文本搜索。它支持基于…...

C盘满了怎么办?教你清理C盘的20个大招,值得收藏备用

C盘满了怎么办&#xff1f;教你清理C盘的20个大招&#xff0c;值得收藏备用 今天给大家介绍20种C盘清理的方法&#xff0c;下次遇到C盘满了红了就知道怎么做了&#xff0c;喜欢请点赞收藏关注点评。 清理更新缓存 清理微信缓存 查找大文件清理或者迁移 磁盘缓存清理 系统还…...

原生js实现下滑到当前模块进度条填充

<div style"height: 1500px;"></div> <div class"progress-container"><div class"progress-bar" data-progress"90%"><p class"progress-text">Google Ads在Google搜索引擎上覆盖超过90%的互…...

显示弹出式窗口的方法

文章目录 1. 概念介绍2. 使用方法3. 示例代码 我们在上一章回中介绍了Sliver综合示例相关的内容&#xff0c;本章回中将介绍PopupMenuButton组件.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在本章回中介绍的PopupMenuButton组件位于AppBar右侧&#xf…...

Java-什么是缓存线程池?

什么是缓存线程池? 缓存线程池 (CachedThreadPool) 是一种特殊的线程池,它能够动态地调整线程的数量,以适应任 务的需求。这种线程池非常适合处理大量短暂的任务,因为它会根据任务的数量自动增加或减少线 程的数量。 缓存线程池的特点: 线程数量动态调整:缓存线程池…...

esbuild中的Binary Loader:处理二进制文件

在前端或Node.js项目中&#xff0c;有时需要处理二进制文件&#xff0c;如图片、音频、视频或其他非文本资源。esbuild提供了一款名为Binary Loader的插件&#xff0c;它能够在构建时将二进制文件加载为二进制缓冲区&#xff0c;并使用Base64编码将其嵌入到打包文件中。在运行时…...

深度好文:从《黑神话:悟空》看未来游戏趋势:高互动性、个性化与全球化

引言 在数字时代的浪潮中&#xff0c;游戏产业以其独特的魅力和无限的可能性&#xff0c;成为了全球娱乐文化的重要组成部分。随着科技的飞速发展&#xff0c;特别是高性能计算和人工智能技术的突破&#xff0c;游戏的世界变得越来越真实、细腻且富有深度。而在这股技术洪流中…...

【中项第三版】系统集成项目管理工程师 | 第 12 章 执行过程组

前言 本章属于10大管理的内容&#xff0c;上午题预计会考8-10分&#xff0c;下午案例分析也会进行考查。学习要以教材为主。 目录 12.1 指导与管理项目工作 12.1.1 主要输入 12.1.2 主要输出 12.2 管理项目知识 12.2.1 主要输入 12.2.2 主要输出 12.3 管理质量 12.3.…...

C语言自动生成宏定义枚举类型和字符串

#include <stdio.h>// 定义错误枚举 #define ERROR_LIST(e) \e(SUCCESS) \e(FAILURE) \e(NOT_FOUND) \e(TIMEOUT)// 使用宏生成枚举 #define GENERATE_ENUM(ENUM) ENUM, typedef enum {ERROR_LIST(GENERATE_ENUM) } ErrorCode;// 使用宏生成字符串数组…...

C#单例模式

&#xfeff;using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace _3._3._6_单例模式 {public class Singleton{private static Singleton s_instance;private int _state;private Singleton(int …...

10-使用sentinel流控

本文介绍sentinel的直接流控的使用。 0、环境 jdk 1.8sentinel 1.8.2springboot 2.4.2 1、sentinel环境搭建 从官方发布的网站上下载: sentinel Jar&#xff0c;下载对应版本。 下载完成后&#xff0c;进入刚才下载的Jar文件所在的目录&#xff0c;执行如下命令&#xff1a…...

redis AOF机制

在redis运行期间&#xff0c;不断将redis执行的写命令写到文件中&#xff0c;redis重启之后&#xff0c;只要将这些命令重复执行一遍就可以恢复数据。因为AOF只是将少量的写命令写入AOF文件中&#xff0c;因此其执行效率高于RDB&#xff0c;开启AOF即使Redis发生故障&#xff0…...

Day 21代码|随想录| 二叉树完结撒花,今日刷题669.修剪二叉搜索树、108.将有序数组转换为二叉搜索树、538.吧二叉搜索树转换为累加树

提示&#xff1a;DDU&#xff0c;供自己复习使用。欢迎大家前来讨论~ 文章目录 二叉树 Part06二、题目题目一&#xff1a;669.修剪二叉搜索树解题思路&#xff1a;递归法迭代法&#xff1a; 题目二&#xff1a; 108.将有序数组转换为二叉搜索树解题思路递归法&#xff1a;迭代…...

cmake教程一

1. Start 1.1 构建简单工程 cmake_minimum_required(VERSION 3.0) project(Step1) add_executable(Step1 main.cpp)设置cmake最低版本要求设置工程名字设置工程生成可执行程序 2. 声明 C Standard set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED True)如果我…...

3D场景标注标签信息,three.js CSS 2D渲染器CSS2DRenderer、CSS 3D渲染器CSS3DRenderer(结合react)

如果你想用HTML元素作为标签标注三维场景中模型信息&#xff0c;需要考虑定位的问题。比如一个模型&#xff0c;在代码中你可以知道它的局部坐标或世界坐标xyz&#xff0c;但是你并不知道渲染后在canvas画布上位置&#xff0c;距离web页面顶部top和左侧的像素px值。自己写代码把…...

C++参悟-单例模式

单例模式 一、概述1. 特点2. 实现方式3. 应用场景 二、实现代码1. 静态局部变量的懒汉单例2. 加锁的懒汉式单例3. 使用 C11 中的 std::call_one 的懒汉单例4. 饿汉式单例 一、概述 这里记录一下单例模式的最常用使用&#xff0c;单例模式&#xff08;Single Pattern&#xff0…...

【题解】—— LeetCode一周小结32

&#x1f31f;欢迎来到 我的博客 —— 探索技术的无限可能&#xff01; &#x1f31f;博客的简介&#xff08;文章目录&#xff09; 【题解】—— 每日一道题目栏 上接&#xff1a;【题解】—— LeetCode一周小结31 5.不含连续1的非负整数 题目链接&#xff1a;600. 不含连续…...

详解线索分层的目的、维度与创新实践

线索分层是一个系统性的过程&#xff0c;旨在更有效地管理、跟踪和利用线索资源。这一过程可以借鉴多种策略和方法&#xff0c;特别是在用户运营和市场营销中。 1、线索分层的目的 线索分层的主要目的是根据线索的不同特征或成熟度&#xff0c;将其分类管理&#xff0c;以便更…...

电子网站建设考试/手机广告推广软件

本文主要讲如何自定义NSOperation&#xff0c;以及自定义NSOperation的一些注意事项&#xff0c;以下载图片为例。 新建一个类&#xff0c;继承于NSOperation。 CustomOperation.h 代码 #import <Foundation/Foundation.h> #import <UIKit/UIKit.h>interface Custo…...

做网站刷东西/营销网店推广的软文

如果存在自增列那就比较麻烦&#xff0c;需要写明需要插入列的具体名称&#xff0c;比较麻烦&#xff0c;可以用下面的语句实现。 --删除主键 ALTER TABLE 表名 DROP constraint 主键约束; --删除ID alter table 表名 drop column ID; --新增ID alter table 表名 add ID in…...

wordpress拼音/百度竞价推广效果好吗

C语言之类型转换 类型之间的转换 1.c语言中的数据类型可以进行转换 —强制类型转换 —隐式类型转换 //示例1 int main() {long l 800;int i (int)l; //强制类型转换return 0; }//示例2 int main() {short s 800;int i s; //隐式类型转换//no error,no warningreturn …...

台州千寻网站建设公司/深圳网络营销推广中心

使用tree命令导出windows的文件夹/文件的目录树 TREE [drive:][path] [/F] [/A] /F 显示每个文件夹中文件的名称。&#xff08;带扩展名&#xff09; /A 使用 ASCII 字符&#xff0c;而不使用扩展字符。 tree /f > list.txt -- 将带扩展名的文件目录输出到list.txt…...

北京百度糯米团购有做网站的电话吗/网页设计软件dreamweaver

十多年前&#xff0c;大学刚毕业&#xff0c;在Autodesk上海做c开发工程师。 十一年后&#xff0c;已经人到中年&#xff0c;还在努力中。↖(^ω^)↗ 努力&#xff0c;奋斗~ 转载于:https://www.cnblogs.com/SunWentao/p/9204971.html...

杭州专业网站设计制作/广告推广语

性能优化方案--之一方案计划 Oracle优化 Oracle是咱们存储软件&#xff0c;他自身的优化是决定咱们系统软件性能的根本。 相关需要优化的配置&#xff1a; PGA&#xff1a;适当大小&#xff0c;保证SQL高速缓存命中能在99%以上。 SGA&#xff1a;调整适当增加共享内存池大小&am…...