Linux 进程基础

news2024/11/18 7:39:12

目录

1、进程的概念

2、进程与线程的区别、进程与程序的区别

2.1 进程与线程的区别

2.2 进程与程序的区别

3、进程相关 shell 命令

3.1 ps

3.3.1 参数说明

3.3.2 结果说明

3.2 pidof

3.3 pstree

3.4 top

3.5 kill

4、进程相关函数

4.1 fork

4.1.1 fork的函数原型

4.1.2 父子进程之间的继承

4.1.3 父子进程之间的区别

4.1.4 vfork

4.1.5 实例代码

4.2 getpid / getppid

4.3 exec函数族

4.3.1 什么是exec函数族

4.3.2 exec函数族函数原型

4.3.3 函数参数说明:

4.3.4 函数记忆方法

4.3.5 实例代码

4.4 _exit / exit

4.4.1 _exit:系统调用

4.4.2 exit():库函数

4.5 wait / waitpid

4.5.1 wait 函数原型

4.5.2 waitpid 函数原型

4.5.3 函数正常退出方式

4.5.4 函数异常退出方式

4.5.5 WEXITSTATUS(wstatus):提取进程退出传递的状态值

4.5.6 WIFEXITED(wstatus):判断子进程是否正常退出

4.5.7 关于参数status

4.5.8 实例代码

5、Linux中的特殊进程

5.1 孤儿进程(orphan)

5.1.1 孤儿进程是什么

5.1.2 实例代码

5.2 僵尸进程(zombie)

5.2.1 僵尸进程是什么

5.2.2 实例代码

5.2.3 如何回收僵尸进程

5.3 守护进程/幽灵进程(daemon)

5.3.1 什么是守护进程

5.3.2 查看守护进程 ps axj

5.3.3 创建守护进程

5.3.4 实例代码


1、进程的概念

1、进程是一个独立的可调度的任务。

2、进程是一个程序的一次执行的过程。

 3、进程在被调度的时候,系统会自动分配和释放各种资源(时间片,cpu资源,进程调度块,内存资源等等)。

 4、进程是一个抽象的概念。

2、进程与线程的区别、进程与程序的区别

2.1 进程与线程的区别

(1)一个线程从属于一个进程;一个进程可以包含多个线程。

(2)一个线程挂掉,对应的进程挂掉;一个进程挂掉,不会影响其他进程。

(3)进程是系统资源调度的最小单位;线程CPU调度的最小单位。

(4)进程系统开销显著大于线程开销;线程需要的系统资源更少。

(5)进程在执行时拥有独立的内存单元,多个线程共享进程的内存,如代码段、数据段、扩展段;但每个线程拥有自己的栈段和寄存器组。

(6)进程切换时需要刷新TLB并获取新的地址空间,然后切换硬件上下文和内核栈,线程切换时只需要切换硬件上下文和内核栈。

(7)通信方式不一样。

(8)进程适应于多核、多机分布;线程适用于多核

2.2 进程与程序的区别

程序是静态的,它是一些保存在磁盘上的指令的有序集合,没有任何执行的概念
进程是一个动态的概念,它是程序执行的过程,包括创建、调度和消亡

3、进程相关 shell 命令

3.1 ps

3.3.1 参数说明

-e   显示所有进程。
-f    全格式。
-h   不显示标题。
-l    长格式。
-w   宽输出。
-a    显示终端上的所有进程,包括其他用户的进程。
-r    只显示正在运行的进程。
-u    以用户为主的格式来显示程序状况。
-x    显示所有程序,不以终端机来区分。

3.3.2 结果说明

UID:      程序被该UID 所拥有,指的是用户 ID
PID:      就是这个程序的ID
PPID:    PID的上级父进程的 ID
C:          CPU使用的资源百分比
STIME: 系统启动时间
TTY:      登入者的终端机位置
TIME:    使用掉的CPU 时间
CMD:     所下达的指令为何

STAT:    进程状态

               D    不能被中断的阻塞态;
               R    运行状态
               S    休眠状态
               T    挂起态
               t    被追踪状态
               X    死亡态,死亡是一瞬间的,不能被捕获到。
               Z    僵尸状态,进程已经死亡,但是没有被回收。

               <    高优先级
               N    低优先级
               L    有些页被锁进内存。
               s    会话组组长,有子进程的进程
               l    多线程
               +    运行在前端的进程

3.2 pidof

根据程序名获取PID号

