Linux_17进程控制

前提回顾:

页表可以将无序的物理地址映射为有序的;

通过进程地址空间,避免将内存直接暴漏给操作系统;

cr3寄存器存放的有当前运行进程的页表的物理地址;

一、查看命令行参数和环境变量的地址

因为命令行参数和环境变量都是字符串的形式,所以这里我们可以通过字符串直接打印地址;

通过验证最后可以知道:命令行参数和环境变量位于栈区的上方! 

(子进程继承父进程的环境变量 --- 因为进程地址空间存放的有!)

代码是不可写入的(无论是父进程还是子进程,映射到代码区,页表对应的权限关系是只读的!)

操作系统怎么识别到父子共享的数据块,此时需要进行写时拷贝呢?

        如果我们要对当前的父进程的数据进行写入的时候,无论数据是只读还是可写,系统都会将其改为只读权限!(子进程也是只读),此时如果想写入就会触发系统权限的问题(不做异常处理),此时操作系统会对要访问的区域进行检查,如果权限是可写的就会进行写时拷贝!

通过fork创建多个进程

下面我们通过循环创建多个线程:

父子进程(或兄弟进程)被创建出来后,谁先运行不可知!(由调度器决定!)

 进程退出的场景都有哪些?

  • 代码运行完毕,结果正确;
  • 代码运行完毕,结果不正确;
  • 代码异常终止;

进程中,一般谁会关心我们的运行情况呢?(--- 父进程!)

相比于正确的结果,我们一般关心的是为什么程序运行会出错?

在C语言中,可以用return的不同返回值数字,表征不同的出错原因(即退出码);

即:main函数的返回值得到本质表示的是:进程运行时是否是正确的结果,如果不是,可以用不同的数字来表示不同的出错原因!

使用 $? 可以获取上一个进程运行结束后的退出码(最近一个进程运行的退出码);

这里第一次执行查看的时./myproc的进程码,接下来几次查看的都是echo的进程码!

错误码0、1...都是给计算机查看的,人自己查看可以通过Linux提供的接口将其转化为字符串(需要包含string.h这个头文件);

这里当我们进行ls查看一个不存在的文件的时候,此时我们使用echo $? 打印的结果正好是错误代码中2!

我们也可以自定义错误码,而不是一定非要根据系统的体系走;

此时返回错误码对应的自定义的下标即可! 

C语言有个errno全局变量!(用于保存最近一次执行的错误码!)

当我们调用全局函数失败的时候,此时errno会返回对应的错误码!

此时我们可以打印errno对应的错误代码,并将其返回给父进程,让父进程直到此时调用有问题;

代码异常终止,我们可以认为:代码可能没有跑完(如果程序在main返回之前异常终止,此时我们认为该错误码没有意义,不关心退出码!);

进程的控制是由信号控制的:

我们可以通过kill -l对对应的进程进行控制;

结论:进程出现异常,本质上就是进程收到了对应的信号! 

这里exit()在任何地方被调用后,都表示调用该进程直接退出!(不会执行后面的打印语句)

此时如果我们进行return,函数会直接返回,然后在main里面继续打印printf的内容!

_exit()这是一个系统调用接口;exit是库函数!

exit()实际上是先将对应的缓存区和自定义的函数清理,然后再调用_exit() ;

此时我们提出一个问题:这个缓存区应该在内存的那个位置?

一定不在内核区,如果在内核区的话此时_exit()也会刷新缓存流!

二、进程等待

我们要通过等待进程,获取紫禁城的退出情况 --- 直到我们给子进程布置的任务完成的怎么样,可以关心也可以不关系(不关心就设置为NULL)

异常实质上就是信号!(运行中报错)

如果我们对应的wait在调用的时候,此时子进程一直在执行工作,没有退出;

那么此时父进程就会阻塞等待子进程!(阻塞状态不仅发生在等待硬件 - 键盘、也会发生在等待软件 - 进程)

输出型参数:在函数调用的时候,由函数内部修改其值并返回调用者的参数(例如malloc)

status就是一个输出型参数!

父进程要拿子进程的状态数据,为什么必须要用wait等系统调用呢?

