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

C语言进程的相关操作

C语言进程的相关操作

进程简介

  • 每个进程都有一个非负整数形式到的唯一编号,即PID(Process Identification,进程标识)
  • PID在任何时刻都是唯一的,但是可以重用,当进程终止并被回收以后,其PID就可以为其他进程使用
  • 进程的PID由系统内核根据延迟重用算法生成,以确保新进程的PID不同于最近终止进程到的PID
  • 其中0号进程,叫做交换进程,系统内核中的一部分,所有进程的根进程,磁盘上没有它的可执行文件
  • 1号进程是init进程,在系统自举过程结束时由调度进程创建,读写与系统相关的初始化文件,引导系统至一个特定状态,以超级用户特权运行的普通进程,永不终止
  • 除去调度进程以外,系统中的每个进程都有一个唯一的父进程,对任何一个子进程而言,其父进程的PID即是它的PPID
  • 下面这些函数都包含在unistd.h头文件中
  • pid_t getpid(void);返回调用进程的PID
  • pid_t getppid(void);返回调用进程的父进程的PID
  • uid_t getuid(void);返回调用进程的实际用户ID
  • gid_t getgid(void);返回调用进程的实际组ID
  • uid_t geteuid(void);返回调用进程的有效用户ID
  • gid_t getegid(void);返回调用进程的有效组ID

创建子进程

  • 创建子进程的函数包含在unistd.h头文件中

  • fork函数

    • pid_t fork(void);
      • 功能:创建调用进程的子进程
      • 返回值:失败返回-1,成功情况下返回的变量在父进程中是PID,在子进程中是0
      • 可以通过这个返回值来执行父进程和子进程
      • 当系统中的总的线程数达到了上限,或者用户的总进程达到了上限,fork函数会失败。
  • 创建子进程示例代码

    #include <stdio.h>
    #include <unistd.h>int main(void)
    {printf("haha\n");// 创建子进程int pid = fork();printf("heihei\n");return 0;
    }/*
    haha
    heihei
    heihei
    */
    

父子进程间的关系

  • 以下是父子进程中数据相关copy的示例图
    在这里插入图片描述

  • 验证上图

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <time.h>int global = 100;  // 父进程全局变量->数据区int main(void)
    {
    int local = 200;  // 父进程局部变量->栈区
    int *heap = malloc(sizeof(int));  // 动态分配内存->堆区
    *heap = 3;printf("父进程第一次打印: PID->%d %p->%d %p->%d %p->%d\n", getpid(), &global, global, &local, local, heap, *heap);
    // 创建子进程
    pid_t pid = fork();
    if(pid == 0)
    {
    // 子进程操作,数据会从父进程copy一份过来,这里执行++操作
    printf("子进程打印: PID->%d PPID->%d %p->%d %p->%d %p->%d\n", getpid(), getppid(), &global, ++global, &local, ++local, heap, ++*heap);
    return 0;
    }
    sleep(1);  // 这里等1s,让子进程++
    printf("父进程第二次打印: PID->%d %p->%d %p->%d %p->%d\n", getpid(), &global, global, &local, local, heap, *heap);return 0;
    }/*
    父进程第一次打印: PID->1674604 0x5577e1acc010->100 0x7ffd4d4bfaa8->200 0x5577e23422a0->3
    子进程打印: PID->1674605 PPID->1674604 0x5577e1acc010->101 0x7ffd4d4bfaa8->201 0x5577e23422a0->4
    父进程第二次打印: PID->1674604 0x5577e1acc010->100 0x7ffd4d4bfaa8->200 0x5577e23422a0->3这里的父进程和子进程地址一样是虚拟地址里面一样,因为每个进程都有一个独立的虚拟地址池,相互不影响的
    发现子进程跟父进程互相不影响,验证了上图的案例
    */
    
  • 父子进程操作文件,其实是共享一个文件表项的
    在这里插入图片描述

  • 验证上图

    #include <stdio.h>
    #include <string.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <time.h>int main(void)
    {// 父进程打开文件int fd = open("./test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664);if(fd == -1){perror("open");return -1;}// 父进程写入数据char *data = "hello bhlu!";if(write(fd, data, strlen(data)) == -1){perror("write");return -1;}// 创建子进程pid_t pid = fork();if(pid == 0){// 子进程修改文件读写位置if(lseek(fd, -5, SEEK_END) == -1){perror("lseek");return -1;}return 0;}// 再次插入数据,验证子进程修改的读写位置是否生效sleep(1);  // 先等1s,让子进程执行完data = "linux\n";if(write(fd, data, strlen(data)) == -1){perror("write");return -1;}// 关闭文件close(fd);return 0;
    }/*
    cat test.txt
    hello linux
    发现是修改成功的,说明上图是对的,子进程和父进程共用一个文件表项
    */
    

