进程控制相关 API-创建进程、进程分离、进程退出、进程阻塞

进程控制相关 API

p.s 进程控制中的状态转换 相关 API,用户很少用到,在此不提。

一般来说,这些内核标准 API,在执行出错(可能是资源不够、权限不够等等)会返回负值(比如 -1),并设置 errno 值。

父进程创建子进程 fork()

在 Linux 中,为了创建一个子进程,父进程用系统调用 fork() 来创建子进程。fork() 其实就是把父进程复制了一份(子进程有自己的特性,比如标识、状态、数据空间(堆栈区和数据区)等(这些是子进程独有的);子进程和父进程共同使用程序代码、共用时间片(这些是共有的)等)。

通常在调用fork函数之后,程序会设计一个if选择结构。当PID等于0时,说明该进程为子进程,那么让它执行某些指令,比如说使用exec库函数(library function)读取另一个程序文件,并在当前的进程空间执行 (这实际上是我们使用fork的一大目的: 为某一程序创建进程);而当PID为一个正整数时,说明为父进程,则执行另外一些指令。由此,就可以在子进程建立之后,让它执行与父进程不同的功能。

pid_t fork();fork() 对子进程 返回 0,对父进程 返回 子进程的 ID,返回 小于 0 值为出错。

#include<stdio.h>
#include<unistd.h>
​
int main()
{int p_num = 0;int c_num = 0;int pid = fork();if(pid == 0) // 返回的pid为0为子进程{c_num++;}else{p_num++; // 返回的pid大于0为父进程}printf("p_num=%d, c_num=%d\n",p_num,c_num);printf("pid=%d\n",pid);return 0;
}
​
// 运行结果如下所示
p_num=1, c_num=0
pid=36101
p_num=0, c_num=1
pid=0

子进程总可以查询自己的 PPID 来知道自己的父进程是谁,这样,一对父进程和子进程就可以随时查询对方。

其它:

  • fork() 的 写时复制 概念,可网搜了解。即 用到某个资源时候才会复制,不需要修改的资源不会复制,尽量推迟高系统消耗的操作直到必要时才会执行。

  • vfork() 不常用,实现可能不会完全没问题,概念可网搜来了解。

进程分离 exec 族函数

通过 fork 后,子进程并没有和父进程独立开,用的是相同的代码。另外还有一个问题时,这个时候子进程的时间片是和父进程一分为二来共享的。为了彻底将父进程和子进程分离开来,就要用到一个系统调用 exec 族函数,这是读取另一个程序文件,并在当前的进程空间执行。当我们创建了一个进程之后,通常将子进程替换成新的进程映象,这可以用 exec 系列的函数来进行,且新进程与原进程有相同的 PID。

参考 Linux下exec函数族(execl,execv,execle,execve,execlp,execvp,fexecve)的使用和对比leumber的博客-CSDN博客exec和execv,exec系列函数(execl,execlp,execle,execv,execvp)使用_gauss的博客-CSDN博客 这个里面有每个 API 的使用例子,exec和execv区别 - CSDN。

  • 各个 API 原型:#include <unistd.h>

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

    int execlp(const char *file, const char *arg, ...);

    int execle(const char *path, const char *arg, ..., char * const envp[]);

    int execv(const char *path, char *const argv[]);

    int execvp(const char *file, char *const argv[]);

    int execve(const char *path, char *const argv[], char *const envp[]);

    传入参数:path 参数表示你要启动程序的名称包括路径名;file 参数表示 要启动的 程序 / 文件 的文件名(系统从环境变量 PATH 里面寻找该程序,因此不用带路径全名);arg 参数表示启动程序所带的参数,一般第一个参数为要执行命令名,不是带路径且arg必须以NULL结束;

    返回值:成功返回0,失败返回-1。

  • exec 族函数名中 l 表示列表 list,v 表示数组。

    • execl、execlp、execle 将新程序的每个命令行参数都以一个单独的参数,这种参数列表以NULL结尾。

    • execv、execvp、execve 和 fexecve 则应先构造一个指向各参数的指针数组,然后将该数组地址作为参数传入。

  • exec 族函数名中 p 结尾表示函数第一个参数取 filename。

    • execlp、execvp与其他函数不同就是第一个参数取filename,用 PATH 环境变量寻找可执行文件,filename 既可以是文件路径加程序名,也可以是PATH环境变量下的 /sbin: /bin: /usr/bin: 即shell命令。

  • exec 族函数名中 e 结尾表示可以传递环境表信息 environ。

    • execle、execve、fexecve 可以传递一个指向环境字符串指针数组的指针。

