一文学会进程控制

news2024/9/30 1:37:11

目录

  • 进程的诞生
    • fork函数
      • fork的本质
      • fork的常规用法
      • fork调用失败的原因
  • 进程的死亡
    • 进程退出的场景
    • 常见的进程退出方法
      • 正常终止(代码跑完)
        • echo $?
        • main函数返回
        • 调用exit
        • 调用_exit
        • exit和_exit的区别
  • 进程等待
    • 进程等待的重要性
    • 进程等待的函数
      • wait
      • waitpid
    • 进程退出信息——status
      • status是什么
      • status怎么用
      • Linux源码中的退出码对照表
    • 进程程序替换
      • 替换原理
      • 替换函数
        • execl
        • execv
        • execlp
        • execvp
        • execle
        • execve
  • 50行代码实现小型shell
    • 获取命令行
    • 解析命令
    • 创建子进程(fork)
    • 子进程进行程序替换(exec系列函数)
    • 父进程等待子进程(waitpid)
    • 代码

进程的诞生

fork函数

  • 在Linux中,进程用来创建子进程的函数就是fork。
    在这里插入图片描述
    函数返回值为:
  1. 子进程返回0。
  2. 父进程返回子进程pid(父进程可能有多个子进程,父进程通过fork返回子进程pid区分各个子进程)。
  3. 出错比如创建进程失败返回-1。

在进程概念这一片文章中我们已经使用过fork函数,接下来,我们来了解一下fork函数到底做了一些什么。

fork的本质

  • 我们知道fork创建进程后会有两个执行流,但是不要认为这两个执行流实在fork完成后产生的,其实它们在fork内部就已经产生了,这就是为什么fork有两个返回值的原因。
    我们来了解一下fork到底做了一些什么。

在这里插入图片描述

  1. 我们知道操作系统通过管理相关结构体管理进程,所以创建一个进程其实就是创建并填充task_struct,mm_struct,页表等相关结构体,所以fork第一步就是向内存申请一块空间,然后创建相关结构体并且拷贝父进程的数据。
  2. 在创建相关结构体后,操作系统会将子进程添加到系统进程列表中即将这些结构体链接到相关数据结构中,比如将task_struct链接到cpu的调度队列中等等,在上述操作完成后,进程就已经创建完成,此时,就多了一个执行流。

在这里插入图片描述

fork的常规用法

  • 使用if判断语句通过fork的返回值进行分流
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>

int main()
{
	
	pid_t pid=fork();
	if(pid > 0){
		//father do something...
	}
	else if(pid == 0){
		//child do something...
	}
	
	return 0;
}
  • 通过exec函数进行程序替换,可以实现一个进程执行另一个程序的代码。(后面细嗦)

fork调用失败的原因

  • 系统中进程太多导致内存不足。
  • 用户的进程数超过系统限制。

进程的死亡

进程退出的场景

  • 代码跑完结果正确。
  • 代码跑完结果不正确。
  • 进程异常退出(发生除0、栈溢出、野指针、越界访问等等)。
    任何进程退出的情况都属于上面几种。

常见的进程退出方法

正常终止(代码跑完)

echo $?

查看最近的进程退出码
在这里插入图片描述

main函数返回

这个方式是我们最为熟悉的,在我们写C\C++代码时最后写的**“return n;”就是所谓的main函数返回**。
main函数中,return操作过后,返回值会当作exit的参数。
注意:只有main函数中的return具有退出进程的作用,main函数是程序的入口,return只是将返回值传给调用函数,并不能在main函数调用的函数退出进程。
比如下面add函数的return时,只是返回main函数调用add函数的地方,所以return的作用严谨的讲是结束当前函数,将返回值穿给调用函数。

#include<stdio.h>

int add(int a, int b)
{
	return a+b;
}

int main()
{
	int a=10;
	int b=20;
	int ret=add(a,b);
	return ret;
}

