GroupRank:分组重排,让大模型在 RAG 中又快又好地“挑重点”

核心内容摘要

Fish-Speech 1.5实战:如何用AI生成自然流畅的语音
THINKSAFE:推理模型的自生成安全对齐

解锁抢购工具成功率提升:2024最新京东自动化抢购全攻略

进程概念进程指的是正在运行中的程序程序一旦运行就是一个进程进程是一个动态概念并非一个静止的文件。

程序一般指的是可执行程序或可执行文件其本质还是一个文件是一个静态的概念文件一般存储在磁盘中。

进程号(PID)每一个进程号都唯一标识一个进程。

main函数是程序的入口在执行main函数前还会执行其他代码这段代码叫做引导程序引导程序不需要我们来编写在程序编译链接时链接器会自动将引导程序加载到程序中。

进程终止进程终止分为正常终止和异常终止正常终止指的是在程序进行了资源清理关闭打开的文件描述符等清理工作后退出异常终止指的是没有进行清理工作后就退出。

这些清理工作指的是由系统内核来完成的清理工作。

exit和_exit的区别exit是库函数_exit是系统调用两者需要包含不同的头文件。

exit和_exit在退出前所执行的操作不同exit在退出前不仅会执行_exit的相关操作还会执行调用进程终止处理函数和刷新IO缓存操作如下图所示进程终止处理函数由atexit函数来指定该函数的作用是使进程注册进程处理函数参数是指向进程终止处理函数的函数指针进程处理函数会在进程调用exit函数退出时执行并且进程处理函数可以有多个当有多个进程处理函数时其调用顺序与进程处理函数的注册顺序相反。

exit和return的区别exit是库函数而return是编程语言的语句。

exit函数执行后程序就会终止退出return语句执行后程序会返回到上层调用有上层调用来继续执行后终止。

注意return函数退出后程序也会执行进程终止处理函数也会刷新IO缓存实际上在return返回后其上层程序会调用到exit函数。

exit和abort的区别exit退出的程序是正常退出而abort退出的程序是异常退出。

abort退出的进程并不会执行进程终止处理函数和刷新IO缓存。

进程中的环境变量进程中的环境变量指的是在进程运行环境中定义的变量。

环境变量类似于全局变量可以在程序的任何一个位置使用只需要声明即可但是又不是全局变量因为创建子进程后子进程会继承父进程的环境变量具有继承性。

环境变量的存储形式是一个没有类型的字符串其存储在一个字符串数组中即环境表。

环境变量的定义形式是namevalue的形式name即环境变量的名称value即环境变量的值。

env命令可以在终端显示出当前所有的环境变量echo可以显示出某个指定的环境变量的值export可以导出环境变量即添加或修改一个环境变量unset可以删除指定的环境变量。

常见的环境变量有PATH指定了可执行程序的搜索路径、HOME指定了当前用户的家目录、LOGNAME指定了登录的用户名、HOSTNAME指定主机名、SHELL指定了shell解析器、PWD指定了当前的工作目录。

在程序中获取环境变量首先可以通过environ变量通过extern声明该environ变量该变量是一个字符串数组指针即环境表指针。

也可以通过main函数的第三个参数来获取我们常见的main函数的形式有int main(void)、int main(int argc,char *argv[])还有 int mian (int argc,char* argv[],char* env[])最后一种形式的main函数的第三参数接收的就是环境表指针就可以用来获取到环境变量。

也可以通过getenv函数该函数的作用是获取指定环境变量的值参数是环境变量名称的字符串指针返回值是对应环境变量名的值字符串需要注意返回的字符串指针就是环境变量所在的地址不应该修改该指针指向的内容不然会影响到环境变量。

环境变量的添加修改和删除putenv函数的作用是添加/修改环境变量参数是以环境变量定义格式的字符串即namevalue注意putenv函数中是对字符串的直接引用而并没有拷贝到一个新的内存中所以使用该函数添加了环境变量后字符串的值不应该被修改若修改了会直接影响环境变量、使用该函数添加一个同变量名的环境变量其总是会覆盖原来的环境变量的值。