进程的终止

以下内容只是简单的介绍进程的终止,以便理解

  • 进程的终止分为两种

    1. 正常终止:分为三种情况

      1. main函数中正常返回

      2. 使用exit函数终止:exit函数可以在任何函数中执行令进程结束,return语句只有在main函数中执行才能令进程结束

        #include <stdlib.h>void exit(int status);
        /* 
        功能: 令进程终止
        参数: status 进程的退出码,相当于main函数的返回值
        无返回值
        *//*
        exit函数在终止前会做以下几件收尾工作
        1. 调用实现通过atexit或on_exit函数注册的函数退出函数
        2. 冲刷并关闭所有仍处于打开状态的标准I/O流
        3. 删除所有通过tmpfile函数创建的临时文件
        4. 执行_exit(status);
        使用exit函数令进程终止,通常使用EXIT_SUCCESS和EXIT_FAILUR两个宏
        EXIT_SUCCESS -> 1; EXIT_FAILUR -> 0;
        */
        
      3. 调用_exit/_Exit函数令进程终止

        // _exit函数
        #include <unistd.h>void _exit(int status);
        /*
        参数: status 进程的退出码,相当于main函数的返回值
        无返回值
        */// _Exit函数
        #include <stdlib.h>void _Exit(int status);
        /*
        参数: status 进程的退出码,相当于main函数的返回值
        无返回值
        *//*
        _exit函数在终止前会做以下几件收尾工作
        1. 关闭所有仍处于打开状态的文件描述符
        2. 将调用进程的所有子进程托付过init进程
        3. 向调用进程的父进程发送SIGCHLD(7)信号
        4. 令调用进程终止运行,将status的低八位作为退出码保存在其终止状态中
        */
        
    2. 异常终止

      1. 进程执行了系统认为具有危险性的操作时,或者系统本身发生故障或意外,内核会向进程发送特定的信号

        SIGILL(4) -> 进程试图执行非法指令
        SIGBUS(7) -> 硬件或对齐错误
        SIGEPE(8) -> 浮点异常
        SIGSEGV(11) -> 无效内存访问
        SIGPWR(30) -> 系统供电不足
        
      2. 人为触发信号

        SIGINT(2) -> Ctrl+c
        SIGQUIT(3) -> Ctrl+\
        SIGKILL(9) -> 不能被捕获或忽略的进程终止信号
        SIGTERM(15) -> 可以被捕获或忽略的进程终止编号
        
      3. 向进程自己发送信号

        #include <stdlib.h>void abort(void);
        /*
        功能: 想进城发送SIGABRT(6)信号,该信号默认情况下可以使进程结束
        无返回值
        */
        

在使用exit函数或main函数正常退出时,如果注册了atexiton_exit,那就会触发退出函数,以下是示例代码