// process.c
#include<stdio.h>
#include<unistd.h>
​
int main()
{int pid = fork();if(pid == 0){execv("./test.o",NULL);  // test.o是一个经过编译的c语言文件,这里记得要放test.o的绝对路径}printf("This is parent process\n");return 0;
}
​
// test.c
#include<stdio.h>
int main()
{printf("This is child process");return 0;
}
​
// 运行结果如下所示
This is parent process
This is child process

exec 族函数的使用例子

/* exec.c */
#include <unistd.h>
main()
{char *envp[]={"PATH=/tmp","USER=lei","STATUS=testing",NULL}; /* 数组 必须以 NULL 做结尾 */char *argv_execv[]={"echo", "excuted by execv", NULL};char *argv_execvp[]={"echo", "executed by execvp", NULL};char *argv_execve[]={"env", NULL};if(fork()==0)if(execl("/bin/echo", "echo", "executed by execl", NULL)<0) /* 路径全名,传入参数写全,以NULL结尾 */perror("Err on execl");if(fork()==0)if(execlp("echo", "echo", "executed by execlp", NULL)<0) /* 只写执行程序的文件名,系统会去 PATH 环境变量寻找 */perror("Err on execlp");if(fork()==0)if(execle("/usr/bin/env", "env", NULL, envp)<0) /* 可传入环境变量 */perror("Err on execle");if(fork()==0)if(execv("/bin/echo", argv_execv)<0) /* 带 v 的就是 传入参数 以 指针数据(字符串数据)传入,其它与上面的 API 一样 */perror("Err on execv");if(fork()==0)if(execvp("echo", argv_execvp)<0)perror("Err on execvp");if(fork()==0)if(execve("/usr/bin/env", argv_execve, envp)<0)perror("Err on execve");
}
​
/* 执行 ./exec 后返回:
executed by execl
PATH=/tmp
USER=lei
STATUS=testing
executed by execlp
excuted by execv
executed by execvp
PATH=/tmp
USER=lei
STATUS=testing
*/

exec 族函数 的常见的错误返回(exec 返回 -1,并设置 errno 为以下的值):

  • 找不到文件或路径,此时errno被设置为ENOENT;

  • 数组argv和envp忘记用NULL结束,此时errno被设置为EFAULT;

  • 没有对要执行文件的运行权限,此时errno被设置为EACCES。

  • 等等,有很多种类型的错误返回。

更多要注意的地方:

  • 实际操作时, 一般在 调用 exec 函数之前 关闭所有已经打开的文件。也可以通过 fcntl() 让内核去完成。

进程的退出 return/exit()

参考 进程的几种退出机制_Leon_George的博客-CSDN博客,操作系统 — 进程的退出(exit)Dawn_sf的博客-CSDN博客exit 进程退出, exit函数及与return的区别_panda19881的博客-CSDN博客。

几种退出方式

  • 正常退出

    1. 在main()函数中执行return(renturn执行完后把控制权交给调用函数)。

    2. 调用exit()函数(exit执行完后把控制权交给系统)。

    3. 调用_exit()函数(同上)。

  • 异常退出

    1. 调用abort函数(exit是正常终止进程,abort是异常终止,突出在异常)。

    2. 进程收到某个信号,而该信号使程序终止。

exit()_exit()区别exit() 函数是在_exit()函数之上的一个封装,其会调用_exit(),并在调用之前先刷新流(stdin, stdout, stderr ...),即 把文件缓冲区的内容写回文件。exit在头文件stdlib.h中声明,而_exit()声明在头文件unistd.h中声明。使用 exit() 更安全。exit中的参数exit_code为0代表进程正常终止(即 exit(0);),若为其他值表示程序执行过程中有错误发生。

exitreturn 区别:如果return 或者exit出现在main函数中,两者的作用是一样(即 return 0;exit(0); 一样)。如果return出现在子程序中表示返回(仅意味着 退出/结束 其所在的函数或子进程),而exit出现在子进程中表示终止子进程。

但不管是哪种退出方式,系统最终都会执行内核中的某一代码。这段代码用来关闭进程所用已打开的文件描述符,释放它所占用的内存和其他资源。

父子进程终止的先后顺序不同会产生不同的结果

  • 子进程先于父进程终止,而父进程调用了wait函数:子进程退出并被父进程回收(好)

此时父进程会等待子进程结束。

  • 父进程先于子进程终止:子进程变孤儿进程(中)

此种情况就是我们前面所用的孤儿进程。当父进程先退出时,系统会让init进程接管子进程 。孤儿进程会被过继给init进程,init进程也就成了该进程的父进程。init进程负责该子进程终结时调用wait函数。init进程会在有子进程退出时调用wait函数。

  • 子进程先于父进程终止,而父进程又没有调用wait函数:子进程变僵死进程(坏)

此种情况子进程进入僵死状态,并且会一直保持下去直到系统重启。子进程处于僵死状态时,内核只保存进程的一些必要信息以备父进程所需。此时子进程始终占有着资源,同时也减少了系统可以创建的最大进程数。

因此要尽量避免这种情况发生,否则 僵死进程 不但占用着资源,而且可能越积累越多。一个糟糕的程序也完全可能造成子进程的退出信息滞留在内核中的状况(父进程不对子进程调用wait函数),这样的情况下,子进程成为僵尸(zombie)进程。当大量僵尸进程积累时,内存空间会被挤占。

僵死状态:一个已经终止、但是其父进程尚未对其进行善后处理(获取终止子进程的有关信息,释放它仍占有的资源)的进程被称为僵死进程(zombie)。

僵死进程 和 孤儿进程 的释义:

总结:这三种进程退出,最好情况是 父进程调用 wait 正常回收 终止的子进程,其次是 父进程提前终止 / 子进程称为孤儿进程 / 子进程被过继给 init 进程 / init进程会在有子进程退出时调用wait函数,最坏是 父进程没有调用 wait 而子进程退出 / 子进程成为 僵死进程。

注册 进程退出时 调用的函数:#include <stdlib.h> int atexit (void (*function)(void)); ,注册的函数会在 调用 exit(); 或 从 main 退出 或 收到终止进程的信号(SIGTERM 或 SIGKILL)时候被调用一次。注册的函数内不能再调用 exit(),否则会引起无限递归。

进程的阻塞 wait()

处于运行状态的进程,在其运行过程中期待某一事件的发生,如等待键盘的输入、等待磁盘数据传输完成、等待其他进程发送信息,当被等待的时间未发生时,由进程自己执行阻塞原语,使自己的运行状态变为阻塞态。

即 休眠/阻塞 来 等待 信号(signal)父进程等待子进程退出(再回收其资源)

p.s 对于 等待 信号(signal)在 下面 进程间通讯(IPC) 里的 信号(Signal) 一节介绍。这里只讨论 父进程等待子进程退出(再回收其资源)。

引用 exec和execv区别 - CSDN(回过头意识到,我就相当于把格式整理地更漂亮了一遍。。)。

父进程 wait 阻塞来等待子进程退出(再回收其资源)

子进程终止时,内核会向其父进程 发送 SIGCHILD 信号。

当子进程终结时,它会通知父进程,并清空自己所占据的内存,并在内核里留下自己的退出信息(exit code,如果顺利运行,为0;如果有错误或异常状况,为>0的整数)。父进程在得知子进程终结时,有责任对该子进程使用wait系统调用(否则子进程就成了僵死进程,应当尽量避免)。这个wait函数能从内核中取出子进程的退出信息,并清空该信息在内核中所占据的空间。

#include <sys/types.h> /* 提供类型pid_t的定义 */ 
#include <sys/wait.h> 
pid_t wait(int *status);

进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。

参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针。但如果我们对这个子进程是如何dump掉的毫不在意,只想把这个僵尸进程消灭掉,我们就可以设定这个参数为NULL。

如果成功,wait会返回被收集的子进程的进程ID,如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被置为ECHILD。

例子:

/* wait1.c */
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
main()
{pid_t pc, pr;pc = fork();if(pc < 0)            /* 如果出错 */printf("error ocurred!\n");else if(pc == 0){    /* 如果是子进程 */ printf("This is child process with pid of %d\n",getpid());sleep(10);       /* 睡眠10秒钟 */}else{                 /* 如果是父进程 */pr = wait(NULL); /* 在这里等待子进程的退出,并不在意其 exit 退出返回值 */printf("I catched a child process with pid of %d\n"),pr);}exit(0);
}