进程具有独立性!如果我们全局变量,此时子进程将这个全局变量进行修改,无法传递给父进程!(必须得通过系统调用来实现!)

前8位表示的是终止信号,(例如我们勇敢什么特殊信号杀掉进程);

第八位为标志位,此时我们暂不关心;

8~15表示的是退出状态(即子进程的退出状态码);

 

这里父进程在对应的数据和代码里面,通过系统调用接口,查询子进程的退出信息(exit_code, exit_signal),其中这两个主要的信息包含在tast_struct里面!

因此waitpid函数的本质是:读取子进程的task_struct的内核数据结构对象;

等待子进程返回信息的时候,什么时候会等待失败呢?

当等待的子进程不是对应的子进程!此时返回-1;

  • WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出) --- 本质上检查的是信号位(也就是status & 0x7f --- > 取小的七位!)
  • WEXITSTATUS(status): WIFEXITED非零,提取子进程退出码。(查看进程的退出码)也就是(status>>8)& 0xFF
用法如下所示:

waitpid中的option选项:

  • 当此时为0的时候,即为阻塞等待方式;
  • WNOHANG(wait no 夯住了) --- 非阻塞轮询!

阻塞等待方式:子进程此时一直为R状态运行,父进程需要等待,把父进程投递在等待队列中,

(我们可以认为子进程维护的PCB里面也维护了一个等待队列!--- 为等待该进程的使用的)

引入非阻塞轮询的概念:

阻塞等待的时候,此时父进程只能等待,而不能先进行一会自己的工作;

        但是非阻塞轮询此时会一直询问子进程的状态,如果没准备好,此时父进程会先忙自己的工作,过段时间再进行询问;

对于非阻塞轮询有三种返回条件:

  • ret_pid>0(也就是对应子进程的PID) | ret_pid <0,此时表示此时等待的条件已经就绪或者失败!
  • ret_pid = 0 表示还没有继续,此时会继续等待;

为了实现非阻塞轮询,这里我们使用while循环来实现!

信号是没有0号信号的!这是因为0号代表进程正常执行,没有异常,即没有信号干扰! 

waitpid 中的 options 参数用于控制该函数的行为模式,允许父进程以更灵活的方式等待子进程的状态变化。以下是 options 参数的详细解析:

主要选项及其作用

  1. 0(默认阻塞模式)

    • 当 options 设置为 0 时,waitpid 会以阻塞方式等待子进程结束或状态变化。父进程会暂停执行,直到满足等待条件(如子进程终止)后才继续运行。
    • 适用场景:需要父进程必须等待子进程结束后才能继续执行的简单场景 。
       
  2. WNOHANG(非阻塞模式)

    • 若子进程尚未结束或状态未变化,waitpid 会立即返回,而非阻塞父进程。此时返回值可能是:
      • 子进程 PID:子进程已结束,状态被成功收集。
      • 0:子进程仍在运行,但未结束。
    • 适用场景:父进程需要同时处理其他任务(如轮询多个子进程状态)时,避免阻塞以提高效率 。
       
  3. WUNTRACED(报告停止状态)

    • 当子进程因信号(如 SIGSTOP被暂停(而非终止)时,waitpid 会返回其状态。
    • 需配合宏 WIFSTOPPED(status) 和 WSTOPSIG(status) 解析状态,获取导致暂停的信号(如 SIGSTOP) 。
  4. WCONTINUED(报告继续状态)

    • 当子进程因 SIGCONT 信号恢复执行时,waitpid 会返回其状态。需通过宏 WIFCONTINUED(status) 检测此状态 。
       

三、进程替换

excl类函数的作用:执行一个文件;

标准写法要以NULL为结尾! (execl后面的代码不会执行!)

单进程的程序进行程序替换:

        当我们名为mycommand的程序进行运行的时候,刚开始printf进行打印,执行到execl这一行命令的时候,此时会将ls的代码和数据移至内存当中(对原来的数据和代码进行覆盖)!此时整个页表左侧的结构和东西不变!(task_struct和进程地址空间和页表左侧) 

当我们运行到程序替换所对应的程序的位置的时候:将新程序的数据替换老程序的数据;

页表对应的左侧都没有发生变化!此时是页表对应的右侧的物理内存发生了变化,将新的程序加载到内存当中并替换页表;

execl如果执行成功!此时后面的代码都不会被执行!哪怕此时后面存在exit(0);

但是如果execl执行失败,此时函数会返回1,然后继续执行后面的代码!(execl后面的代码和数据也算是老程序的代码和数据)

问题:子进程执行程序替换的时候,是否会影响父进程?

当子进程进行进程替换的时候,不会影响父进程!此时子进程会进行写时拷贝!

程序替换不会创建子进程!只进行程序的代码和数据的替换工作!

程序替换的现象:

  • 程序替换之后,exec* 之后的代码不会执行,替换失败呢,才有可能执行后面的代码;
  • exec* 函数只有失败才有返回值!!成功没有!

问题:当我们加载新的程序的时候,CPU如何知道新的程序的入口地址?

  • Linux中的可执行程序是有格式的!(ELF),在可执行程序的表头中有可执行程序的入口地址;
  • 新进程的表头可以被CPU读取,进行替换新的进程;

execl接口介绍

list指的是像链表一样一个节点一个节点的进行传递(参数为可变参数); 

所有的exec* 系列的函数的第一个参数都是为了找到执行该程序的地址;

找到该程序后应该怎么办?

主要是确定如何执行该程序,该程序需不需要覆盖选项?(命令行中如何传,此时我们就如何传)

execlp接口介绍

带p指的是:PATH,也就是说execlp会在自己默认的PATH中进行查找!

虽然这里显示我们调用写了两个ls,但是实际上!第一个参数ls是为了确定在哪里找到整个程序(即我们需要执行谁)!第二个ls是为了确定我们需要怎么进行执行!

execv接口介绍

这里的v指的是数组!

以NULL作为结尾!

实际上就是将我们对应的可变参数列表替换为了字符串指针数组!

这里在使用的时候我们需要先定义一个数组!

char* const myargv[] 

ls有main函数,那么ls的main函数有命令行参数吗?

有!这里ls的命令行参数是由myargv传入的!

execvp接口介绍

实际上就是vector + PATH!

此时调用更加简单! 

execle接口介绍

这里的e指的是env:也就是我们自己维护的环境变量!

补充点:对于C++的程序,后缀吹了.cpp,还可以为.cc或.cxx;

如何通过一个Makefile形成两个或者多个可执行文件?

可以定义一个伪目标文件all;

此时形成这个伪目标文件需要先形成其他两个可执行程序! 

当前我们有两个可执行程序!此时我们想要通过mycommand来调用otherExe!

我们可以通过下面的格式进行调用!

  • 第一个参数表示的是执行程序的路径(相对路径);
  • 第二个参数是我们需要执行什么程序;
  • 第三个参数是附加的选项(这里为NULL!);

问题:C/C++语言程序是否可以调用其他语言或脚本的程序?

答案:可以!

脚本语言开头都以#!为开头!后面对应的是脚本语言的解释器!

假如说我们当前有如下所示的脚本文件:

./shell脚本不能执行!(得通过bash来执行!)

bash  test.sh 

此时如果我们想通过C/C++语言程序执行这个脚本,那我们应该怎么执行?

这里得第一个参数为bash解释器得地址!

第二个为需要执行的指令!(按照在命令行输入指令的形式)

如果当前我们有一个Python脚本也是对应的!

问题:无论是我们的可执行程序,还是脚本语言,为什么都能跨语言调用呢?

这是因为所有语言运行出来,本质上都是进程! 

问题:我们的exec* 能调用系统指令,那么能不能执行我们自己的指令?

一个程序代码是否能通过exec*传递命令行参数,另一个可执行程序通过main中的命令行参数接受到可执行参数?

这里我们没有传递环境变量,但是子进程依然可以显示环境变量!

问题:环境变量是什么时候传递给子进程的? 

结论:环境变量也是数据,创建子进程的时候,环境变量就已经被子进程继承下去了!(extern cahr** environ)

所以程序替换中,环境变量信息不会被替换!

问题:为什么程序被替换的时候,环境变量信息不回被替换?(deepseek)

1. 环境变量的存储位置与程序替换的覆盖范围

  • 环境变量存储区域: 环境变量通常存储在进程地址空间的 栈区之上独立的环境变量表 中,与程序的代码段(.text)、数据段(.data)分离。程序替换仅覆盖代码段和数据段,而不会修改栈区和堆区等内存区域,因此环境变量所在区域未被触及。

  • 程序替换的本质: 替换过程通过加载新程序的代码和数据到内存中,并更新页表映射,但不会修改进程的上下文信息(如环境变量、文件描述符等)。

2. 环境变量的继承机制

  • 父子进程的继承性: 子进程通过 fork 创建时,会复制父进程的环境变量表。即使后续调用 exec 替换程序,子进程仍保留父进程传递的环境变量,除非显式指定新的环境变量数组。

  • 默认行为exec 系列函数(如 execlexecv 等)默认使用父进程的环境变量,仅当调用 execleexecve 时才会通过参数 envp[] 覆盖式传递 新环境变量。例如:

char *envp[] = {"PATH=/custom/path", NULL};
execle("/bin/program", "program", NULL, envp);  // 覆盖原有环境变量

3. 操作系统对进程上下文的管理

  • 进程控制块(PCB)的独立性: 程序替换仅修改进程的代码和数据,而 PCB 中的优先级、环境变量指针等元数据保持不变。环境变量作为进程运行时的上下文信息,独立于代码逻辑。

  • 性能优化: 环境变量的继承避免了重复加载和初始化,提升进程创建效率。若每次替换程序都重新加载环境变量,会增加系统开销。

问题:如果我们想给子进程传递环境变量,此时应该怎么进行传递呢?

  • 新增环境变量;

第一种情况:我们可以在bash上面新增一个环境变量,此时 mycommand是bash的子进程,会继承该环境变量;otherExe是mycommand的子进程,会继承其的环境变量,最后得到新增的环境变量!

第二种情况:我们不想让bash有该环境变量!

需要在父进程中导入!

我们可以使用上面的系统调用接口新增环境变量! 

第三种情况:通过exec* 系列带e的函数可以实现:

但是上面实际还是将父进程的环境变量传递过去,默认的也是将父进程的传过去! 

  • 彻底替换;

我们可以通过自定义环境变量,然后进行替换! 

结论:这里execle采用的策略是覆盖而不是替换!

man的3号手册一般被称为库函数!(2号手册是系统调用!)

这里的execve实际上是系统调用接口!

因此实际上其他库函数最后都是调用execve这个系统调用函数!

再谈shell 

shell被称为外壳程序,shell/bash也被称为一个进程,执行命令的时候,本质上是创建子进程!

当我们在bash上面执行命令的时候,此时左侧对应的用户名 + 主机名 + 路径等,我们可以通过环境变量来获取(系统调用也可以!)

自定义简单shell

补充点1:C语言中,相邻字符串具有自动连接的特点!

如下所示:

printf("This is a very long string ""that spans multiple lines ""in the source code.");

输出结果:This is a very long string that spans multiple lines in the source code.

补充点二:fgets函数的用法(deepseek)

        在Linux系统中,fgets()是C标准库中用于安全读取字符串的函数,尤其适合处理文本文件的逐行读取需求。以下是其核心特性、使用方法和注意事项的综合解析:

  • 参数解析
    • str:指向字符数组的指针,用于存储读取的字符串。
    • n:最大读取字符数(包括结尾的\0),即最多读取n-1个字符。
    • stream:输入流指针,可以是文件指针(如fopen()返回的)或标准输入(stdin) 。

返回值

  • 成功时返回str指针;
  • 失败或到达文件末尾时返回NULL,需通过feof()ferror()判断具体原因 。

assert在编译的时候起效果,运行的时候没有效果!

注意点一:

对有的编译器来说,有时候创建变量若没有使用,则此时会出现警告甚至是报错,因此此时我们可以采用下面这种方式:

通过(void)s 避免报错;

注意点二:

这里我们为了不想当输入完指令后,此时回车也会被记录到对应的指令中,因此此时我们可以进行下面的处理:

这里的strlen会带上\n,因此此时我们将对应的\n替换为\0,即可实现!

注意点三:

为实现不同的功能,这里我们需要对字符串进行分割,这里我们调用一个函数strtok;

 strtok是C标准库中用于分割字符串的核心函数,常用于解析以特定分隔符分隔的文本数据。

#include <string.h>
char *strtok(char *str, const char *delim);
  • 参数
    • str:首次调用时传入待分割的字符串;后续调用需设为NULL,以继续处理剩余部分。
    • delim:分隔符集合字符串,函数会将其中任意字符视为分隔符。
  • 返回值:返回指向当前子串的指针,若无可分割内容则返回NULL 。
     

该函数一次只会分割一次字串!

strtok的基本用法:

#include <stdio.h>
#include <string.h>int main() {char str[] = "apple,banana;cherry";char *token = strtok(str, ",;"); // 分隔符可以是逗号或分号while (token != NULL) {printf("%s\n", token);token = strtok(NULL, ",;"); // 后续调用传入NULL}return 0;
}