调用exit

在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>

int add(int a, int b)
{
	exit(1);
	return a+b;
}

int main()
{
	int a=10;
	int b=20;
	int ret=add(a,b);
	return 0;
}

在这里插入图片描述
我们可以从结果看出进程确实是在add函数中退出,说明exit可以在任意位置结束进程。

调用_exit

在这里插入图片描述
在用法上与exit一致,那我们来聊聊_exit和exit的区别。

exit和_exit的区别

  • exit会在退出进程前执行用户定义的清理函数。
  • exit会冲刷缓存(将缓存区中的数据刷新),关闭流(c语言中默认打开的标准输入流、标准输出流、标准错误流)。
  • exit最后会调用_exit。
    在这里插入图片描述

进程等待

进程等待的重要性

  • 在进程概念中讲过僵尸进程,当子进程退出,父进程如果一直不去读取子进程的退出信息时,子进程会变成僵尸进程,从而导致内存泄漏。
  • 僵尸进程一旦形成,kill也没办法。
  • 父进程把任务派发给子进程,父进程应关心子进程的完成情况:任务是否正确完成,子进程是否异常退出。
  • 父进程通过进程等待的方式,回收子进程资源,读取子进程退出信息。

进程等待的函数

wait

在这里插入图片描述

  • 返回值:若等待成功返回等待进程的pid,失败则返回-1。
  • 参数:输出型参数,用于获取进程的退出信息,不关心则传NULL。

我们通过一段代码了解他的使用方式。

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
	pid_t pid=fork();
	if(pid == 0){
		//child
		sleep(5);
		exit(0);
	}
	else if(pid > 0)
	{
		pid_t ret_id=wait(NULL);
		if(pid == ret_id){
			printf("pid == ret_id\n");
		}
		else{
			printf("error!!!\n");
		}
	}
	else{
		printf("fork error!!!\n");
	}
	return 0;
}

在这里插入图片描述

因为子进程sleep了5秒,父进程没有,父进程应该退出,子进程变成僵尸进程。
在这里插入图片描述
但是我们可以看到父进程明明没有sleep但是它的STAT和子进程都是S,说明wait是阻塞式等待

waitpid

在这里插入图片描述
返回值:

  • 等待成功则返回等待进程pid
  • 如果设置了选项WNOHANG,而调用waitpid发现没有已退出的子进程则返回0。
  • 调用出现错误则返回-1,且error会被设置成相应的值。

pid:

  • pid=-1时,父进程等待任一子进程,效果与wait相同
  • pid>0时,父进程等待指定pid的子进程。

status:

  • 输出型参数,进程退出信息。

options:

  • 选项WNOHANG,若等待进程没有结束则返回0继续跑自己的代码,当等待正常结束的子进程时,返回子进程pid。

现在我们先不关心进程的退出信息,先实现一下回收多个子进程。

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
	int pid_array[5]={0};
	int i=0;
	while(i < 5){
		pid_array[i]=fork();
		if(pid_array[i] == 0){
			//child
			printf("child do something...\n");
			exit(0);
		}
		else if(pid_array[i] > 0){
			//father
		}
		else{
			printf("fork error!!!\n");
		}
		i++;
	}
	
	sleep(5);
	
	i=0;
	while(i < 5){
		pid_t pid = waitpid(pid_array[i],NULL,0);
		if(pid == pid_array[i]){
			printf("pid == ret_id\n");
		}
		else{
			printf("error!!!\n");
		}
		sleep(1);
		i++;
	}

	return 0;
}

在这里插入图片描述

进程退出信息——status

status是什么

  • 我们知道wait和waitpid都有一个参数叫做status,这个参数是父进程用于获取子进程退出信息,是输出性参数,由操作系统填充。
  • 如果传入NULL,表示不关心退出信息
  • status不能简单地看成一个整形,可以看待成一个位图。

