C语言进程的相关操作
C语言进程的相关操作
进程简介
- 每个进程都有一个非负整数形式到的唯一编号,即PID(Process Identification,进程标识)
- PID在任何时刻都是唯一的,但是可以重用,当进程终止并被回收以后,其PID就可以为其他进程使用
- 进程的PID由系统内核根据延迟重用算法生成,以确保新进程的PID不同于最近终止进程到的PID
- 其中0号进程,叫做交换进程,系统内核中的一部分,所有进程的根进程,磁盘上没有它的可执行文件
- 1号进程是init进程,在系统自举过程结束时由调度进程创建,读写与系统相关的初始化文件,引导系统至一个特定状态,以超级用户特权运行的普通进程,永不终止
- 除去调度进程以外,系统中的每个进程都有一个唯一的父进程,对任何一个子进程而言,其父进程的PID即是它的PPID
- 下面这些函数都包含在
unistd.h
头文件中 pid_t getpid(void);
返回调用进程的PIDpid_t getppid(void);
返回调用进程的父进程的PIDuid_t getuid(void);
返回调用进程的实际用户IDgid_t getgid(void);
返回调用进程的实际组IDuid_t geteuid(void);
返回调用进程的有效用户IDgid_t getegid(void);
返回调用进程的有效组ID
创建子进程
-
创建子进程的函数包含在
unistd.h
头文件中 -
fork
函数pid_t fork(void);
- 功能:创建调用进程的子进程
- 返回值:失败返回-1,成功情况下返回的变量在父进程中是PID,在子进程中是0
- 可以通过这个返回值来执行父进程和子进程
- 当系统中的总的线程数达到了上限,或者用户的总进程达到了上限,
fork
函数会失败。
-
创建子进程示例代码
#include <stdio.h> #include <unistd.h>int main(void) {printf("haha\n");// 创建子进程int pid = fork();printf("heihei\n");return 0; }/* haha heihei heihei */
父子进程间的关系
-
以下是父子进程中数据相关
copy
的示例图
-
验证上图
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <time.h>int global = 100; // 父进程全局变量->数据区int main(void) { int local = 200; // 父进程局部变量->栈区 int *heap = malloc(sizeof(int)); // 动态分配内存->堆区 *heap = 3;printf("父进程第一次打印: PID->%d %p->%d %p->%d %p->%d\n", getpid(), &global, global, &local, local, heap, *heap); // 创建子进程 pid_t pid = fork(); if(pid == 0) { // 子进程操作,数据会从父进程copy一份过来,这里执行++操作 printf("子进程打印: PID->%d PPID->%d %p->%d %p->%d %p->%d\n", getpid(), getppid(), &global, ++global, &local, ++local, heap, ++*heap); return 0; } sleep(1); // 这里等1s,让子进程++ printf("父进程第二次打印: PID->%d %p->%d %p->%d %p->%d\n", getpid(), &global, global, &local, local, heap, *heap);return 0; }/* 父进程第一次打印: PID->1674604 0x5577e1acc010->100 0x7ffd4d4bfaa8->200 0x5577e23422a0->3 子进程打印: PID->1674605 PPID->1674604 0x5577e1acc010->101 0x7ffd4d4bfaa8->201 0x5577e23422a0->4 父进程第二次打印: PID->1674604 0x5577e1acc010->100 0x7ffd4d4bfaa8->200 0x5577e23422a0->3这里的父进程和子进程地址一样是虚拟地址里面一样,因为每个进程都有一个独立的虚拟地址池,相互不影响的 发现子进程跟父进程互相不影响,验证了上图的案例 */
-
父子进程操作文件,其实是共享一个文件表项的
-
验证上图
#include <stdio.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <time.h>int main(void) {// 父进程打开文件int fd = open("./test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664);if(fd == -1){perror("open");return -1;}// 父进程写入数据char *data = "hello bhlu!";if(write(fd, data, strlen(data)) == -1){perror("write");return -1;}// 创建子进程pid_t pid = fork();if(pid == 0){// 子进程修改文件读写位置if(lseek(fd, -5, SEEK_END) == -1){perror("lseek");return -1;}return 0;}// 再次插入数据,验证子进程修改的读写位置是否生效sleep(1); // 先等1s,让子进程执行完data = "linux\n";if(write(fd, data, strlen(data)) == -1){perror("write");return -1;}// 关闭文件close(fd);return 0; }/* cat test.txt hello linux 发现是修改成功的,说明上图是对的,子进程和父进程共用一个文件表项 */
进程的终止
以下内容只是简单的介绍进程的终止,以便理解
-
进程的终止分为两种
-
正常终止:分为三种情况
-
main函数中正常返回
-
使用
exit
函数终止:exit
函数可以在任何函数中执行令进程结束,return
语句只有在main
函数中执行才能令进程结束#include <stdlib.h>void exit(int status); /* 功能: 令进程终止 参数: status 进程的退出码,相当于main函数的返回值 无返回值 *//* exit函数在终止前会做以下几件收尾工作 1. 调用实现通过atexit或on_exit函数注册的函数退出函数 2. 冲刷并关闭所有仍处于打开状态的标准I/O流 3. 删除所有通过tmpfile函数创建的临时文件 4. 执行_exit(status); 使用exit函数令进程终止,通常使用EXIT_SUCCESS和EXIT_FAILUR两个宏 EXIT_SUCCESS -> 1; EXIT_FAILUR -> 0; */
-
调用
_exit/_Exit
函数令进程终止// _exit函数 #include <unistd.h>void _exit(int status); /* 参数: status 进程的退出码,相当于main函数的返回值 无返回值 */// _Exit函数 #include <stdlib.h>void _Exit(int status); /* 参数: status 进程的退出码,相当于main函数的返回值 无返回值 *//* _exit函数在终止前会做以下几件收尾工作 1. 关闭所有仍处于打开状态的文件描述符 2. 将调用进程的所有子进程托付过init进程 3. 向调用进程的父进程发送SIGCHLD(7)信号 4. 令调用进程终止运行,将status的低八位作为退出码保存在其终止状态中 */
-
-
异常终止
-
进程执行了系统认为具有危险性的操作时,或者系统本身发生故障或意外,内核会向进程发送特定的信号
SIGILL(4) -> 进程试图执行非法指令 SIGBUS(7) -> 硬件或对齐错误 SIGEPE(8) -> 浮点异常 SIGSEGV(11) -> 无效内存访问 SIGPWR(30) -> 系统供电不足
-
人为触发信号
SIGINT(2) -> Ctrl+c SIGQUIT(3) -> Ctrl+\ SIGKILL(9) -> 不能被捕获或忽略的进程终止信号 SIGTERM(15) -> 可以被捕获或忽略的进程终止编号
-
向进程自己发送信号
#include <stdlib.h>void abort(void); /* 功能: 想进城发送SIGABRT(6)信号,该信号默认情况下可以使进程结束 无返回值 */
-
-
在使用
exit
函数或main
函数正常退出时,如果注册了atexit
或on_exit
,那就会触发退出函数,以下是示例代码#include <stdio.h> #include <stdlib.h>void func(void) {exit(6); }void goto1(void) {printf("goto1\n"); }void goto2(int status, void *arg) {printf("status = %d\n", status);printf("arg = %s\n", (char *)arg); }int main(void) {atexit(goto1); // 退出之前执行goto1on_exit(goto2, "heihei"); // 退出之前执行goto2,可以传参func();return 0; }/* 相当于钩子函数,在退出之前执行,可以进行一些回收操作 status = 6 arg = heihei goto1 */
回收子进程
- 如果不回收子进程的话,会导致有很多僵尸进程的存在,从而消耗更多的系统资源。
- 父进程需要等待子进程到的终止,以继续后续工作
- 父进程需要了解子进程终止的原因,是正常终止,还是异常终止
阻塞回收
-
wait
函数是用于回收子进程的一个函数,它使用的是阻塞回收,使用它必须包含sys/wait.h
头文件 -
wait
函数-
pid_t wait(int *status);
-
功能:等待和回收任意子进程
-
参数:
status
用于输出子进程的终止状态,可置NULL
-
补充:可以使用以下工具宏分析子进程的终止状态
if(WIFEXITED(status))// 真printf("正常终止: 进程退出码是%d\n", WEXITSTATUS(status)); else// 假printf("异常终止: 终止进程的信号是%d\n", WTERMSIG(status));// 下面跟上面判断条件相反 if(WIFSIGNALED(status))// 真printf("异常终止: 终止进程的信号是%d\n", WTERMSIG(status)); else// 假printf("正常终止: 进程退出码是%d\n", WEXITSTATUS(status));
-
-
返回值:成功返回回收的子进程PID,失败返回-1
-
-
-
简单代码示例
// 子进程的回收 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h>int main(void) {// 创建子进程pid_t pid = fork();if(pid == -1){perror("fork");return -1;}// 子进程相关操作if(pid == 0){printf("%d进程: 我是子进程!\n", getpid());// sleep(5);// exit(3);// _exit(5);// return 2;// abort(); // 向进程发送信号异常结束// 以下两句会造成内存无效访问,会返回11char *p = NULL;*p = 123;}// 父进程等待回收子进程printf("%d进程: 我是父进程!\n", getpid());int s; // 用来输出所回收的子进程终止状态pid_t childpid = wait(&s);if(childpid == -1){perror("wait");return -1;}printf("父进程回收了%d进程的僵尸!\n", childpid);// 根据返回值判断子进程是否是正常结束if(WIFEXITED(s))printf("正常结束: %d\n", WEXITSTATUS(s));elseprintf("异常结束: %d\n", WTERMSIG(s));return 0; }
-
以下代码是一个循环创建5个进程,然后父进程挨个回收
#include <stdio.h> #include <unistd.h> #include <sys/wait.h> #include <errno.h>int main(void) {printf("%d进程: 我是父进程!\n--------------------------\n", getpid());sleep(1);// 创建子进程for(int i = 0; i < 5; i++){pid_t pid = fork();if(pid == -1){perror("fork");return -1;}// 子进程操作if(pid == 0){printf("%d进程: 我是子进程!\n", getpid());sleep(i+1);return i+1;}}// 父进程操作: 回收子进程while(1){int s; // 用户接收子进程的终止状态pid_t childpid = wait(&s);if(childpid == -1){if(errno == ECHILD){printf("没有子进程可以回收了!\n");break;}else{perror("wait");return -1;}}// 判断子进程的终止状态if(WIFEXITED(s))printf("正常结束: %d\n", WEXITSTATUS(s));elseprintf("异常终止: %d\n", WTERMSIG(s));}return 0; }
非阻塞回收
-
waitpid
函数一般用于非阻塞回收子进程,还可以回收特定子进程,使用这个函数需要引用sys/wait.h
头文件 -
waitpid
函数pid_t waitpid(pid_t pid, int *status, int options);
- 功能:等待并回收任意或特定子进程
- 参数
pid
:取-1等待并回收任意子进程,相当于wait
函数,>0等待回收特定子进程status
:用于输出子进程的终止状态,可置NULL
option
:0代表阻塞模式,WNOHANG
代表非阻塞模式,如果等待的进程还在运行,则返回0
- 返回值:成功返回回收子进程的PID或者0,失败返回-1
-
以下是使用非阻塞回收的方法回收子进程
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/wait.h> #include <errno.h>int main(void) {printf("%d进程: 我是父进程!\n-------------------------\n", getpid());// 创建子进程for(int i = 0; i < 5; i++){pid_t pid = fork();if(pid == -1){perror("fork");return -1;}// 子进程相关操作if(pid == 0){printf("%d进程: 我是子进程!\n", getpid());sleep(i+1);// 三种效果if(i == 3){abort();}else if(i == 4){char *p = NULL;*p = 123;}else{return i+1;}}}// 父进程回收子进程sleep(1);while(1){int s; // 用于保存进程的终止状态pid_t childpid = waitpid(-1, &s, WNOHANG); // 这里使用的是非阻塞模式if(childpid == -1){// 报错或者没有子进程了if(errno == ECHILD){printf("没有子进程了!\n");break;}else{perror("waitpid");return -1;}}else if(childpid == 0){// 子进程还在运行printf("子进程在运行,无法回收,先睡会!\n");sleep(2);}else{// 回收成功并判断是否正常终止printf("%d子进程回收成功!\n", childpid);if(WIFEXITED(s))printf("%d进程正常终止, 进程退出码: %d\n\n", childpid, WEXITSTATUS(s));elseprintf("%d进程异常终止, 终止进程信号: %d\n\n", childpid, WTERMSIG(s));}}return 0; }/* 代码执行效果 1761797进程: 我是父进程! ------------------------- 1761798进程: 我是子进程! 1761799进程: 我是子进程! 1761800进程: 我是子进程! 1761801进程: 我是子进程! 1761802进程: 我是子进程! 子进程在运行,无法回收,先睡会! 1761798子进程回收成功! 1761798进程正常终止, 进程退出码: 11761799子进程回收成功! 1761799进程正常终止, 进程退出码: 2子进程在运行,无法回收,先睡会! 1761800子进程回收成功! 1761800进程正常终止, 进程退出码: 31761801子进程回收成功! 1761801进程异常终止, 终止进程信号: 6子进程在运行,无法回收,先睡会! 1761802子进程回收成功! 1761802进程异常终止, 终止进程信号: 11没有子进程了! */
补充
- 实际情况下,无论进程是正常终止还是异常终止,都会通过系统内核向其父进程发送一个SIGCHLD(17)信号,我们可以提供一个针对该信号的处理函数,在信号处理函数中异步的方式回收子进程,这样不仅流程简单,回收效率还高,僵尸进程的存活时间也会很短。
创建新进程
与fork函数不同,这里使用的exec
函数是创建一个新的进程,新进程会取代调用自身的进程,新进程覆盖之前的进程地址空间,进程的PID不会改变。
-
exec
不是一个函数,而是一堆函数,功能一样,用法相似 -
#include <unistd.h>
-
int execl(const char *path, const char *arg, ...);
execl("/bin/ls", "ls", "-a", "-l", NULL); /* path使用的是路径名 使用NULL作为arg的结尾 失败返回-1,成功不返回 */
-
int execlp(const char *file, const char *arg, ...);
execlp("ls", "ls", "-a", "-l", NULL); /* file使用的是文件名,会从环境变量中一个个的找 使用NULL作为arg的结尾 失败返回-1,成功不返回 */
-
int execle(const char *path, const char *arg, ..., char *const envp[]);
char *envp[] = {"NAME=bhlu", "AGE=25", NULL}; execle("/usr/bin/env", "env", NULL, envp); /* 比excel多一个envp,用于设置环境变量,它设置什么,新进程的环境变量就只有什么 失败返回-1,成功不返回 环境变量输出:NAME=bhluAGE=25 */
-
int execv(const char *path, char *const argv[]);
char *argv[] = {"ls", "-a", "-l", NULL}; execv("/bin/ls", argv); /* execv系列使用的都是字符指针数组,字符数组是以NULL结尾 失败返回-1,成功不返回 */
-
int execvp(const char *file, char *const argv[]);
char *argv[] = {"ls", "-a", "-l", NULL}; execvp("ls", argv); /* 跟execv差不多,就第一个参数是文件名 失败返回-1,成功不返回 */
-
int execve(const char *path, char *const argv[], char *const envp[]);
char *argv[] = {"env", NULL}; char *envp[] = {"NAME=bhlu", "AGE=25", NULL}; execve("/usr/bin/env", argv, envp); /* 跟execle函数差不多,就是这里的第二个参数是字符指针数组 失败返回-1,成功不返回 */
-
-
后缀不同,代码的含义也不同
l
:即list
,新进程的命令以字符指针列表形式传入,列表以空指针结束p
:即path
:第一个参数,不包含/
,就根据PATH
环境变量搜索文件e
:即environment
:设定环境变量,不指定则从调用进程复制v
:即vector
:新进程的命令行参数以字符指针数组的形式传入,数组以空指针结束- 实际底层最后使用的都是
execve
函数
-
使用
exec
函数基本会将原进程的所有信号、属性、数据等都丢失或者恢复初识状态,只有PID
、PPID
、UID
等会被继承下来。 -
一般都会先创建一个子进程,然后在子进程中使用
exec
函数,以下是相关示例#include <stdio.h> #include <unistd.h>int main(void) {// 创建子进程pid_t pid = fork();if(pid == -1){perror("fork");return -1;}// 子进程相关操作if(pid == 0){char *argv[] = {"env", NULL};if(execvp("/bin/env", argv) == -1){perror("execvp");return -1;}}// 父进程操作printf("父进程PID: %d\n", getpid());return 0; }
system
-
下面介绍的是
c
语言执行shell
命令的函数 -
#include <stdlib.h>
int system(const char *command);
- 功能:执行
shell
命令 - 参数:
shell
命令,如果参数取NULL
,返回非0表示Shell
可用,返回0表示不可用 - 返回值:成功返回
command
进程的终止状态, 失败返回-1
- 功能:执行
-
代码实例
#include <stdio.h> #include <stdlib.h> #include <unistd.h>int main(void) {int s = system("echo $PATH");if(s == -1){perror("system");return -1;}printf("父进程PID: %d\n", getpid());return 0; }
-
system
函数内部调用了vfork
、exec
和waitpid
等函数,而且它是标准库函数,可以跨平台使用- 如果调用
vfork
或waitpid
函数出错,则返回-1 - 如果调用
exec
函数出错,则在子进程中执行exit(127)
- 如果都成功,会从
waitpid
获取command
进程的终止状态
- 如果调用
相关文章:
C语言进程的相关操作
C语言进程的相关操作 进程简介 每个进程都有一个非负整数形式到的唯一编号,即PID(Process Identification,进程标识)PID在任何时刻都是唯一的,但是可以重用,当进程终止并被回收以后,其PID就可…...
数据结构学习系列之链式栈
链式栈:即:栈的链式存储结构;分析:为了提高程序的运算效率,应采用头插法和头删法;进栈: int push_link_stack(stack_t *link_stack,int data) {if(NULL link_stack){printf("入参合理性检…...
too many session files in /var/tmp
Linux中Too many open files 问题分析和解决_e929: too many viminfo temp files-CSDN博客...
【7.0】打开未知来源安装应用
默认打开未知来源安装应用 frameworks\base\packages\SettingsProvider\res\values\defaults.xml <bool name"def_install_non_market_apps">false</bool>...
安装ipfs-swarm-key-gen
安装ipfs-swarm-key-gen Linux安装go解释器安装ipfs-swarm-key-gen Linux安装go解释器 https://blog.csdn.net/omaidb/article/details/133180749 安装ipfs-swarm-key-gen # 编译ipfs-swarm-key-gen二进制文件 go get -u github.com/Kubuxu/go-ipfs-swarm-key-gen/ipfs-swarm…...
BASH shell脚本篇5——文件处理
这篇文章介绍下BASH shell中的文件处理。之前有介绍过shell的其它命令,请参考: BASH shell脚本篇1——基本命令 BASH shell脚本篇2——条件命令 BASH shell脚本篇3——字符串处理 BASH shell脚本篇4——函数 在Bash Shell脚本中,可以使用…...
ElementUI之首页导航及左侧菜单(模拟实现)
目录 编辑 前言 一、mockjs简介 1. 什么是mockjs 2. mockjs的用途 3. 运用mockjs的优势 二、安装与配置mockjs 1. 安装mockjs 2. 引入mockjs 2.1 dev.env.js 2.2 prod.env.js 2.3 main.js 三、mockjs的使用 1. 将资源中的mock文件夹复制到src目录下 2. 点击登…...
Java开源工具库使用之Lombok
文章目录 前言一、常用注解1.1 AllArgsConstructor/NoArgsConstructor/RequiredArgsConstructor1.2 Builder1.3 Data1.4 EqualsAndHashCode1.5 Getter/Setter1.6 Slf4j/Log4j/Log4j2/Log1.7 ToString 二、踩坑2.1 Getter/Setter 方法名不一样2.2 Builder 不会生成无参构造方法2…...
uboot启动流程涉及reset函数
一. uboot启动流程中函数 之前了解了uboot链接脚本文件 u-boot.lds。 从 u-boot.lds 中我们已经知道了入口点是 arch/arm/lib/vectors.S 文件中的 _start。 本文了解 一下,uboot启动过程中涉及的 reset 函数。本文继上一篇文章学习,地址如下ÿ…...
端口被占用怎么解决
第一步:WinR 打开命令提示符,输入netstat -ano|findstr 端口号 找到占用端口的进程 第二步: 杀死使用该端口的进程,输入taskkill /t /f /im 进程号( !!!注意是进程号,不…...
python reportlab 生成多页pdf
多页 from reportlab.pdfgen import canvas from reportlab.platypus import (SimpleDocTemplate, Paragraph, PageBreak, Image, Spacer, Table, TableStyle) from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY from reportlab.lib.styles import P…...
word 多级目录的问题
一、多级标题自动编号 --> 制表符 -> 空格 网址: 【Word技巧】2 标题自动编号——将多级列表链接到样式 - YouTube 二、多级列表 --> 正规形式编号 网址:Word 教学 - 定框架:文档格式与多级标题! - YouTube 三、目…...
python使用mitmproxy和mitmdump抓包之拦截和修改包(四)
我认为mitmproxy最强大的地方,就是mitmdump可以结合python代理,灵活拦截和处理数据包。 首先,mitmdump的路径如下:(使用pip3 install mitmproxy安装的情况,参考我的文章python使用mitmproxy和mitmdump抓包…...
邓俊辉《数据结构》→ “2.6.5 二分查找(版本A)”之“成功查找长度”递推式推导
【问题描述】 邓俊辉的《数据结构(C语言版)(第3版)》(ISBN:9787302330646)中,开始于第48页的“2.6.5 二分查找(版本A)”内容在第50页详述了“成功查找长度”的…...
Linux文件查找,别名,用户组综合练习
1.文件查看: 查看/etc/passwd文件的第5行 [rootserver ~]# head -5 /etc/passwd root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologi…...
【MATLAB第77期】基于MATLAB代理模型算法的降维/特征排序/数据处理回归/分类问题MATLAB代码实现【更新中】
【MATLAB第77期】基于MATLAB代理模型算法的降维/特征排序/数据处理回归/分类问题MATLAB代码实现 本文介绍基于libsvm代理模型算法的特征排序方法合集,包括: 1.基于每个特征预测精度进行排序(libsvm代理模型) 2.基于相关系数corr的…...
第三章 图标辅助元素的定制
第三章 图标辅助元素的定制 1.认识图表常用的辅助元素 图表的辅助元素是指除了根据数据绘制的图形之外的元素,常用的辅助元素包括坐标轴、标题、图例、网格、参考线、参考区域、注释文本和表格,它们都可以对图形进行补充说明。 上图中图表常用辅…...
【前端】ECMAScript6从入门到进阶
【前端】ECMAScript6从入门到进阶 1.ES6简介及环境搭建 1.1.ECMAScript 6简介 (1)ECMAScript 6是什么 ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在2015年6月正式发布了。它的目标ÿ…...
Android Shape设置背景
设置背景时,经常这样 android:background“drawable/xxx” 。如果是纯色图片,可以考虑用 shape 替代。 shape 相比图片,减少资源占用,缩减APK体积。 开始使用。 <?xml version"1.0" encoding"utf-8"?…...
什么是GraphQL?它与传统的REST API有什么不同?
聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 什么是GraphQL?⭐ 与传统的REST API 的不同⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 欢迎来到前端入门之旅!感兴趣的可以订阅本专栏哦!这个专栏是为那些对Web开发感兴趣…...
如何定时备份使用Docker构建的MySQL容器中的数据库
👨🏻💻 热爱摄影的程序员 👨🏻🎨 喜欢编码的设计师 🧕🏻 擅长设计的剪辑师 🧑🏻🏫 一位高冷无情的编码爱好者 大家好,我是 DevO…...
Java【手撕链表】LeetCode 143. “重排链表“, 图文详解思路分析 + 代码
文章目录 前言一、两数相加1, 题目2, 思路分析2,1 找到中间结点2.2, 逆序后半段链表2.3, 合并两个链表 3, 代码 前言 各位读者好, 我是小陈, 这是我的个人主页, 希望我的专栏能够帮助到你: 📕 JavaSE基础: 基础语法, 类和对象, 封装继承多态, 接口, 综合小练习图书管…...
C语言 cortex-A7核 按键中断 实验【重点】
一、KEY1 include/key.h #ifndef __KEY_H__ #define __KEY_H__#include "stm32mp1xx_rcc.h" #include "stm32mp1xx_gpio.h" #include "stm32mp1xx_exti.h" #include "stm32mp1xx_gic.h"//RCC/GPIO章节初始化 void hal_rcc_gpio_init…...
freertos中函数调用和启动第一个任务(栈相关!!!!!!)
本内容仅就一些较难理解的点讲解,请结合其它文章实用 在函数调用时,m3的处理器使用r0-r3共四个寄存器传参,其余的使用栈传参。 但是,如果传入的参数是全局变量,则不需传参,因为全局变量在函数内部是可见的…...
【PHP】如何关闭buffer实时输出内容到前端
前言 默认情况下,我们在PHP里使用echo等函数输出的内容,是不会马上发送给前端的,原因是有 buffer 的存在,buffer又分两处,一处是PHP本身的buffer,另一处是Nginx的buffer。只有当buffer满了之后,…...
Scala第二章节
Scala第二章节 scala总目录 章节目标 掌握变量, 字符串的定义和使用掌握数据类型的划分和数据类型转换的内容掌握键盘录入功能理解Scala中的常量, 标识符相关内容 1. 输出语句和分号 1.1 输出语句 方式一: 换行输出 格式: println(里边写你要打印到控制台的数据);方式二…...
Spring修炼之路(2)依赖注入(DI)
一、概念 依赖注入(Dependency Injection,DI)。 测试pojo类 : Address.java 依赖 : 指Bean对象的创建依赖于容器 . Bean对象的依赖资源 . 注入 : 指Bean对象所依赖的资源 , 由容器来设置和装配 . 二、 注入方式 2.1构造器注入 我们在之前的案例已经…...
编写Android.mk / Android.bp 引用三方 jar 包,aar包,so 库
一.前言 在Android10之后,所有项目工程中,官方推荐使用Android.bp去编译构建,以前使用Android.mk构建的项目随着版本迭代升级,慢慢需要变更为Android.bp, 两者的语法都需要去了解并熟练使用。 笔者之前写过Android.mk的…...
【kylin】【ubuntu】搭建本地源
文章目录 一、制作一个本地源仓库制作ubuntu本地仓库制作kylin本地源 二、制作内网源服务器ubuntu系统kylin系统 三、使用内网源ubuntukylin 一、制作一个本地源仓库 制作ubuntu本地仓库 首先需要构建一个本地仓库,用来存放软件包 mkdir -p /path/to/localname/pac…...
为什么 Go 语言 struct 要使用 tags
在 Go 语言中,struct 是一种常见的数据类型,它可以用来表示复杂的数据结构。在 struct 中,我们可以定义多个字段,每个字段可以有不同的类型和名称。 除了这些基本信息之外,Go 还提供了 struct tags,它可以用…...
免费发布信息平台网/搜索引擎优化与推广技术
当前,区块链被视为新一代互联网的底层技术,并逐渐应用到金融、物流、传媒等领域。在应用的过程中,区块链网络的运行与维护对算力的要求也越来越高,如今北京市正在建设的区块链算力平台就是支撑区块链场景应用的重要基建之一。 什…...
wordpress部署https/廊坊首页霸屏优化
集群配置:1个nsqlookupd, 1个nsqadmin,3个nsqd 分区:1个order-topic,分区数为100,副本数为3 扩容时,新增一个nsqd-4。刚开始,nsqd-4没有任何分区副本。 接下来通过nsqadmin页面发现ÿ…...
数字尾巴+wordpress/互联网培训机构排名前十
查找题目描述:输入数组长度 n输入数组 a[1...n]输入查找个数m输入查找数字b[1...m]输出 YES or NO 查找有则YES 否则NO 。输入:输入有多组数据。每组输入n,然后输入n个整数,再输入m,然后再输入m个整数(1<m<n<…...
wordpress滑动相册/域名注册查询网站
背景当前业务存在以下场景:在一个事务内的最后一步是发送kafka消息,消费端收到通知后读取数据并做处理。但是由于kafka几乎是即时收到消息,导致偶尔出现“在发完kafka和提交事务的间隙,消费端收到了消息并读取到了事务提交前的数据…...
速升网站/搜索引擎优化技巧
一、单项选择题(本大题共20小题,每小题2分,共40分)在每小题列出的四个备选项中只有一个是符合题目要求的,请将其代码填写在题后的括号内。错选、多选或未选均无分。1.下面不属于网络操作系统功能的是(B …...
linux服务器下如何新建网站/厦门seo排名优化公司
文章目录一、使用场合二、磁环的作用1. 防止大功率设备影响到数据线2. 防止数据线影响到设备接收灵敏度三、磁环EMC应用一、使用场合 产品使用形态上常见的是,两种电子产品通过数据线相连进行数据交互或者主从控制。尤其是在通信产品中,为了扩展通信产品…...