setenv函数的作用也是添加/修改环境变量第一个参数是环境变量的名称第二个参数是环境变量的值第三个参数是一个整数标志位用来指定当环境变量的名称一样时是否要覆盖原来的环境变量非0值表示覆盖0值表示不覆盖。

在执行程序时要添加环境变量则需要在执行程序命令位置前按照环境变量的定义格式输入所要添加的环境变量即可。

unsetenv函数的作用时删除一个环境变量参数是要删除的环境变量的名称字符串。

清空环境变量清空环境变量可以直接将environ变量即指向环境表的指针直接赋值为NULL。

clearenv函数的作用是清空环境变量没有参数。

进程的创建所有的进程都是由其父进程创建在终端下运行的程序进程是由shell进程创建而来shell进程就是shell解析器所有进程的祖先进程是init进程init进程的进程号为1。

所有的进程都是独立的运行在各自的进程空间中互不影响提高数据的安全性和可靠性每个进程被创建出来就是一个新的进程拥有自己的进程号自己的PCB。

fork函数的作用就是创建一个子进程函数没有参数。

调用一次fork函数会产生两次返回值若返回值为0则是子进程的返回值返回值为非0值则是父进程的返回值这次的返回值就是创建的子进程的进程号。

使用fork函数创建的子进程几乎完全拷贝了父进程的数据段、代码段、栈堆等是父进程的一个副本。

子进程执行的程序和父进程执行的程序是同一个但是要注意的是子进程执行的是fork返回后的程序之前的程序子进程不会执行。

fork函数的应用场景创建一个和父进程几乎一样的子进程或创建子进程后让其执行新的程序。

fork函数的一个缺陷在绝大多数情况下创建子进程的应用场景都是第二种即创建出来子进程后就要让其来执行一个新的程序这是我们可以通过一些手段使新的程序加载到子进程中替换掉从父进程中复制来的代码段、数据段、堆栈等所以先前fork函数的拷贝工作就是无效工作导致效率比较低。

vfork函数的作用也是创建一个子进程其参数和返回值与fork函数一样其是为了解决fork函数应用场景二中效率比较低的问题引出的该函数创建的子进程在没有终止或执行新的程序之前都和父进程共享地址空间所以需要注意不要在子进程还没有从父进程分离出来之前调用对应的函数或修改某些值不然会对父进程造成一些影响。

不够再现在fork函数经过优化效率已经有了很大的提升一般我们还是使用fork函数来创建一个子进程。

父子进程的文件共享和竞争关系因为创建的子进程几乎完全拷贝了父进程所以父进程所打开的文件描述符也被子进程复制过去了所以父子进程就会出现文件共享的情况此时的文件描述符也是一个复制关系所以其指向同一个文件表具体如下图所示所以该文件表中的内容也是共享的如文件偏移量即读写指针位置若使用该文件描述符来对一个文件进行写操作时其写操作是接绪写。

父子进程间的竞争关系指的是父进程调用fork先返回执行后面的程序还是子进程调用fork先返回然后执行后面的程序结论就是不确定但是绝大多数情况下都是父进程先返回然后子进程后返回。

但是对于vfork函数则该函数保证了子进程先返回然后执行程序就算是使用sleep函数使子进程进入睡眠状态父进程也不能先于子进程执行而是会一直阻塞等待子进程执行exec函数来执行新的程序或者子进程终止后才会执行。

监视子进程wait函数的作用是用来监视子进程的状态改变参数是获取到的子进程终止时的状态信息的整型指针返回值若成功则返回终止的子进程进程号失败返回-1。

该信息可以通过对应的宏来进行转化然后得到其中所含有的子进程终止后对应的信息WIFEXITED若子进程正常退出返回true异常退出返回false即1和

WEXITSTATUS表示子进程退出时的状态信息即使用return或exit等函数中所填入的值、WIFSIGNALED若子进程由信号终止则返回true否则返回false即1和

WTERMSIG返回子进程是由哪个信号终止的信号编号WCOREDUMP若子进程终止时产生了核心转储文件则返回非0值没有则返回0值。

wait函数不仅有监视子进程终止时状态信息的功能还会对终止的子进程进行回收且一个wait函数只能回收一个终止的子进程且不能指定某个子进程且只能监视子进程终止时的状态信息而不能监视子进程被信号停止或恢复时状态改变信息。

