Linux进程的创建结束类系统调用总结
tags: Linux OS Syscall C
写在前面
总结一下Linux系统的进程创建/终止/等待等系统调用, 参考:
- Linux/Unix系统编程手册.
下面主要给出例子, 关于函数原型可以参考书中或者man 2 syscall
(例如man 2 fork
).
测试环境: Ubuntu 20.04 x86_64
gcc-9
进程创建: fork()
用于创建新的进程, 创建出来的新进程称为子进程, 拥有和父进程一样的代码段/数据段/栈段/堆段.
所以创建新进程的资源消耗较大, 后续采用多线程方式可以解决这个问题.
由于这个函数的设计比较奇怪, 有两个返回值, 在父进程中返回子进程的进程ID, 在子进程中返回0, 错误返回-1, 所以可以用下面的语句制定创建子进程之后的进一步操作:
pid_t childPid;switch (childPid = fork()) {case -1:/* error handling */case 0: // child process/* actions to child */default:/* actions to parent */
}
下面主要讨论数据共享和文件(句柄)共享, 为探讨进程间通信做准备.
数据共享
一个例子, 关于同时操作一份数据:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h> // pid_t
#include <unistd.h> // fork
static int idata = 111;int main(int argc, char *argv[]) {pid_t childPId;int istack = 222;switch (childPId = fork()) {case -1:fprintf(stderr, "fork error\n");case 0:idata *= 3;istack *= 3;break;default:sleep(3);break;}printf("PID=%ld %s idata=%d istack=%d\n", (long)getpid(),(childPId == 0) ? "(child) " : "(parent)", idata, istack);/* PID=526436 (child) idata=333 istack=666 *//* PID=526435 (parent) idata=111 istack=222 */return 0;
}
文件共享
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <unistd.h>int main(int argc, char* argv[]) {int fd, flags;char template[] = "/tmp/test-XXXXXX";setbuf(stdout, NULL);// 无缓冲fd = mkstemp(template);if (fd == -1) fprintf(stderr, "mkstemp");printf("File offset before fork: %lld\n",(long long)lseek(fd, 0, SEEK_CUR));flags = fcntl(fd, F_GETFL);if (flags == -1) fprintf(stderr, "fcntl - F_GETFL");printf("O_APPEND flag before fork() is %s\n",(flags & O_APPEND) ? "on" : "off");switch (fork()) {case -1:fprintf(stderr, "fork");case 0: // childif (lseek(fd, 1000, SEEK_SET) == -1) fprintf(stderr, "lseek");flags = fcntl(fd, F_GETFL);if (flags == -1) fprintf(stderr, "fcntl - F_GETFL");flags |= O_APPEND;if (fcntl(fd, F_SETFL, flags) == -1)fprintf(stderr, "fcntl - F_SETFL");_exit(EXIT_SUCCESS);default: // parentif (wait(NULL) == -1) fprintf(stderr, "wait");printf("child has exited\n");printf("file offset in parent is %lld\n",(long long)lseek(fd, 0, SEEK_CUR));flags = fcntl(fd, F_GETFL);if (flags == -1) fprintf(stderr, "fcntl - F_GETFL");printf("O_APPEND in parent is %s\n",(flags & O_APPEND) ? "on" : "off");exit(EXIT_SUCCESS);}return 0;
}
/* :!cc fork-file-shared.c && ./a.out */
/* File offset before fork: 0 */
/* O_APPEND flag before fork() is off */
/* child has exited */
/* file offset in parent is 1000 */
/* O_APPEND in parent is on */
新进程创建-节约资源版: vfork()
最好不要用vfork
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main(int argc, char *argv[]) {int istack = 222;switch (vfork()) {case -1:fprintf(stderr, "vfork");exit(1);case 0: // childsleep(3);write(STDOUT_FILENO, "child executing\n", 16);istack *= 3;_exit(EXIT_SUCCESS);default:write(STDOUT_FILENO, "Parent executing\n", 17);printf("istack=%d\n", istack);exit(EXIT_SUCCESS);}return 0;
}
// 子进程对变量的修改影响了父进程的对应变量/* child executing */
/* Parent executing */
/* istack=666 */
子进程会共享父进程的内存, 父进程会一直挂起直到子进程终止或者调用exec
fork竞态条件与同步
竞态条件
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>int main(int argc, char *argv[]) {int numChild, j;pid_t childPid;numChild = 100;setbuf(stdout, NULL); // 关闭缓存for (j = 0; j < numChild; ++j) {switch (childPid = fork()) {case -1:fprintf(stderr, "fork\n");case 0: // childprintf("%d child\n", j);_exit(EXIT_SUCCESS);default: // parentprintf("%d parent\n", j);wait(NULL);break;}}return 0;
}
几乎全是父进程先输出结果, 然后是子进程. 这就说明在Linux中fork执行之后会继续执行父进程, 而不是子进程.
不过, 这也取决于内核的调度算法实现.
所以不要对fork之后父子进程的执行顺序做任何假设, 如果一定要确保某一特定的执行顺序, 一定要采用某种进程间通信技术(同步技术), 例如: 文件锁, 信号量, 消息传送(基于管道, pipe).
使用信号机制解决进程竞态条件
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>#define BUF_SIZE 1000
#define SYNC_SIG SIGUSR1 /* Synchronization signal */static void /* Signal handler - does nothing but return */
handler(int sig) {}char *currTime(const char *format) {static char buf[BUF_SIZE]; /* Nonreentrant */time_t t;size_t s;struct tm *tm;t = time(NULL);tm = localtime(&t);if (tm == NULL) return NULL;s = strftime(buf, BUF_SIZE, (format != NULL) ? format : "%c", tm);return (s == 0) ? NULL : buf;
}int main(int argc, char *argv[]) {pid_t childPid;sigset_t blockMask, origMask, emptyMask;struct sigaction sa;setbuf(stdout, NULL); /* Disable buffering of stdout */sigemptyset(&blockMask);sigaddset(&blockMask, SYNC_SIG); /* Block signal */if (sigprocmask(SIG_BLOCK, &blockMask, &origMask) == -1)fprintf(stderr, "sigprocmask");sigemptyset(&sa.sa_mask);sa.sa_flags = SA_RESTART;sa.sa_handler = handler;if (sigaction(SYNC_SIG, &sa, NULL) == -1) fprintf(stderr, "sigaction");switch (childPid = fork()) {case -1:fprintf(stderr, "fork");case 0: /* Child *//* Child does some required action here... */printf("[%s %ld] Child started - doing some work\n", currTime("%T"),(long)getpid());sleep(2); /* Simulate time spent doing some work *//* And then signals parent that it's done */printf("[%s %ld] Child about to signal parent\n", currTime("%T"),(long)getpid());if (kill(getppid(), SYNC_SIG) == -1) fprintf(stderr, "kill");/* Now child can do other things... */_exit(EXIT_SUCCESS);default: /* Parent *//* Parent may do some work here, and then waits for child tocomplete the required action */printf("[%s %ld] Parent about to wait for signal\n", currTime("%T"),(long)getpid());sigemptyset(&emptyMask);if (sigsuspend(&emptyMask) == -1 && errno != EINTR)fprintf(stderr, "sigsuspend");printf("[%s %ld] Parent got signal\n", currTime("%T"),(long)getpid());/* If required, return signal mask to its original state */if (sigprocmask(SIG_SETMASK, &origMask, NULL) == -1)fprintf(stderr, "sigprocmask");exit(EXIT_SUCCESS);}
}/* :!cc fork-sig-sync.c -Wall && ./a.out */
/* [20:01:11 742971] Parent about to wait for signal */
/* [20:01:11 742977] Child started - doing some work */
/* [20:01:13 742977] Child about to signal parent */
/* [20:01:13 742971] Parent got signal */
进程终止
exit和_exit
#include <unistd.h>void _exit(int status); // syscall
status参数就是传入的终止状态.
#include <stdlib.h>void exit(int status); // libc
会依次执行下面三个步骤:
- 调用退出处理程序: 通过
atexit()
和on_exit()
注册的函数 - 刷新
stdio
流缓冲区 - 使用由
status
提供的值执行_exit()
系统调用
执行
return n;
相当于执行exit(n);
进程终止后的细节
- 关闭所有打开的文件描述符, 目录流, 信息目录描述符以及转换描述符
- 释放进程持有的所有文件锁
- 分离任何已连接的System V共享内存段
...
注册退出处理程序
#include <stdlib.h>int atexit(void (*func)(void)); // 出错返回非零值, 不一定为-1
其中传入的参数为一个参数列表和返回值均为void
的函数指针.
atexit函数会将传入的func函数指针加入到一个函数列表中, 进程终止时会调用该函数列表的所有函数.
以及一个类似的库函数: (可以传入状态)
#include<stdlib.h>int on_exit(void (*func)(int, void *), void *arg);
其中传入的参数为一个参数列表为int和指针, 返回值为void
的函数指针.
例子
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>static void atexitFunc1(void) { printf("atexit func1 called\n"); }
static void atexitFunc2(void) { printf("atexit func2 called\n"); }static void onexitFunc(int exitStatus, void *arg) {printf("on_exit func called: status=%d, arg=%ld\n", exitStatus, (long)arg);
}int main(int argc, char *argv[]) {if (on_exit(onexitFunc, (void *)10)) fprintf(stderr, "on_exit 1\n");if (atexit(atexitFunc1)) fprintf(stderr, "atexit 1\n");if (atexit(atexitFunc2)) fprintf(stderr, "atexit 2\n");if (on_exit(onexitFunc, (void *)20)) fprintf(stderr, "on_exit 2\n");return 0;
}/* on_exit func called: status=0, arg=20 */
/* atexit func2 called */
/* atexit func1 called */
/* on_exit func called: status=0, arg=10 */
监控子进程
wait(): 等待子进程的基本调用
等待调用进程的任一子进程终止, 同时在status所指向的缓冲区中返回子进程的终止状态.
#include<sys/wait.h>pid_t wait(int *status);
wait()执行如下动作:
- 如果调用进程(指父进程)的一个(先前未等待的)子进程已经终止, 调用将一直阻塞, 直至某个子进程终止. 如果调用时已有子进程终止, wait将立即返回
- status非空, 则存入状态信息.
- 内核将为父进程下所有子进程的运行总量追加进程CPU时间以及资源使用数据.
- 将终止子进程的ID作为结果返回.
出错情况: (返回值为-1)
调用进程没有(先前未等待的)子进程, 此时errno置为ECHILD. 所以可以用下面代码检测所有子进程是否退出.
while (childPid = wait(NULL) != -1) continue; if (errno != ECHILD) exit(1); // error
例子
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#define BUF_SIZE 1000
#define NUM 4
int times[NUM] = {0, 7, 1, 4};char *currTime(const char *format) {static char buf[BUF_SIZE]; /* Nonreentrant */time_t t;size_t s;struct tm *tm;t = time(NULL);tm = localtime(&t);if (tm == NULL) return NULL;s = strftime(buf, BUF_SIZE, (format != NULL) ? format : "%c", tm);return (s == 0) ? NULL : buf;
}int main(int argc, char *argv[]) {int numDead;pid_t childPid;int j;setbuf(stdout, NULL);for (j = 1; j < NUM; ++j) {switch (fork()) {case -1:fprintf(stderr, "fork\n");case 0:printf("[%s] child %d started with PID %ld, sleeping %d ""seconds\n",currTime("%T"), j, (long)getpid(), times[j]);sleep(times[j]);_exit(0);default:break;}}numDead = 0;for (;;) {childPid = wait(NULL);if (childPid == -1) {if (errno == ECHILD) {printf("No more children -bye!\n");exit(0);} else {fprintf(stderr, "wait\n");}}numDead++;printf("[%s] wait() return child PID %ld (numDead=%d)\n",currTime("%T"), (long)childPid, numDead);}
}
/* [08:09:53] child 2 started with PID 825604, sleeping 1 seconds */
/* [08:09:53] child 1 started with PID 825603, sleeping 7 seconds */
/* [08:09:53] child 3 started with PID 825605, sleeping 4 seconds */
/* [08:09:54] wait() return child PID 825604 (numDead=1) */
/* [08:09:57] wait() return child PID 825605 (numDead=2) */
/* [08:10:00] wait() return child PID 825603 (numDead=3) */
waitpid(): 升级版的wait
wait存在以下的一些限制:
- 如果父进程已经创建了多个子进程, 使用wait将无法等待某一个确定的 子进程完成, 只能按顺序等待下一个子进程终止.
- 如果没有子进程退出, wait总是保持阻塞, 但是有时候会希望执行非阻塞等待.
- 使用wait只能发现那些已终止的子进程, 对于子进程因为哪个信号终止或者已停止的子进程收到SIGCONT信号后恢复执行的情况不得而知.
#include <sys/wait.h>pid_t waitpid(pid_t pid, int *status, int options);
其中, 返回值和status参数的描述与wait相同, 下面是参数pid的含义:
- pid>0pid>0pid>0: 等待进程的PID为pid的子进程.
- pid=0pid=0pid=0: 等待与调用进程(父进程)同一个进程组的所有子进程.
- pid<−1pid<-1pid<−1: 等待进程组标识符与pid绝对值相等的所有子进程.
- pid=−1pid=-1pid=−1: 等待任意子进程.
wait(&status);
等价于waitpid(-1, &status, 0);
.
参数options是一个位掩码, 可以包含(按位或操作)0个或多个如下标志:
- WUNTRACED: 除了返回终止子进程的信息外, 还返回因信号而停止的子进程信息
- WCONTINUED: 返回因收到SIGCONT信号而恢复执行的已停止子进程的状态信息
- WNOHANG:
- pid指定的子进程状态未改变, 返回, 不阻塞(poll,轮询), waitpid返回0.
- 调用进程没有与pid匹配的子进程, 报错, errno=ECHILD.
程序的执行
execve: 将新程序加载到某一进程的内存空间
在这一过程中, 将丢弃旧有程序, 而进程的栈,数据以及堆内存都会被新程序的相应部件所替换.
在执行了各种C语言函数库的运行时启动代码以及程序的初始化代码之后, 新程序会从main()函数位置开始执行.
#include <unistd.h>int execve(const char *pathname, char *const argv[], char *const envp[]);
例子:
// envargs.c
#include <stdio.h>extern char **environ;int main(int argc, char *argv[]) {int j;char **ep;for (j = 0; j < argc; ++j) printf("argv[%d]=%s\n", j, argv[j]);for (ep = environ; *ep != NULL; ++ep) printf("environ: %s\n", *ep);return 0;
}// execve-1.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>int main(int argc, char *argv[]) {char *argVec[10];char *envVec[] = {"a=b", "c=d", NULL};argVec[0] = strrchr(argv[1], '/');if (argVec[0] != NULL)argVec[0]++;elseargVec[0] = argv[1];argVec[1] = "hello";argVec[2] = "goodbye";argVec[3] = NULL;execve(argv[1], argVec, envVec);fprintf(stderr, "execve\n");return 0;
}
system: 执行系统命令
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main (int argc, char *argv[])
{system("ls | wc");/* 13 13 170 */return 0;
}
相关文章:
Linux进程的创建结束类系统调用总结
tags: Linux OS Syscall C 写在前面 总结一下Linux系统的进程创建/终止/等待等系统调用, 参考: Linux/Unix系统编程手册. 下面主要给出例子, 关于函数原型可以参考书中或者man 2 syscall(例如man 2 fork). 测试环境: Ubuntu 20.04 x86_64 gcc-9 进程创建: fork() 用于创建…...
Git分支的合并策略有哪些?Merge和Rebase有什么区别?关于Merge和Rebase的使用建议
Git分支的合并策略有哪些?Merge和Rebase有什么区别?关于Merge和Rebase的使用建议1. 关于Git的一些基本原理1.1 Git的工作流程原理2. Git的分支合并方式浅析2.1 分支是什么2.2 分支的合并策略2.2.1 Three-way-merge(三向合并原理)2…...
2022-2-23作业
一、通过操作Cortex-A7核,串口输入相应的命令,控制LED灯进行工作 1.例如在串口输入led1on,开饭led1灯点亮 2.例如在串口输入led1off,开饭led1灯熄灭 3.例如在串口输入led2on,开饭led2灯点亮 4.例如在串口输入led2off,开饭led2灯熄灭 5.例如在串口输…...
1.基于Label studio的训练数据标注指南:信息抽取(实体关系抽取)、文本分类等
文本抽取任务Label Studio使用指南 1.基于Label studio的训练数据标注指南:信息抽取(实体关系抽取)、文本分类等 2.基于Label studio的训练数据标注指南:(智能文档)文档抽取任务、PDF、表格、图片抽取标注等…...
“高退货率”标签引热议,亚马逊跨境电商是好是坏?
在多数卖家不知情的情况下,亚马逊“高退货率”标签上线,该消息已被官方证实,目的是为了践行以客户为中心的理念和推动卖家提升服务。 官方确认上线“高退货率”标签 近期,有亚马逊卖家发现产品详情页出现了“高退货率”标签&…...
Pinia2
一、入门案例 1、安装 npm i pinia -S 2、注册插件 //main.ts import { createPinia } from pinia app.use(createPinia()) 3、创建store/countStore.ts import { defineStore } from "pinia"; const useCounterStore defineStore(counterStore,{ state(){ return{…...
服务器配置 | 在Windows本地打开服务器端Tensorboard结果
文章目录方法1:直接cmd使用ssh登录远程服务器方法2:利用Xshell设置本地端口进行监听方法3:利用MobaXterm设置本地端口监听这里介绍三个方法,在在Windows本地打开服务器端Tensorboard结果 方法1:直接cmd使用ssh登录远程…...
13 nuxt3学习(新建页面 内置组件 assets 路由)
新建页面 Nuxt项目中的页面是在 pages目录 下创建的 在pages目录创建的页面,Nuxt会根据该页面的目录结构和其文件名来自动生成对应的路由。页面路由也称为文件系统路由器(file system router),路由是Nuxt的核心功能之一 方式一…...
Linus命令记录(持续编辑版)
目录 一、前言 二、2023年2月查找Linus命令记录 1、竖线 |,双竖线 ||,&和&& 2、wc 3、free 和 top 4、c 库函数 strcpy() 5、c 库函数 memmove() 6、open 三、2023年3月查找Linus命令记录 1、sort 2、uniq 一、前言 有时候遇到不…...
玩转ThreadLocal
前言 ThreadLocal想必都不陌生,当多线程访问同一个共享变量时,就容易出现并发问题,为了保证线程安全,我们需要对共享变量进行同步加锁,但这又带来了性能消耗以及使用者的负担,那么有没有可能当我们创建一个…...
亚马逊二审来袭,跨境电商传统验证算法真的靠谱吗?
多个大卖突遭二审 已有卖家账号被封 近期有不少卖家在论坛上反映称自己收到了亚马逊的二次视频验证邮件。 邮件上称: 卖家必须要完成额外的身份审查,才有资格在亚马逊继续销售商品;亚马逊要求卖家出示注册时提交的身份证原件和营业执照原件…...
微信小程序|基于小程序+云开发制作一个租房小程序
经济发展的同时伴随着大批人群的流动,租房需求一直是持久不衰的话题,如何租好房,好租房,跟随此文一起制作一个租房小程序,让租房不再困难。 一、小程序1. 创建小程序2. 首页3. 房源列表页4. 房源详情页5. 个人中心页</...
2.4 群辉驱动:多网口,系统网络只能识别两个网口 解决教程
所需工具下载:链接:https://pan.baidu.com/s/1CMLl6waOuW-Ys2gKZx7Jgg?pwdchct提取码:chct安装的黑群晖华硕z490i主板自带一个i225 2.5G,后又插了一个4口8125B四口网卡,发现控制面板->网络->网络界面 只识别了其…...
Android正确使用资源res文件
观看此文注意首先有的UI改颜色,没用,发现无法更改按钮背景颜色。我的AS下载的是最新版本,Button按钮的背景颜色一直都是亮紫色,无法更改。为什么呢?首先在你的清单文件中看你应用的是哪个主题。我现在用的是这个可能你…...
5分钟搭建第一个k8s集群
急速上手Minikube搭建单节点 k8s集群实战什么是Minikube?环境准备安装步骤一.安装Docker1.安装yml2.设置阿里云镜像3.查看可安装的docker版本4. 安装docker5. 查看docker版本6.配置docker开机自启动7. 启动docker, 查看docker 启动状态二.安装k8s1.配置镜像源2.安装kubectl3.安…...
【MySQL】查询操作(基础篇)
目录 1、查询操作(Retrieve) 1.1 全列查询 1.2 指定列查询 1.3 查询字段为表达式 1.4 别名 1.5 去重:DISTINCT 1.6 排序:ORDER BY 1.7 条件查询:WHERE 1.8 分页查询 1、查询操作(Retrieve) 查询操作算的上是 SQL 中最复杂的操作了…...
工程管理系统+spring cloud 系统管理+java 系统设置+二次开发
工程项目各模块及其功能点清单 一、系统管理 1、数据字典:实现对数据字典标签的增删改查操作 2、编码管理:实现对系统编码的增删改查操作 3、用户管理:管理和查看用户角色 4、菜单管理:实现对系统菜单的增删改查操…...
MyBatisPlus Study Notes
文章目录1 MyBatisPlus概述1.1 MyBatis介绍1.2 MyBatisPlus特性2 标准数据层开发2.1 MyBatisPlus的CRUD操作API2.2 分页功能接口实现2.2.1 config(配置层)拦截器实现2.2.2 Dao(Mapper)数据访问层(CRUD)操作2.2.3 Junit单元测试进行…...
【Vu3 测试篇】自动化测试
一、为什么需要测试 自动化测试能够预防无意引入的 bug,并鼓励开发者将应用分解为可测试、可维护的函数、模块、类和组件。这能够帮助你和你的团队更快速、自信地构建复杂的 Vue 应用。与任何应用一样,新的 Vue 应用可能会以多种方式崩溃,因…...
Android system实战 — Android R(11) 第三方apk权限
Android system实战 — 第三方apk权限问题0. 前言1. 源码实现1.1 主要函数1.2 修改思路和实现1.2.1 修改思路1.2.2 方案一1.2.3 方案二0. 前言 最近在调试时遇到了第三方apk申请运行时权限,以及signature级别 install 权限不允许赋予给第三方apk,虽然这是…...
面试总结1
这里写目录标题什么是ORM?为什么mybatis是半自动的ORM框架?动态sqlJDBC步骤:jdbc的缺点:JDBC,MyBatis的区别:MyBatis相比JDBC的优势缓存一级缓存一级缓存在下面情况会被清除二级缓存最近在面试,发现了许多自…...
【Hello Linux】程序地址空间
作者:小萌新 专栏:Linux 作者简介:大二学生 希望能和大家一起进步! 本篇博客简介:简单介绍下进程地址空间 程序地址空间程序地址空间语言中的程序地址空间矛盾系统中的程序地址空间为什么要有进程地址空间思维导图总结…...
电脑崩溃蓝屏问题如何重装系统
电脑是我们日常生活和工作中必不可少的工具,但在使用过程中,难免会遇到各种问题,例如系统崩溃、蓝屏、病毒感染等,这些问题会严重影响我们的使用体验和工作效率。而小白一键重装系统可以帮助我们快速解决这些问题,本文…...
《商用密码应用与安全性评估》第一章密码基础知识1.2密码评估基本原理
商用密码应用安全性评估(简称“密评”)的定义:在采用商用密码技术、产品和服务集成建设的网络与信息系统中,对其密码应用的合规性、正确性、有效性等进行评估 信息安全管理过程 相关标准 国际:ISO/IEC TR 13335 中国:GB/T …...
【编程基础之Python】7、Python基本数据类型
【编程基础之Python】7、Python基本数据类型Python基本数据类型整数(int)基本的四则运算位运算比较运算运算优先级浮点数(float)布尔值(bool)字符串(str)Python数据类型变换隐式类型…...
Kakfa详解(一)
kafka使用场景 canal同步mysqlelk日志系统业务系统Topic kafka基础概念 Producer: 消息生产者,向kafka发送消息Consumer: 从kafka中拉取消息消费的客户端Consumer Group: 消费者组,消费者组是多个消费者的集合。消费者组之间互不影响,所有…...
图解LeetCode——剑指 Offer 12. 矩阵中的路径
一、题目 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相…...
particles在vue3中的基本使用
第三方库地址 particles.vue3 - npm 1.安装插件 npm i particles.vue3 npm i tsparticles2.在main.js中引入 import Particles from particles.vue3 app.use(Particles) // 配置相关的文件常用 api particles.number.value>粒子的数量particles.number.density粒子的稀密…...
04 Android基础--RelativeLayout
04 Android基础--RelativeLayout什么是RelativeLayout?RelativeLayout的常见用法:什么是RelativeLayout? 相对布局(RelativeLayout)是一种根据父容器和兄弟控件作为参照来确定控件位置的布局方式。 根据父容器定位 在相…...
python基础命令
1.现在包的安装路径 #pip show 包名 2.pip讲解 相信对于大多数熟悉Python的人来说,一定都听说并且使用过pip这个工具,但是对它的了解可能还不一定是非常的透彻,今天小编就来为大家介绍10个使用pip的小技巧,相信对大家以后管理和…...
网站seo收费/seo最新
原标题:Mac小知识--软件的三种安装/卸载方法,优缺点分析mac电脑怎么卸载软件?Mac系统如何卸载/删除软件?对于mac系统操作还不了解的用户不要着急,小编为大家带来Mac实用技巧之:三种安装/卸载软件的方法及其优缺点&…...
网站建设前期准备方案/百度关键词排名爬虫
2019独角兽企业重金招聘Python工程师标准>>> http://zilongsky-gmail-com.iteye.com/blog/2032001 转载于:https://my.oschina.net/u/1266221/blog/737971...
做网站的备案/百度一下你就知道手机版
正则表达式经常被用于字段或任意字符串的校验,比如下面这段校验基本日期格式的JavaScript代码:var reg /^(\\d{1,4})(-|\\/)(\\d{1,2})\\2(\\d{1,2})$/; var r fieldValue.match(reg); if(rnull)alert(Date format error!); 1、校验密码强…...
wordpress 简洁模板/市场营销策略有哪4种
文章目录1.标准参数1.1.概述1.2.-x参数1.2.1. java -version1.2.1.java -help 获取java参数帮助命令2.非标准参数2.1.-xx参数2.1.1.Boolean类型:是否使用启动参数2.1.2.非Boolean类型2.2.其他参数2.2.1.比如:设置栈深度 -Xss100K等价于-XX:ThreadStackSi…...
备案用网站建设方案/灰色行业推广
新的一年,本该在年前整理的年终总结被拖到了年后开工。去年大量时间投入在Catlike教程的翻译上,截止目前位置,教程的进度已经完全追平原作者。去年还有一部分是断断续续的更新SLG实战教程,但遗憾的是年前换工作了。SLG的游戏已经属…...
个人网站建设程序设计/google ads
Shell文本处理三剑客之一awk(2) 表达式与其他编程语言一样,awk表达式用于存储,操作和获取数据 一个awk表达式可由数值,字符常量,变量,操作符函数和正则表达式自由组合而成 变量是一个值的标识符,定义awk变量…...