#include <stdio.h>
#include <stdlib.h>void func(void)
{exit(6);
}void goto1(void)
{printf("goto1\n");
}void goto2(int status, void *arg)
{printf("status = %d\n", status);printf("arg = %s\n", (char *)arg);
}int main(void)
{atexit(goto1);  // 退出之前执行goto1on_exit(goto2, "heihei");  // 退出之前执行goto2,可以传参func();return 0;
}/*
相当于钩子函数,在退出之前执行,可以进行一些回收操作
status = 6
arg = heihei
goto1
*/

回收子进程

  • 如果不回收子进程的话,会导致有很多僵尸进程的存在,从而消耗更多的系统资源。
  • 父进程需要等待子进程到的终止,以继续后续工作
  • 父进程需要了解子进程终止的原因,是正常终止,还是异常终止

阻塞回收

  • wait函数是用于回收子进程的一个函数,它使用的是阻塞回收,使用它必须包含sys/wait.h头文件

  • wait函数

    • pid_t wait(int *status);

      • 功能:等待和回收任意子进程

      • 参数:status用于输出子进程的终止状态,可置NULL

        • 补充:可以使用以下工具宏分析子进程的终止状态

          if(WIFEXITED(status))// 真printf("正常终止: 进程退出码是%d\n", WEXITSTATUS(status));
          else// 假printf("异常终止: 终止进程的信号是%d\n", WTERMSIG(status));// 下面跟上面判断条件相反
          if(WIFSIGNALED(status))// 真printf("异常终止: 终止进程的信号是%d\n", WTERMSIG(status));
          else// 假printf("正常终止: 进程退出码是%d\n", WEXITSTATUS(status));
          
      • 返回值:成功返回回收的子进程PID,失败返回-1

  • 简单代码示例

    // 子进程的回收
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/wait.h>int main(void)
    {// 创建子进程pid_t pid = fork();if(pid == -1){perror("fork");return -1;}// 子进程相关操作if(pid == 0){printf("%d进程: 我是子进程!\n", getpid());// sleep(5);// exit(3);// _exit(5);// return 2;// abort();  // 向进程发送信号异常结束// 以下两句会造成内存无效访问,会返回11char *p = NULL;*p = 123;}// 父进程等待回收子进程printf("%d进程: 我是父进程!\n", getpid());int s;  // 用来输出所回收的子进程终止状态pid_t childpid = wait(&s);if(childpid == -1){perror("wait");return -1;}printf("父进程回收了%d进程的僵尸!\n", childpid);// 根据返回值判断子进程是否是正常结束if(WIFEXITED(s))printf("正常结束: %d\n", WEXITSTATUS(s));elseprintf("异常结束: %d\n", WTERMSIG(s));return 0;
    }
    
  • 以下代码是一个循环创建5个进程,然后父进程挨个回收

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/wait.h>
    #include <errno.h>int main(void)
    {printf("%d进程: 我是父进程!\n--------------------------\n", getpid());sleep(1);// 创建子进程for(int i = 0; i < 5; i++){pid_t pid = fork();if(pid == -1){perror("fork");return -1;}// 子进程操作if(pid == 0){printf("%d进程: 我是子进程!\n", getpid());sleep(i+1);return i+1;}}// 父进程操作: 回收子进程while(1){int s;  // 用户接收子进程的终止状态pid_t childpid = wait(&s);if(childpid == -1){if(errno == ECHILD){printf("没有子进程可以回收了!\n");break;}else{perror("wait");return -1;}}// 判断子进程的终止状态if(WIFEXITED(s))printf("正常结束: %d\n", WEXITSTATUS(s));elseprintf("异常终止: %d\n", WTERMSIG(s));}return 0;
    }
    