pidof a.out

3.3 pstree

显示进程关系树

3.4 top

实时显示进程的状态

top -d 秒数

top -p pid

3.5 kill

杀死进程

kill -9 pid             根据pid杀死进程 

kill -9 进程名字   根据指定名字,杀死进程

4、进程相关函数

4.1 fork

4.1.1 fork的函数原型

pid_t fork(void);
/*
头文件:
       #include <sys/types.h>
       #include <unistd.h>
功能:创建一个进程
返回值: >0, 父进程中返回,创建出来的那个子进程的PID号
        =0, 子进程中,返回0;
        =-1, 父进程返回-1,同时更新错误码。此时没有成功创建出子进程。
*/

4.1.2 父子进程之间的继承

使用 fork 函数得到的子进程从父进程的继承了整个进程的地址空间,包括:

进程上下文、进程堆栈、内存信息、打开的文件描述符、信号控制设置、进程优先级、进程组号、当前工作目录、根目录、资源限制、控制终端等。

子进程会拷贝父进程的虚拟地址空间。子进程会拷贝父进程的数据段,正文段,栈。

4.1.3 父子进程之间的区别

1 、父进程设置的锁,子进程不继承
2 、各自的进程 ID 和父进程 ID 不同
3 、子进程的未决告警被清除
4 、子进程的未决信号集设置为空集

5、父进程会拷贝一份资源给子进程,父子进程的资源是一致的,但是子进程不会运行创建它的那个fork函数以及fork以上的代码

6、fork后的父子进程哪个先运行不确定,只要看cpu调度机制以及时间片;

4.1.4 vfork

1、由于 fork 完整地拷贝了父进程的整个地址空间,因此执行速度是比较慢的。为了提高效率,Unix 系统设计者创建了 vfork。
2、vfork 也创建新进程,但不产生父进程的副本 。
3、它通过允许父子进程可访问相同物理内存从而伪装了对进程地址空间的真实拷贝,当子进程需要改变内存中数据时才拷贝父进程。
4、这 就是著名的 "写操作时拷贝"(copy on write) 技术

4.1.5 实例代码

int main()
{
	pid_t pid;
	
	if ((pid = fork()) == -1) {
		perror("fork");
		return  -1;
	} else if (pid == 0) {
		/* this is child process */
		printf("The return value is %d  In child process!!  My PID is %d,  My PPID is %d\n", pid,getpid(), getppid());
	} else {
		/* this is parent */
		printf("The return value is %d In parent process!!  My PID is %d,  My PPID is %d\n", pid,getpid(), getppid());
	}
	return  0;
}

4.2 getpid / getppid

pid_t getpid(void);
pid_t getppid(void);
/*
头文件:
       #include <sys/types.h>
       #include <unistd.h>
功能:获取当前进程/父进程的pid号
返回值:当前进程的pid / 父进程的pid
*/

4.3 exec函数族

4.3.1 什么是exec函数族

        exec 函数族提供了一种在进程中启动另一个程序执行的方法。它可以根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段。
        在执行完之后原调用进程的内容除了进程号外其他全部都被替换了。
        可执行文件既可以是二进制文件也可以是任何 Linux 下可执行的脚本文件。

        当进程认为自己不能再为系统和用户做出任何贡献了时就可以调用 exec 函数,让自己执行新的程序。
        如果某个进程想同时执行另一个程序,它就可以调用fork 函数创建子进程,然后在子进程中调用任何一个exec 函数。这样看起来就好像通过执行应用程序而产生了一个新进程一样。

4.3.2 exec函数族函数原型

头文件#include <unistd.h>
函数原型int execl(const char * path,const char * arg,…);
int execle(const char * path,const char * arg,char * const envp[]);
int execlp(const char * file,const char * arg,…);
int execv(const char * path,char * const argv[]);
int execve(const char * path,char * const argv[],char * const envp[]);

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

返回值-1代表调用exec失败,无返回代表调用成功

4.3.3 函数参数说明:

path :可执行文件的路径名字

arg:可执行程序所带的参数,第一个参数为可执行文件名字,没有带路径且arg必须以NULL结束。

file:如果参数file中包含/,则就将其视为路径名,否则就按PATH环境变量,在它所指定的各目录中搜寻可执行文件。

4.3.4 函数记忆方法