在这里插入图片描述
我们知道在进程退出时,情况分为:

  • 进程正常退出(通过查看退出状态判断结果是否正确)
  • 进程异常终止(退出状态无意义)

status怎么用

  • 可以通过位操作获得退出信息。(此方法麻烦,不推荐)
  • 可以通过相关宏获得退出信息

WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
	pid_t pid=fork();
	if(pid == 0){
		//child
		sleep(5);
		exit(0);
	}
	else if(pid > 0)
	{
		int status=0;
		pid_t ret_id=waitpid(pid,&status,0);
		if(WIFEXITED(status)){
			printf("exit_code:%d\n",WEXITSTATUS(status));
			
		}
		else{
			printf("error!!!\n");
		}
	}
	else{
		printf("fork error!!!\n");
	}
	return 0;
}

在这里插入图片描述
关于退出码:其实我们并不擅长处理数据信息,我们更加擅长出来字符串信息,所以在获得退出码后我们往往需要在退出码对照表中找到相应信息。

Linux源码中的退出码对照表

路径:/include/asm-generic/errno-base.h链接

#ifndef _ASM_GENERIC_ERRNO_BASE_H
#define _ASM_GENERIC_ERRNO_BASE_H

#define	EPERM		 1	/* Operation not permitted */
#define	ENOENT		 2	/* No such file or directory */
#define	ESRCH		 3	/* No such process */
#define	EINTR		 4	/* Interrupted system call */
#define	EIO		 5	/* I/O error */
#define	ENXIO		 6	/* No such device or address */
#define	E2BIG		 7	/* Argument list too long */
#define	ENOEXEC		 8	/* Exec format error */
#define	EBADF		 9	/* Bad file number */
#define	ECHILD		10	/* No child processes */
#define	EAGAIN		11	/* Try again */
#define	ENOMEM		12	/* Out of memory */
#define	EACCES		13	/* Permission denied */
#define	EFAULT		14	/* Bad address */
#define	ENOTBLK		15	/* Block device required */
#define	EBUSY		16	/* Device or resource busy */
#define	EEXIST		17	/* File exists */
#define	EXDEV		18	/* Cross-device link */
#define	ENODEV		19	/* No such device */
#define	ENOTDIR		20	/* Not a directory */
#define	EISDIR		21	/* Is a directory */
#define	EINVAL		22	/* Invalid argument */
#define	ENFILE		23	/* File table overflow */
#define	EMFILE		24	/* Too many open files */
#define	ENOTTY		25	/* Not a typewriter */
#define	ETXTBSY		26	/* Text file busy */
#define	EFBIG		27	/* File too large */
#define	ENOSPC		28	/* No space left on device */
#define	ESPIPE		29	/* Illegal seek */
#define	EROFS		30	/* Read-only file system */
#define	EMLINK		31	/* Too many links */
#define	EPIPE		32	/* Broken pipe */
#define	EDOM		33	/* Math argument out of domain of func */
#define	ERANGE		34	/* Math result not representable */

#endif

路径:/include/asm-generic/errno.h链接

#ifndef _ASM_GENERIC_ERRNO_H
#define _ASM_GENERIC_ERRNO_H

#include <asm-generic/errno-base.h>

#define	EDEADLK		35	/* Resource deadlock would occur */
#define	ENAMETOOLONG	36	/* File name too long */
#define	ENOLCK		37	/* No record locks available */
#define	ENOSYS		38	/* Function not implemented */
#define	ENOTEMPTY	39	/* Directory not empty */
#define	ELOOP		40	/* Too many symbolic links encountered */
#define	EWOULDBLOCK	EAGAIN	/* Operation would block */
#define	ENOMSG		42	/* No message of desired type */
#define	EIDRM		43	/* Identifier removed */
#define	ECHRNG		44	/* Channel number out of range */
#define	EL2NSYNC	45	/* Level 2 not synchronized */
#define	EL3HLT		46	/* Level 3 halted */
#define	EL3RST		47	/* Level 3 reset */
#define	ELNRNG		48	/* Link number out of range */
#define	EUNATCH		49	/* Protocol driver not attached */
#define	ENOCSI		50	/* No CSI structure available */
#define	EL2HLT		51	/* Level 2 halted */
#define	EBADE		52	/* Invalid exchange */
#define	EBADR		53	/* Invalid request descriptor */
#define	EXFULL		54	/* Exchange full */
#define	ENOANO		55	/* No anode */
#define	EBADRQC		56	/* Invalid request code */
#define	EBADSLT		57	/* Invalid slot */