非阻塞回收

  • waitpid函数一般用于非阻塞回收子进程,还可以回收特定子进程,使用这个函数需要引用sys/wait.h头文件

  • waitpid函数

    • pid_t waitpid(pid_t pid, int *status, int options);
      • 功能:等待并回收任意或特定子进程
      • 参数
        • pid:取-1等待并回收任意子进程,相当于wait函数,>0等待回收特定子进程
        • status:用于输出子进程的终止状态,可置NULL
        • option:0代表阻塞模式,WNOHANG代表非阻塞模式,如果等待的进程还在运行,则返回0
      • 返回值:成功返回回收子进程的PID或者0,失败返回-1
  • 以下是使用非阻塞回收的方法回收子进程

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/wait.h>
    #include <errno.h>int main(void)
    {printf("%d进程: 我是父进程!\n-------------------------\n", getpid());// 创建子进程for(int i = 0; i < 5; i++){pid_t pid = fork();if(pid == -1){perror("fork");return -1;}// 子进程相关操作if(pid == 0){printf("%d进程: 我是子进程!\n", getpid());sleep(i+1);// 三种效果if(i == 3){abort();}else if(i == 4){char *p = NULL;*p = 123;}else{return i+1;}}}// 父进程回收子进程sleep(1);while(1){int s; // 用于保存进程的终止状态pid_t childpid = waitpid(-1, &s, WNOHANG);  // 这里使用的是非阻塞模式if(childpid == -1){// 报错或者没有子进程了if(errno == ECHILD){printf("没有子进程了!\n");break;}else{perror("waitpid");return -1;}}else if(childpid == 0){// 子进程还在运行printf("子进程在运行,无法回收,先睡会!\n");sleep(2);}else{// 回收成功并判断是否正常终止printf("%d子进程回收成功!\n", childpid);if(WIFEXITED(s))printf("%d进程正常终止, 进程退出码: %d\n\n", childpid, WEXITSTATUS(s));elseprintf("%d进程异常终止, 终止进程信号: %d\n\n", childpid, WTERMSIG(s));}}return 0;
    }/*
    代码执行效果
    1761797进程: 我是父进程!
    -------------------------
    1761798进程: 我是子进程!
    1761799进程: 我是子进程!
    1761800进程: 我是子进程!
    1761801进程: 我是子进程!
    1761802进程: 我是子进程!
    子进程在运行,无法回收,先睡会!
    1761798子进程回收成功!
    1761798进程正常终止, 进程退出码: 11761799子进程回收成功!
    1761799进程正常终止, 进程退出码: 2子进程在运行,无法回收,先睡会!
    1761800子进程回收成功!
    1761800进程正常终止, 进程退出码: 31761801子进程回收成功!
    1761801进程异常终止, 终止进程信号: 6子进程在运行,无法回收,先睡会!
    1761802子进程回收成功!
    1761802进程异常终止, 终止进程信号: 11没有子进程了!
    */
    

补充

  • 实际情况下,无论进程是正常终止还是异常终止,都会通过系统内核向其父进程发送一个SIGCHLD(17)信号,我们可以提供一个针对该信号的处理函数,在信号处理函数中异步的方式回收子进程,这样不仅流程简单,回收效率还高,僵尸进程的存活时间也会很短。

创建新进程