字符说明
l使用参数列表
v应该先构造一个指向各参数的指针数组,然后将该数组的地址作为这些函数的参数。
p使用文件名,并从 PATH 环境寻找可执行文件
e多了 envp[] 数组,使用新的环境变量代替调用进程的环境变量

4.3.5 实例代码

int main(int argc,char **argv)
{
	pid_t pid;
	
	printf("PID = %d\n",getpid());
	
	pid=fork();
	if(pid==0)
	{	
		execl("/bin/ls","ls","-l",NULL);
        execl("/bin/ls",“ls”,NULL,NULL);

        execlp("ls","ls","-al","/",NULL);

        char *argv[] = {"ls","-l",NULL};

        execv("/bin/ls",argv);    

	    execvp("ls",argv);
	
		sleep(10);
	} else if(pid!=-1) {
		//父进程		
		printf("\nParrent porcess,PID = %d\n",getpid());
	} else {
		printf("error fork() child proess!");
	}
	return 0 ;
}

4.4 _exit / exit

4.4.1 _exit:系统调用

void _exit(int status);
/*
头文件:
       #include <unistd.h>
功能:终止进程,清除进程使用的内容空间;直接销毁缓冲区,不会刷新缓冲区;
参数:
    @status:可以利用这个参数传递进程终止的状态值,可以输入任意整型;
				这个参数会被wait/waitpid函数接收走;
*/

4.4.2 exit():库函数

void exit(int status);
/*
功能:终止进程,并刷新缓冲区;
头文件:
    #include <stdlib.h>       
参数:
    @status:可以利用这个参数传递进程终止的状态值,可以输入任意整型;
			这个参数会被wait/waitpid函数接收走;
*/

4.5 wait / waitpid

4.5.1 wait 函数原型

pid_t wait(int *wstatus);
/*
功能:阻塞函数,等待子进程退出,并回收子进程的资源;
头文件:
       #include <sys/types.h>
       #include <sys/wait.h>       
参数:
    @wstatus:获取子进程的退出状态:接收_exit/exit中的status;
				如果不想接收,填NULL;
返回值:
    成功,返回退出的子进程的pid;
	失败,返回-1,更新errno;

如果进程没有创建子进程,调用wait函数不阻塞吗
1. 阻塞等待子进程退出,并回收子进程资源;
2. 如果没有子进程,父进程不等待,函数直接返回;
*/

4.5.2 waitpid 函数原型

pid_t waitpid(pid_t pid, int *wstatus, int options);
/*
功能:等待指定进程退出,并回收指定进程的资源。
     注意:只能是父进程回收子进程,子进程不能回收父进程.
头文件:
       #include <sys/types.h>
       #include <sys/wait.h>
参数:
    @pid:;
       <-1   回收指定进程组下的任意一个子进程。指定的进程组pgid == pid的绝对值。
       -1    等待当前进程下的任意一个子进程。
       0     回收当前进程组下的任意一个子进程。
       >0    回收指定子进程的资源,子进程的id == pid;
	@wstatus:获取子进程的退出状态:接收_exit/exit中的status;
			  如果不想接收,填NULL;
	@options:
			0:阻塞方式回收,如果指定进程没有退出,当前函数阻塞等待;
			WNOHANG:非阻塞方式回收,就算指定进程没有退出,当前函数也是不阻塞的,
                    立即返回,并执行后面代码;
返回值:
    成功,阻塞方式回收:返回被回收的进程的pid; 
		非阻塞方式回收:-->如果指定进程退出,则返回被回收的进程的pid;
            		 -->如果指定进程没有退出,但是函数运行成功,则返回0;
	失败,返回-1,更新errno; 
例子:
        //阻塞方式回收,若指定进程没有退出,则当前函数阻塞
        waitpid(pid, NULL, 0);    

        //非阻塞方式,就算指定进程没有退出,当前函数不阻塞;
        waitpid(pid, NULL, WNOHANG);
*/

4.5.3 函数正常退出方式

1、Mian函数调用return

2、进程调用exit(),标准c库

3、进程调用_exit()或者——Exit(),属于系统调用

4、进程最后一个线程返回

5、最后一个线程调用pthread_exit

4.5.4 函数异常退出方式

1、调用abort

2、当进程收到某些信号时候,如ctrl+C

3、最后一个线程对取消(cancellation),请求作出响应

4.5.5 WEXITSTATUS(wstatus):提取进程退出传递的状态值

int status = WEXITSTATUS(wstatus); 

4.5.6 WIFEXITED(wstatus):判断子进程是否正常退出