这里需要注意的是,后续用来分隔的时候,需要传入NULL;

输出如下所示:

apple
banana
cherry

当我们执行mkdir等的时候,都可以正常运行,但是执行cd ..  或者 cd /等却不能正常运行!

这是因为这些命令我们都是通过fork之后的子进程来运行的!但是子进程的运行结果不会影响父进程!

因此这些命令需要父进程自己来运行!这些命令也就成为内建命令!

在自定义的shell时,内建命令需要我们自己罗列起来;

引用新的系统调用接口:chdir

在Linux系统中,chdir函数是用于更改进程当前工作目录的核心接口,属于C标准库(libc)的一部分。

函数原型:

#include <unistd.h>
int chdir(const char *path);
  • 参数path可以是绝对路径(如/home/user)或相对路径(如../doc) 。
     
  • 返回值:成功返回0,失败返回-1并设置errno以指示具体错误 。

函数的使用场景:

#include <stdio.h>
#include <unistd.h>int main() {char buf[256];getcwd(buf, sizeof(buf));  // 获取当前目录printf("原目录: %s\n", buf);if (chdir("/tmp") == 0) {  // 切换目录到/tmpgetcwd(buf, sizeof(buf));printf("新目录: %s\n", buf);} else {perror("chdir失败");}return 0;
}