与fork函数不同,这里使用的exec函数是创建一个新的进程,新进程会取代调用自身的进程,新进程覆盖之前的进程地址空间,进程的PID不会改变。
在这里插入图片描述

  • exec不是一个函数,而是一堆函数,功能一样,用法相似

  • #include <unistd.h>

    1. int execl(const char *path, const char *arg, ...);

      execl("/bin/ls", "ls", "-a", "-l", NULL);
      /*
      path使用的是路径名
      使用NULL作为arg的结尾
      失败返回-1,成功不返回
      */
      
    2. int execlp(const char *file, const char *arg, ...);

      execlp("ls", "ls", "-a", "-l", NULL);
      /*
      file使用的是文件名,会从环境变量中一个个的找
      使用NULL作为arg的结尾
      失败返回-1,成功不返回
      */
      
    3. int execle(const char *path, const char *arg, ..., char *const envp[]);

      char *envp[] = {"NAME=bhlu", "AGE=25", NULL};
      execle("/usr/bin/env", "env", NULL, envp);
      /*
      比excel多一个envp,用于设置环境变量,它设置什么,新进程的环境变量就只有什么
      失败返回-1,成功不返回
      环境变量输出:NAME=bhluAGE=25
      */
      
    4. int execv(const char *path, char *const argv[]);

      char *argv[] = {"ls", "-a", "-l", NULL};
      execv("/bin/ls", argv);
      /*
      execv系列使用的都是字符指针数组,字符数组是以NULL结尾
      失败返回-1,成功不返回
      */
      
    5. int execvp(const char *file, char *const argv[]);

      char *argv[] = {"ls", "-a", "-l", NULL};
      execvp("ls", argv);
      /*
      跟execv差不多,就第一个参数是文件名
      失败返回-1,成功不返回
      */
      
    6. int execve(const char *path, char *const argv[], char *const envp[]);

      char *argv[] = {"env", NULL};
      char *envp[] = {"NAME=bhlu", "AGE=25", NULL};
      execve("/usr/bin/env", argv, envp);
      /*
      跟execle函数差不多,就是这里的第二个参数是字符指针数组
      失败返回-1,成功不返回
      */
      
  • 后缀不同,代码的含义也不同

    • l:即list,新进程的命令以字符指针列表形式传入,列表以空指针结束
    • p:即path:第一个参数,不包含/,就根据PATH环境变量搜索文件
    • e:即environment:设定环境变量,不指定则从调用进程复制
    • v:即vector:新进程的命令行参数以字符指针数组的形式传入,数组以空指针结束
    • 实际底层最后使用的都是execve函数
  • 使用exec函数基本会将原进程的所有信号、属性、数据等都丢失或者恢复初识状态,只有PIDPPIDUID等会被继承下来。

  • 一般都会先创建一个子进程,然后在子进程中使用exec函数,以下是相关示例

    #include <stdio.h>
    #include <unistd.h>int main(void)
    {// 创建子进程pid_t pid = fork();if(pid == -1){perror("fork");return -1;}// 子进程相关操作if(pid == 0){char *argv[] = {"env", NULL};if(execvp("/bin/env", argv) == -1){perror("execvp");return -1;}}// 父进程操作printf("父进程PID: %d\n", getpid());return 0;
    }
    

system

  • 下面介绍的是c语言执行shell命令的函数

  • #include <stdlib.h>

    • int system(const char *command);
      • 功能:执行shell命令
      • 参数:shell命令,如果参数取NULL,返回非0表示Shell可用,返回0表示不可用
      • 返回值:成功返回command进程的终止状态, 失败返回-1
  • 代码实例

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>int main(void)
    {int s = system("echo $PATH");if(s == -1){perror("system");return -1;}printf("父进程PID: %d\n", getpid());return 0;
    }
    
  • system函数内部调用了vforkexecwaitpid等函数,而且它是标准库函数,可以跨平台使用

    • 如果调用vforkwaitpid函数出错,则返回-1
    • 如果调用exec函数出错,则在子进程中执行exit(127)
    • 如果都成功,会从waitpid获取command进程的终止状态

相关文章:

C语言进程的相关操作

C语言进程的相关操作 进程简介 每个进程都有一个非负整数形式到的唯一编号&#xff0c;即PID&#xff08;Process Identification&#xff0c;进程标识&#xff09;PID在任何时刻都是唯一的&#xff0c;但是可以重用&#xff0c;当进程终止并被回收以后&#xff0c;其PID就可…...

数据结构学习系列之链式栈

链式栈&#xff1a;即&#xff1a;栈的链式存储结构&#xff1b;分析&#xff1a;为了提高程序的运算效率&#xff0c;应采用头插法和头删法&#xff1b;进栈&#xff1a; int push_link_stack(stack_t *link_stack,int data) {if(NULL link_stack){printf("入参合理性检…...

