一文学会进程控制
目录
- 进程的诞生
- 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。
函数返回值为:
- 子进程返回0。
- 父进程返回子进程pid(父进程可能有多个子进程,父进程通过fork返回子进程pid区分各个子进程)。
- 出错比如创建进程失败返回-1。
在进程概念这一片文章中我们已经使用过fork函数,接下来,我们来了解一下fork函数到底做了一些什么。
fork的本质
- 我们知道fork创建进程后会有两个执行流,但是不要认为这两个执行流实在fork完成后产生的,其实它们在fork内部就已经产生了,这就是为什么fork有两个返回值的原因。
我们来了解一下fork到底做了一些什么。
- 我们知道操作系统通过管理相关结构体管理进程,所以创建一个进程其实就是创建并填充task_struct,mm_struct,页表等相关结构体,所以fork第一步就是向内存申请一块空间,然后创建相关结构体并且拷贝父进程的数据。
- 在创建相关结构体后,操作系统会将子进程添加到系统进程列表中即将这些结构体链接到相关数据结构中,比如将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的运行过程:
- 读取命令
- bash创建子进程执行命令,bash阻塞等待
- 重复上述过程
所以我们的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调用失败的原因进程的死亡进程退出的场景常见的进程退出方法正常终止(代码跑完)echo $?main函数返回调用exit调用_exitexit和_exit的区别进程等待进程等待的重要性进程等待的函数waitwaitpid进程退出…...
5.2 BGP水平分割
5.2.2实验2:BGP水平分割 1. 实验目的 熟悉BGP水平分割的应用场景掌握BGP水平分割的配置方法 2. 实验拓扑 实验拓扑如图5-2所示: 图5-2:BGP水平分割 3. 实验步骤 (1)配置IP地址 R1的配置 <Huawei>…...
华为OD机试 - TLV 编码 | 备考思路,刷题要点,答疑 【新解法】
最近更新的博客 【新解法】华为OD机试 - 关联子串 | 备考思路,刷题要点,答疑,od Base 提供【新解法】华为OD机试 - 停车场最大距离 | 备考思路,刷题要点,答疑,od Base 提供【新解法】华为OD机试 - 任务调度 | 备考思路,刷题要点,答疑,od Base 提供【新解法】华为OD机试…...
【C语言每日一题】——猜名次
【C语言每日一题】——猜名次😎前言🙌猜名次🙌解题思路分享:😍解题源码分享:😍总结撒花💞😎博客昵称:博客小梦 😊最喜欢的座右铭:全神…...
Agilent E4982A、Keysight E4982A、LCR 表,1 MHz 至 3 GHz
Agilent E4982A、Keysight E4982A、HP E4982A LCR 表,1 MHz 至 3 GHz 产品概览 KEYSIGHT E4982A(安捷伦) Keysight E4982A LCR 表为需要高频(1 MHz 至 3 GHz)阻抗测试的无源元件制造行业提供一流的性能,…...
SAP 系统的配置传输
在SAP项目的实施过程中,经常会遇到关于配置传输的问题。即我们在某个client下面做系统配置,配好了之后再传到其他系统之中。 配置传输分为两种情况:同服务器配置传输,异服务器配置传输。同服务器配置传输: 在DEV配置cl…...
华为OD机试 - 喊七(Python)
喊七 题目 喊 7,是一个传统的聚会游戏, N 个人围成一圈,按顺时针从1 - 7编号, 编号为1的人从1开始喊数, 下一个人喊得数字是上一个人喊得数字+1, 但是当将要喊出数字7的倍数或者含有7的话, 不能喊出,而是要喊过。 假定N个人都没有失误。 当喊道数字k时, 可以统计每…...
Docker下快速搭建RabbitMQ单例及集群
引子生命在于折腾,为上数据实时化用到了消息传送的内容,当时也和总公司人员商量选型,kafka不能区分分公司就暂定用了RbtMQ刚好个人也在研究容器及分布式部署相关内容就在docker上实践单机 docker(要想快 先看问题 避免踩坑&#x…...
python代码写开心消消乐
♥️作者:小刘在C站 ♥️个人主页:小刘主页 ♥️每天分享云计算网络运维课堂笔记,努力不一定有收获,但一定会有收获加油!一起努力,共赴美好人生! ♥️夕阳下,是最美的绽放,树高千尺,落叶归根人生不易,人间真情 目录 一.python是什么 二.游戏代码效果呈现 三.主代...
【郭东白架构课 模块一:生存法则】09|法则四:为什么要顺应技术的生命周期?
你好,我是郭东白。今天我们来讲架构师的第四条生存法则,那就是尊重技术的生命周期。 人类的各种活动都要遵循事物的客观生命周期。不论是农业社会种田打渔,还是资本社会投资创业,行动太早或太晚,都会颗粒无收。技术也…...
Linux之进程控制
一.进程创建 1.1 fork函数 我们创建进程的方式有./xxx和fork()两种 在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。 #include <unistd.h> pid_t fork(void); 返回值:自进程…...
SpringBoot社区版专业版带你配置热部署
💟💟前言 友友们大家好,我是你们的小王同学😗😗 今天给大家打来的是 SpringBoot社区版专业版带你配置热部署 希望能给大家带来有用的知识 觉得小王写的不错的话麻烦动动小手 点赞👍 收藏⭐ 评论…...
影响AFE采样精度的因素有哪些?
**AFE(Analog Front End)**是模拟前端电路的缩写,它是模拟信号传感器和数字信号处理器之间的连接点。AFE采样精度是指模拟信号被数字化后的准确度,对于很多电子设备来说,这是一个至关重要的性能指标。本文将介绍影响AF…...
mysqlbackup备份报error:redo log was overwritten
问题原因 备份时redo log被覆盖 解决方案 方法1:增加innodb_log_file_size、innodb_log_files_in_group大小,需要重启数据库 vi my.cnf innodb_log_file_size 2G innodb_log_files_in_group 4 方法2: 动态配置redo log archive,不需要重启…...
Android支持库
# 支持库 注意:Android 9.0(API 级别 28)发布后,新版支持库 AndroidX 也随之诞生,它属于 Jetpack。除了现有的支持库,AndroidX 库还包含最新的 Jetpack 组件。 您可以继续使用此支持库以往的工件(这里指的是版本 27 及更早版本,且已打包为 android.support.*)在 Googl…...
Vue:filters过滤器
日期、时间格式化是Vue前端项目中较为常遇到的一个需求点,此处,围绕Vue的过滤器来介绍如何更为优雅的解决此类需求。 过滤器filters使用注意点 Vue允许开发者自定义过滤器,可以实现一些常见的文本格式化等需求。 使用时要注意的点在于&#…...
Windows环境下安装和配置Gradle
1. 概述 Gradle是Google公司基于JVM开发的一款项目构建工具,支持Maven,JCenter多种第三方仓库,支持传递性依赖管理,使用更加简洁和支持多种语言的build脚步文件,更多详情可以参阅Gradle官网 2. 下载 由于Gradle与S…...
数据结构时间空间复杂度笔记
🕺作者: 迷茫的启明星 本篇内容:数据结构时间空间复杂度笔记 😘欢迎关注:👍点赞🙌收藏✍️留言 🏇家人们,码字不易,你的👍点赞🙌收藏❤…...
基于注意力的知识蒸馏Attention Transfer原理与代码解析
paper:Paying More Attention to Attention: Improving the Performance of Convolutional Neural Networks via Attention Transfercode:https://github.com/megvii-research/mdistiller/blob/master/mdistiller/distillers/AT.py背景一个流行的假设是存…...
利尔达在北交所上市:总市值突破29亿元,叶文光为董事长
2月17日,利尔达科技集团股份有限公司(下称“利尔达”,BJ:832149)在北京证券交易所上市。本次上市,利尔达的发行价格为5.00元/股,发行数量为1980万股,发行市盈率为12.29倍,募资总额为…...
C#操作字符串方法 [万余字总结 · 详细]
C#操作字符串方法总结C#常用字符串函数大全C#常用字符串操作方法C#操作字符串方法总结C#常用字符串函数大全 Compare 比较字符串的内容,考虑文化背景(场所),确定某些字符是否相等 CompareOrdinal 与Compare一样,但不考虑文化背景 Format 格…...
极兔一面:10亿级ES海量搜索狂飙10倍,该怎么办?
背景说明: ES高性能全文索引,如果不会用,或者没有用过,在面试中,会非常吃亏。 所以ES的实操和底层原理,大家要好好准备。 另外,ES调优是一个非常、非常核心的面试知识点,大家要非…...
【Mysql基础 —— SQL语句(一)】
文章目录概述使用启动/停止mysql服务连接mysql客户端数据模型SQLSQL语句分类DDL数据库操作表操作查询创建数据类型修改删除DML添加数据修改数据删除数据DQL基础查询条件查询聚合函数分组查询排序查询分页查询执行顺序DCL管理用户权限控制概述 数据库(Database&#…...
华为OD机试 - 新员工座位安排系统(Python) | 机试题算法思路
最近更新的博客 华为OD机试 - 招聘(Python) | 备考思路,刷题要点,答疑 【新解法】华为OD机试 - 五键键盘 | 备考思路,刷题要点,答疑 【新解法】华为OD机试 - 热点网络统计 | 备考思路,刷题要点,答疑 【新解法】华为OD机试 - 路灯照明 | 备考思路,刷题要点,答疑 【新解…...
MySQL - 介绍
前言 本篇介绍认识MySQL,重装mysql操作 如有错误,请在评论区指正,让我们一起交流,共同进步! 本文开始 1.什么是数据库? 数据库: 一种通过SQL语言操作管理数据的软件; 重装数据库的卸载数据库步骤 : ① 停止MySQL服…...
ChatGPT国内镜像站初体验:聊天、Python代码生成等
ChatGPT国内镜像站初体验,聊天、Python代码生成。 (本文获得CSDN质量评分【92】)【学习的细节是欢悦的历程】Python 官网:https://www.python.org/ Free:大咖免费“圣经”教程《 python 完全自学教程》,不仅仅是基础那么简单………...
SAP数据导入工具(LSMW) 超级详细教程(批量导入内部订单)
目录 第一步:记录批导步骤编辑数据源对应字段 第二步:维护数据源 第三步:维护数据源对应字段(重要) 第四步:维护数据源关系。 第五步:维护数据源与导入字段的对应关系。 第六步࿰…...
第9天-商品服务(电商核心概念,属性分组开发及分类和品牌的级联更新)
1.电商核心概念 1.1.SPU与SKU SPU:Standard Product Unit(标准化产品单元) 是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个 产品的特性。 决定商品属性的值 SKU:Stock…...
动漫人物眼睛画法
本期的动漫绘画课程教大家来学习动漫人物眼睛画法,结合板绘软件从草稿开始一步步教你画出动漫人物眼睛,不用报动漫培训班也能学会,快来跟着本期的动漫人物眼睛画法教程试试吧! 动漫人物眼睛画法步骤教程: 注意&#x…...
张晨光-JAVA零基础保姆式JDBC技术教程
JDBC文档 JDBC概述 JDBC概述 Java DataBase Connectivity Java 数据库连接技术 JDBC的作用 通过Java语言操作数据库,操作表中的数据 SUN公司为**了简化、**统一对数据库的操作,定义了一套Java操作数据库的规范,称之为JDBC JDBC的本质 是官方…...
怎么做阿里国际网站的数据分析/最新全国疫情消息
1. springMVC中controller的几种返回类型 Controller方法的返回值可以有以下几种: 1、返回ModelAndView 返回ModelAndView时最常见的一种返回结果。需要在方法结束的时候定义一个ModelAndView对象,并对Model和View分别进行设置。 2、返回String 1&a…...
营销型高端网站建设价格/百度竞价冷门产品
欢迎关注”生信修炼手册”!对于任意的表达量数据,定量加差异分析都是一套经典的组合拳。当我们想要展示特定基因的组间差异结果时,下面这种图表就派上了用场横坐标为基因,纵坐标是基因表达量,每一组的表达量采用了箱体图的形式来展…...
公司网站报价/百度搜索风云榜总榜
想要保证缓存与数据库的双写一致,一共有4种方式: 先更新缓存,再更新数据库; 先更新数据库,再更新缓存; 先删除缓存,再更新数据库; 先更新数据库,再删除缓存。 我们需要做…...
b2b网站大全 网址大全/泰州网站建设优化
本文翻译自:Getting error “No such module” using Xcode, but the framework is thereIm currently coding in Swift, and Ive got an error: 我目前正在使用Swift进行编码,但出现错误: No such module Social 没有这样的模块社交 But I…...
宝宝投票网站怎么做的/2345软件为什么没人管
JDBC(Java Database Connectivity): JDBC API为访问不同的数据库提供了一种统一的途径,象ODBC一样,JDBC对开发者屏蔽了一些细节问题,另外,JDCB对数据库的访问也具有平台无关性。JDBC跟ODBC代码类似,都是进行数据库链接…...
叫别人做网站后怎么更改密码/我想找一个营销团队
北京数字空间科技有限公司ArcGIS教程:空间参考和地理处理地理数据集的空间参考由以下各部分组成:包含地图投影和基准面的坐标系XY分辨率、M和Z分辨率和域(可选)XY容差、M和Z容差(可选)这些空间参考属性对地理处理工具的性能和生成的结果具有重大影…...