waitpid函数的作用也是用来监视子进程的状态改变第一个参数如果大于0时是需要监视的子进程进程号、等于-1时是监视所有子进程、等于0则是监视于父进程同一进程组的进程第二个参数是子进程状态改变时的状态信息同wait函数的参数第三个参数是用来指定该函数作用标志位即一些宏值。

这些宏值有WNOHANG表示非阻塞等待若子进程发生状态改变则立即返回0不会阻塞等待可以通过循环来轮询查询是否有子进程状态改变然后回收、WUNTRACED除了返回因信号终止的的子进程状态改变信息外还会返回因信号停止的子进程的状态改变信息、WCONTINUED除了返回因信号终止的子进程状态改变信息还会返回因信号恢复的子进程的状态改变信息。

返回值同wait函数除了0值。

异步回收机制当子进程状态改变即子进程终止或停止或恢复时内核会向该子进程的父进程发送一个SIGCHLD信号所以我们可以给父进程绑定一个信号处理函数来进行对子进程终止时的异步回收。

若子进程正在运行一个信号处理函数则进程会自动将该信号添加到信号掩码中则后续发送相同的信号给父进程该信号就会被放在阻塞信号集中等待若这时有两个子进程几乎同时终止然后连续发送了两个这个信号给父进程因为信号集中不能存放相同的信号所以有一个信号就会丢失导致有一个子进程未被父进程回收出现资源占用要解决这个问题就可以使用waipid中的宏WNOHANG来进行非阻塞轮询回收子进程。

具体程序可以参考如下#include stdio.h #include stdlib.h #include unistd.h #include sys/types.h #include sys/wait.h #include signal.h //signal handle void wait_sig(int sig) { printf(recycle child process\n); while(waitpid(-1,NULL,WNOHANG)