返回1,子进程正常退出。

返回0,子进程异常退出

4.5.7 关于参数status

此处为引用自以下这篇博客

进程(五)—— 进程退出、进程等待(waitpid函数)_程序退出函数_仲夏夜之梦~的博客-CSDN博客

        status不能简单的当作整型来看,要从二进制的角度来看,32位下,整型转化为二进制有32个bit位但是我们仅关注低16位

(1) 正常退出时
        进程正常退出时,子进程会返回退出码,即退出状态,8~15位记录着正常退出时的退出码,既然是正常退出就不会收到中止信号,所以0~7位都是 0

 

(2) 异常退出时

        进程异常退出时,一般会收到一个中止进程的信号,而且不会执行到return 这句,所以自然就没有退出码,为了知道发生了何种异常,我们使用低 7 位,也就是 0~6 来记录 “中止信号”

         注意:core dump是指在进程异常退出的时候,进程会把用户空间的内存数据保存到磁盘文件,文件名为core

4.5.8 实例代码

int main(int argc, const char *argv[])
{
	pid_t pid = fork();
	if(pid > 0)
	{
		//父进程运行
		int wstatus;
		
		//回收子进程pid参数可以填-1,也可以填子进程的id
		//阻塞方式回收,若制定进程没有退出,则当前函数阻塞
		pid_t w_pid = waitpid(pid, &wstatus, 0); 	

		//非阻塞方式,就算制定进程没有退出,当前函数不阻塞;
		pid_t w_pid = waitpid(pid, NULL, WNOHANG); 	

		//获取子进程退出状态值
		int status = WEXITSTATUS(wstatus);
		printf("wstatus = %d %d\n", wstatus, status);

		//判断子进程是否正常退出
		int ret = WIFEXITED(wstatus);
		if(ret)
			printf("子进程正常退出\n");
		else
			printf("子进程异常退出\n");

		while(1) {
			printf("parent %d %d\n", getpid(), pid);
			sleep(1);
		}

	} else if(0 == pid) {
		//子进程运行
		while(1) {
			printf("child %d %d\n", getppid(), getpid());
			sleep(1);
		}
	} else {
		perror("fork");
		return -1;
	}
	return 0;
}

5、Linux中的特殊进程

5.1 孤儿进程(orphan)

5.1.1 孤儿进程是什么

        父进程如果不等待子进程退出,在子进程结束前就了结束了自己的“生命”,此时子进程就叫做孤儿进程。

        Linux避免系统存在过多孤儿进程,init进程收留孤儿进程,变成孤儿进程的父进程[ init进程(pid=1)是系统初始化进程 ]。init 进程会自动清理所有它继承的僵尸进程。

        孤儿进程不能用ctrl + c 退出,但是可以被kill -9杀死

5.1.2 实例代码

int main(int argc, const char *argv[])
{
    //创建一个子进程,让父进程退出,子进程不退出
    pid_t pid = fork();
    if(pid > 0) {
        //父进程运行                                                     
    } else if(0 == pid) {
    //子进程
        while(1) {
            printf("this is child %d %d\n", getppid(), getpid());
            sleep(1);
        }
    } else {
        perror("fork");
        return -1;
    }

    return 0;
}

5.2 僵尸进程(zombie)

5.2.1 僵尸进程是什么

子进程退出,父进程没有退出,且子进程的资源没有被回收。这时候子进程就是僵尸进程。

5.2.2 实例代码

int main(int argc, const char *argv[])
{
    //父进程没有退出,子进程退出,且父进程没有回收子进程的资源。
    pid_t pid = fork();

    if(pid > 0) {                                                                            
        //父进程运行
        while(1) {
            printf("this is parent\n");
            sleep(1);
        }
    } else if(0 == pid) {
        //子进程运行
    } else {
        perror("fork");
        return -1;
    }

    return 0;
}

5.2.3 如何回收僵尸进程

         第一种方式,使用waitpid函数让父进程停下来等待子进程退出,并回收子进程,此时就能解决僵尸进程。

        第二种方式,子进程在退出的时候,会给父进程发送一个SIGCHLD信号,这个时候我们可以使用处理信号的知识来解决这个问题。

        具体解决可以借鉴以下博客

解决僵尸进程的两种方式(重温waitpid函数、了解17号信号SIGCHLD)_僵尸进程怎么处理_仲夏夜之梦~的博客-CSDN博客