#define	EDEADLOCK	EDEADLK

#define	EBFONT		59	/* Bad font file format */
#define	ENOSTR		60	/* Device not a stream */
#define	ENODATA		61	/* No data available */
#define	ETIME		62	/* Timer expired */
#define	ENOSR		63	/* Out of streams resources */
#define	ENONET		64	/* Machine is not on the network */
#define	ENOPKG		65	/* Package not installed */
#define	EREMOTE		66	/* Object is remote */
#define	ENOLINK		67	/* Link has been severed */
#define	EADV		68	/* Advertise error */
#define	ESRMNT		69	/* Srmount error */
#define	ECOMM		70	/* Communication error on send */
#define	EPROTO		71	/* Protocol error */
#define	EMULTIHOP	72	/* Multihop attempted */
#define	EDOTDOT		73	/* RFS specific error */
#define	EBADMSG		74	/* Not a data message */
#define	EOVERFLOW	75	/* Value too large for defined data type */
#define	ENOTUNIQ	76	/* Name not unique on network */
#define	EBADFD		77	/* File descriptor in bad state */
#define	EREMCHG		78	/* Remote address changed */
#define	ELIBACC		79	/* Can not access a needed shared library */
#define	ELIBBAD		80	/* Accessing a corrupted shared library */
#define	ELIBSCN		81	/* .lib section in a.out corrupted */
#define	ELIBMAX		82	/* Attempting to link in too many shared libraries */
#define	ELIBEXEC	83	/* Cannot exec a shared library directly */
#define	EILSEQ		84	/* Illegal byte sequence */
#define	ERESTART	85	/* Interrupted system call should be restarted */
#define	ESTRPIPE	86	/* Streams pipe error */
#define	EUSERS		87	/* Too many users */
#define	ENOTSOCK	88	/* Socket operation on non-socket */
#define	EDESTADDRREQ	89	/* Destination address required */
#define	EMSGSIZE	90	/* Message too long */
#define	EPROTOTYPE	91	/* Protocol wrong type for socket */
#define	ENOPROTOOPT	92	/* Protocol not available */
#define	EPROTONOSUPPORT	93	/* Protocol not supported */
#define	ESOCKTNOSUPPORT	94	/* Socket type not supported */
#define	EOPNOTSUPP	95	/* Operation not supported on transport endpoint */
#define	EPFNOSUPPORT	96	/* Protocol family not supported */
#define	EAFNOSUPPORT	97	/* Address family not supported by protocol */
#define	EADDRINUSE	98	/* Address already in use */
#define	EADDRNOTAVAIL	99	/* Cannot assign requested address */
#define	ENETDOWN	100	/* Network is down */
#define	ENETUNREACH	101	/* Network is unreachable */
#define	ENETRESET	102	/* Network dropped connection because of reset */
#define	ECONNABORTED	103	/* Software caused connection abort */
#define	ECONNRESET	104	/* Connection reset by peer */
#define	ENOBUFS		105	/* No buffer space available */
#define	EISCONN		106	/* Transport endpoint is already connected */
#define	ENOTCONN	107	/* Transport endpoint is not connected */
#define	ESHUTDOWN	108	/* Cannot send after transport endpoint shutdown */
#define	ETOOMANYREFS	109	/* Too many references: cannot splice */
#define	ETIMEDOUT	110	/* Connection timed out */
#define	ECONNREFUSED	111	/* Connection refused */
#define	EHOSTDOWN	112	/* Host is down */
#define	EHOSTUNREACH	113	/* No route to host */
#define	EALREADY	114	/* Operation already in progress */
#define	EINPROGRESS	115	/* Operation now in progress */
#define	ESTALE		116	/* Stale NFS file handle */
#define	EUCLEAN		117	/* Structure needs cleaning */
#define	ENOTNAM		118	/* Not a XENIX named type file */
#define	ENAVAIL		119	/* No XENIX semaphores available */
#define	EISNAM		120	/* Is a named type file */
#define	EREMOTEIO	121	/* Remote I/O error */
#define	EDQUOT		122	/* Quota exceeded */

