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

一文学会进程控制

目录

  • 进程的诞生
    • 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){//childsleep(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){//childprintf("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){//childsleep(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){//childexecl("/usr/bin/ls","ls","-a","-l",NULL); //将子进程替换成ls}else if(pid > 0){//fatherpid_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){//childchar* arg[]={"ls","-a","-l"};execv("/usr/bin/ls",arg);}else if(pid > 0){//fatherpid_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){//childexeclp("ls","ls","-a","-l",NULL);}else if(pid > 0){//fatherpid_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){//childchar* arg[]={"ls","-a","-l"};execvp("ls",arg);}else if(pid > 0){//fatherpid_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){//childchar* envp[]={"PATH=/bin:/usr/bin"};execle("my_printf","my_printf","-a","-b",NULL,envp);}else if(pid > 0){//fatherpid_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){//childexecvp(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 32int 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){//childexecvp(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的实现有利于我们理解本篇博客的内容。

相关文章:

一文学会进程控制

目录进程的诞生fork函数fork的本质fork的常规用法fork调用失败的原因进程的死亡进程退出的场景常见的进程退出方法正常终止&#xff08;代码跑完&#xff09;echo $?main函数返回调用exit调用_exitexit和_exit的区别进程等待进程等待的重要性进程等待的函数waitwaitpid进程退出…...

5.2 BGP水平分割

5.2.2实验2&#xff1a;BGP水平分割 1. 实验目的 熟悉BGP水平分割的应用场景掌握BGP水平分割的配置方法 2. 实验拓扑 实验拓扑如图5-2所示&#xff1a; 图5-2&#xff1a;BGP水平分割 3. 实验步骤 &#xff08;1&#xff09;配置IP地址 R1的配置 <Huawei>…...

华为OD机试 - TLV 编码 | 备考思路,刷题要点,答疑 【新解法】

最近更新的博客 【新解法】华为OD机试 - 关联子串 | 备考思路,刷题要点,答疑,od Base 提供【新解法】华为OD机试 - 停车场最大距离 | 备考思路,刷题要点,答疑,od Base 提供【新解法】华为OD机试 - 任务调度 | 备考思路,刷题要点,答疑,od Base 提供【新解法】华为OD机试…...

【C语言每日一题】——猜名次

【C语言每日一题】——猜名次&#x1f60e;前言&#x1f64c;猜名次&#x1f64c;解题思路分享&#xff1a;&#x1f60d;解题源码分享&#xff1a;&#x1f60d;总结撒花&#x1f49e;&#x1f60e;博客昵称&#xff1a;博客小梦 &#x1f60a;最喜欢的座右铭&#xff1a;全神…...

Agilent E4982A、Keysight E4982A、LCR 表,1 MHz 至 3 GHz

Agilent E4982A、Keysight E4982A、HP E4982A LCR 表&#xff0c;1 MHz 至 3 GHz 产品概览 KEYSIGHT E4982A&#xff08;安捷伦&#xff09; Keysight E4982A LCR 表为需要高频&#xff08;1 MHz 至 3 GHz&#xff09;阻抗测试的无源元件制造行业提供一流的性能&#xff0c…...

SAP 系统的配置传输

在SAP项目的实施过程中&#xff0c;经常会遇到关于配置传输的问题。即我们在某个client下面做系统配置&#xff0c;配好了之后再传到其他系统之中。 配置传输分为两种情况&#xff1a;同服务器配置传输&#xff0c;异服务器配置传输。同服务器配置传输&#xff1a; 在DEV配置cl…...

华为OD机试 - 喊七(Python)

喊七 题目 喊 7,是一个传统的聚会游戏, N 个人围成一圈,按顺时针从1 - 7编号, 编号为1的人从1开始喊数, 下一个人喊得数字是上一个人喊得数字+1, 但是当将要喊出数字7的倍数或者含有7的话, 不能喊出,而是要喊过。 假定N个人都没有失误。 当喊道数字k时, 可以统计每…...

Docker下快速搭建RabbitMQ单例及集群

引子生命在于折腾&#xff0c;为上数据实时化用到了消息传送的内容&#xff0c;当时也和总公司人员商量选型&#xff0c;kafka不能区分分公司就暂定用了RbtMQ刚好个人也在研究容器及分布式部署相关内容就在docker上实践单机 docker&#xff08;要想快 先看问题 避免踩坑&#x…...

python代码写开心消消乐

♥️作者:小刘在C站 ♥️个人主页:小刘主页 ♥️每天分享云计算网络运维课堂笔记,努力不一定有收获,但一定会有收获加油!一起努力,共赴美好人生! ♥️夕阳下,是最美的绽放,树高千尺,落叶归根人生不易,人间真情 目录 一.python是什么 二.游戏代码效果呈现 三.主代...

【郭东白架构课 模块一:生存法则】09|法则四:为什么要顺应技术的生命周期?

你好&#xff0c;我是郭东白。今天我们来讲架构师的第四条生存法则&#xff0c;那就是尊重技术的生命周期。 人类的各种活动都要遵循事物的客观生命周期。不论是农业社会种田打渔&#xff0c;还是资本社会投资创业&#xff0c;行动太早或太晚&#xff0c;都会颗粒无收。技术也…...

Linux之进程控制

一.进程创建 1.1 fork函数 我们创建进程的方式有./xxx和fork()两种 在linux中fork函数时非常重要的函数&#xff0c;它从已存在进程中创建一个新进程。新进程为子进程&#xff0c;而原进程为父进程。 #include <unistd.h> pid_t fork(void); 返回值&#xff1a;自进程…...

SpringBoot社区版专业版带你配置热部署

&#x1f49f;&#x1f49f;前言 ​ 友友们大家好&#xff0c;我是你们的小王同学&#x1f617;&#x1f617; 今天给大家打来的是 SpringBoot社区版专业版带你配置热部署 希望能给大家带来有用的知识 觉得小王写的不错的话麻烦动动小手 点赞&#x1f44d; 收藏⭐ 评论&#x1…...

影响AFE采样精度的因素有哪些?

**AFE&#xff08;Analog Front End&#xff09;**是模拟前端电路的缩写&#xff0c;它是模拟信号传感器和数字信号处理器之间的连接点。AFE采样精度是指模拟信号被数字化后的准确度&#xff0c;对于很多电子设备来说&#xff0c;这是一个至关重要的性能指标。本文将介绍影响AF…...

mysqlbackup备份报error:redo log was overwritten

问题原因 备份时redo log被覆盖 解决方案 方法1&#xff1a;增加innodb_log_file_size、innodb_log_files_in_group大小&#xff0c;需要重启数据库 vi my.cnf innodb_log_file_size 2G innodb_log_files_in_group 4 方法2: 动态配置redo log archive&#xff0c;不需要重启…...

Android支持库

# 支持库 注意:Android 9.0(API 级别 28)发布后,新版支持库 AndroidX 也随之诞生,它属于 Jetpack。除了现有的支持库,AndroidX 库还包含最新的 Jetpack 组件。 您可以继续使用此支持库以往的工件(这里指的是版本 27 及更早版本,且已打包为 android.support.*)在 Googl…...

Vue:filters过滤器

日期、时间格式化是Vue前端项目中较为常遇到的一个需求点&#xff0c;此处&#xff0c;围绕Vue的过滤器来介绍如何更为优雅的解决此类需求。 过滤器filters使用注意点 Vue允许开发者自定义过滤器&#xff0c;可以实现一些常见的文本格式化等需求。 使用时要注意的点在于&#…...

Windows环境下安装和配置Gradle

1. 概述 Gradle是Google公司基于JVM开发的一款项目构建工具&#xff0c;支持Maven&#xff0c;JCenter多种第三方仓库&#xff0c;支持传递性依赖管理&#xff0c;使用更加简洁和支持多种语言的build脚步文件&#xff0c;更多详情可以参阅Gradle官网 2. 下载 由于Gradle与S…...

数据结构时间空间复杂度笔记

&#x1f57a;作者&#xff1a; 迷茫的启明星 本篇内容&#xff1a;数据结构时间空间复杂度笔记 &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;家人们&#xff0c;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤…...

基于注意力的知识蒸馏Attention Transfer原理与代码解析

paper&#xff1a;Paying More Attention to Attention: Improving the Performance of Convolutional Neural Networks via Attention Transfercode&#xff1a;https://github.com/megvii-research/mdistiller/blob/master/mdistiller/distillers/AT.py背景一个流行的假设是存…...

利尔达在北交所上市:总市值突破29亿元,叶文光为董事长

2月17日&#xff0c;利尔达科技集团股份有限公司&#xff08;下称“利尔达”&#xff0c;BJ:832149&#xff09;在北京证券交易所上市。本次上市&#xff0c;利尔达的发行价格为5.00元/股&#xff0c;发行数量为1980万股&#xff0c;发行市盈率为12.29倍&#xff0c;募资总额为…...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

什么是库存周转?如何用进销存系统提高库存周转率?

你可能听说过这样一句话&#xff1a; “利润不是赚出来的&#xff0c;是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业&#xff0c;很多企业看着销售不错&#xff0c;账上却没钱、利润也不见了&#xff0c;一翻库存才发现&#xff1a; 一堆卖不动的旧货…...

高危文件识别的常用算法:原理、应用与企业场景

高危文件识别的常用算法&#xff1a;原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件&#xff0c;如包含恶意代码、敏感数据或欺诈内容的文档&#xff0c;在企业协同办公环境中&#xff08;如Teams、Google Workspace&#xff09;尤为重要。结合大模型技术&…...

MySQL JOIN 表过多的优化思路

当 MySQL 查询涉及大量表 JOIN 时&#xff0c;性能会显著下降。以下是优化思路和简易实现方法&#xff1a; 一、核心优化思路 减少 JOIN 数量 数据冗余&#xff1a;添加必要的冗余字段&#xff08;如订单表直接存储用户名&#xff09;合并表&#xff1a;将频繁关联的小表合并成…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

怎么让Comfyui导出的图像不包含工作流信息,

为了数据安全&#xff0c;让Comfyui导出的图像不包含工作流信息&#xff0c;导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo&#xff08;推荐&#xff09;​​ 在 save_images 方法中&#xff0c;​​删除或注释掉所有与 metadata …...

AI语音助手的Python实现

引言 语音助手(如小爱同学、Siri)通过语音识别、自然语言处理(NLP)和语音合成技术,为用户提供直观、高效的交互体验。随着人工智能的普及,Python开发者可以利用开源库和AI模型,快速构建自定义语音助手。本文由浅入深,详细介绍如何使用Python开发AI语音助手,涵盖基础功…...

Unity中的transform.up

2025年6月8日&#xff0c;周日下午 在Unity中&#xff0c;transform.up是Transform组件的一个属性&#xff0c;表示游戏对象在世界空间中的“上”方向&#xff08;Y轴正方向&#xff09;&#xff0c;且会随对象旋转动态变化。以下是关键点解析&#xff1a; 基本定义 transfor…...

React、Git、计网、发展趋势等内容——前端面试宝典(字节、小红书和美团)

React React Hook实现架构、.Hook不能在循环嵌套语句中使用 , 为什么&#xff0c;Fiber架构&#xff0c;面试向面试官介绍&#xff0c;详细解释 用户: React Hook实现架构、.Hook不能在循环嵌套语句中使用 , 为什么&#xff0c;Fiber架构&#xff0c;面试向面试官介绍&#x…...