5.3 守护进程/幽灵进程(daemon)

5.3.1 什么是守护进程

1、守护进程脱离终端,并且在后台运行

2、守护进程在执行中不会将任何信息显示在终端上。并且不会被任何终端产生的终端信息打断

3、守护进程独立于控制终端,并且周期性执行某个任务或等待处理某些事情

4、大多数服务器都是由守护进程实现的,比如:像我们的tftp,samba,nfs等相关服务

5.3.2 查看守护进程 ps axj

所有的守护进程都有以下特点:

1、所有的守护进程都是以超级用户启动的(UID为0)

2、没有控制终端(TTY为?)

3、终端进程组ID为-1(TPGID表示终端进程组ID,该值表示与控制终端相关的前台进程组,如果未和任何终端相关,其值为-1

5.3.3 创建守护进程

(1)创建孤儿进程

        由于守护进程是脱离控制终端的,因此,完成第一步后就会在shell终端里造成一程序已经运行完毕的假象。之后的所有后续工作都在子进程中完成,而用户在shell终端里则可以执行其他的命令,从而在形式上做到了与控制终端的脱离。

        由于父进程已经先于子进程退出,会造成子进程没有父进程,从而变成一个孤儿进程。在Linux中,每当系统发现一个孤儿进程,就会自动由1号进程收养。原先的子进程就会变成init进程的子进程。

(2)创建新的会话

使子进程完全独立,子进程创建新的会话,不在依附于父进程的会话组,需要调用 setid() 函数

 setid()函数主要有以下三个作用

让进程摆脱原会话的控制。 

让进程摆脱原进程组的控制。 

让进程摆脱原控制终端的控制。

        由于在调用fork()函数时,子进程全盘复制了父进程的会话期、进程组和控制终端等,虽然父进程退出了,但原先的会话期、进程组和控制终端等并没有改变,因此,还不是真正意义上的独立。而setsid()函数能够使进程完全独立出来,从而脱离所有其他进程和终端的控制。 

setid()函数原型

pid_t setsid(void);
/*
功能:创建会话组;
头文件:
       #include <sys/types.h>
       #include <unistd.h>     
返回值:
    成功,创建会话组的sid;
    失败,返回(pid_t) -1,更新errno;

1.创建新的会话,成为会话组组长;
2.创建新的进程组,成为进程组组长;
*/

(3)修改当前孤儿进程的运行目录为不可卸载的文件系统

        这一步也是必要的步骤。使用fork()创建的子进程继承了父进程的当前工作目录。

        由于在进程运行过程中,当前目录所在的文件系统(如“/mnt/usb”等)是不能卸载的,这对以后的使用会造成诸多的麻烦(如系统由于某种原因要进入单用户模式)。

        因此,通常的做法是让“/”作为守护进程的当前工作目录,这样就可以避免上述问题。当然,如有特殊需要,也可以把当前工作目录换成其他的路径,如/tmp。改变工作目录的常见函数是chdir()。

chdir()函数原型

int chdir(const char *path);
/*
头文件
    #include <unistd.h>
功能:修改到指定目录下
参数:
    @path:指定修改到哪个目录下
例如:
    chdir("/");
	chdir("/temp");
*/

(4)重设文件权限掩码:一般清0

        文件权限掩码是指屏蔽掉文件权限中的对应位。

        例如,有一个文件权限掩码是050,它就屏蔽了文件组拥有者的可读与可执行权限。由于使用fork()函数新建的子进程继承了父进程的文件权限掩码,这就给该子进程使用文件带来了诸多的麻烦。

        因此,把文件权限掩码设置为0,可以大大增强该守护进程的灵活性。设置文件权限掩码的函数是umask()。在这里,通常的使用方法为umask(0)。即赋予最大的能力。

umask(0);

(5)关闭所有文件描述符

        同文件权限掩码一样,用fork()函数新建的子进程会从父进程那里继承一些已经打开的文件。这些被打开的文件可能永远不会被守护进程读或写,但它们一样消耗系统资源,而且可能导致所在的文件系统无法被卸载。

        在上面的第(2)步之后,守护进程已经与所属的控制终端失去了联系,因此,从终端输入的字符不可能达到守护进程,守护进程中用常规方法(如printf())输出的字符也不可能在终端上显示出来。

        所以,文件描述符为0、1和2的3个文件(常说的输入、输出和报错这3个文件)已经失去了存在的价值,也应被关闭。

for(i=0; i<1024; i++)
	close(i);

此处为什么是1024呢?

        文件打开的文件数量是有限制的,我们通过 ulimit -a,即查看当前所有的资源限制。其中就包括了打开的最大文件数。

5.3.4 实例代码

int main(int argc, const char *argv[])
{
	//创建孤儿进程
	pid_t pid = fork();
	if(0 == pid) {
		//创建新的会话
		pid_t sid = setsid();
		//printf("sid = %d %d\n", sid, getpid());

		//修改运行目录为不可卸载的文件系统
		chdir("/");

		//文件权限掩码
		umask(0);

		//关闭所有文件描述符
		int i = 0;
		for(i=0; i<1024; i++) {
			close(i);
		}

		while(1) {
			//周期性执行的功能代码
			sleep(1);
		}

	} else if(pid > 0) {
		//父进程
	} else {
		perror("fork");
		return -1;
	}

	return 0;
}

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

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

相关文章

医院内导航及智能导医,医院导诊图怎么制作?

在大型综合性医院&#xff0c;由于专业分工精细&#xff0c;一个诊疗过程涉及的功能单元往往分布在不同的楼宇、不同楼层的不同位置&#xff0c;再加上多数患者对医院环境不熟悉&#xff0c;导致滞院的时间长、诊疗效率低、患者对服务的满意度下降。为解决这一问题&#xff0c;…

VMware Aria Operations for Logs 8.12 - 集中式日志管理

VMware Aria Operations for Logs 8.12 - 集中式日志管理 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-aria-operations-for-logs/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org 集中式日志管理 VMware Aria …

蓝牙mesh数据包格式解析

蓝牙mesh数据包的结构如下图&#xff1a; 总长31个字节。 Length (1Byte)&#xff1a;数据长度 Type (1Byte)&#xff1a;广播类型 IVI (1bit)&#xff1a;用来认证加密Network PDU的IV index的最低位 NID (7bits)&#xff1a;网络ID&#xff0c;network ID。从网络密钥(Ne…

学习了两个多月就进了我们公司,顺利过了试用期,我心塞了...

转行起因 公司前段时间来了个大专机械专业毕业的&#xff0c;挺好奇他在如今这个环境下怎么进来的而且非本科非科班&#xff0c;后面我请他喝了一次酒&#xff0c;我才了解到他的故事&#xff0c;写出来与大家分享&#xff0c;希望对各位有点启迪。 他以前在一个大厂做售后工…

新来的00后真卷,我想离职了···

都说00后躺平了&#xff0c;但是有一说一&#xff0c;该卷的还是卷。 这不&#xff0c;前段时间我们公司来了个00后&#xff0c;工作没两年&#xff0c;跳槽到我们公司起薪20K&#xff0c;都快接近我了。后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了。…

sentinel基本原理以及核心类介绍

Sentinel 核心类解析 架构图 ProcessorSlotChain Sentinel 的核心骨架&#xff0c;如上图结构&#xff0c;将不同的 Slot 按照顺序串在一起&#xff08;责任链模式&#xff09;&#xff0c;从而将不同的功能&#xff08;限流、降级、系统保护&#xff09;组合在一起。slot ch…

2023.05.11-利用GPT4free免费使用ChatGPT4

1. 简介 现在OpenAI&#xff0c;虽然出了ChatGPT4&#xff0c;但是只给plus会员用&#xff0c;对于国内的用户来说&#xff0c;不仅需要魔法&#xff0c;还需要有一张外网的信用卡来开通会员&#xff0c;使用起来重重不便&#xff0c;有一种想要花钱买服务&#xff0c;都找不到…

tf卡文件隐藏怎样恢复,原来有这三种方法,你了解多少呢?

TF卡是一种便携式存储设备&#xff0c;非常方便用于存储数据。但是&#xff0c;有时TF卡中的数据会被不小心隐藏了&#xff0c;也许是误操作&#xff0c;也许是病毒攻击等原因。所以&#xff0c;下面将讲述如何找回TF卡中被隐藏的数据。 【一】关于TF卡概述 TF卡&#xff08;…

uniapp打包ios保姆式教程【最新】

uniapp打包 打包方式ios打包一、前往官网登录二、添加证书 三、添加标识符(Identifiers)四、添加安装ios测试机(Devices)五、获取证书profile文件六、生成并下载p12文件七、开始打包 打包方式 安卓打包直接使用公共测试证书即可打包成功&#xff0c;简单方便&#xff0c;这里我…

【数据库数据恢复】sql server数据库无法附加查询的数据恢复案例

数据库数据恢复环境&#xff1a; 一台Dell PowerEdge某型号存储&#xff0c;数块SAS硬盘分别组建raid1和raid5两组磁盘阵列。其中2块磁盘组建的RAID1&#xff0c;用于安装操作系统&#xff1b;其余几块磁盘组建raid5&#xff0c;用于存放数据。 上层安装的windows服务器&#x…

vue中动态使用vh calc设置高度

动态设置高度 <div class"user_menu_box" :style"menuHeight"><!-- 用户信息 --><div class"user_info" :class"{ menu-collase: isCollapse }"><imgsrc"/assets/login_images/bg.jpg"alt"暂无图…

【python数据分析】对python开发岗位需求进行分析可视化

前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 什么是数据分析 明确目的–获得数据(爬虫&#xff0c;现有&#xff0c;公开的数据)–数据预处理——数据可视化——结论 准备 环境使用&#xff1a; 在开始写我们的代码之前&#xff0c;我们要准备好运行代码的程序 Anacon…

空中下载技术(OTA)电控信息安全

随着汽车电子控制系统功能复杂度和数据颗粒度呈阶梯式增加&#xff0c;其发展速度逐渐超越网络安全防护方法、技术和标准的发展&#xff0c;现阶段汽车电子正面临巨大的网络信息安全风险&#xff0c;对功能安全的潜在影响也仍在探索和解决中&#xff0c;信息安全问题已经成为影…

maven从入门到精通 第五章 在IDEA2023中使用Maven

这里写自定义目录标题 一 Maven基础回顾1 archetype2 指定自己的maven工程所在的位置3 接下来是用文本编辑器打开自己下载的maven文件下的 conf >settings 二 创建maven子工程1 配置环境&#xff0c;测试运行2 打包maven的三种方式2.1 点击maven左侧的lifecycle2.2 点击m标签…

苹果录屏功能在哪?苹果如何进行屏幕录制?

案例&#xff1a;想知道苹果手机和苹果电脑的录屏功能在哪&#xff1f; 【用苹果手机和电脑很久了&#xff0c;但是我还是不知道它们的录屏功能在哪&#xff0c;如何使用&#xff1f;有没有小伙伴了解苹果的录屏功能&#xff1f;可以教教我吗&#xff1f;】 苹果手机和电脑自…

copilot 逆向

原文&#xff1a; copilot-explorer | Hacky repo to see what the Copilot extension sends to the server 对我来说&#xff0c;Github Copilot 极其有用。它经常能神奇地读懂我的想法并给出有用的建议。最让我惊讶的是&#xff0c;它能够从周围的代码中正确地“猜测”出函数…

Android 音频开发——Audio概览(八)

Audio 是 Android 系统中比较重要的一个模块,在 Android 中负责音频方面的数据流传输和控制功能,也负责音频设备的管理。 一、系统架构 Android 音频架构定义了音频功能的实现方式,并指出实现中所涉及的相关源代码。 应用框架 应用框架包含应用代码,该代码使用 android.me…

【分布式】分布式共识算法 --- RAFT

2.CAP原则 CAP原则又称CAP定理&#xff0c;指的是在一个分布式系统中&#xff0c;一致性&#xff08;Consistency&#xff09;、可用性&#xff08;Availability&#xff09;、分区容错性&#xff08;Partition tolerance&#xff09; It states, that though its desirable t…

【Google I/O 2023】PaLM2 大语言模型与 Bard 使用体验

欢迎关注【youcans的学习笔记】原创作品&#xff0c;火热更新中 【Google I/O 2023】PaLM2 大语言模型与 Bard 使用体验 1. PaLM2 大型语言模型1.1 谷歌发布 PaLM21.2 PaLM2 的功能与性能 2. 基于 PaLM2 的谷歌 AI 产品2.1 智能助手 Duet AI2.2 Gmail&#xff1a;帮我写邮件2.3…

【JMM】保证线程间的可见性,还只知道volatile?

本文目录 前言 举例&#x1f330; 情形1 int->Integer 情形2 System.out.println() 情形3 storeFence() 情形4 Thread.yield() 情形5 LockSupport.unpark() 情形6 增长循环内代码执行时间 总结分析 volatile分析 字节码解释器实现 模版解释器实现 其他情形…