对 退出返回值做进一步分析的宏:

  • WIFEXITED(status) 这个宏用来指出子进程是否为正常退出的,如果是,它会返回一个非零值。(请注意,虽然名字一样,这里的参数status并不同于wait唯一的参数--指向整数的指针status,而是那个指针所指向的整数)

  • WEXITSTATUS(status) 当WIFEXITED返回非零值时,我们可以用这个宏来提取子进程的返回值,如果子进程调用exit(5)退出WEXITSTATUS(status)就会返回5;如果子进程调用exit(7),WEXITSTATUS(status)就会返回7。请注意,如果进程不是正常退出的,也就是说,WIFEXITED返回0,这个值就毫无意义。

另一篇文章再讲一遍:

pid_t wait(int *status);,wait 系统调用会使父进程阻塞直到一个子进程结束或者是父进程接受到了一个信号。如果没有父进程没有子进程或者他的子进程已经结束了 wait 回立即返回。成功时(因一个子进 程结束)wait 将返回子进程的 ID,否则返回-1,并设置全局变量 errno。status 是子进程的 退出状态。子进程调用 exit / _exit 或者是 return 来设置这个值。为了得到这个值 Linux 定 义了几个宏来测试这个返回值。

另一篇文章具体讲几个宏的区别:

#include <sys/wait.h>
int WIFEXITED (status);
int WIFSIGNALED (status);
int WIFSTOPPED (status);
int WIFCONTINUED (status);
int WEXITSTATUS (status);
int WTERMSIG (status);
int WSTOPSIG (status);
int WCOREDUMP (status);