#define	ENOMEDIUM	123	/* No medium found */
#define	EMEDIUMTYPE	124	/* Wrong medium type */
#define	ECANCELED	125	/* Operation Canceled */
#define	ENOKEY		126	/* Required key not available */
#define	EKEYEXPIRED	127	/* Key has expired */
#define	EKEYREVOKED	128	/* Key has been revoked */
#define	EKEYREJECTED	129	/* Key was rejected by service */

/* for robust mutexes */
#define	EOWNERDEAD	130	/* Owner died */
#define	ENOTRECOVERABLE	131	/* State not recoverable */

#define ERFKILL		132	/* Operation not possible due to RF-kill */

#define EHWPOISON	133	/* Memory page has hardware error */

#endif

进程程序替换

替换原理

进程用fork创建子进程(其代码和数据拷贝至父进程),虽说可以用条件语句分流,但是这样使用并不方便,我们往往让子进程调用exec系列函数实现程序替换,当进程调用exec系列函数时,代码和数据会被磁盘中的可执行程序完全覆盖,从而实现进程程序替换。但是注意exec系列函数并不是创建新的进程,而是将调用进程的代码和数据进行替换。

替换函数

exec系列函数(不同后缀不同用法)的头文件为unistd.h

execl

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

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
	pid_t pid=fork();
	if(pid == 0){
		//child
		execl("/usr/bin/ls","ls","-a","-l",NULL); //将子进程替换成ls
	}
	else if(pid > 0){
		//father
		pid_t pid=wait(NULL);
		if(pid > 0){
			printf("wait child success\n");
		}
		else{
			printf("wait error\n");
			exit(1);
		}
	}
	return 0;
}

在这里插入图片描述

l后缀代表以可变参数列表的方式传参,以NULL结束。

execv

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

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
	pid_t pid=fork();
	if(pid == 0){
		//child
		char* arg[]={
			"ls",
			"-a",
			"-l"
		};
		execv("/usr/bin/ls",arg);
	}
	else if(pid > 0){
		//father
		pid_t pid=wait(NULL);
		if(pid > 0){
			printf("wait child success\n");
		}
		else{
			printf("wait error\n");
			exit(1);
		}
	}
	return 0;
}

在这里插入图片描述
v后缀代表通过数组方式传参。

execlp

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

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
	pid_t pid=fork();
	if(pid == 0){
		//child
		execlp("ls","ls","-a","-l",NULL);
	}
	else if(pid > 0){
		//father
		pid_t pid=wait(NULL);
		if(pid > 0){
			printf("wait child success\n");
		}
		else{
			printf("wait error\n");
			exit(1);
		}
	}
	return 0;
}

在这里插入图片描述
p后缀代表自动搜索环境变量PATH,替换的可执行程序可以不带路径。
注意带l后缀的请务必在传参时用NULL结束。

execvp

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

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
	pid_t pid=fork();
	if(pid == 0){
		//child
		char* arg[]={
			"ls",
			"-a",
			"-l"
		};
		execvp("ls",arg);
	}
	else if(pid > 0){
		//father
		pid_t pid=wait(NULL);
		if(pid > 0){
			printf("wait child success\n");
		}
		else{
			printf("wait error\n");
			exit(1);
		}
	}
	return 0;
}