输出结果如下所示:

原目录: /home/user
新目录: /tmp

引用函数调用接口:sprintf

   sprintf 是 C 语言标准库中用于将格式化数据写入字符数组的核心函数,其作用是将变量、常量等数据按指定格式组合成一个字符串,并将结果存储到用户提供的缓冲区中。

函数原型与参数

int sprintf(char *str, const char *format, ...);
  • str:指向目标字符数组的指针,用于存储结果字符串。需确保缓冲区足够大,否则可能引发溢出 。
     
  • format:格式字符串,包含普通字符和格式说明符(如 %d%s)。格式说明符决定参数的类型和显示方式 。
     
  • ...:可变参数列表,参数数量与格式字符串中的 % 标签数量一致 。

 示例场景:

int num = 123;
char buffer[20];
sprintf(buffer, "%d", num);  // 转换为 "123"

引用函数调用接口:getcwd

getcwd 是用于获取进程当前工作目录(Current Working Directory)的 C 标准库函数,其底层通过系统调用实现。

#include <unistd.h>
char *getcwd(char *buf, size_t size);
  • 参数
    • buf:存储路径的缓冲区,若为 NULL 且 size=0,函数自动分配内存(需手动 free 释放)。
    • size:缓冲区大小,若路径长度超过 size,返回 NULL 并设置 errno=ERANGE 。
  • 返回值:成功返回路径指针,失败返回 NULL 。

这里我们还需要处理export和echo打印环境变量!

这里export也需要是内建命令!如果不是内建命令,当我们到入环境变量时,通过fork子进程导入对应的环境变量,此时父进程无法显示!但是如果父进程执行该命令,此时由于继承环境变量,子进程也会有新的环境变量!

