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

【Linux】进程控制2——进程等待(waitwaitpid)

1. 进程等待必要性

  • 我们知道,子进程退出,父进程如果不管不顾,就可能造成"僵尸进程”的问题,进而造成内存泄漏
  • 另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。
  • 最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出。
  • 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息

2. 进程等待的方法

2.1 wait方法

在Linux中,wait函数是一个系统调用用于等待子进程的终止并获取其终止状态。该函数的原型如下所示:

#include<sys/types.h>
#include<sys/wait.h>pid_t wait(int*status);返回值:成功返回被等待进程pid,失败返回-1。参数:输出型参数,获取子进程退出状态,不关心则可以设置成为NULL

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

        当子进程终止后,wait函数会返回子进程的进程ID(PID),并将子进程的终止状态存储在指针status指向的变量中。

        status参数是一个指向整型变量的指针,用于存储子进程的终止状态。通过status可以获取子进程的退出状态、终止信号等信息如果不关心终止状态,可以将status设置为NULL。

wait函数返回的PID有以下几种可能的取值:

  • 如果成功等待到一个子进程的终止,返回子进程的PID。
  • 如果调用进程没有子进程,wait函数会返回-1
  • 如果调用进程被一个信号中断,wait函数会返回-1

 如何使用wait进行等待?

  1. 调用wait函数,进程等待子进程的退出。
  2. 当子进程退出后,会变成一个僵尸进程(短暂的存在不会造成什么影响),然后通过wait函数,进程状态从僵尸状态(Z)变成死亡状态(X)。
  3. 如果子进程没有退出,父进程必须阻塞等待,直到子进程变成Z,wait自动回收返回。
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include <cstdlib>
int main()
{pid_t fd = fork();if (fd == 0){sleep(5);std::cout << "child: " << getpid() << std::endl;exit(0);}if (fd > 0){sleep(1);std::cout << "parent: " << getpid() << std::endl;}sleep(50);return 0;
}

子进程退出后由于父进程没有等待回收,子进程变成僵尸进程: 

调用wait后,子进程就会回收释放了:

#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include <cstdlib>
int main()
{pid_t fd = fork();if (fd == 0){sleep(5);std::cout << "child: " << getpid() << std::endl;exit(0);}if (fd > 0){sleep(1);std::cout << "parent: " << getpid() << std::endl;wait(NULL);}sleep(50);return 0;
}

我们再深入理解一下

注:

  当父进程忘了用wait()函数等待已终止的子进程时,子进程就会进入一种无父进程的状态,此时子进程就是僵尸进程.

  wait()要与fork()配套出现,如果在使用fork()之前调用wait(),wait()的返回值则为-1,正常情况下wait()的返回值为子进程的PID.

  如果先终止父进程,子进程将继续正常进行,只是它将由init进程(PID为1)继承,当子进程终止时,init进程捕获这个状态.

  参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针。但如果我们对这个子进程是如何死掉毫不在意,只想把这个僵尸进程消灭掉,(事实上绝大多数情况下,我们都会这样想),我们就可以设定这个参数为NULL,就像下面这样:

pid = wait(NULL);

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

  如果参数status的值不是NULL,wait就会把子进程退出时的状态取出并存入其中, 这是一个整数值(int),指出了子进程是正常退出还是被非正常结束的,以及正常结束时的返回值,或被哪一个信号结束的等信息。

        由于这些信息 被存放在一个整数的不同二进制位中,所以用常规的方法读取会非常麻烦,人们就设计了一套专门的宏(macro)来完成这项工作,下面我们来学习一下其中最常用的两个:

  • 1,WIFEXITED(status) :这个宏用来指出子进程是否为正常退出的,如果是,它会返回一个非零值。

        (请注意,虽然名字一样,这里的参数status并不同于wait唯一的参数——指向整数的指针status,而是那个指针所指向的整数,切记不要搞混了。)

  • 2, WEXITSTATUS(status): 当WIFEXITED返回非零值时,我们可以用这个宏来提取子进程的返回值,如果子进程调用exit(5)退出,WEXITSTATUS(status) 就会返回5;如果子进程调用exit(7),WEXITSTATUS(status)就会返回7。

        请注意,如果进程不是正常退出的,也就是说, WIFEXITED返回0,这个值就毫无意义。