too many session files in /var/tmp

Linux中Too many open files 问题分析和解决_e929: too many viminfo temp files-CSDN博客...

【7.0】打开未知来源安装应用

默认打开未知来源安装应用 frameworks\base\packages\SettingsProvider\res\values\defaults.xml <bool name"def_install_non_market_apps">false</bool>...

安装ipfs-swarm-key-gen

安装ipfs-swarm-key-gen Linux安装go解释器安装ipfs-swarm-key-gen Linux安装go解释器 https://blog.csdn.net/omaidb/article/details/133180749 安装ipfs-swarm-key-gen # 编译ipfs-swarm-key-gen二进制文件 go get -u github.com/Kubuxu/go-ipfs-swarm-key-gen/ipfs-swarm…...

BASH shell脚本篇5——文件处理

这篇文章介绍下BASH shell中的文件处理。之前有介绍过shell的其它命令&#xff0c;请参考&#xff1a; BASH shell脚本篇1——基本命令 BASH shell脚本篇2——条件命令 BASH shell脚本篇3——字符串处理 BASH shell脚本篇4——函数 在Bash Shell脚本中&#xff0c;可以使用…...

ElementUI之首页导航及左侧菜单(模拟实现)

目录 ​编辑 前言 一、mockjs简介 1. 什么是mockjs 2. mockjs的用途 3. 运用mockjs的优势 二、安装与配置mockjs 1. 安装mockjs 2. 引入mockjs 2.1 dev.env.js 2.2 prod.env.js 2.3 main.js 三、mockjs的使用 1. 将资源中的mock文件夹复制到src目录下 2. 点击登…...

Java开源工具库使用之Lombok

文章目录 前言一、常用注解1.1 AllArgsConstructor/NoArgsConstructor/RequiredArgsConstructor1.2 Builder1.3 Data1.4 EqualsAndHashCode1.5 Getter/Setter1.6 Slf4j/Log4j/Log4j2/Log1.7 ToString 二、踩坑2.1 Getter/Setter 方法名不一样2.2 Builder 不会生成无参构造方法2…...

uboot启动流程涉及reset函数

一. uboot启动流程中函数 之前了解了uboot链接脚本文件 u-boot.lds。 从 u-boot.lds 中我们已经知道了入口点是 arch/arm/lib/vectors.S 文件中的 _start。 本文了解 一下&#xff0c;uboot启动过程中涉及的 reset 函数。本文继上一篇文章学习&#xff0c;地址如下&#xff…...

端口被占用怎么解决

第一步&#xff1a;WinR 打开命令提示符&#xff0c;输入netstat -ano|findstr 端口号 找到占用端口的进程 第二步&#xff1a; 杀死使用该端口的进程&#xff0c;输入taskkill /t /f /im 进程号&#xff08; &#xff01;&#xff01;&#xff01;注意是进程号&#xff0c;不…...

python reportlab 生成多页pdf

多页 from reportlab.pdfgen import canvas from reportlab.platypus import (SimpleDocTemplate, Paragraph, PageBreak, Image, Spacer, Table, TableStyle) from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY from reportlab.lib.styles import P…...

word 多级目录的问题

一、多级标题自动编号 --> 制表符 -> 空格 网址&#xff1a; 【Word技巧】2 标题自动编号——将多级列表链接到样式 - YouTube 二、多级列表 --> 正规形式编号 网址&#xff1a;Word 教学 - 定框架&#xff1a;文档格式与多级标题&#xff01; - YouTube 三、目…...

python使用mitmproxy和mitmdump抓包之拦截和修改包(四)

我认为mitmproxy最强大的地方&#xff0c;就是mitmdump可以结合python代理&#xff0c;灵活拦截和处理数据包。 首先&#xff0c;mitmdump的路径如下&#xff1a;&#xff08;使用pip3 install mitmproxy安装的情况&#xff0c;参考我的文章python使用mitmproxy和mitmdump抓包…...