当我们们登录的时候,系统会帮我们启动一个shell进程,此时可以引出问题:shell本身的环境变量表是从哪里得来的?

命令行中所有执行的环境变量都是从bash得到的。

在当前用户的家目录下,有这个.bash_profile这个文件!

即当用户登陆的时候,shell会读取用户目录下的.bash_profile文件,里面保存了导入环境变量的方式!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NVIDIA k8s-device-plugin源码分析与安装部署

在《kubernetes Device Plugin原理与源码分析》一文中&#xff0c;我们从源码层面了解了kubelet侧关于device plugin逻辑的实现逻辑&#xff0c;本文以nvidia管理GPU的开源github项目k8s-device-plugin为例&#xff0c;来看看设备插件侧的实现示例。 一、Kubernetes Device Pl…

MySql索引下推(ICP)是什么?有什么用?

目录 基本介绍为什么需要索引下推&#xff1f;未引入ICP&#xff08;x&#xff09;引入ICP&#xff08;√&#xff09; 如何指导sql优化适用场景sql优化 基本介绍 索引下推&#xff08;Index Condition Pushdown, ICP&#xff09;&#xff0c;是MySQL5.6 引入的优化技术&#…

用户可免费体验!国家超算互联网平台上线阿里开源推理模型接口服

近日&#xff0c;国家超算互联网平台上线阿里巴巴开源推理模型QwQ-32B API接口服务&#xff0c;现在用户可获得免费的100万Tokens。基于国产深算智能加速卡以及全国一体化算力网&#xff0c;平台支持海量用户便捷调用QwQ-32B、DeepSeek-R1等国产开源大模型的接口服务。 了解QwQ…

大数据学习(63)- Zookeeper详解

&&大数据学习&& &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4dd;支持一下博主哦&#x1f91e; &#x1f…

【蓝桥杯python研究生组备赛】003 贪心

题目1 股票买卖 给定一个长度为 N 的数组&#xff0c;数组中的第 i 个数字表示一个给定股票在第 i 天的价格。 设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易&#xff08;多次买卖一支股票&#xff09;。 注意&#xff1a;你不能同时参与多笔交易&…

mmdet3d.models.utils的clip_sigmoid理解

Sigmoid 函数 标准的 sigmoid 函数定义为&#xff1a; 容易得出结论&#xff1a; 取值范围(0, 1) clip_sigmoid 是在标准的 sigmoid 函数基础上进行 裁剪&#xff08;clip&#xff09;&#xff0c;即对 sigmoid 输出的结果加以限制&#xff0c;避免其超出特定范围。 import …

侯捷 C++ 课程学习笔记:进阶语法之lambda表达式(二)

侯捷 C 课程学习笔记&#xff1a;进阶语法之lambda表达式&#xff08;二&#xff09; 一、捕获范围界定 1. 局部变量与函数参数 ​非静态局部变量&#xff1a;Lambda 所在作用域内定义的局部变量&#xff08;如函数内部的 int x&#xff09;会被完整复制其当前值。捕获后外部变…

有必要使用 Oracle 向量数据库吗?

向量数据库最主要的特点是让传统的只能基于具体值/关键字的数据检索&#xff0c;进化到了可以直接基于语义的数据检索。这在AI时代至关重要&#xff01; 回到标题问题&#xff1a;是否有必要使用 Oracle 向量数据库&#xff1f; 这实际还要取决于你的具体应用需求。 客观来讲…

论文解读 | AAAI'25 CoRA:基于大型语言模型权重的协作信息感知用于推荐

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 点击 阅读原文 观看作者讲解回放&#xff01; 个人信息 作者&#xff1a;刘禹廷&#xff0c;东北大学博士生 内容简介 将协作信息融入大型语言模型&#xff08;LLMs&#xff09;是一种有前景的适应推荐任务的技…

es扩容节点以后写入数据量增加1倍

背景&#xff1a; es扩容一倍的数据节点以后 写入数据量增加1倍 业务反馈业务访问量没增加。 最后定位是监控数据&#xff1a; PUT _cluster/settings {"persistent": {"xpack.monitoring.collection.enabled" : "false"} }这个索引记录的是 节…