我们看个例子 

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main() {pid_t id = fork();if (id == 0) {// 子进程printf("子进程开始执行\n");sleep(3);printf("子进程执行完毕\n");exit(0);} else if (id > 0) {// 父进程printf("父进程等待子进程终止\n");int status;pid_t child_pid = wait(&status);if (child_pid == -1){perror("wait");exit(1);}if (WIFEXITED(status)) {printf("子进程正常终止,退出状态:%d\n", WEXITSTATUS(status));} else if (WIFSIGNALED(status)) {printf("子进程被信号终止,信号编号:%d\n", WTERMSIG(status));}printf("父进程继续执行\n");} else {perror("fork");exit(1);}return 0;
}

在上面的示例中,父进程通过fork创建了一个子进程。

子进程会执行一段耗时的操作(这里使用sleep模拟),然后退出。

父进程调用wait函数等待子进程的终止,并获取子进程的终止状态。

最后,父进程继续执行。 

2.2.参数status

2.2.1、status 参数是位图结构 

wait 和 waitpid,都有这个 status 参数,如果传递 NULL,则表示不关心子进程的退出状态信息。否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。 

该参数是一个 输出型参数 (即通过调用该函数,从函数内部拿出来特定的数据)。

什么叫输出型参数?就是函数调用结束以后,会将参数的值写到这个变量里。

        换句话说,这个status是用来接收的,本质上不是用来传参的。

        我们把我们的status定义好了之后,放到该函数里,作为参数传递过去,函数调用完后,操作系统就会把status的值自动填充好,然后还给我们。实现的原理很简单,因为其用的是指针,传递的是变量的地址。倘若我们不关心这个status状态,那么直接传递NULL即可。

并且,status 参数是由操作系统填充的!是一个整数,该整数就是下面我们要详细研究的。

也就是说我们下面说的参数status不是wait唯一的参数——指向整数的指针status,而是那个指针所指向的整数

它虽然是一个 int 型整数,但是不能简单地将其看作整型,而是被当作一个 位图结构 看待。

不过,关于 status 我们只需要关心该整数的 低 16 个比特位!

我们不必去关心它的高 16 位,因为凭借低 16 位就足以判断了。

然而,整数的低 16 位,其中又可以分为 最低八位 和 次低八位(具体细节看图):

我们之研究status的低16比特位

最低八位(包括core dump)存储的是终止信号,次低八位存储的是退出状态

 2、次低八位:拿子进程退出码

重点:通过提取 status 的次低八位,就可以拿到子进程的退出码。 

我们需要使用下面的代码来解析:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>int main () 
{pid_t id = fork();if (id == 0) {int cnt = 5;   // 循环5次// childwhile (cnt--) {// 五秒之内运行状态printf("我是子进程,我正在运行... Pid: %d\n", getpid());sleep(1);// 五秒之后子进程终止}exit(233);   // 方便辨识,退出码我们设置为233,这是我们的预期结果}else {printf("我是父进程: pid: %d,我将耐心地等待子进程!\n", getpid());// ***** 使用waitpid进行进程等待int status = 0;  // 接收 waitpid 的 status 参数pid_t ret = waitpid(id, &status, 0);if (ret > 0) {   // 等待成功printf ("等待成功,ret: %d, 我所等待的子进程退出码: %d\n", ret,(status>>8)&0xFF);}}
}

我们说了,status 并不是整体使用的,而是区域性使用的,我们要取其次低八位。

我们可以用 位操作 来完成,将 status 右移八位再按位与上 \textrm{0xFF},即 (status>>8)&0xFF ,就可以提取到 status 的次低八位了。

 

3、 最低七位:提取子进程的退出信号

重点:通过提取 status 的最低七位,就可以拿到子进程的退出信号。

我们的 status 的低八位用于表示处理异常的地方,其中有 1 位是 core dump,我们下面讲。

除去 core dump,剩余七位用于进程中的退出信号,这就是 最低七位。

进程退出,如果异常退出,是因为这个进程收到了特定的信号。

我们虽然还没有开始讲解信号,但是我们前几张就介绍了 kill -9 这样的杀进程操作。

这个 -9 我们当时说了,就是一个信号,发送该信号也确实可以终止进程。

刚才我们讲的 wait/waitpid 和次低八位的时侯,都是关于进程的 正常退出。

如果进程 异常退出 呢?

我们来模拟一下进程的异常退出。

结果:

因为子进程是个死循环,父进程又调了 waitpid,导致父进程一直在 "阻塞式" 地等待子进程。

父进程在等待子进程期间什么都没有干,就搬了张板凳坐在那等子进程死。

信号是可以杀掉进程的,我们现在主动输入 kill -9:

此时我们就成功拿到了子进程的退出信号,9 是因为我们输入的信号就是 9。

此时父进程看到子进程寄了,终于可以不用等了,可以给子进程收尸了

还是那句话,代码跑完结果是什么已经不重要了,我们最关心的是因为什么原因退出的。

当进程收到信号时,就代表进程异常了。进程程出,如果是异常退出,是因为该进程收到了特定的信号。其实除了 9 号信号还有很多信号,输入 kill -l 就可以查看这些;

总结:退出信号代表进程是否异常,退出码代表进程在退出之时代码对还是不对。

4、进程退出的宏

我们今天写的代码,是通过位操作去截 status 得到退出码和退出信号的。

实际上,你也可以不用位操作,因为  已经给我们提供了一些宏供我们直接调用。

它们是 WEXITSTATUS 和 WIFEXITED,在这之前,我们再思考一个问题:

思考:一个进程退出时,可以拿到退出码和退出信号,我们先看谁?

        一旦程序发现异常,我们只关心退出信号,退出码没有任何意义。

        所以,我们先关注退出信号,如果有异常了我们再去关注退出码

WEXITSTATUS 宏用于查看进程的退出码,若非 0,提取子进程退出码。

WEXITSTATUS(status)

WIFEXITED 宏用于查看进程是否正常退出,如果是正常终止的子进程返回状态,则为真。

WIFEXITED(status)

结果是

当然了,如果你压根就不关注推出信息和退出码,你直接把 status 设置为 NULL 就行。

2.3. waitpid方法

waitpid函数是Linux中用于等待指定子进程终止的系统调用

        与wait函数类似,waitpid函数也可以用于获取子进程的终止状态。

#include <sys/types.h>
#inlclude <sys/wait.h>pid_ t waitpid(pid_t pid, int *status, int options);

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

从本质上讲,系统调用waitpid和wait的作用是完全相同的,但waitpid多出了两个可由用户控制的参数pid和options,从而为我们编程提供了另一种更灵活的方式。

下面我们就来详细介绍一下这两个参数:

pid:

从参数的名字pid和类型pid_t中就可以看出,这里需要的是一个进程ID。但当pid取不同的值时,在这里有不同的意义。

  1.   pid>0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。
  2.   pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。
  3.   pid=0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。
  4.   pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。

options:

options提供了一些额外的选项来控制waitpid,目前在Linux中只支持WNOHANG和WUNTRACED两个选项,这是两个常数,可以用"|"运算符把它们连接起来使用,比如:

  ret = waitpid(-1,  NULL,  WNOHANG | WUNTRACED);

如果我们不想使用它们,也可以把options设为0,如:

     ret = waitpid(-1,  NULL,  0);

如果使用了WNOHANG参数调用waitpid,即使没有子进程退出,它也会立即返回,不会像wait那样永远等下去。

而WUNTRACED参数,由于涉及到一些跟踪调试方面的知识,加之极少用到,这里就不多费笔墨了,有兴趣的读者可以自行查阅相关材料。

看到这里,聪明的读者可能已经看出端倪了:wait不就是经过包装的waitpid吗?

没错,察看<内核源码目录>/include/unistd.h文件349-352行就会发现以下程序段:

static inline pid_t wait(int * wait_stat){return waitpid(-1,wait_stat,0);}

返回值和错误

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

  1. 当正常返回的时候,waitpid返回收集到的子进程的进程ID;
  2. 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
  3. 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;

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

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{pid_t pc, pr;pc = fork();if (pc < 0) /* 如果fork出错 */{printf("Error occured on forking.\n");}else if (pc == 0) /* 如果是子进程 */{sleep(10); /* 睡眠10秒 */exit(0);}/* 如果是父进程 */do {pr = waitpid(pc, NULL, WNOHANG); /* 使用了WNOHANG参数,waitpid不会在这里等待 */if (pr == 0) /* 如果没有收集到子进程 */{printf("No child exited\n");sleep(1);}} while (pr == 0); /* 没有收集到子进程,就回去继续尝试 */if (pr == pc){printf("successfully get child %d\n", pr);}elseprintf("some error occured\n");}

父进程经过10次失败的尝试之后,终于收集到了退出的子进程。

因为这只是一个例子程序,不便写得太复杂,所以我们就让父进程和子进程分别睡眠了10秒钟和1秒钟,代表它们分别作了10秒钟和1秒钟的工作。父子进程都有工作要做,父进程利用工作的简短间歇察看子进程的是否退出,如退出就收集它。

提示:可以尝试在最后一个例子中把pr=waitpid(pc, NULL, WNOHANG); 改为pr=waitpid(pc, NULL, 0);或者pr=wait(NULL);看看运行结果有何变化?(修改后的结果使得父进程将自己阻塞,直到有子进程退出为止!)

3.进程的阻塞等待方式

3.1.阻塞等待

  •  如果子进程没有退出,而父进程在进行执行waitpid进行等待,阻塞等待。
  • 大部分IO类的函数例如scanf各种各样的接口,只要涉及IO的或多或少会可能出现阻塞的状态。
  • 现在所用的大部分接口都是阻塞接口(逻辑简单,容易实现)
  •  **阻塞等待(Blocking Wait)**在编程中通常指的是一个线程或进程在等待某个条件满足或某个操作完成之前,会暂停执行其他任务,处于等待状态。这种状态会一直持续,直到等待的条件满足或操作完成,线程或进程才会继续执行后续的任务。在Java中,阻塞等待常用于多线程编程中,用于线程之间的同步和通信。

3.2.进程阻塞:

  1. 把进程的R状态设置为S状态
  2. 把进程的PCB从运行队列移动到等待队列中,不再被调度,而是等待
  3. 本质上是等待某种条件发生。
  • 软件条件满足(子进程退出)
  • 硬件资源就绪(scanf键盘输入数据发生)
#include <sys/wait.h>                                                                                                                  
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
int main()
{pid_t pid;pid = fork();if (pid < 0) {printf("%s fork error\n", __FUNCTION__);return 1;}else if (pid == 0) { //childprintf("子进程已运行, pid is : %d\n", getpid());sleep(5);exit(257);}else {int status = 0;pid_t ret = waitpid(-1, &status, 0);//父进程在这里阻塞式等待,等待5Sprintf("这是等待的测试\n");if (WIFEXITED(status) && ret == pid){printf("等待子进程5秒成功,子进程返回代码为:%d.\n", WEXITSTATUS(status));}else {printf("等待失败, return.\n");return 1;}}return 0;
}

4.进程的非阻塞等待方式: 

在子进程运行期间,父进程除了等待子进程或者是休眠,能不能干点其他的事情❓

        当然可以,在父进程等待,阻塞状态。可以通过设置options来让父进程干点事情。不阻塞等待而是非阻塞等待。

4.1.什么又是非阻塞等待呢?用代码该怎么去实现呢?

        **非阻塞等待(Non-blocking Wait)**则与阻塞等待相反。当线程或进程在等待某个条件满足或某个操作完成时,它不会暂停执行其他任务,而是会继续执行后续的任务。也就是说,即使等待的条件还没有满足或操作还没有完成,线程或进程也不会被阻塞,而是会继续执行其他的操作。

        通过设置options的宏值WNOHANG(wait no hang 等待没有阻塞 = 非阻塞等待)

在计算机中,"HANG" 通常指的是程序或系统出现无响应或停顿的状态,也就是常说的“卡住”或“死机”。

        当程序或系统由于某种原因(如资源锁定、死循环、死锁或外部系统交互问题等)而无法继续正常执行时,就可能会出现"HANG"的情况。这种情况下,用户可能无法与程序或系统进行交互,需要等待程序或系统恢复正常或进行重启操作。

        另外,在一些特定的语境下,"HANG" 也可能被用来描述服务器或数据库的某些服务出现故障或无法访问的情况,这也可以被视为一种"宕机"现象。在这种情况下,"HANG" 指的是服务器或数据库的服务因为某种原因而停止响应或无法提供服务。

具体操作

  1. options这个参数只要一设置就会出现非阻塞等待。
  2. 设置waitpid的WNOHANG本质上是检测一次进程的状态变化。
  3. 调用一次waipid就检测一次。每次调用都是检测,多次调用多次检测。
  4. 非阻塞等待调用多次waitpid,调用waitpid检测是否退出等待过程无问题,只是子进程还未终止,需要等待下次等待。

综上:非阻塞等待的时候 + 循环 = 非阻塞轮询

#include<stdio.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include<stdlib.h>
int main()
{pid_t pid;pid = fork();if (pid < 0) {printf("%s fork error\n", __FUNCTION__);return 1;}else if (pid == 0) { //childprintf("child is run, pid is : %d\n", getpid());sleep(5);exit(1);}else {int status = 0;pid_t ret = 0;do{ret = waitpid(-1, &status, WNOHANG);//非阻塞式等待if (ret == 0) {printf("child is running\n");}sleep(1);} while (ret == 0);if (WIFEXITED(status) && ret == pid) {printf("wait child 5s success, child return code is :%d.\n", WEXITS    TATUS(status));}else {printf("wait child failed, return.\n");return 1;}}return 0;
}

5.阻塞等待VS非阻塞等待

场景:张三找李四求助帮他复习期末考试。张三在李四的楼下等待李四就绪。

5.1.非阻塞等待:

张三每隔几分钟就给李四打电话询问他是否就绪了,张三在没有打电话的时间看书/游戏/抖音

  1. 就绪的过程本质就是非阻塞等待。
  2. 张三非阻塞等待李四过程 == 函数调用
  3. 张三给李四打电话 == 函数传参
  4. 李四说等着没好 == 函数的返回值
  5. 每次函数调用的本质是检测李四的状态(是否就绪)
  6. 立刻有返回值,多次等待,多次返回。
  • pid_ t waitpid(pid_t pid, int *status, WNOHANG);
  • pit_t == 0 :检测是成功的,只不过子进程还没退出,需要你下一次进行重复等待。
  • pit_t > 0 :等待成功,子进程退出了,并且父进程回收成功。
  • pit_t < 0 :等待失败。

5.2.阻塞等待:

        张三一直给李四打着电话,直到李四就绪,期间张三一直等待李四就绪,不敢别的事情。一直检测李四的状态(不就绪,就不返回)
      一直等待。直到子进程终止才返回。

  1. pid_ t waitpid(pid_t pid, int *status, 0);
  2. pit_t > 0 :等待成功,子进程退出了,并且父进程回收成功。
  3. pit_t < 0 :等待失败。

相关文章:

【Linux】进程控制2——进程等待(waitwaitpid)

1. 进程等待必要性 我们知道&#xff0c;子进程退出&#xff0c;父进程如果不管不顾&#xff0c;就可能造成"僵尸进程”的问题&#xff0c;进而造成内存泄漏。另外&#xff0c;进程一旦变成僵尸状态&#xff0c;那就刀枪不入&#xff0c;“杀人不眨眼”的kill -9 也无能为…...

SpringBoot 统计接口调用耗时的多种方式

在实际开发中&#xff0c;了解项目中接口的响应时间是必不可少的事情。SpringBoot 项目支持监听接口的功能也不止一个&#xff0c;接下来我们分别以 AOP、ApplicationListener、Tomcat 三个方面去实现三种不同的监听接口响应时间的操作。 AOP 首先我们在项目中创建一个类 &am…...

Linux系统安装Ruby语言

Ruby是一种面向对象的脚本语言&#xff0c;由日本的计算机科学家松本行弘设计并开发&#xff0c;Ruby的设计哲学强调程序员的幸福感&#xff0c;致力于简化编程的复杂性&#xff0c;并提供一种既强大又易于使用的工具。其语法简洁优雅&#xff0c;易于阅读和书写&#xff0c;使…...

网络安全练气篇——OWASP TOP 10

1、什么是OWASP&#xff1f; OWASP&#xff08;开放式Web应用程序安全项目&#xff09;是一个开放的社区&#xff0c;由非营利组织 OWASP基金会支持的项目。对所有致力于改进应用程序安全的人士开放&#xff0c;旨在提高对应用程序安全性的认识。 其最具权威的就是“10项最严重…...

python实现进度条的方法和实现代码

在Python中&#xff0c;有多种方式可以实现进度条。这里&#xff0c;我将介绍七种常见的方法&#xff1a;使用tqdm&#xff08;这是一个外部库&#xff0c;非常流行且易于使用&#xff09;、rich、click、progressbar2等库以及纯Python的print函数与time库来模拟进度条。 目录…...

被拷打已老实!面试官问我 #{} 和 ${} 的区别是什么?

引言&#xff1a;在使用 MyBatis 进行数据库操作时&#xff0c;#{} 和 ${} 的区别是面试中常见的问题&#xff0c;对理解如何在 MyBatis 中安全有效地处理 SQL 语句至关重要。正确使用这两种占位符不仅影响应用的安全性&#xff0c;还涉及到性能优化。 题目 被拷打已老实&…...

C# —— while循环语句

作用 让顺序执行的代码 可以停下来 循环执行某一代码块 // 条件分支语句: 让代码产生分支 进行执行 // 循环语句 : 让代码可以重复执行 语法 while循环 while (bool值) { 循环体(条件满足时执行的代码块) …...

力扣第205题“同构字符串”

在本篇文章中&#xff0c;我们将详细解读力扣第205题“同构字符串”。通过学习本篇文章&#xff0c;读者将掌握如何使用哈希表来解决这一问题&#xff0c;并了解相关的复杂度分析和模拟面试问答。每种方法都将配以详细的解释&#xff0c;以便于理解。 问题描述 力扣第205题“…...

探索RESTful API开发,构建可扩展的Web服务

介绍 当我们浏览网页、使用手机应用或与各种互联网服务交互时&#xff0c;我们经常听到一个术语&#xff1a;“RESTful API”。它听起来很高深&#xff0c;但实际上&#xff0c;它是构建现代网络应用程序所不可或缺的基础。 什么是RESTful API&#xff1f; 让我们将RESTful …...

苹果安卓网页的H5封装成App的应用和原生开发的应用有什么不一样?

H5封装类成App的应用和原生应用有什么不一样&#xff1f;——一对比谈优缺点 1. 开发速度和复用性 H5封装的App优势&#xff1a;一次编写&#xff0c;多平台运行。你只需要使用一种语言编写代码&#xff0c;就可以发布到不同的平台&#xff0c;降低开发成本。 原生应用优势&…...

IO流2.

字符流-->字符流的底层其实就是字节流 public class Stream {public static void main(String[] args) throws IOException {//1.创建对象并关联本地文件FileReader frnew FileReader("abc\\a.txt");//2.读取资源read()int ch;while((chfr.read())!-1){System.out…...

详解MySQL中的PERCENT_RANK函数

目录 1. 引入1. 基本使用2&#xff1a;分组使用3&#xff1a;处理重复值4. 使用优势4.1 手动计算百分等级4.2 使用 PERCENT_RANK 的优势4.3 使用 PERCENT_RANK 5. 总结 在 MySQL 中&#xff0c;PERCENT_RANK 函数用于计算一个值在其分组中的百分等级。 它的返回值范围是从 0 …...

宏任务与微任务

一、宏任务 1、概念 指消息队列中等地被主线程执行的事件 2、种类 script主代码块、setTimeout 、setInterval 、nodejs的setImmediate 、MessageChannel&#xff08;react的fiber用到&#xff09;、postMessage、网络I/O、文件I/O、用户交互的回调等事件、UI渲染事件&#x…...

昇思大模型学习·第一天

mindspore快速入门回顾 导入mindspore包 处理数据集 下载mnist数据集进行数据集预处理 MnistDataset()方法train_dataset.get_col_names() 打印列名信息使用create_tuple_iterator 或create_dict_iterator对数据集进行迭代访问 网络构建 mindspore.nn: 构建所有网络的基类用…...

python调用chatgpt

简单写了一下关于文本生成接口的调用&#xff0c;其余更多的调用方法可在官网查看 import os from dotenv import load_dotenv, find_dotenv from openai import OpenAI import httpxdef gpt_config():# 为了安全起见&#xff0c;将key写到当前项目根目录下的.env文件中# find…...

YOLOV8 目标检测:训练自定义数据集

1、下载 yolov8项目&#xff1a;ultralytics/ultralytics&#xff1a;新增 - PyTorch 中的 YOLOv8 &#x1f680; > ONNX > OpenVINO > CoreML > TFLite --- ultralytics/ultralytics: NEW - YOLOv8 &#x1f680; in PyTorch > ONNX > OpenVINO > CoreM…...

动态更新自建的Redis连接池连接数量

/*** 定时更新Redis连接池信息&#xff0c;防止资源让费*/private static final ScheduledThreadPoolExecutor DYNAMICALLY_UPDATE_REDIS_POOL_THREAD new ScheduledThreadPoolExecutor(1, new ThreadFactory() {Overridepublic Thread newThread(Runnable r) {Thread thread …...

浅谈设计师的设计地位

在当今这个创意无限的时代&#xff0c;设计师的地位日益凸显。他们以独特的视角和精湛的技能&#xff0c;为我们的生活带来了无尽的色彩与灵感。然而&#xff0c;随着行业的不断发展&#xff0c;设计师如何在众多同行中脱颖而出&#xff0c;提升自己的设计地位呢&#xff1f;答…...

C/C++ string模拟实现

1.模拟准备 1.1因为是模拟string&#xff0c;防止与库发生冲突&#xff0c;所以需要命名空间namespace隔离一下&#xff0c;我们来看一下基本内容 namespace yx {class string{private://char _buff[16]; lunix下小于16字节就存buff里char* _str;size_t _size;size_t _capac…...

微信小程序学习(八):behaviors代码复用

小程序的 behaviors 方法是一种代码复用的方式&#xff0c;可以将一些通用的逻辑和方法提取出来&#xff0c;然后在多个组件中复用&#xff0c;从而减少代码冗余&#xff0c;提高代码的可维护性。 如果需要 behavior 复用代码&#xff0c;需要使用 Behavior() 方法&#xff0c…...

【The design pattern of Attribute-Based Dynamic Routing Pattern (ADRP)】

In ASP.NET Core, routing is one of the core functionalities that maps HTTP requests to the corresponding controller actions. While “Route-Driven Design Pattern” is a coined name for a design pattern, we can construct a routing-centric design pattern base…...

2713. 矩阵中严格递增的单元格数

题目 给定一个 m x n 的整数矩阵 mat&#xff0c;我们需要找出从某个单元格出发可以访问的最大单元格数量。移动规则是可以从当前单元格移动到同一行或同一列的任何其他单元格&#xff0c;但目标单元格的值必须严格大于当前单元格的值。需要返回最大可访问的单元格数量。 示例…...

git创建子模块

有种情况我们经常会遇到&#xff1a;某个工作中的项目需要包含并使用另一个项目。 也许是第三方库&#xff0c;或者你独立开发的&#xff0c;用于多个父项目的库。 现在问题来了&#xff1a;你想要把它们当做两个独立的项目&#xff0c;同时又想在一个项目中使用另一个。 Git …...

把Deepin塞进U盘,即插即用!Deepin To Go来袭

前言 小伙伴之前在某篇文章下留言说&#xff1a;把Deepin塞进U盘的教程。 这不就来了吗&#xff1f; 事实是可以的。这时候你要先做点小准备&#xff1a; 一个大小为8GB或以上的普通U盘 一个至少64GB或以上的高速U盘 一个Deepin系统镜像文件 普通U盘的大概介绍&#xff1…...

​​给【AI硬件】创业者的论文、开源项目和产品整理

一、AI 硬件精选论文 《DrEureka: Language Model Guided Sim-To-Real Transfer》 瑜伽球上遛「狗」这项研究由宾夕法尼亚大学、 NVIDIA 、得克萨斯大学奥斯汀分校的研究者联合打造&#xff0c;并且完全开源。他们提出了 DrEureka&#xff08;域随机化 Eureka&#xff09;&am…...

模拟面试题卷二

1. 什么是JavaEE框架&#xff0c;你能列举一些常用的JavaEE框架吗&#xff1f; 答&#xff1a;JavaEE框架是一套用于开发企业级应用的技术规范和工具集合。常用的JavaEE框架有Spring、Hibernate、Struts、JSF等。 2. 请解释一下面向对象技术和设计原则是什么&#xff0c;你能…...

22种常用设计模式示例代码

文章目录 创建型模式结构型模式行为模式 仓库地址https://github.com/Xiamu-ssr/DesignPatternsPractice 参考教程 refactoringguru设计模式-目录 创建型模式 软件包复杂度流行度工厂方法factorymethod❄️⭐️⭐️⭐️抽象工厂abstractfactory❄️❄️⭐️⭐️⭐️生成器bui…...

Java面试题:对比ArrayList和LinkedList的内部实现,以及它们在不同场景下的适用性

ArrayList和LinkedList是Java中常用的两个List实现&#xff0c;它们在内部实现和适用场景上有很大差异。下面是详细的对比分析&#xff1a; 内部实现 ArrayList 数据结构&#xff1a;内部使用动态数组&#xff08;即一个可变长的数组&#xff09;实现。存储方式&#xff1a;…...

ping: www.baidu.com: 未知的名称或服务(IP号不匹配)

我用的是VMware上的Red Hat Enterprise Linux 9&#xff0c;出现了能联网但ping不通外网的情况。 问题描述&#xff1a;设置中显示正常连接&#xff0c;而且虚拟机右上角有联网的图标&#xff0c;但不能通外网。 按照网上教程修改了/etc/resolv.conf和/etc/sysconfig/network-…...

谷神前端组件增强:子列表

谷神Ag-Grid导出Excel // 谷神Ag-Grid导出Excel let allDiscolumns detailTable.getAllDisColumns() let columnColIds columns.map(column > column.colId) let columnKeys columnColIds.filter(item > ![select, "_OPT_FIELD_"].includes(item)) detailT…...

做普通网站选择什么服务器/快速提升网站关键词排名

在敏捷、DevOps、CI/CD以及其他任何我们想加入的行业词汇的世界里&#xff0c;开发人员和测试人员面临着快速交付高质量、功能性软件的巨大压力。需求必须由一系列的测试来支持&#xff0c;这样才能看到分配的需求是否得到了满足。许多组织在Jira&#xff08;或其他平台&#x…...

eclipse与jsp网站开发/seo排名技术软件

一、什么是SMO1、SMO&#xff1a;SQL SERVER Management Object2、扩展了SQL-DMO&#xff08;Distributed Management Object&#xff09; 更好的性能 更佳的控制 包含了绝大部分SQL-DMO的功能 支持SQL Server 20053、兼容&#xff1a;SQL Server7.0&#xff0c;SQL Se…...

深圳企业网站建设定制开发服务/网站开发流程

题目&#xff1a;原题链接&#xff08;简单&#xff09; 标签&#xff1a;字符串、哈希表 解法时间复杂度空间复杂度执行用时Ans 1 (Python)O(N)O(N)O(N)O(1)O(1)O(1)36ms (83.40%)Ans 2 (Python)O(N)O(N)O(N)O(1)O(1)O(1)44ms (66.36%)Ans 3 (Python) 解法一&#xff08;哈希…...

seo 网站 结构/专业营销推广团队

老舍说&#xff1a; “人&#xff0c;即使活到八九十岁&#xff0c;有母亲便可以多少还有点孩子气。 失了慈母便像花插在瓶子里&#xff0c;虽然还有色有香&#xff0c;却失去了根。” 《劝孝歌》里讲&#xff1a;“人不孝其亲&#xff0c;不如草与木。” 百善孝为先&#xff…...

厦门网站建设公司怎么选/百度网站大全旧版

欢迎关注”生信修炼手册”!UROPA是一个命令行工具&#xff0c;可以对基因组区域进行注释&#xff0c;这里的基因组区域要求是BED格式&#xff0c;比如chip&#xff0c;ATAC_seq等数据产生的peak区间。同时需要提供一个GTF格式的基因组注释信息&#xff0c;比如从UCSC,ensemble,…...

wordpress 摘要字数/手机百度最新正版下载

[判断物体是否在主角前方60、距离小于10的范围内] 解决思路&#xff1a;判断是否在前方要运用到点积&#xff0c;公式为a•b|a||b|cosθ&#xff0c;所以只需要判断cosθ(物体与主角正前方的夹角为θ)与cos30的大小关系。余弦函数在0~90之间&#xff0c;函数值随着角度的增大反…...