在这里插入图片描述

execle

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

test.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
	pid_t pid=fork();
	if(pid == 0){
		//child
		char* envp[]={
			"PATH=/bin:/usr/bin"
		};
		execle("my_printf","my_printf","-a","-b",NULL,envp);
	}
	else if(pid > 0){
		//father
		pid_t pid=wait(NULL);
		if(pid > 0){
			printf("wait child success\n");
		}
		else{
			printf("wait error\n");
			exit(1);
		}
	}
	return 0;
}

my_pritnf.c

#include<stdio.h>
#include<stdlib.h>

int main(int argc,char* argv[],char* env[])
{
	int i=0;
	for(; i<argc; i++){
		printf("argv[i]:%s\n",argv[i]);
	}
	printf("PATH:%s\n",getenv("PATH"));
	return 0;
}

在这里插入图片描述
我们在环境变量中了解到main函数的第三个变量是环境变量,e后缀代表自己传环境变量

execve

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

在这里插入图片描述
我们可以看到exec系列函数中除了execve,其他函数都在一个文件中,这是因为execve是系统调用接口,其他的都是经过封装后方便我们使用的函数,它们底层都是调用的execve函数。

在这里插入图片描述

50行代码实现小型shell

我们先想想shell的运行过程:

  1. 读取命令
  2. bash创建子进程执行命令,bash阻塞等待
  3. 重复上述过程

所以我们的shell的运行逻辑是:

获取命令行

向标准输入流中读取命令

fgets(cmd, LEN, stdin);

解析命令

使用strtok函数以空格为分隔符,将读取的命令字符串截取为一个个选项方便给execvp传参。

cmd[strlen(cmd)-1] = '\0';
        myarg[0] = strtok(cmd, " ");
        int i = 1;
        while(myarg[i] = strtok(NULL, " ")){
            i++;
        }

创建子进程(fork)

pid_t id = fork();

子进程进行程序替换(exec系列函数)

if(id == 0){
            //child
            execvp(myarg[0], myarg);
            exit(11);
        }

父进程等待子进程(waitpid)

    int status = 0;
        pid_t ret = waitpid(id, &status, 0);
        if(WIFEXITED(status)){
            printf("exit code: %d\n", WEXITSTATUS(status));
        }

代码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#define LEN 1024
#define NUM 32

int main()
{
    char cmd[LEN];
    char *myarg[NUM];
    while(1){
        printf("[psr@my-centos_mc dir]# ");
        fgets(cmd, LEN, stdin);
        cmd[strlen(cmd)-1] = '\0';
        myarg[0] = strtok(cmd, " ");
        int i = 1;
        while(myarg[i] = strtok(NULL, " ")){
            i++;
        }
        //printf("%s", cmd);
        pid_t id = fork();
        if(id == 0){
            //child
            execvp(myarg[0], myarg);
            exit(11);
        }

        int status = 0;
        pid_t ret = waitpid(id, &status, 0);
        if(WIFEXITED(status)){
            printf("exit code: %d\n", WEXITSTATUS(status));
        }
		else{
			exit(1);
		}
    }
    return 0;
}

在这里插入图片描述

虽然这个shell的功能单一并且涉及到管道等等的命令不能实现,但是小型shell的实现有利于我们理解本篇博客的内容。

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

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

相关文章

uniapp中条件编译

官方&#xff1a;https://uniapp.dcloud.net.cn/tutorial/platform.html#%E8%B7%A8%E7%AB%AF%E5%85%BC%E5%AE%B9 #ifndef H5 代码段… #endif 表示除了H5其他都可以编译 #ifdef H5 代码段… #endef 表示只能编译H5&#xff0c;其他的都不能编译 其他编译平台请查看官方文档。 …

