3. 进程概念
目录
1. 冯诺依曼体系结构
2. 操作系统
3. 理解进程的一般思路
4. 查看进程
5. fork初识
6. 进程状态
6.1 一般操作系统
6.2 Linux系统是怎么维护进程状态的
7. 进程优先级
先谈硬件-再谈软件-最后谈进程。
1. 冯诺依曼体系结构
我们常见的计算机(笔记本电脑),不常见的计算机(服务器)都遵守冯诺依曼体系。
我们所认识的计算机,都是由一个个的硬件组成:
- 中央处理器(cpu):含有运算器和控制器
- 运算器:对数据进行数据运算、逻辑运算
- 控制器:对计算机硬件流程进行一定控制
- 输入单元:键盘、鼠标、磁盘、网卡
- 输出单元:显示器、打印机、磁盘、网卡
- 存储器:指的是内存,磁盘是外存。
输入设备和输出设备统称为外设-有的设备纯输入、输出,也有的既是输入也是输出。
上图中5个单元都是独立个体,各个单元用“线”连接,这种线就叫“总线”。其中:
- 内存和cpu之间的线:系统总线
- 内存和输入输出设备之间的线:IO总线
关于这个体系,必须要强调:
- CPU只能对存储器进行读写,不能直接访问输入输出设备。
- 外设要输入或输出数据,也只能写入内存或从内存中读取。
为什么呢?
核心原因在于:外设与CPU之间处理数据的速度差距太大了,如果输入输出设备直接与CPU连接,由于木桶原理,整机效率趋向于外设的效率,cpu处理完了,歇着了,其他两兄弟还忙不停。
这时候引入了内存,内存就像和事佬,催输入输出设备的同时,又能平复cpu的状态,最后整机的效率趋向于内存的效率。输入设备将数据放入内存中时,cpu可能进行其他计算,算完再处理内存中的数据,这样就可以实现硬件间的并行。这是由操作系统完成的,相当于把数据预加载到内存中,cpu直接运算就可以了,不用和外设交互。
这里的存储器是硬件级别的存储空间,正因为有存储器,才能让计算机走进千家万户。
下面我们来看一个具体的例子:
解释从登上qq,和某位朋友发信息聊天,数据的流动过程。(不考虑网络,还没学)
如果传的是一个文件,那么我的输入设备就是磁盘,把文件读取到内存中,然后.....
朋友收到后,点下载,就存到朋友的磁盘中,显示器只是显示一下。
2. 操作系统
在整个计算机软硬件架构中,操作系统的定位是:纯正的搞管理的软件-管理底层的软硬件资源。
计算机层状结构图:
最底层包括各种各样的硬件,硬件上面是驱动,硬件被软件访问需要驱动程序,再往上是操作系统。操作系统之上还有系统调用接口,用系统调用接口封装出来的库、外壳、指令供给用户使用。
系统调用和库函数的关系:上下层的调用和被调用的关系。开发者对部分系统调用进行适度封装,从而形成了库,有利于更上层的用户进行二次开发。
为什么要有操作系统?
- 帮助用户管理好下面的软硬件资源。
- 为了给用户提供一个良好(稳定、高效、安全)的运行环境。
操作系统通过管理好底层的软硬件资源(手段),为用户提供一个良好的执行环境(目的)。
操作系统为什么要提供系统调用接口呢?
操作系统里面有很多各种各样的数据,但是操作系统不相信任何用户,因为群众里面有坏人,操作系统为了保证自己的数据安全,也为了能给用户提供服务,操作系统以接口的方式给用户提供调用的入口,来获取系统内部的数据。(银行对外服务的小窗口)
这个接口是操作系统提供的用C实现的,自己内部的函数调用。这就是系统调用。
所有访问操作系统的行为,都只能通过系统调用完成。
那操作系统是怎么做到可以管理软硬件的呢?
举个例子:
- 最典型的管理者:校长
- 我们就是最典型的被管理者:学生
校长在管理学生时,并没有直接与学生见面,才能管理学生。
所以管理者和被管理者是不需要见面的。
不见面怎么做到管理的呢?
校长只需要知道一个学生的基本信息,比如姓名,学号,电话,家庭住址等等,就可以对学生进行管理,所以见面不是必要的,只要能够获得管理信息,就可以进行管理决策。
管理的本质:通过对数据的管理,达到对人的管理。
校长和学生面都不见,怎么获得学生的数据呢?通过辅导员(执行者)。
那么对应到计算机结构中:
- 校长--管理者--操作系统
- 辅导员--执行者--驱动程序
- 学生--被管理者--各种软硬件资源
学生把自己的各种信息交给辅导员,辅导员再把数据传给校长。
此时校长手里有上万学生的数据,怎么进行高效的管理呢?通过数据结构:
上万个个体,每个人的信息都不一样,但是他们有一个共同点就是学生,他们的信息种类是相同的:每个人都有名字,学号等等。
这时就可以抽象出一个学生结构体,其中的成员就是学生的各种信息,同时包含指向下个学生结构体的指针。用每个学生的信息初始化这个结构体,就得到了上万的结构体对象,通过指针相连,此时校长只需要把这个学生链表管理好就可以了。成功的将对学生的管理工作转化成了对链表的增删查改。
对学生抽象出学生结构体的过程被称为“描述”,把一堆学生结构体连接起来的过程就是“组织”。
先描述一个,再组织一堆。在操作系统中,管理任何对象,最终都可以转化成对某种数据结构的增删查改。
其实语言的学习过程就是为了管理二字做准备的,C++先学类就是学习如何描述,后面的STL等库,容器本质就是各种数据结构,就是在学如何组织。
操作系统是怎么进行进程管理的呢?
先把进程描述起来,再把进程组织起来。
3. 理解进程的一般思路
教材中说:一个已经加载到内存中的程序就是进程,也说正在运行的程序叫做进程。
一个操作系统,不仅仅只能运行一个进程,可以同时运行多个进程。所以操作系统必须将进程管理起来,如何管理进程呢?先描述,再组织!
人类是怎么辨认一个事物或对象的:通过事物的属性认识的。当这个事物的属性足够多,这一堆属性的集合,就是那个事物。
所以任何一个进程,在加载到内存的时候,形成真正的进程时,除了要加载自己的代码和数据之外,操作系统要先创建描述进程(属性)的结构体对象--PCB--process ctrl block--进程控制块。
PCB就是进程属性的集合。(描述)
进程 = 内核PCB数据结构对象 + 磁盘中的可执行程序和数据
所以对进程做管理就是对PCB对象做管理,PCB中包含指针信息,可以让操作系统找到该进程对应的代码。内存中的操作系统中把各个进程的PCB通过某种数据结构连接起来,这就是组织。
此时对进程进行管理就变成了对数据结构进行增删查改。
那么具体的Linux是怎么做的呢?
Linux下的PCB就是 task_struct结构体,里面包含进程的所有属性,组织进程task_struct的方式是双向链表。
4. 查看进程
1.动态运行的所有进程可以通过 ls /proc 系统文件夹查看。
2.其中蓝色的目录就是PID,查看某个具体的进程:
其中cwd:current work dir:当前进程的工作目录,在创建进程时就会创建这个属性,比如这个proc.c:
fopen就会默认在这个工作目录下创建文件,而不是什么别的目录,也不需要自己指定。
3. ps axj 也可以用来查看
ps axj | head -1 可以显示各项进程属性。
最后的COMMAND是指令,也就是什么指令形成的这个进程。
可为什么我们过滤proc过滤出来两个,因为grep本身也是进程,让他过滤proc,就得把proc这个信息传给他,它就有了proc这个信息,所以把自己也滤出来了。(侧面证明指令也是进程)
想去掉也很简单:
想杀掉进程也很简单:kill -9 进程pid
ps的原理就是遍历PCB链表,拿到每个task_struct中的内容,显示出来。
接下来是我人生中第一个系统调用接口:
getpid():获得当前进程id
getppid():获得当前进程父进程的id
printf("pid:%d\n", getpid());printf("ppid:%d\n", getppid());
pid每次运行都会改变,ppid不变。
我们可以发现14970对应的进程正是bash,也就是命令行命令,所以每一条指令或者我们自己的程序都属于 bash 的子进程。bash就是那个用来解释命令行的进程。
不过如果我们重登一次ssh,bash的id就变了
5. fork初识
我们之前创建进程的方式是运行我们的可执行程序,fork是一个函数,而且它也可以创建进程。
fork很特别,fork有两个返回值,一个函数,有两个返回值,这简直闻所未闻。
int main()
{printf("begin:我是一个进程,pid:%d,ppid:%d\n",getpid(), getppid());pid_t id = fork();if(id == 0){while(1){printf("我是子进程,pid:%d,ppid:%d\n", getpid(), getppid());sleep(1);}}else if(id > 0){while(1) { printf("id:%d,我是父进程,pid:%d,ppid:%d\n",id, getpid(), getppid()); sleep(1);}}else{//error}
我们可以发现,根据fork返回值id的不同,两个分支里的while循环在同时跑,这就很抽象。
我们首先发现,这个可执行程序一执行,他自己就是一个进程,有自己的 pid=16735,ppid=14970,这个pid就是自己,ppid就是bash。
接下来,进入父进程循环的id值,也就是fork的返回值,是16736,也就是子进程的pid,这个父进程本身的pid是16735,ppid是14970,它的pid和这个程序的pid一样,ppid和bash一样,说明这个父进程就是可执行程序本身。
子进程的id是0,pid是16736,ppid是16735,也就是父进程。
接下来我们解释几个问题:
1.为什么 fork 要给子进程返回0,给父进程返回子进程的pid
生活中,一个父亲可以有多个孩子,但是一个孩子只有一个父亲;在进程中,父进程可以有多个子进程,所以为了能让父进程区分是哪个子进程,所以要给父进程返回子进程的pid,但是子进程就不用区分父亲是谁,因为有且仅有一个,所以只需要给它一个0标识一下他是子进程就可以了。
2. 一个函数是如何做到返回两次的?如何理解?
fork创建了一个子进程,既然系统中多了一个进程,那么当然要把它管理起来,就有了它的PCB,进程是由PCB和代码数据构成的,但是它自己又没有代码和数据,所以代码他会直接用父进程的。所以,fork后的代码共享。
既然代码都一样,为什么要创建这个子进程呢?直接让父进程干不就好了?创建子进程,当然是为了让父子去干不同的事情,想让他们做不同的事,就得让他们执行不同的代码块!代码虽然一样,但是fork的返回值不一样,父子根据fork返回的id执行同一份代码中不同的部分!
那这是怎么实现的呢?这就不得不谈谈fork的实现了。
我们已经知道了fork是用来创建子进程的,创建的过程如下:
- 创建子进程PCB并填充
- 让子进程和父进程指向相同的代码
- 父子进程都有自己的PCB,可以被CPU调度运行了
- ......
- return ret;
我们发现在最后返回结果之前,就已经创建好子进程了,创建好以后代码就共享了,return ret属于代码内容!所以return ret 也有两个!所以有两个返回值。子进程的代码中返回0,父进程的代码中返回子进程的id,可是这两个不同的id在我们的代码中是用一个变量id接收的。
3. 一个变量id怎么会有不同的内容?如何理解?
我们知道我们的可执行程序本身就是我们所说的父进程。那这个id是什么呢?id
只是一个存储 fork()
返回值的变量,不是父进程的代码,而是运行时的数据。我们前面提到,创建好子进程之后的代码会共享,那数据会不会也共享呢?
进程的核心设计原则是相互隔离,一个进程的崩溃不应影响另一个进程。因为数据可能被修改,不能让父进程和子进程共享同一份数据!如果共享数据,子进程的错误操作(如越界写入)可能破坏父进程的内存。
而子进程没数据是不行的,所以必须拷贝一份父进程的数据,子进程的数据不是共享的,是新拷贝出来的,又因为父进程中有大量的数据,子进程中可能都不会用到,如果全都拷贝会造成空间的浪费。
所以当子进程尝试去修改父进程中的某个数据时,OS就会拷贝给子进程那个数据,这就是数据层面的“写时拷贝”。用多少,给多少。
回归问题,fork在返回时,是往id里写入数据,父进程返回时直接写入id就行了,子进程的话因为试图修改父进程中的id,会发生写时拷贝,最后父子进程的数据id是不同的。
4.那同一个id是怎么做到让我们看到不同的值?涉及到地址空间,现在暂不做表述
5. fork应用例子
bash为了保证解释命令时如果失败了不影响自己,通过创建子进程的方式完成的命令解释。那bash如何创建子进程--->用了fork,子进程去执行命令,他自己继续解释命令。
6. 进程状态
为了弄清楚显示出来的进程都是什么状态,他们在干嘛,我们需要知道每种状态符代表什么意思。一般教材中经常讲到运行、阻塞、挂起。我们先讲一下一般的操作系统学科中提到的状态都是什么意思,再讲讲Linux是怎么具体实现的。
6.1 一般操作系统
运行态:
我们现在有一堆进程,OS要把他们管理起来,也就是管理了一堆PCB,操作系统要把他们放到CPU中去运行,那肯定是有序的,不能乱来,那么操作系统中就有了一个运行队列,运行队列的头指向PCB链表的头,运行队列的尾指向PCB链表的尾,运行队列中这些已经准备好,可以随时被CPU调度的进程的状态,就叫运行态。
所以处于运行队列中的进程就处于运行状态了,并不是真正要在CPU中运行起来才算运行态。也就是说运行状态并不意味着进程一定在运行中,可能是在运行队列里。
问题:一个进程只要把自己放到CPU上开始运行,是不是要一直执行完毕,才把自己放下来?
不是!每一个进程都有一个叫做时间片的概念,操作系统分配给每个可运行进程的一段CPU执行时间,时间片用尽后,进程会被剥夺CPU使用权,放回运行队列,等待下次调度。所以在某个时间段内,所有的进程都会被执行,这种大量把进程放到CPU上然后拿下来的操作称为进程切换。
我们感受不到这种切换是因为实在是太快了,不要用我们对时间的感受去衡量CPU。
阻塞状态:
在等待特定设备的进程,叫做该进程处于阻塞状态。
操作系统是管理软硬件资源的,当然也要把底层的硬件管理起来,描述+组织,每个设备都被不同的进程所访问,比如键盘,准备进行读取以后,我就是不输入,它就一直接收不到,此时这个进程就放在键盘的等待队列中,一直等待设备就绪。
输入后,这个进程就放到运行队列中,进入运行态。每个设备都有一个等待队列,存放那些等待设备就绪的进程。
唤醒:就是把进程放到运行队列中,从阻塞状态变成运行状态。
挂起:
操作系统中管理很多进程,内存容易不足,要保证正常情况下,省出来内存。阻塞或等待状态时,这个进程的代码也没有在运行,处于空闲状态,干占空间,所以只保留PCB就行了,把数据和代码放到磁盘中(换出),用到的时候再拿回来(换入)。
某进程的代码没被使用,放到了磁盘中,这种状态的进程称为挂起。
6.2 Linux系统是怎么维护进程状态的
1. R(running)
这个是有打印内容的进程 :
这个是没打印内容的进程:
我们发现,有打印内容时,进程的状态是S+,没打印内容是的进程状态是R+,但是这个进程确实一直在运行啊,为什么不是运行状态呢?
因为printf涉及到IO设备,显示器,CPU等待显示器准备好的时间是相当长的,对于他来说,告诉显示器打印只需要一瞬间,剩下的就一直在等,CPU运行的那一刹那难以捕捉,不要用我们的感受去感受CPU的速度!
而如果不需要打印的话,那就一直while循环,不需要其他设备参与,当然一直是R状态了。
+号代表这个进程是前台进程,在前台运行时,此时当前渠道不能输入其他指令,可以使用ctrl+c停止进程。./proc & 就是后台运行了,此时只能用kill -9杀进程。
2. S(sleeping)浅度睡眠,可以被唤醒
上面的代码会等待我们输入值,如果不输入,就一直等待,此时是S状态,所以阻塞其实就是S
大部分进程其实都是S,在等待某种资源就绪,bash同理。
3. D(disk sleeping)深度睡眠,是阻塞的一种
有这样一个场景可以说明深度睡眠是干嘛用的:
某一个进程要往磁盘里写1GB数据,机械磁盘写入时,进程需要等待写入结果,成功还是失败了?什么原因失败的?等等这些信息,若OS在内存不足的情况下,发生这个进程就在这干等着,什么都不干,就会杀掉这个进程,此时当磁盘写入失败后,返回结果,本该接收返回信息的进程消失了,那么大部分磁盘会把已经写入的数据丢掉,数据丢失。如果是很重要的数据,会造成严重的后果。
所以进程在等待过程中不能以浅度睡眠S状态等待,需要把自己设为D状态,这样就没人能杀,对于进程来说就是免死金牌,不响应操作系统的任何请求。
4. T(stopped)
kill 的19号SIGSTOP就是暂停命令,这个进程我既不想停掉,也不想删掉,就可以这样把它暂停
这时就可以执行别的命令了,kill -18 进程PID 就可以继续进程了。不过继续后这个进程会变成后台进程。常用于调试状态时,想干个别的,暂停一下调试。
5. t(tracing stop)
调试中遇到断点停住的时候,就是t,也是一种暂停状态。
6. X(dead)终止态
进程终止,回收空间,只是一个返回状态,不会在任务列表里看到这个状态
7. Z(zombie)僵尸态
进程终止时不会直接进入X,先是僵尸态,维持一段时间。
当进程退出,它的父进程没有接收子进程退出返回代码时就会产生僵尸进程。僵尸进程会以终止状态保持在进程表中,并且会一直等待父进程读取退出状态代码。
所以,只要子进程退出,父进程还在运行并且没有读取子进程状态,子进程就会进入Z状态。
我们来创建一个例子:
int main()
{pid_t id = fork();if(id == 0) {int i = 5;while(i) {printf("我是子进程,pid:%d,ppid:%d,n:%d\n", getpid(), getppid(), i); i--;sleep(1);}exit(0);} else if(id > 0){while(1) { printf("我是父进程,pid:%d,ppid:%d\n", getpid(), getppid()); sleep(1);}//父进程没有对子进程做任何事情}
这个例子中子进程会运行5s然后结束,父进程一直运行
我们可以看到,当子进程结束时,31663变成Z,后续子进程会一直让自己处于Z状态,该进程的相关资源尤其是PCB不能被释放,这个进程会被一直占用,一直占用内存不释放。
僵尸状态必须维持下去,因为他要告诉父进程它的状态,可父进程一直不理它,那它就一直那么僵着。造成内存泄漏!如何避免?以后再说。
另一种情况,父进程直接被回收,为什么子进程也没了?
这就要讲讲孤儿进程了。
父进程如果提前退出,子进程后退出,进入Z状态后,找不到该接收状态的父进程,该如何处理呢?之前是父进程在运行,不接受,现在是父进程都不在了,想接收也接受不了,怎么办?
int main()
{pid_t id = fork();if(id == 0) {int i = 500;while(i) {printf("我是子进程,pid:%d,ppid:%d,n:%d\n", getpid(), getppid(), i); i--; sleep(1);}exit(0);} else if(id > 0){int i = 5;while(i--){ printf("我是父进程,pid:%d,ppid:%d\n", getpid(), getppid()); sleep(1);}//父进程没有对子进程做任何事情}
当父进程结束后:
我们发现这个子进程的父进程变成了1号进程,也就是操作系统。操作系统进程成了孤儿进程的父进程。孤儿子进程未来也得去释放,被1号进程领养。bash是领养不了的,因为它没有处理孙子进程的逻辑,它只是创建了父进程。
而且我们发现ctrl+c停不掉这个进程了,只能kill。
所以父进程回收后,子进程被1领养,如果子进程处于Z状态,则马上被1号领养然后回收;如果子进程还在运行,那么会等他运行结束后才会回收。1 号进程的作用是兜底清理,确保没有进程被遗漏。
7. 进程优先级
优先级是什么?
我们之前学过的权限是能不能访问某个进程,而优先级是在能访问的基础上,决定谁先谁后。也就是CPU资源分配的先后顺序,指进程的优先权。
为什么要有优先级?
因为资源是有限的,进程是有很多的,注定了进程之间是竞争关系。操作系统要保证进程之间的良性竞争,所以要确认优先级。如果我们的进程长时间得不到CPU资源,该进程的代码长时间无法得到推进--就会导致进程的饥饿问题。表现出进程长时间无响应,是否关闭。在windows中还是很常见的。
linux中是怎么实现优先级的?
UID:执行者的身份(root是0)
PRI:代表进程可被执行的优先级,其值越小,优先级越高。(priority)
NI:这个进程的nice值,PRI的修正数据
在调整进程优先级时,调整的其实是NI的值,PRI(new) = PRI(old)+ NI
当NI值是负的,PRI就会变小,优先级变高。NI是正的,PRI就会变大,优先级变低。
所以在linux下,调整进程优先级就是调整NI值。
调整方法:
top--r--输入进程PID--输入NI值
Linux不想过多的让用户参与优先级的调整,所以只能在一定范围内调整,nice:[-20, 19],也就是说PRI:[60, 99],就算我们在输入NI时超出范围也没用。
注意:显示的 PRI
列是用户态可见的“动态优先级”,由内核动态计算后导出给用户。
内核调度器的真实设计:
-
总范围:0-139(共140级),分为两部分:
-
0-99:实时进程(
SCHED_FIFO
/SCHED_RR
),优先级绝对高于普通进程。 -
100-139:普通进程(
SCHED_NORMAL
),对应nice
值调整的范围。
-
用户态无需关心内核的完整优先级范围(0-139),只需通过 nice
值调整普通进程的相对优先级。实时进程的优先级(0-99)对普通用户不可见,我们也不关心。
为什么是PRI越小,优先级越高?
在Linux内核的O(1)调度器中,CPU的运行队列(runqueue)的设计采用了两个大小为140的优先级数组(active
和expired
),并通过优先级范围划分实时任务和普通任务。
active
数组存放当前可调度的任务
expired
数组存放时间片耗尽的任务,等待重新分配时间片后转移到active
数组
数组的每个元素是一个链表,存放同一优先级的任务(如所有PRI=20的任务在一个链表中)。每个数组附带一个5*32个比特位的位图(bitmap[5]),每一位表示对应优先级队列是否非空。
当任务的时间片用完时,它会被从active
数组移到expired
数组,并分配新的时间片。当active
数组为空时,直接交换active
和expired
的指针。怎么判空也一样用到位图,如果全为0则空。
这种结构就使得遍历数组时是从最小下标开始的,也就是PRI越小,优先级越高。
相关文章:
3. 进程概念
目录 1. 冯诺依曼体系结构 2. 操作系统 3. 理解进程的一般思路 4. 查看进程 5. fork初识 6. 进程状态 6.1 一般操作系统 6.2 Linux系统是怎么维护进程状态的 7. 进程优先级 先谈硬件-再谈软件-最后谈进程。 1. 冯诺依曼体系结构 我们常见的计算机(笔记本电…...
yolov8的数据处理lableimg的安装以及使用
视频数据集准备 video cv2.VideoCapture("./BVN.mp4") num 0 # 计数器 save_step 30 # 间隔帧 while True:rel, frame video.read()if not ret:breaknum 1if num % save_step 0:cv2.imwrite("./demo images/" str(num) ".jpg", frame)l…...
小刚说C语言刷题——1035 判断成绩等级
1.题目描述 输入某学生成绩,如果 86分以上(包括 86分)则输出 VERY GOOD ,如果在 60到 85之间的则输出 GOOD (包括 60和 85),小于 60 的则输出 BAD。 输入 输入只有一行,包括 1个整数。 输出 输出只有一行…...
Spring 依赖冲突解决方案详解
引言 在Spring框架中,依赖管理是一个核心功能,它使得开发者能够轻松地管理应用程序中的各种组件和服务。然而,随着项目的增长和复杂度的增加,依赖冲突问题也变得日益常见。本文将详细介绍Spring中不同类型的依赖冲突及其解决方法…...
P11299 [NOISG 2021 Finals] Fraud 题解
题目背景 你被任命为第 24 届全国信息学奥林匹克竞赛的负责人! 题目描述 本次竞赛共有 N 名参赛者和 2 轮比赛。第 i 名参赛者在第一轮获得了分,在第二轮获得了 分。 每轮比赛分别有一个正整数权重 X 和 Y。第 i 名参赛者的最终得分 计算公式为&a…...
AI时代下 你需要和想要了解的英文缩写含义
在AI智能时代下,越来愈多的企业都开始重视并应用以及开发AI相关产品,这个时候都会或多或少的涉及到英文,英文还好,但是如果是缩写,如果我们没有提前了解过,我们往往很难以快速Get到对方的意思。在这里&…...
大数据平台简介
一、分布式系统基础架构 (一)定义与核心特征 分布式系统是由多台计算机(节点)通过网络协作组成的系统,对外表现为一个统一整体。其核心特征包括: 去中心化:节点平等或分角色协作(如…...
电脑端移植至手机平板:攻克难题,仙盟架构显神通——仙盟创梦IDE
在将电脑端应用移植到手机和平板的过程中,常面临诸多棘手问题。像 1.x 号关闭按钮因位置设计欠佳,难以被用户精准点击,字体过小导致阅读与操作不便等。未来之窗仙盟创梦凭借创新的仙盟架构,巧妙且高效地化解了这些困扰开发者与用户…...
基于Python的中国象棋小游戏的设计与实现
基于Python的中国象棋小游戏的设计与实现 第一章 绪论1.1 研究背景1.2 研究意义 第二章 需求分析2.1 需求分析2.1.1核心功能需求2.1.2 用户体验需求2.1.3 衍生功能需求 2.2 可行性分析2.2.1 技术可行性2.2.2 经济可行性2.2.3 市场可行性2.2.4 法律与合规性 第三章 概要设计3.1 …...
HCIP --- OSPF综合实验
一、拓扑图 二、实验要求 1,R5为ISP,其上只能配置IP地址;R4作为企业边界路由器,出口公网地址需要通过PPP协议获取,并进行chap认证。 2,整个0SPF环境IP基于172.16.0.8/16划分。 3,所有设备均可访问R5的环…...
【OpenGL】OpenGL学习笔记-1:VS2019配置OpenGL开发环境
在Visual Studio 2019中可以通过手动配置库文件或NuGet包管理器快速安装的方法配置OpenGL环境,详细步骤如下: 一、打开VS2019,创建新的控制台项目 二、方法一:手动配置GLEW/GLFW/GLAD库 GLFW是窗口管理和输入事件的基础设施&…...
GWAS_LD
局部LDblock 绘图 1. 查看显著位点附近基因情况 链接pvalue显著位点文件 ln -s ~/yiyaoran/GWAS/my_GWAS_J/P3.GWAS/01.tassel/mlm_output.manht_figure.sigSite.out . #也可以自己筛选awk $2 9 && $4 < 0.000028481 mlm_output.manht_input>368_GWAS.snpsnp两…...
WinForms开发基础:实现带X按钮的ClearableTextBox控件
前言 我们经常看到这样的带X按钮的输入框 如果使用WinForms开发中,该如何进行设计,普通的TextBox控件如何进行改造?为了提升用户体验,在TextBox文本框内添加一个“x”按钮,方便用户一键清除内容。本文将介绍如何通过继…...
直线轴承常规分类知多少?
直线轴承的分类方式多样,以下是从材质、结构形状和常规系列三个维度进行的具体分类: 按主要材质分类 外壳材质:常见的有不锈钢,具有良好的耐腐蚀性,适用于一些对环境要求较高、易受腐蚀的工作场景;轴承…...
算法期末复习
算法期末复习 1.单选题 \1. 二分搜索算法是利用( A)实现的算法。 A. 分治策略 B. 动态规划法 C. 贪心法 D. 回溯法 \2. 回溯法解旅行售货员问题时的解空间树时( C ) 。 A. 子集树 B. 深度优先生成树 C. 排序树 D. 广度优先生成树 \3. 下列算法中通常以自底向上的方式求解最…...
LeetCode 5:最长回文子串
1、题目描述 给你一个字符串 s,找到 s 中最长的 回文 子串。 示例 1: 输入:s "babad" 输出:"bab" 解释:"aba" 同样是符合题意的答案。 示例 2: 输入:s "cbbd" 输出&#…...
2025年4月19日 记录大模型出现的计算问题
2025年4月19日 记录大模型出现的计算问题,用了四个大模型计算json的数值,3个错误,1个正确 问题 Class Train Val answer 2574 853 screen 5025 1959 blackBoard 7847 3445 teacher 8490 3228 stand…...
Python语法系列博客 · 第3期 数据结构入门(列表、元组、字典、集合)
上一期小练习解答(第2期回顾) ✅ 练习1:判断一个数是正数、负数还是零 num float(input("请输入一个数:")) if num > 0:print("正数") elif num < 0:print("负数") else:print("零&q…...
【对Linux文件权限的深入理解】
Linux文件权限 Linux下权限概念概念相关命令 Linux的文件权限管理1.文件访问者的分类(⼈)文件类型和访问权限(事物属性)文件权限值的表示方法⽂件访问权限的相关设置方法目录的权限(比较重要)粘滞位 Linux下…...
2025.04.19【Spider】| 蜘蛛图绘制技巧精解
Basic multi-group radar chart Start with a basic version, learn how to format your input dataset Radar chart with ggradar A Spider chart made using the ggradar package and a lot of customization.A work by Tuo Wang 文章目录 Basic multi-group radar chartRa…...
AtCoder ABC402 A~D 题解
A - CBC 题目大意 给点字符串 S S S,输出其中所有大写字母。 思路 根据题意模拟即可。 代码 #include <cstdio> #include <iostream> #include <algorithm> using namespace std;int main() {string s;cin >> s;for (int i 0; i &l…...
双指针算法(部分例题解析)
快慢指针左右指针 前言 双指针,它通过设置两个指针来遍历数据,从而实现高效的查找、排序、去重等操作。双指针算法的核心在于通过合理地移动这两个指针,减少不必要的遍历,提高算法的效率。 283. 移动零 - 力扣(LeetCo…...
PHP怎样判断浏览器类型和浏览器语言?
获取浏览器类型 $_SERVER[HTTP_USER_AGENT]包含了用户代理字符串,该字符串包含了浏览器、操作系统等信息。通过分析这个字符串,可以大致判断用户使用的浏览器类型。 <?phpfunction getBrowserType() {$userAgent $_SERVER[HTTP_USER_AGENT];$brow…...
利用 i2c 快速从 Interface 生成 Class
利用 i2c 快速从 Interface 生成 Class(支持 TS & ArkTS) 在日常 TypeScript 或 ArkTS 开发中,需要根据 interface 定义手动实现对应的 class,这既重复又容易出错。分享一个命令行工具 —— interface2class,简称…...
Vue+Notification 自定义消息通知组件 支持数据分页 实时更新
效果图: message.vue 消息组件 子组件 <template><div class"custom-notification"><div class"content"><span click"gotoMessageList(currentMessage.split()[1])">{{ currentMessage.split()[0] }}</…...
机械设计【】技术要求(实际使用)
目录 台板技术要求加工件技术要求钣金件技术要求工装型腔技术要求铝型材框架技术要求装配体技术要求焊接件技术要求1(外形尺寸≥1500mm)焊接件技术要求2(外形尺寸<1500mm)焊接件技术要求3(不锈钢)其他要求台板技术要求 1.台板下表面周边不倒角,其余未注倒角C0.5; 2.去…...
遨游科普:防爆平板是指什么?有哪些应用场景?
在石油开采平台的炽热甲板、化工园区的反应釜旁、矿井巷道的瓦斯弥漫区,总能看到一群手持特殊设备的作业人员。他们手中的平板并非寻常消费电子产品,而是专门应对极端环境的防爆平板。防爆平板承载着工业安全的核心诉求,其技术演进与应用拓展…...
【GCC】gcc编译学习
文章目录 1. 过程2. 常用命令选项3. 多个源文件编译参考内容 1. 过程 step1 : 预处理,生成.i文件(预处理器cpp) gcc -E [源文件] -o [生成的.i文件] gcc -E test.c -o test.istep2 : 汇编,将预处理后的文件转换为汇编语言生成.s…...
不确定与非单调推理的可信度方法
可信度方法是肖特里菲(E.H.Shortliffe)等人在确定性理论(Theoryof Comfirmation)的基础上,结合概率论等提出的一种不确定性推理方法,首先在专家系统MYCIN中得到了成功的应用。由于该方法比较直观、简单,而且效果也比较好,因而受到人们的重视。目前,许多专家系统都是基于…...
个人自用-导入安装Hexo
因为本人原来就有备份好的资料,所以重新安装起来会很方便,这个教程也只适合我自己用 但是所有的命令行都要在Git的命令行里面使用(因为我就是这样操作的) 1 安装Git Git的官网 Git git --version 这个是查看Git的版本 git --…...
2025年最新版 Git和Github的绑定方法,以及通过Git提交文件至Github的具体流程(详细版)
文章目录 Git和Github的绑定方法与如何上传至代码仓库一. 注册 GitHub 账号二.如何创建自己的代码仓库:1.登入Github账号,完成登入后会进入如下界面:2.点击下图中红色框选的按钮中的下拉列表3.选择New repostitory4.进入创建界面后࿰…...
Java 动态代理实现
Java 动态代理实现 一、JDK动态代理二、CGLIB动态代理三、动态代理的应用场景四、JDK代理与CGLIB代理比较 动态代理是Java中一种强大的技术,它允许在运行时创建代理对象,用于拦截对目标对象的方法调用。 一、JDK动态代理 JDK动态代理是Java标准库提供的代…...
从零开始搭建CLIP模型实现基于文本的图像检索
目录 CLIP原理简介代码实现参考链接 CLIP原理简介 论文链接,源码链接 CLIP模型由OpenAI在2021年提出,利用双Decoder(Dual Encoder)的架构来学习图像和文本之间的对应关系,是多模态大模型的开创之作,为后续许…...
健康养生之道
在快节奏的现代生活中,健康养生不再是中老年人的专属话题,越来越多的人开始意识到,合理的养生方式是保持良好身体状态和生活质量的关键。 饮食养生是健康的基石。遵循 “食物多样、谷类为主” 的原则,保证每天摄入足够的蔬菜、…...
基于autoware.1.14与gazebo联合仿真进行Hybrid A* 算法规划控制代价地图版
1.首先安装autoware ,大家可以以下一下博客进行安装,如果缺少库什么的直接问ai安装对应的库就行。ubuntu18.04安装Autoware1.14---GPU版 最全环境配置说明_autoware1.14安装教程-CSDN博客 安装成功后运行: source install/setup.bash roslau…...
5G基站设计难题:尺寸、重量、功耗和散热
设计5G基站的工程师们必须应对能源消耗、重量、尺寸和散热等问题,这些因素会影响到设计决策。 5G新空口(NR)采用了多用户大规模多输入多输出(MU-MIMO)技术、集成接入与回传(IAB)技术࿰…...
【leetcode100】分割等和子集
1、题目描述 给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 示例 1: 输入:nums [1,5,11,5] 输出:true 解释:数组可以分割成 [1, 5, 5] 和 [11…...
sed命令笔记250419
sed命令笔记250419 sed(Stream Editor)是 Linux/Unix 系统中强大的流编辑器,主要用于对文本进行过滤和转换(按行处理)。它支持正则表达式,适合处理文本替换、删除、插入等操作。以下是 sed 的详细解析&…...
LinearLayout 线性布局
目录 Android LinearLayout(线性布局)简单介绍与使用示例 一、效果介绍 二、布局文件(XML) 三、Java 代码 四、程序运行效果 五、总结 在 Android 移动应用开发中,LinearLayout(线性布局)…...
System.in 详解
System.in 详解 System.in 是 Java 提供的标准输入流(InputStream 类型),默认关联键盘输入,通常用于从控制台读取用户输入。由于它是字节流(InputStream),直接使用较麻烦,一般会配合…...
JAVA IO、BIO、NIO、AIO及零拷贝
概述 IO,常写作 I/O,是 Input/Output 的简称,是 Input/Output 的简称,即输入/输出。通常指数据在内部存储器(内存)和外部存储器(硬盘、优盘等)或其他周边设备之间的输入和输出。 目前有三种 IO 共存。分别是 BIO、NIO 和 AIO。 BIO 全称 Block-IO 是一种同步且阻塞的…...
AI预测3D新模型百十个定位预测+胆码预测+去和尾2025年4月19日第57弹
从今天开始,咱们还是暂时基于旧的模型进行预测,好了,废话不多说,按照老办法,重点8-9码定位,配合三胆下1或下2,杀1-2个和尾,再杀6-8个和值,可以做到100-300注左右。 (1)定…...
REST 架构详解:从概念到应用的全面剖析
REST(Representational State Transfer)即表述性状态转移,是一种用于构建网络应用程序的架构风格和设计理念,由计算机科学家罗伊・菲尔丁(Roy Fielding)在 2000 年提出。以下是关于它的详细介绍:…...
SICAR程序标准功能块 FB1512 “Robot_kuka_FB“
1、FB1512功能块截图 2、FB1512 功能块引脚功能定义 一、输入引脚 EN:使能输入,决定功能块是否执行。IDENTIFIER(WSTRING#"FW010_R01"):设备标识,指定关联的机器人设备。OPMODE_USER_INTERFACE_OUT:操作模式输入,定义机器人工作模式(如手动、自动),数据源…...
win安装软件
win安装软件 jdk安装 jdk安装 首先去官网下载适合系统版本的JDK,下载地址: http://www.oracle.com/technetwork/java/javase/downloads/index.html进入下载页面,如下图: 首先选择:Accept License Agreement单选按钮&…...
文本生成与采样策略 (Text Generation Sampling)
我们已经学习了如何构建和训练一个基于 Transformer Decoder-only 的语言模型。模型训练的目标是学习预测给定前缀下下一个 token 的概率分布。但是,训练完成后,我们如何利用这个模型来生成全新的、连贯的文本呢? 这就涉及到推理过程和采样策略。推理是模型投入实际使用、生…...
为什么 waitress 不支持 WebSocket?
waitress 是一个纯 Python 实现的 WSGI 服务器,主要用于生产环境部署 Python Web 应用。但它不支持 WebSocket 协议,因为它只实现了 WSGI 规范,而 WebSocket 协议需要 ASGI(Asynchronous Server Gateway Interface)支持…...
[C++] 高精度加法(作用 + 模板 + 例题)
高精度加法-目录 高精度加法用途高精度加法模板string转数位数组int 转数位数组(附加型知识点)高精度输出高精度加法函数大合集!!! 高精度加法用途 高精度加法通常用于加很大的数(真的很大, 超unsigned long long的那种). 高精度加法模板 注: 本篇数组下标0(x[0])存储的是该…...
python程序的流程
三大基本流程: 顺序结构、分支结构(又称为选择结构)、循环结构 分支结构又分为单分支、双分支、多分支 从键盘上输入一个数字,并输出奇数或偶数 #从键盘上输入一个数字,并输出奇数或偶数 nint(input("n ")…...
基于大模型的下肢静脉曲张全流程预测与诊疗方案研究报告
目录 一、引言 1.1 研究背景与意义 1.2 研究目的与创新点 1.3 研究方法与数据来源 二、下肢静脉曲张概述 2.1 定义与病理生理 2.2 风险因素与临床表现 2.3 诊断方法与现有治疗手段 三、大模型预测原理与构建 3.1 大模型技术简介 3.2 预测模型的数据收集与预处理 3.…...