Linux进程控制详解
目录
- 前言
- 一、进程创建
- 1.1 fork函数初识
- 1.2 写时拷贝
- 1.3 fork常规用法
- 1.4 fork调用失败的原因
- 二、进程终止
- 2.1 进程终止时,操作系统做了什么??
- 2.2 进程终止的常见方式有哪些??
- 2.3 如何用代码终止一个进程
- 三、进程等待
- 3.1 进程等待的必要性
- 3.2 进程等待的方法
- wait方法
- waitpid方法
- 补充知识
- 四、waitpid进一步讲解
- 五、进程替换
- 5.1 概念及原理
- 5.2 操作
- 5.2.1 不创建子进程
- execl
- 5.2.2 创建子进程
- execv
- execlp
- execvp
- execle + 执行其他的C二进制程序
- 执行其他语言的程序
- 5.3 execve
- 六、自己实现一个简易的shell程序
- 6.1 函数介绍
- 6.2 myshell.c完整代码
- 总结
前言
前面的文章都是关于进程概念的学习,今天我们就要进入一个新的章节,关于进程控制的解析,这其中我会穿插着前面已经讲过的知识,在以前知识的基础上学习新的知识,如果其中有哪里不懂的地方,大家可以去看一看我前面的文章,下面我们一起来学习新的知识吧。
一、进程创建
1.1 fork函数初识
在Linux中fork函数是非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
进程调用fork,当控制转移到内核中的fork代码后,内核做:
1.分配新的内存块和内存数据结构给子进程
2.将父进程部分数据结构内容拷贝至子进程
3.添加子进程到系统进程列表中
4.fork返回,开始调度器调度
fork之后,父子进程代码的共享情况
1.2 写时拷贝
通常,父子代码共享,父子在不写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式变为各自一份副本,具体可见下图
写时拷贝的好处: 因为有写时拷贝技术的存在,所以,父子进程得以彻底分离!完成了进程独立性的技术保证!
写时拷贝是一种延时申请技术,可以提高整机内存的使用率
1.3 fork常规用法
1.一个父进程希望复制自己,使父子进程同时执行不同的代码段。
2.一个进程要执行不同的程序
1.4 fork调用失败的原因
1.系统中有太多的进程
2.实际用户的进程数超过了限制(我们平时在写代码的时候,进程数其实是有限的)
二、进程终止
2.1 进程终止时,操作系统做了什么??
当然是释放进程申请的相关内核数据和对应的数据和代码,本质就是释放系统资源。
2.2 进程终止的常见方式有哪些??
a.代码跑完,结果正确
b.代码跑完,结果不正确
c.代码没有跑完,程序崩溃了(这部分涉及到了信号部分,我们先说一点点,后面再详细的说)
这里我们主要分析a、b两条内容,我先提一个问题:
大家平时在写C/C++程序的时候,一定都会写main函数,main函数最后都会有return 0,那么这里的return 0的含义是什么呢?为什么总是0,main函数返回的意义是什么???
在做下面的实验前我再补充一个小的知识点
echo $?:获取最近一个进程,执行完毕的退出码。
现在相信大家已经知道main函数最后的return 0是什么含义了,每一个退出码都有着不同的含义,我们通过下面的这个函数把C语言中的退出码是什么含义打印一下看看。
我们今天学习完退出码的概念,大家以后写代码的时候要可以将其应用起来,大家一起看下面的这个例子
int main()
{FILE* fp = fopen();if(fp == NULL)return 1;return 0;
}
当我们需要打开一个文件的时候,就可以使用上述的if判断语句来判断我们的这个文件是否被成功打开,当程序的退出码为1的时候,就是不允许操作,即这个文件打开失败,这样就可以更快地排查错误。
a、b两条内容分析完后,我们还要谈一谈c的内容,代码没有跑完,程序崩溃了。
2.3 如何用代码终止一个进程
什么是一个正确的终止???
1.return
main函数内的return语句,就是终止进程的,return 退出码
只有在main函数返回,进程退出
调用一些其他的函数,return则是退出当前的函数并返回一个值。
2.exit,C语言层面的函数
通过上面的情况再与return进行比较我们可以发现:
return只有在main函数中有结束进程的作用,exit在代码的任何位置调用,都表示直接终止进程!!!
3._exit,系统层面的函数
上面图片中的_exit与_Exit的作用类似,我们只用_exit来举例子,让大家看一下与_exit与exit有什么不同之处
从图中大家可以看到_exit()与exit()的区别,在平时写代码的时候还是建议大家使用exit()函数。
三、进程等待
在讲述进程等待前我们先来试想两种场景
1.子进程退出,父进程不管子进程,子进程就要处于僵尸状态,一直处于僵尸状态不释放资源,就会导致内存泄漏
2.父进程创建了子进程,是要让子进程完成一些任务的,那么子进程把任务完成得怎么样?父进程需不需要关心,如果需要,怎么得知子进程的情况,如果不需要,又要怎么处理呢?
子进程把任务完成的情况也是分为三种情况:
a.代码跑完,结果正确
b.代码跑完,结果不正确
c.代码没有爬完,程序崩溃了
3.1 进程等待的必要性
1.子进程退出,父进程如果不管不顾,就可能造成僵尸进程的问题,进而造成内存泄漏。另外,进程一旦变成僵尸状态,那就刀枪不入,即使是kill -9也无能为力,因为谁也没有办法杀去一个已经死去的进程。
2.最后,父进程派给子进程的任务完成的如何,我们其实是需要知道的,比如子进程运行完成后,结果对不对,或者最后是否正常退出,都是我们需要考虑的问题
父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息
3.2 进程等待的方法
我们先来看一下不让父进程去回收子进程的资源的情景,来进行一下对比
在上面的代码中,我们使用fork()函数创建了一个子进程,然后让其循环五次,打印同一行代码,循环结束后,结束进程,而父进程则一直进行循环,这样当子进程结束后,父进程还在死循环,无法收集子进程的资源,那么子进程就将成为僵尸进程。下面我们来看结果
这样就导致了僵尸进程的产生,那么如何解决这个问题呢?
wait方法
主要验证回收僵尸进程的问题
wait接口,我们可以通过man 2 wait来查询关于wait的信息
当然其中也有waitpid的信息。
下面我们先来简单认识一下wait()
下面我们来看一下实验代码以及运行结果
实验一:我们先来看一下如果父进程属于睡眠状态不调用wait函数,子进程是不是还是变为僵尸进程
运行结果分析
实验二:当父进程不处于睡眠状态时,又是一种怎样的情景
运行结果分析
父进程:wait时,父进程处于阻塞式等待,父进程处于阻塞态,等子进程退出。当子进程退出时,操作系统再将父进程唤醒,放到运行队列里,由父进程调用wait函数,再返回。
父进程处于阻塞式的等待意味着: 子进程不退出,父进程也不退出
waitpid方法
进程异常退出或者崩溃,本质是操作系统杀掉了你的进程!!
操作系统如何杀掉的呢?本质是通过发送信号的方式!
下面我们来看一下实验代码以及运行结果
实验一:子进程正常结束
运行结果
实验二:子进程非正常结束(1)
注: 当进程非正常结束时,进程的退出码就没有任何意义,主要观察进程收到的信号编号
运行结果
关于信号的信息我们可以通过kill -l(小写的l)来查看
其中1~31为普通信号,没有0号信号
实验二:子进程非正常结束(2)
运行结果
实验二:子进程非正常结束(3)
程序异常,不光光是内部的代码有问题,也可能是外力杀掉了子进程(子进程代码有没有跑完并不确定)
上面的代码我们让子进程死循环,那么这个进程就会一直运行
此时我们在另一个窗口将该进程杀掉
最后父进程收到的信号编号就是9号信号
总结:
1.如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息
2.如果在任意时刻调用wait/waitpid,子进程存在企鹅撑场运行,则进程可能阻塞
3.如果不存在该子进程,则立即出错返回
4.往后我们编写多进程,基本的写法就是fork + wait/waitpid
补充知识
1.父进程通过wait/waitpid可以拿到子进程的退出结果,为什么要用wait/waitpid函数呢?可不可以使用全局变量?
答:不可以
因为我们在前面就说过进程具有独立性,那么数据就要发生写时拷贝,父进程无法拿到子进程内的数据,更何况是信号了
2.既然进程是具有独立性的,那进程退出码也应该是子进程的数据,父进程凭什么能拿到呢,wait/waitpid究竟做了什么事情?
答:wait/waitpid的本质其实是读取子进程的task_struct结构。
3.这又和task_struct结构有啥关系呢??
其实最后进程退出的时候,一个进程的退出码和退出信号会被写到task_struct中(task_struct结构中有exit_pid,exit_signal两个变量),僵尸进程也是一样的,子进程退出,那么他的代码和数据就不会被运行和应用,就可以释放相关的数据,但是至少他会保留该进程的PCB信息,task_struct里面保留了任何进程退出时的退出结果信息, wait/waitpid读取task_struct结构后再将exit_pid,exit_signal(两个字段)进行位操作设置到传入的status变量中,然后再返回出来,就可以得到子进程的信号编号和退出码
4.wait/waitpid有这个权利么?
答:肯定有这个权利
wait/waitpid属于系统调用函数,本质是操作系统去拿取task_struct中的数据,而task_struct是内核数据结构对象,所以操作系统可以对其进行读取也就没有任何问题了。
四、waitpid进一步讲解
options中的WNOHANG选项。
WNOHANG选项,代表父进程非阻塞等待!
非阻塞等待:我们的父进程通过调用waitpid来进行等待,如果子进程没有退出,我们waitpid这个系统调用会立刻返回。
了解了上面的宏定义后,我们先来使用一下,顺便再结合而代码详细介绍一下我们上面所讲的内容
下面我们来看一下waitpid的伪代码
阻塞等待我们已经讲述完毕,后面就是非阻塞等待
非阻塞等待我们还是通过代码来演示
其实非阻塞等待的最大意义在于:
当子进程还在处理自己事情的时候,父进程不需要进入阻塞状态,父进程依然可以去做一些自己的事情,当子进程处理完自己的事情以后,父进程再去回收子进程的资源和数据。
运行结果:
通过上面的运行结果,我们可以看到,当子进程还在运行的时候,父进程可以腾出来时间来处理自己的任务,这就是非阻塞等待的好处。
如果未来编写网络代码的话,大部分都是IO类别,会不断面临阻塞和非阻塞的接口
五、进程替换
5.1 概念及原理
fork()之后,父子代码是共享的,数据写时拷贝,各自一份,父子进程各自执行父进程代码的一部分,但是如果子进程就想执行一个全新的程序呢?
进程的程序替换可以完成这个功能,程序替换是通过特定的接口,加载磁盘上的一个权限的程序(代码和数据),加载到调用进程的地址空间中,阿里让子进程执行其他的程序
可能上面说的内容比较抽象,大家不好理解,那么我们下面就通过画图的方式,来剖析一下进程替换的原理
5.2 操作
关于进程替换的函数有以下六个,看似很复杂,其实还是比较好理解的
下面我们先来进行一下基本的演示
1.不创建子进程
2.创建子进程
从这两个方面介绍,同时只使用最简单的exec函数,先看一看进行进程替换后会发生什么事情
后面我们会详细展开其他函数的用法
5.2.1 不创建子进程
execl
第一个我们要介绍的函数就是execl
其实该函数的可变参数列表可以参考我们平时最常用的命令行参数
下面我们就以平时用的最多的ls命令进行举例,先来看一下ls命令
红框内就是ls的路径,那么我们下面就来用ls命令进行进程替换,先看代码
在代码中,我在main函数的开始和结尾都加上了一句打印,这样就可以看到进程进行的情况,然后中间去调用函数进行进程替换。
代码运行结果
通过代码运行的结果,我们可以看到一些问题
程序刚开始运行的时候,打印了第一句代码,后面进行了程序替换,执行了ls的命令,同时也将其对应的结果打印了出来,但是最后面的一行代码并没有被打印出来,这个就是程序替换的特性
针对代码结果进行分析
1.为什么最后一句代码不打印呢??
execl是程序替换,调用该函数成功之后,会将当前进程的所有代码和数据都进行替换,包括已经执行的和没有执行的
简单来说,就是执行execl之后,execl之前和之后的代码都会被替换掉,只不过之前的代码运气比较好,在替换之前就已经执行完了,所以才有了最开始的打印结果。而后面所有的代码,全都不会执行
2.execl的返回值
我们看手册的介绍可以得知,如果调用失败,会返回-1,但是调用成功,并不会返回值,这又是为什么呢?
为什么调用成功没有返回值呢?
这个其实也很好理解,因为execl根本不需要进行函数返回值判定,如果替换成功,execl会将main函数中包含execl本身的代码全部都替换掉,前面用于接受返回值的数据也都已经被替换了,所以也就不需要有返回值了。
如果想让我们打印的字体也颜色的话,就可以看ls的内容再加一些代码
讲解后面知识之前,我们先来回忆一下之前的内容
加载新程序之前,父子的数据和代码的关系:
代码共享,数据写时拷贝
当子进程加载新程序的时候,就是一种写入,那么这个时候代码要不要进程写时拷贝呢?
一定会进行写时拷贝,父子进程的代码必须要分离
所以当我们调用execl函数的时候,父子进程在代码和数据上就彻底分开了。
5.2.2 创建子进程
为什么要创建子进程呢?
为了不影响父进程,我们想让父进程聚焦在读取数据,分析数据,指派进程执行代码的功能,如果不创建,那么我们替换的进程只能是父进程,如果创建了,替换的进程就是父进程,从而不会影响父进程
在上面我们已经看了execl的使用,所以这里就不多说了,关于exec系列还有几个函数,我们一起来看一下
execv
实验代码及结果
execlp
实验代码及结果
execvp
实验代码及结果
以上的代码基本都没有什么问题,那么我们来探究一个新的问题
1.如何执行其他我自己写的C、C++二进制程序
2.如何执行其他语言的程序
execle + 执行其他的C二进制程序
Makefile操作延伸
在Makefile中替换单词
例如:将Makefile中所有的exec替换为myshell
1.先按Esc
2.输入 :(英文冒号)
3.%s/exec/myshell/g + 回车
exec.c代码
mycmd.c代码
经过我们上面的代码,成功地实现了一个程序调用另一个程序
回顾 + 总结: 环境变量具有全局属性,可以被子进程继承下去,通过execle就可以将父进程的环境变量传给子进程
执行其他语言的程序
我们这里就简单的演示一下执行python语言的程序和执行shell程序
python代码,只输出一下,能够看到我们调用了即可
shell程序代码
运行python: python test.py
运行test.sh: bash test.sh
如果我们使用shmod + x test.py也可以执行python程序,这句话的意思就是给test.py加上可执行权限,就可以将test.py变为可执行程序,那么也就能直接执行该程序,了解了这些以后,下面开始进行一下简单的调用
1.调用python程序
1.调用shell程序
以上就是调用其他程序的全部内容。大家只需要了解就可以了
5.3 execve
其实在程序替换中,操作系统只提供了这一个接口,这个才是真正的系统调用接口。前面讲的函数是系统提供了基础封装(C语言做的封装),从而用来满足上层的不同的调用场景,上面的函数接受的数据经过整合后再传给execve函数
execve没有带p,所以需要带全路径
六、自己实现一个简易的shell程序
shell执行的命令通常有两种
1.第三方提供的对应的在磁盘中有具体二进制文件的可执行程序(由子进程执行)
2.shell内部,自己实现的方法,由自己(父进程)来执行(shell代表的是用户)
有些命令就是要影响shell本身,比如cd,export
我们在自己写shell的时候需要创建子进程去完成命令,为什么呢?
因为我们执行的一些命令可能会出现一些错误,子进程执行的话只会影响子进程的运行,不会影响到父进程。
6.1 函数介绍
fgets函数介绍
strtok函数介绍
chdir函数介绍
父进程设置的环境变量,传递到子进程的模拟实现
我们在父进程可能会设置环境变量,环境变量是具有全局性的,所以我们设置的环境变量也是会传到子进程的,下面我们自己来模拟一下其过程
我们再写一个测试程序,看看子进程是否能得到我们在父进程设置的环境变量
运行我们自己写的shell,并设置环境变量,并且调用mytest程序
总结: 程序替换会替换代码和数据,但是与系统有关的数据比如环境变量,其相关的数据就不会被替换,同时也体现出来了环境变量具有全局属性,进程替换一定和应用场景有关,我们有时候必须让子进程执行新的程序
补充: shell的环境变量是从哪里来的?
环境变量是写在配置文件中的,shell启动的时候,通过读取配置文件获得的起始环境变量。
linux中的.bash_profile 、.bashrc 、/etc/bashrc等文件中就有环境变量的信息
6.2 myshell.c完整代码
总结
以上就是进程控制全部的内容了,可以看到其中涉及到的知识点还是非常的多的,有的也不是很好理解,所以我们要多去看,多去总结,同时自己也要将这些函数全都用一下,这样才可以加深印象,如果文章中有错误的话,希望大家评论指出,我后面一定会改正,最后,如果你感觉我的文章对你有用的话,就给波三连吧!!谢谢大家
相关文章:
Linux进程控制详解
目录前言一、进程创建1.1 fork函数初识1.2 写时拷贝1.3 fork常规用法1.4 fork调用失败的原因二、进程终止2.1 进程终止时,操作系统做了什么??2.2 进程终止的常见方式有哪些??2.3 如何用代码终止一个进程三、进程等待3.…...
C语言深度剖析之程序环境和预处理
1.程序的翻译环境和执行环境 第一种是翻译环境,在这个环境中源代码被转换为可执行的机器指令 第二种是执行环境,它用于实际执行代码 2.翻译环境 分为四个阶段 预编译阶段 ,编译,汇编,链接 程序编译过程:多个…...
【Spark分布式内存计算框架——Spark Core】9. Spark 内核调度(上)
第八章 Spark 内核调度 Spark的核心是根据RDD来实现的,Spark Scheduler则为Spark核心实现的重要一环,其作用就是任务调度。Spark的任务调度就是如何组织任务去处理RDD中每个分区的数据,根据RDD的依赖关系构建DAG,基于DAG划分Stag…...
Vulkan教程(15): Graphics pipeline之Render passes(渲染通道)
Vulkan官方英文原文: https://vulkan-tutorial.com/Drawing_a_triangle/Graphics_pipeline_basics/Render_passes对应的Vulkan技术规格说明书版本: Vulkan 1.3.2Setup设置Before we can finish creating the pipeline, we need to tell Vulkan about the…...
乐观锁、雪花算法、MyBatis-Plus多数据源
乐观锁、雪花算法、MyBatis-Plus多数据源e>雪花算法2、乐观锁a>场景b>乐观锁与悲观锁c>模拟修改冲突d>乐观锁实现流程e>Mybatis-Plus实现乐观锁七、通用枚举a>数据库表添加字段sexb>创建通用枚举类型c>配置扫描通用枚举d>测试九、多数据源1、创建…...
详解Redisson分布式限流的实现原理
我们目前在工作中遇到一个性能问题,我们有个定时任务需要处理大量的数据,为了提升吞吐量,所以部署了很多台机器,但这个任务在运行前需要从别的服务那拉取大量的数据,随着数据量的增大,如果同时多台机器并发…...
[python入门㊹] - python测试类
目录 ❤ 断言方法 assertEqual 和 assertNotEqual assertTrue 和 assertFalse assertIsNone 和 assertIsNotNone ❤ 一个要测试的类 ❤ 测试AnonymousSurvey类 ❤ setUp() 和 teardown() 方法 ❤ 断言方法 常用的断言方法: 方法 用途 assertEqual(a, b) 核实a …...
Web 框架 Flask 快速入门(二)表单
课程地址:Python Web 框架 Flask 快速入门 文章目录🌴 表单1、表单介绍2、表单的简单实现1. 代码2. 代码的执行逻辑3、使用wtf扩展实现4、bug记录:表单验证总是失败🌴 表单 1、表单介绍 当我们在网页上填写账号密码进行登录的时…...
C++基础(5) - 复合类型(上)
文章目录数组1、什么是数组2、数组的声明3、数组的初始化4、数组的访问5、二维数组6、memset —— 给数组中每一个元素赋同样的值字符串(字符数组)1、string.h 头文件1.1 strlen()1.2 strcmp()1.3 strcpy()1.4 strcat()string 类简介1、C11 字符串初始化…...
java重写(@Override)介绍及实例说明
1.概述方法的重写(override)是封装的特性之一。在子类中可以根据需要对基类中继承来的方法进行重写。重载和重写没有任何关系。作用:通过重写,子类既可以继承父类的东西,又可以灵活的扩充。1.override注解是告诉编译器…...
基于STM32的虚拟示波器
仓库地址 https://github.com/shuai132/ScopeMCU ScopeMCU Oscilloscope for MCU MCU: STM32F103C8Tx 需配合ScopeGUI使用 截图说明见wiki 最新版Releases Introduction 用最少的硬件成本,做一个实用的虚拟示波器。 这是硬件部分,基于STM32最小…...
搭建云端vscode-server,使用web ide进行远程开发
使用乌班图系统,搭建自己的网页vs code开发环境github地址:GitHub - coder/code-server: VS Code in the browser安装脚本curl -fsSL https://code-server.dev/install.sh | sh出现deb package has been installed.表示已经正确安装。测试启动2.1修改配置…...
Linux clock子系统及驱动实例
文章目录基本概念CLK子系统时钟API的使用clock驱动实例1、时钟树2、设备树3、驱动实现fixed_clk固定时钟实现factor_clk分频时钟实现gate_clk门控时钟实现基本概念 晶振:晶源振荡器 PLL:Phase lock loop,锁相环。用于提升频率 OSC:…...
GIS数据格式坐标转换(地球坐标WGS84、GCJ-02、火星坐标、百度坐标BD-09、国家大地坐标系CGCS2000)
文章目录前言一、坐标系1.地球坐标 (WGS84)2.国测局坐标系(GCJ-02、火星坐标系)3.百度坐标(BD-09)4.国家大地2000坐标系(CGCS2000)二、百度坐标系(BD-09) 与火星坐标系(GCJ-02)的转换1.核心代码2.转换验证百度地图高德地图腾讯地图三、火星坐标系 (GCJ-02) 与百度坐标系 (BD-09…...
流媒体传输系列文章汇总
流媒体传输系列文章汇总 文章目录流媒体传输系列文章汇总引言流媒体交互协议详解视频封装协议详解流媒体环境搭建其他引言 从去年开始编写有关流媒体传输相关知识的文章,已发表文章22篇,阅读量也超过了10万,为了方便各位阅读,本文…...
“万字“ Java I/O流讲解
Java I/O流讲解 每博一文案 谁让你读了这么多书,又知道了双水村以外还有一个大世界,如果从小你就在这个天地里,日出而作,日落而息。 那你现在就会和众乡亲抱同一理想:经过几年的辛劳,像大哥一样娶个满意的…...
数据库(Spring)事务的四种隔离级别
文章目录Spring(数据库)事务隔离级别分为四种(级别递减)1、Serializable(串行化)2、REPEATABLE READ(可重复读)3、READ COMMITTED(读以提交)4、Read Uncommit…...
RabbitMQ详解(一):RabbitMQ相关概念
RabbitMQ是目前非常热门的一款消息中间件,不管是互联网大厂还是中小企业都在大量使用。作为一名合格的开发者,有必要对RabbitMQ有所了解,本系列是RabbitMQ快速入门文章,主要内容包括RabbitMQ是什么、RabbitMQ核心概念、五种消息模…...
ICLR 2023 | GReTo:以同异配关系重新审视动态时空图聚合
©PaperWeekly 原创 作者 | 周正阳单位 | 中国科学技术大学论文简介动态时空图数据结构在多种不同的学科中均普遍存在,如交通流、空气质量观测、社交网络等,这些观测往往会随着时间而变化,进而引发节点间关联的动态时变特性。本文的主要…...
线程池分享总结
线程池介绍 可以复用线程池的每一个资源 控制资源的总量 为什么要使用线程池 问题一:反复创建线程开销大 问题二:过多的线程会占用太多内存 解决以上两个问题的思路 • 用少量的线程——避免内存占用过多 • 让这部分线程都保持工作,且可…...
AOSP Android11系统源码和内核源码
推荐阅读 商务合作 安全产品 安全服务 2023年招聘 安全培训服务 软件定制服务 Android系统定制服务 安全/软件开发的课程列表 1.下载repo工具 (1).创建bin,并加入到PATH中 mkdir ~/binPATH~/bin:$PATH (2).安装依赖库 sudo apt-get install bison g-mult…...
layui框架学习(6:基础菜单)
菜单是应用系统的必备元素,虽然网页中的导航也能作为菜单使用,但菜单和导航的样式和用途有所不同(不同之处详见参考文献5)。Layui中用不同的预设类定义菜单和导航的样式,同时二者依赖的模块也不一样。本文主要学习和记…...
第十三届蓝桥杯 C++ B组省赛 C 题——刷题统计(AC)
1.刷题统计 1.题目描述 小明决定从下周一开始努力刷题准备蓝桥杯竞赛。他计划周一至周五每天 做 aaa 道题目, 周六和周日每天做 bbb 道题目。请你帮小明计算, 按照计划他将在 第几天实现做题数大于等于 nnn 题? 2.输入格式 输入一行包含三个整数 a,ba,ba,b 和 nnn. 3.输出…...
C++中的多态
【1】表现形式:同样的调用语句有多种不同的表现形态 【2】分类:静态联编和动态联编 静态联编有函数重载(运算符重载是特殊的函数重载),模板 【3】重点说下动态联编 【3.1】动态联编的实现需要以下步骤: 有继承关系、父类函数有virtual关…...
Swift如何保证线程安全
Swift可以通过以下几种方式来保证线程安全 使用互斥锁(Mutex):使用互斥锁可以防止多个线程同时访问共享数据,保证线程安全。 使用OSAtomic操作:OSAtomic操作可以在多线程环境中安全地执行原子操作。 使用DispatchQue…...
整型提升+算术转换——“C”
各位CSDN的uu们你们好呀,今天小雅兰的内容是之前操作符那篇博客中没有讲完的内容,整型提升这个小知识点也非常重要,那现在,就让我们进入操作符的世界吧 隐式类型转换 算术转换 操作符的属性 隐式类型转换 表达式求值的顺序一部…...
Freemarker介绍
2. Freemarker介绍 FreeMarker 是一个用 Java 语言编写的模板引擎,它基于模板来生成文本输出。FreeMarker与 Web 容器无关,即在 Web 运行时,它并不知道 Servlet 或 HTTP。它不仅可以用作表现层的实现技术,而且还可以用于生成 XML…...
【软件测试开发】Junit5单元测试框架
目录1. 注解Test 注解BeforeEach BeforeAllAfterEach AfterAll2. 断言 assertassertequalsassertTrue assertFalseassertNull assertNotNull3. 用例执行顺序方法排序,通过 Order 注解来排序4. 测试套件 Suite5. 参数化单参数stringsints6. 参数化多参数CsvSourceCsv…...
【C语言技能树】程序环境和预处理
Halo,这里是Ppeua。平时主要更新C语言,C,数据结构算法......感兴趣就关注我吧!你定不会失望。 🌈个人主页:主页链接 🌈算法专栏:专栏链接 我会一直往里填充内容哒! &…...
数据库的三大范式
1.为什么需要数据库设计 设计数据表的时候,要考虑很多的问题: 用户需要哪些数据,我们在数据表中要保存哪一些数据怎么保证数据表中的数据的正确性如何降低数据表的冗余度开发人员怎么才能更方便的使用数据库 如果数据库设计得不合理的话,可…...
邯郸网站建设的地方/新手如何学seo
1、类SQL PLUS窗口:File->New->Command Window,这个类似于oracle的客户端工具sql plus,但比它好用多了。2、设置关键字自动大写:Tools->Preferences->Editor,将Keyword case选择Uppercase。这样在窗口中输入sql语句时,…...
wordpress用户中心授权码/营业推广策略
阅读本文前,请您先点击上面的蓝色字体,再点击“关注”,这样您就可以继续免费收到最新文章了。每天都有分享。完全是免费订阅,请放心关注。 …...
太原网站建设随州/杭州企业seo
看到有人在用std::copy这个东西,很简洁和爽啊,,所以找些帖子学习学习 http://blog.sina.com.cn/s/blog_8655aeca0100t6qe.html https://www.so.com/s?qstd%3A%3Acopy%E5%87%BD%E6%95%B0&ieutf-8&srcse7_newtab_new copy函数的函数原型: 1 //fist [IN]: 要拷贝元素的…...
aws的efs可以做网站的什么/博客营销案例
一、各种规则正则表达式 1、大于0的正整数 /^[1-9]\d*$/2、是否是数字和一个英文,分开 const idContent this.form.activityId.replace(/[^\d,]/g, ) this.form.activityId idContent.replace(new RegExp(,, gm), ,)3、只能是一个英文,分开 const idContent this.adddata.…...
系部网站开发计划/全面落实疫情防控优化措施
走进异步编程的世界 - 在 GUI 中执行异步操作 【博主】反骨仔 【原文地址】http://www.cnblogs.com/liqingwen/p/5877042.html 序 这是继《开始接触 async/await 异步编程》、《走进异步编程的世界 - 剖析异步方法》后的第三篇。主要介绍在 WinForm 中如何执行异步操作。 目…...
凡科网做网站要钱吗/成都网站seo性价比高
1、前言分页显示是一种非常常见的浏览和显示大量数据的方法,属于web编程中最常处理的事件之一。对于web编程的老手来说,编写这种代码实在是和呼吸一样自然,但是对于初学者来说,常常对这个问题摸不着头绪,因此特地撰写此…...