{ continue; } } int main(int argc, char *argv[]) { //register signal handle struct sigaction act {0}; act.sa_handler wait_sig; int ret sigaction(SIGCHLD,act,NULL); if(-1 ret) { perror(sigaction error); return 1; } // create child process switch (fork()) { case -1: perror(fork error); return 1; case 0: printf(I am child process!\n); printf(child process id%d\n, getpid()); exit(

; default: sleep(

; printf(I am parent process!\n); int status; while(

{ sleep(

; } return 0; } }

孤儿进程及僵尸进程若父进程先于子进程终止则子进程就成为了孤儿进程此时init进程就成为了该孤儿进程的父进程来对该孤儿进程终止时进行回收。

若子进程先于父进程终止父进程还没有回收该子进程则此时的子进程就变成了僵尸进程。

该进程不能被信号终止因为该进程本来就已经终止了若系统中存在大量的僵尸进程则最终会占满内核进程表影响新进程的创建要想彻底清除僵尸进程则可以杀死父进程或者等待父进程终止。

父进程终止后还没有对该子进程进行回收则此时init进程就会成为这些僵尸进程的父进程来回收这些僵尸进程此时这些进程才被彻底清除了。

执行新的程序execve函数的作用是让程序执行新的程序。

第一个参数是需要执行的新的程序的路径可以是绝对路径也可以是相对路径。

第二个参数是传递给新程序的main函数中第二个参数的字符串数组第三个参数是传递给新程序的环境变量字符串数组。

exec族库函数的作用也是让程序执行新的程序主要分为两类如下execl、execlp、execleexecv、execvp、execvpe。

第一类和第二类的区别主要为第二个参数其中第一类的函数的第二个参数也是传递给main函数的第二个参数的字符串不过这些字符串是罗列出来的以NULL结尾其中execl和execlp的区别主要在第一个参数execl是要执行的程序的路径execlp函数的第一个参数则可以是要执行的程序的程序名这使得使用该函数来执行shell命令很方便函数会自动在系统路径下寻找该程序当然也可以使用路径。

其中execle函数则是比这两个函数多了一个环境变量字符串数组参数。

第二类函数之间的区别于此类似。

详细如下图所示system函数的作用是用来在程序中执行shell命令参数就是shell命令字符串。

该函数可以在参数中执行想要执行的shell命令只需要填入和在终端中写入的shell命令一样的字符串即可该函数内部实际上是调用了fork函数execl函数waitpid函数来进行函数功能的实现的所以效率较低。

进程状态与进程关系进程状态有以下6种运行状态(R)运行状态或可执行状态包括正在运行的程序或在等待队列中的程序实际上包含了两种状态即运行态或就绪态处于就绪态的进程一旦抢到CPU时间片就可以立即进入到运行态运行、停止状态(T)被信号停止的进程当该进程被恢复信号恢复后就会进入到运行状态等待运行、可中断睡眠状态(S)在该状态下的进程正在等待某一资源来唤醒该进程若给该进程发送一个信号该进行也会被唤醒、不可中断睡眠状态(D)在该状态下的进程也是正在等待获取到某一资源来唤醒该进程且向在该状态下的进程发送信号该进程也不会被唤醒、僵尸态(Z)该状态就是进程终止了但是父进程还没有对该进程进行回收所产生的一种状态、死亡态(X)该状态非常短暂命令通常捕捉不到通常指的是僵尸进程将要被回收的前一个状态。

进程间状态的转化具体如下图所示进程间的关系子进程和父进程之间有着父子进程关系。

进程组是一个或多个进程的集合通常父子进程会处于同一个进程组中每个进程组都有一个进程组ID即PGID来标识getpgrp函数的作用是用来获取本进程的进程组ID无参数返回值是获取到进程组ID。

getpgid函数的作用是来获取指定进程的进程组ID参数是进程号返回值是获取到的进程组ID。

setpgid函数的作用是用来将指定进程移动到指定进程组中前提是该进程与该进程组在同一会话中当其中的参数相等时表示由该进程创建一个进程组且该进程为进程组组长进程且当参数都是0时等价于setpgrp函数。

会话是一个或多个进程组的集合每一个会话也有着一个会话ID来标识。

setsid函数的作用是创建一个新的会话调用该函数的进程不能是组长进程即不能是父进程且调用了该函数会创建一个新的进程组和新的会话该进程作为进程组组长和会话的会话首领对于一个会话最多只能有一个前台进程组。

getsid函数的作用是获取指定进程的会话ID参数是进程号当传入0时表示是获取本进程的进程号。

若两者进程间没有以上三个关系则表示这两个进程无关系。

父进程信号处理机制对子进程的影响这里所说的影响指的是子进程有没有继承到父进程这些信号处理机制。

若父进程注册了信号处理函数则子进程也会继承到父进程的信号处理函数并且当捕获到对应的信号后会执行信号处理函数但是当子进程执行了新的程序之后就不会执行信号处理函数了。

父进程的信号掩码也会继承给子进程并且当子进程执行了新的程序之后其信号掩码也会继承给子进程。

不管是使用fork函数还是使用vfork函数以上都是一样的。

守护进程守护进程的两个特征是长期性的守护进程的生命周期是从系统启动开始到系统关闭生命周期长。

无控制终端普通进程都会连接一个控制终端用来进行进行信息的输出显示或者与用户进行交互而守护进程是没有控制终端的。

守护进程的创建步骤

通过fork创建子进程终止父进程

通过setsid创建一个新会话

通过chdir将程序运行目录改为根目录

设置文件权限掩码umask为

使用close来关闭所以的文件描述符

忽略SIGCHLD信号。

具体程序可以参考如下程序#include stdio.h #include stdlib.h #include unistd.h #include signal.h #include sys/types.h #include sys/stat.h int main(int argc, char *argv[]) { //

create child process,terminate parent process int ret fork(); if(-1 ret) { perror(fork error); return 1; } else if(ret

{ exit(

; } else if(ret

{ //

create sid setsid(); //

change dir to root dir if(-1 chdir(/)) { perror(chdir error); return 1; } //

set umask to zero umask(

; //

close all fd for (int i 0; i sysconf(_SC_OPEN_MAX); i) { close(i); } //

ignore SIGCHLD signal if(SIG_ERR signal(SIGCHLD,SIG_IGN)) { perror(signal error); return 1; } while(

{ sleep(

; } } return 0; }

91进-91进应用

百度百家号客服电话人工服务

123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123