waitpid 阻塞等待特定 pid 的子进程退出

#include <sys/types.h> /* 提供类型pid_t的定义 */
#include <sys/wait.h>
pid_t waitpid(pid_t pid,int *status,int options);

waitpid() 参数说明:

  • pid:

    pid>0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。

    pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。

    pid=0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。

    pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。

  • status:承接子进程的 exit 退出返回值,不用时填 NULL。

  • options:options提供了一些额外的选项来控制waitpid,目前在Linux中只支持WNOHANG和WUNTRACED两个选项,这是两个常数,可以用 "|" 运算符把它们连接起来使用,如果我们不想使用它们,也可以把options设为0(不用时设为 0)。

    WNOHANG:即使没有子进程退出,它也会立即返回,不会像wait那样永远等下去。

    WUNTRACED:涉及到一些跟踪调试方面的知识,加之极少用到,有兴趣的读者可以自行查阅相关材料。

  • 返回值:

    waitpid的返回值比wait稍微复杂一些,一共有3种情况:

    1. 当正常返回的时候,waitpid返回收集到的子进程的进程ID;

    2. 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;

    3. 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;

    当pid所指示的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid就会出错返回,这时errno被设置为ECHILD。

    子进程的结束状态返回后存于status,底下有几个宏可判别结束情况:

    • WIFEXITED(status)如果子进程正常结束则为非0值。

    • WEXITSTATUS(status)取得子进程exit()返回的结束代码,一般会先用WIFEXITED 来判断是否正常结束才能使用此宏。

    • WIFSIGNALED(status)如果子进程是因为信号而结束则此宏值为真。

    • WTERMSIG(status)取得子进程因信号而中止的信号代码,一般会先用WIFSIGNALED 来判断后才使用此宏。

    • WIFSTOPPED(status)如果子进程处于暂停执行情况则此宏值为真。一般只有使用WUNTRACED 时才会有此情况。

    • WSTOPSIG(status)取得引发子进程暂停的信号代码。

其它 API

头文件:

#include <unistd.h>;
#include <pwd.h>;
#include <sys/types.h>;
  • pid_t getpid(void);,得到当前进程 pid。

    pid_t getppid(void);,得到当前进程的父进程的 pid。

  • 进程的实际用户、有效用户相关概念:

  • 改变实际用户 API:

  • 改变有效用户 API:

  • 得到用户 ID:

    uid_t getuid(void);uid_t geteuid(void);,分别得到进程的所有者用户的 ID 和 有效用户 ID。 gid_t getgid(void);git_t getegid(void);,分别得到组 ID 和有效组 ID。

  • 得到用户的更多信息:

    struct passwd { /* 这个结构体在 types.h 里面定义 */char *pw_name; /* 登录名称 */ char *pw_passwd; /* 登录口令 */ uid_t pw_uid; /* 用户 ID */ gid_t pw_gid; /* 用户组 ID */ char *pw_gecos; /* 用户的真名 */ char *pw_dir; /* 用户的目录 */ char *pw_shell; /* 用户的 SHELL */ 
    }; 
    struct passwd *getpwuid(uid_t uid); /* 返回 用户 ID 为 uid 的 用户信息的 struct passwd 结构体指针*/
  • sleep(x);,阻塞/延时 x 秒时间。

  • strerror(errno),返回一个指定的错误号的错误信息的字符串。

  • etc.

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

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

相关文章

【Qt QAxObject】使用 QAxObject 高效任意读写 Excel 表

1. 用什么操作 Excel 表 Qt 的官网库中是不包含 Microsoft Excel 的操作库&#xff0c;关于对 Microsoft Excel 的操作库可选的有很多&#xff0c;包含基于 Windows 系统本身的 ActiveX、Qt Xlsx、xlsLib、LibXL、qtXLS、BasicExcel、Number Duck。 库.xls.xlsx读写平台Qt Xls…

JVM的故事——类文件结构

类文件结构 文章目录 类文件结构一、概述二、无关性基石三、Class类文件的结构 一、概述 计算机是只认由0、1组成的二进制码的&#xff0c;不过随着发展&#xff0c;我们编写的程序可以被编译成与指令集无关、平台中立的一种格式。 二、无关性基石 对于不同平台和不同平台的…

docker命令学习

docker vscode插件出现的问题 docker命令 docker images &#xff08;查看所有的镜像&#xff09; docker ps -a &#xff08;查看所有的容器&#xff09; docker ps &#xff08;查看运行的容器&#xff09; docker run imageID docker run --gpus all --shm-size8g -it imag…

网络安全体系架构介绍

网络安全体系是一项复杂的系统工程&#xff0c;需要把安全组织体系、安全技术体系和安全管理体系等手段进行有机融合&#xff0c;构建一体化的整体安全屏障。针对网络安全防护&#xff0c;美国曾提出多个网络安全体系模型和架构&#xff0c;其中比较经典的包括PDRR模型、P2DR模…

Revit SDK 介绍:AvoidObstruction 避免碰撞

前言 这个例子介绍如何让碰撞在一起的管道避免碰撞&#xff0c;即对管道进行调整。 内容 调整前&#xff1a; 调整后&#xff1a; 从结果来看&#xff0c;所有的碰撞都被调整了。作为一个例子&#xff0c;不会去考虑是否合理&#xff0c;仅仅是展示了一下 Revit API 的能…

我开课了!《机器学习》公益课9月4日开课

我是黄海广&#xff0c;大学老师&#xff0c;我上的一门课叫《机器学习》&#xff0c;本科生学机器学习有点难&#xff0c;但也不是没有可能&#xff0c;我在摸索中&#xff0c;设计适合本科生的机器学习课程&#xff0c;写了教材&#xff0c;录了视频&#xff0c;做了课件。我…

手写Openfeign实现原理——极简版

文章目录 前言Openfeign实现思路前期准备基本依赖项 开始实现自定义注解自定义代理类定义创建代理对象的工厂InstantiationAwareBeanPostProcessor实现bean的注入OpenInstantiationAwareBeanPostProcessor 自定义 feign接口启动类小结 踩坑记录ImportComponent和Configuration区…

HTTP与SOCKS5的区别对比

在互联网世界中&#xff0c;服务器是一种重要的工具&#xff0c;可以帮助我们提高网络安全性等。今天&#xff0c;我们将重点关注两种常见的技术&#xff1a;HTTP和SOCKS5。让我们深入了解它们的工作原理、用途和优缺点&#xff0c;并通过Python代码示例学习如何使用它们。 HT…

一文了解tcp/ip协议的运行原理

接触代理ip的人都了解https/sock5等ip协议&#xff0c;那么TCP/IP 协议又是什么&#xff1f; 一、什么是TCP/IP 协议&#xff1f; TCP/IP 协议实际上是一系列网络通信协议的一个统称&#xff0c;他负责具体的数据传输工作&#xff0c;核心的两个协议包括TCP以及IP&#xff0c…