连接器产业深度分析报告,国产化替代如何突出重围?(附厂商名录)

前言 2022年3-4月&#xff0c;上海疫情的封城举措&#xff0c;使得其它地区连接器类产品难以进入上海产业链&#xff0c;车载连接器的终端供应受阻&#xff0c;最终影响到全国多家车企生产&#xff1b; 同年12月&#xff0c;欧洲理事会批准—2024年12月28日之前&#xff0c;各类…

MySQL数据库调优————索引调优技巧

长字段的索引调优 当某张表需要给一个长字段创建索引时&#xff0c;因为索引长度越长&#xff0c;效率越差&#xff0c;所以我们需要对其进行优化。 创建额外的长字段的Hash值列 当长字段需要创建索引时&#xff0c;我们可以为其创建额外的一列&#xff0c;用其Hash值作为值…

如何利用Power Virtual Agents机器人实现成绩查询服务

今天我们继续介绍如何利用Power Virtual Agents来实现成绩查询服务。设计思路是在PVA聊天机器人的对话框中输入学生的姓名和学号来进行成绩的查询。首先&#xff0c;在Microsoft 365的OneDrive中制作一个Excel格式的成绩单。 可以将学生的学号、姓名、各学科成绩进行添加。 在P…

【初探人工智能】2、雏形开始长成

【初探人工智能】2、雏形开始长成【初探人工智能】2、雏形开始长成安装Flask封装Web接口雏形设置接收参数功能验证聊天写代码代码补全生成图片写在后面笔者初次接触人工智能领域&#xff0c;文章中错误的地方还望各位大佬指正&#xff01; 【初探人工智能】2、雏形开始长成 在…

限时活动|凭徽章领披萨大奖,玩转Moonbeam治理论坛

动动手指&#xff0c;无需每天打卡&#xff0c;用刷手机的零碎时间领一份Web3惊喜&#xff01; 本次挑战的目标是鼓励大家参与社区治理、熟悉论坛操作。有关参与方式和原因的信息在Twitter上共享&#xff1a;有兴趣可以和ThinkWildCrypto一起探索论坛以解锁其功能、了解最近和正…

【虹科干货】如何有效运用虹科任意波形发生器工作模式?

图 1&#xff1a;显示从存储器到输出的数据路径的 AWG 概念框图 01引言 任意波形发生器 (AWG) 的强大功能之一是它们可以生成几乎无限数量的波形。 AWG 的工作模式控制这些波形输出方式的时序。 在本应用说明中&#xff0c;我们将研究虹科Spectrum M4i.66xx 系列 AWG 工作模式…

JVM的GC机制和常见GC算法

文章目录[toc]1. 堆内存的分代2. GC分类3. 什么是GC3.1 需要GC的内存区域3.2 GC回收的对象3.3 判断对象存活的两种算法3.3.1 引用计数3.3.2 可达性分析3.4 什么时候触发GC4. 常见的GC算法4.1 标记-清除算法4.2 复制算法4.3 标记-压缩算法1. 堆内存的分代 堆中内存分为新生代和老…

String类 [上]

一、编码的基础介绍 编码&#xff1a;是信息从一种形式或格式转换为另一种形式的过程。 ASCLL 编码表:主要表示的是英文的编码表 Unicode&#xff1a;是为了解决传统的字符编码方案的局限而产生的&#xff0c;它为每种语言中的每个字符设定了统一并且唯一的二进制编码二进制编码…

小白式linux系统怎么安装宝塔面板

有很多小白同学问我linux系统服务器怎么远程连接。那么今天我们重点来教教大家如何用电脑远程服务器配上图文教程&#xff0c;让不懂的新手小白一看就会&#xff0c;分分钟上手教程怎么安装宝塔面板&#xff1f;这个其实很简单接下来跟着我操作。以linux centos7.6 举例Centos安…