邓俊辉《数据结构》→ “2.6.5 二分查找(版本A)”之“成功查找长度”递推式推导

【问题描述】 邓俊辉的《数据结构&#xff08;C语言版&#xff09;&#xff08;第3版&#xff09;》&#xff08;ISBN&#xff1a;9787302330646&#xff09;中&#xff0c;开始于第48页的“2.6.5 二分查找&#xff08;版本A&#xff09;”内容在第50页详述了“成功查找长度”的…...

Linux文件查找,别名,用户组综合练习

1.文件查看: 查看/etc/passwd文件的第5行 [rootserver ~]# head -5 /etc/passwd root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologi…...

【MATLAB第77期】基于MATLAB代理模型算法的降维/特征排序/数据处理回归/分类问题MATLAB代码实现【更新中】

【MATLAB第77期】基于MATLAB代理模型算法的降维/特征排序/数据处理回归/分类问题MATLAB代码实现 本文介绍基于libsvm代理模型算法的特征排序方法合集&#xff0c;包括&#xff1a; 1.基于每个特征预测精度进行排序&#xff08;libsvm代理模型&#xff09; 2.基于相关系数corr的…...

第三章 图标辅助元素的定制

第三章 图标辅助元素的定制 1.认识图表常用的辅助元素 ​ 图表的辅助元素是指除了根据数据绘制的图形之外的元素&#xff0c;常用的辅助元素包括坐标轴、标题、图例、网格、参考线、参考区域、注释文本和表格&#xff0c;它们都可以对图形进行补充说明。 ​ 上图中图表常用辅…...

【前端】ECMAScript6从入门到进阶

【前端】ECMAScript6从入门到进阶 1.ES6简介及环境搭建 1.1.ECMAScript 6简介 &#xff08;1&#xff09;ECMAScript 6是什么 ECMAScript 6.0&#xff08;以下简称 ES6&#xff09;是 JavaScript 语言的下一代标准&#xff0c;已经在2015年6月正式发布了。它的目标&#xff…...

Android Shape设置背景

设置背景时&#xff0c;经常这样 android:background“drawable/xxx” 。如果是纯色图片&#xff0c;可以考虑用 shape 替代。 shape 相比图片&#xff0c;减少资源占用&#xff0c;缩减APK体积。 开始使用。 <?xml version"1.0" encoding"utf-8"?…...

什么是GraphQL?它与传统的REST API有什么不同?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 什么是GraphQL&#xff1f;⭐ 与传统的REST API 的不同⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

docker详细操作--未完待续

docker介绍 docker官网: Docker&#xff1a;加速容器应用程序开发 harbor官网&#xff1a;Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台&#xff0c;用于将应用程序及其依赖项&#xff08;如库、运行时环…...

DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径

目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

Qt Http Server模块功能及架构

Qt Http Server 是 Qt 6.0 中引入的一个新模块&#xff0c;它提供了一个轻量级的 HTTP 服务器实现&#xff0c;主要用于构建基于 HTTP 的应用程序和服务。 功能介绍&#xff1a; 主要功能 HTTP服务器功能&#xff1a; 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

高危文件识别的常用算法:原理、应用与企业场景

高危文件识别的常用算法&#xff1a;原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件&#xff0c;如包含恶意代码、敏感数据或欺诈内容的文档&#xff0c;在企业协同办公环境中&#xff08;如Teams、Google Workspace&#xff09;尤为重要。结合大模型技术&…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比

在机器学习的回归分析中&#xff0c;损失函数的选择对模型性能具有决定性影响。均方误差&#xff08;MSE&#xff09;作为经典的损失函数&#xff0c;在处理干净数据时表现优异&#xff0c;但在面对包含异常值的噪声数据时&#xff0c;其对大误差的二次惩罚机制往往导致模型参数…...