Unity3D 如何在ECS架构下,用Unity引擎进行游戏开发详解

前言 Unity3D是一款强大的游戏引擎&#xff0c;它提供了丰富的功能和工具&#xff0c;可以帮助开发者快速构建高质量的游戏。而Entity Component System&#xff08;ECS&#xff09;是Unity3D中一种新的架构模式&#xff0c;它可以提高游戏的性能和可扩展性。本文将详细介绍在…

Flink 如何定位反压节点?

分析&回答 Flink Web UI 自带的反压监控 —— 直接方式 Flink Web UI 的反压监控提供了 Subtask 级别的反压监控。监控的原理是通过Thread.getStackTrace() 采集在 TaskManager 上正在运行的所有线程&#xff0c;收集在缓冲区请求中阻塞的线程数&#xff08;意味着下游阻…

Redis数据结构总结

Redis 是一款开源的&#xff0c;内存中的数据结构存储系统&#xff0c;它可以用作数据库、缓存和消息代理。Redis 支持多种类型的数据结构&#xff0c;如字符串&#xff08;String&#xff09;、哈希&#xff08;Hashes&#xff09;、列表&#xff08;Lists&#xff09;、集合&…

vue3的面试题

ref里面放对象发生的事情 ref只会对对象的属性进行响应式转换&#xff0c;而不会对对象的原型链上的属性进行转换。如果需要对对象的原型链上的属性进行响应式转换&#xff0c;可以使用reactive函数。 toRefs的适用场景&#xff1f; toRefs是Vue 3中的一个响应式API&#xf…

使用环境中的视觉地标和扩展卡尔曼滤波器定位移动机器人研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

SQL高阶语句

目录 1、概念 1.1、概述 1.2、常见的MySQL高阶语句的概念&#xff1a; 1.3、 SQL高阶语句的作用 2、常用查询 2.1、按关键字排序 2.1.1、概述和作用 2.1.2、 &#xff08;1&#xff09;语法 2.1.3、模板表&#xff1a;ky30 ​编辑2.1.4、分数按降序排列 2.1.5、ORDER…

Kafka环境搭建与相关启动命令

一、Kafka环境搭建 点击下载kafka_2.11-2.3.1.tgz文件链接 1、上传kafka_2.11-2.3.1.tgz&#xff0c;解压kafka_2.11-2.3.1.tgz&#xff0c;得到kafka_2.11-2.3.1文件夹 1&#xff09;上传 #使用mobaxterm将 kafka_2.11-2.3.1.tgz 传入tools文件夹 #用下面代码进入tools文件…

SPSS统计作图教程:频率多边形

SPSS统计作图教程&#xff1a;频率多边形 1、问题与数据 某研究者想了解某数据集中最大携氧能力&#xff08;VO2max&#xff09;是否服从正态分布&#xff0c;部分数据如图1。研究者应如何绘图查看呢&#xff1f; 图1 部分数据 2、对问题的分析 研究者想绘图展示最大携氧能…

深入理解 JVM 之——Java 内存区域与溢出异常

更好的阅读体验 \huge{\color{red}{更好的阅读体验}} 更好的阅读体验 本篇为深入理解 Java 虚拟机第二章内容&#xff0c;推荐在学习前先掌握基础的 Linux 操作、编译原理、计算机组成原理等计算机基础以及扎实的 C/C 功底。 该系列的 GitHub 仓库&#xff1a;https://github…

go学习part20(1)反射

283_尚硅谷_反射基本介绍和示意图_哔哩哔哩_bilibili 1.介绍 1&#xff09;基本数据类型的类型和类别一致&#xff0c;但是结构体等不一样。 2)反射的例子&#xff08;桥连接&#xff0c;序列化&#xff09; 序列化指定tag&#xff0c;会反射生成tag字符串 3&#xff09;refl…

Vue在表格中拿到该行信息的方式(作用域插槽-#default-scope-解决按钮与行点击的顺序问题)

遇到的问题 在做表格的时候&#xff0c;表格是封装好了的&#xff0c;用于展示数据。如果想给单行增加按钮&#xff0c;可以单独写一列存放按钮&#xff0c;最基本的需求是&#xff0c;点击按钮后要拿到数据然后发起请求。 且Vue的element-plus&#xff0c;当我们点击按钮之后…