[计算机操作系统(慕课版)]第二章 进程的描述与控制(学习笔记)

2.1 前驱图和程序执行 2.1.1 前驱图 前驱图是指一个有向无循环图可记为DAG前驱图用于描述进程之间执行的先后顺序。前驱图的每个节点用来表示一个进程或程序段乃至一条语句节点间的有向边表示两个节点之间存在的偏序或前驱关系。进程或程序之间的前驱关系可用→来表示。如果进…

有了这些接口测试用例+工具,测试效率想不提升都难

写在前面&#xff1a;在日常开发过程中&#xff0c;有人做前端开发&#xff0c;有人负责后端开发。接口的主要作用就是连接前后台。但是&#xff0c;由于前端和后端开发的速度可能不一样&#xff0c;尤其是后端开发好了&#xff0c;但前端还未开发。这种时候我们需要做接口测试…

【原创】java+swing+mysql银行ATM管理系统

本文主要介绍使用javaswingmysql去设计一个银行ATM管理系统&#xff0c;模仿实现存款、取款、转账、余额查询等功能。 功能分析&#xff1a; 隐含ATM管理系统一般分为管理员和用户角色&#xff0c;管理员可以进行用户管理、账单管理&#xff0c;用户可以进行转取存款等功能如…

面试不到10分钟就被赶出来了,问的实在是太变态了...

干了两年外包&#xff0c;本来想出来正儿八经找个互联网公司上班&#xff0c;没想到算法死在另一家厂子。 自从加入这家外包公司&#xff0c;每天都在加班&#xff0c;钱倒是给的不少&#xff0c;所以也就忍了。没想到11月一纸通知&#xff0c;所有人不许加班&#xff0c;薪资…

深度学习网络模型——ConvNeXt网络详解、ConvNeXt网络训练花分类数据集整体项目实现

深度学习网络模型——ConvNeXt网络详解、ConvNeXt网络训练花分类数据集整体项目实现1、介绍2、设计方案3、Macro design4、ResNeXt-ify5、Inverted Bottleneck7、Large Kernel Sizes8、Micro Design9、ConvNeXt variants10、ConvNeXt-T 结构图11、网络代码实现&#xff1a;Conv…

内网渗透(三十五)之横向移动篇-IPC配合系统服务横向移动

系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…

Linux文件权限查看与修改

Linux文件的权限linu文件的权限可以分为四类&#xff1a;可读、可写、可执行、没有权限。分别用字符r、w、x、- 表示。2. 用户与用户组Liunx是一个多用户多任务的操作系统&#xff0c;可以通过用户和用户组来更好的控制文件的权限。每个文件都有一个拥有者&#xff08;某一个具…

批处理Batch学习

批处理Batch学习 前几天一个月薪35k的兄弟&#xff0c;给我推了一个人工智能学习网站&#xff0c;看了一段时间挺有意思的。包括语音识别、机器翻译等从基础到实战都有&#xff0c;很详细&#xff0c;分享给大家。大家及时保存&#xff0c;说不定啥时候就没了。 基础认识 批…

Linux下Python脚本的编写解析fio(minimal格式)(三)

在服务器测试(storage)过程中&#xff0c;会看到很多人写跑fio的脚本用minimal格式来解析&#xff0c;因为这种格式返回的结果对与脚本(shell,python)解析log非常方便.下面介绍一下这种方式下,用Python来解析log 1 一般客户会要求结果中出现一下参数的值&#xff1a; bandwidth…

推荐几款免费且优秀的短视频配音软件,你值得拥有

科技的迅猛发展带来了新生事物的不断涌现&#xff0c;短视频就是其中之一&#xff0c;有的小伙伴喜欢在茶余饭后记录生活的点点滴滴&#xff0c;也有人将之变成了日常的主要收入来源&#xff0c;但无论是哪种&#xff0c;一款好的AI配音软件都是必不可少的&#xff0c;很多短视…