G-Star 公益行 | 温暖相约 3.30 上海「开源×AI 赋能公益」Meetup

你是否曾想过&#xff0c;在这个数字化浪潮席卷的时代&#xff0c;公益组织如何突破技术瓶颈&#xff1f;当 AI 成为热门话题&#xff0c;它能为公益事业带来怎样的温度&#xff1f;开源的力量&#xff0c;如何让每一份善意都拥有无限可能&#xff1f; G-Star 公益行&#xff…

MySQL数据库复杂的增删改查操作

在前面的文章中&#xff0c;我们主要学习了数据库的基础知识以及基本的增删改查的操作。接下去将以一个比较实际的公司数据库为例子&#xff0c;进行讲解一些较为复杂且现时需求的例子。 基础知识&#xff1a; 一文清晰梳理Mysql 数据库基础知识_字段变动如何梳理清楚-CSDN博…

kafka-docker版

Kafka-docker版 1 概述 1.1 定义 Kafka传统定义&#xff1a; Kafka是一个分布式的基于发布/订阅模式的消息队列(MessageQucue)&#xff0c;主要应用于大数据实时处理领域。它是一个开源的分布式事件流平台( Event Streaming Platform)&#xff0c;被数千家公司用于高性能数据…

Zabbix 7.2 + Grafana 中文全自动安装ISO镜像

简介 ​ 基于Zabbix 官方的Alma Linux 8 作为基础镜像。 镜像源都改为国内大学镜像站&#xff0c;自动联网安装ZabbixGrafana。 安装中文字体、Zabbix和Grafana也配置默认中文。 Zabbix 也指定中文字体&#xff0c;绘图无乱码。 配置时区为东八区&#xff0c;Zabbix配置We…

使用pip在Windows机器上安装Open Webui,配合Ollama调用本地大模型

之前的文章分享过在 linux 服务器上安装&#xff0c;并使用Open-webui 来实现从页面上访问本地大模型的访问。也写了文章分享了我在家里 Windows Server 台式机上安装 Ollama 部署本地大模型&#xff0c;并分别使用 Chatbox 和 CherryStudio 来访问本地的大模型。今天我来分享一…

【python运行Janus-Pro-1B文生图功能】

前言 体验了一把本地部署Janus-Pro-1B实现文生图功能。 1、开源项目下载 官方开源项目代码直接从Github上下载。 2、模型下载 模型官方下载需要魔法 Janus-Pro-1B模型文件&#xff1a;Janus-Pro-1B模型文件 百度网盘&#xff1a; https://pan.baidu.com/s/16t4H4z-QZe2UDAg4…

18 | 实现简洁架构的 Handler 层

提示&#xff1a; 所有体系课见专栏&#xff1a;Go 项目开发极速入门实战课&#xff1b;欢迎加入 云原生 AI 实战 星球&#xff0c;12 高质量体系课、20 高质量实战项目助你在 AI 时代建立技术竞争力&#xff08;聚焦于 Go、云原生、AI Infra&#xff09;&#xff1b;本节课最终…

宇树ROS1开源模型在ROS2中Gazebo中仿真

以GO1为例 1. CMakelists.txt更新语法 cmake_minimum_required(VERSION 3.8) project(go1_description) if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")add_compile_options(-Wall -Wextra -Wpedantic) endif() # find dependencies find…

LearnOpenGL-笔记-其三

在之前的章节中我们学习了基本的窗口构建方法、着色器的定义与使用以及摄像机的构建&#xff0c;而从今天这个大章节开始我们要来学习光照有关的知识。 颜色 现实世界中有无数种颜色&#xff0c;每一个物体都有它们自己的颜色。我们需要使用&#xff08;有限的&#xff09;数…

cfi网络安全 网络安全hcip

目录 RIP (路由信息协议) 算法 开销 版本 开销值的计算方式 RIPV1和RIPV2的区别 RIP的数据包 Request(请求)包 Reponse(应答)包 RIP的特征 周期更新 RIP的计时器 1&#xff0c;周期更新计时器 2&#xff0c;失效计时器 3&#xff0c;垃圾回收计时器 RIP的核心思…