【linux】进程的概念与控制

news2024/12/27 10:53:06

目录

冯诺依曼体系结构

操作系统(Operator System)

进程 

基本概念

组织进程

查看进程

进程状态

 僵尸进程危害

环境变量

程序地址空间

挂起

进程创建

写时拷贝

进程终止

_exit函数

exit函数

参数:


冯诺依曼体系结构

我们常见的计算机,如笔记本。我们不常见的计算机,如服务器,大部分都遵守冯诺依曼体系
截至目前,我们所认识的计算机,都是有一个个的硬件组件组成

关于冯诺依曼,必须强调几点:

这里的存储器指的是内存不考虑缓存情况,这里的CPU能且只能对内存进行读写,不能访问外设(输入或输出设备)外设(输入或输出设备)要输入或者输出数据,也只能写入内存或者从内存中读取。一句话,所有设备都只能直接和内存打交道。

操作系统(Operator System)

概念
任何计算机系统都包含一个基本的程序集合,称为操作系统 (OS) 。笼统的理解,操作系统包括:
内核(进程管理,内存管理,文件管理,驱动管理)其他程序(例如函数库, shell 程序等等)
设计 OS 的目的
与硬件交互,管理所有的软硬件资源
为用户程序(应用程序)提供一个良好的执行环境
定位
在整个计算机软硬件架构中,操作系统的定位是: 一款纯正的 搞管理 的软件
如何理解 " 管理 "
总结
计算机管理硬件
1. 描述起来,用struct结构体
2. 组织起来,用链表或其他高效的数据结构
系统调用和库函数概念
在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分由操作系统提供的接口,叫做系统调用。
系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发。

进程 

基本概念

课本概念:程序的一个执行实例,正在执行的程序等
内核观点:担当分配系统资源( CPU 时间,内存)的实体。
描述进程-PCB
进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
课本上称之为 PCB process control block ), Linux 操作系统下的 PCB : task_struct
task_struct-PCB 的一种
Linux 中描述进程的结构体叫做 task_struct。task_struct Linux 内核的一种数据结构,它会被装载到 RAM( 内存 ) 里并且包含着进程的信息。
task_ struct 内容分类
标示符: 描述本进程的唯一标示符,用来区别其他进程。
状态: 任务状态,退出代码,退出信号等。
优先级: 相对于其他进程的优先级。
程序计数器: 程序中即将被执行的下一条指令的地址。
内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
上下文数据 : 进程执行时处理器的寄存器中的数据 [ 休学例子,要加图 CPU ,寄存器 ]
I O 状态信息 : 包括显示的 I/O 请求 , 分配给进程的 I O 设备和被进程使用的文件列表。

组织进程

可以在内核源代码里找到它。所有运行在系统里的进程都以 task_struct 链表的形式存在内核里。

查看进程

进程的信息可以通过 /proc 系统文件夹查看
如:要获取 PID 1 的进程信息,你需要查看 /proc/1 这个文件夹。

大多数进程信息同样可以使用topps这些用户级工具来获取 

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
 while(1){
 sleep(1);
 }
 return 0;
}

通过系统调用获取进程标示符
进程 id PID
父进程 id PPID
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
 printf("pid: %d\n", getpid());
 printf("ppid: %d\n", getppid());
 return 0;
}
fork 之后通常要用 if 进行分流
  1. 父进程调用fork,返回子线程pid(>0)

  2. 子进程调用fork,子进程返回0,调用失败的话就返回-1

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
 int ret = fork();
 if(ret < 0){
 perror("fork");
 return 1;
 }
 else if(ret == 0){ //child
 printf("I am child : %d!, ret: %d\n", getpid(), ret);
 }else{ //father
 printf("I am father : %d!, ret: %d\n", getpid(), ret);
 }
 sleep(1);
 return 0;
}

进程状态

看看 Linux 内核源代码怎么说
为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在Linux 内核里,进程有时候也叫做任务)。
/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};
R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠
(interruptible sleep))。
D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态

进程状态查看
ps aux / ps axj 命令

 

Z(zombie)- 僵尸进程
僵死状态( Zombies )是一个比较特殊的状态。当进程退出并且父进程(使用 wait() 系统调用
没有读取到子进程退出的返回代码时就会产生僵死 ( ) 进程
僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入 Z 状态

 

#include <stdio.h>
#include <stdlib.h>
int main()
{
 pid_t id = fork();
 if(id < 0){
 perror("fork");
 return 1; }
 else if(id > 0){ //parent
 printf("parent[%d] is sleeping...\n", getpid());
 sleep(30);
 }else{
 printf("child[%d] is begin Z...\n", getpid());
 sleep(5);
 exit(EXIT_SUCCESS);
 }
 return 0;
}

 僵尸进程危害

进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态?是的!
维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护?是的!
那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?是的!因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间!内存泄漏?是的!
孤儿进程
父进程如果提前退出,那么子进程后退出,进入 Z 之后,那该如何处理呢?
父进程先退出,子进程就称之为 孤儿进程
孤儿进程被 1 init 进程领养,当然要有 init 进程回收喽。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
 pid_t id = fork();
 if(id < 0){
 perror("fork");
 return 1;
 }
 else if(id == 0){//child
 printf("I am child, pid : %d\n", getpid());
 sleep(10);
 }else{//parent
 printf("I am parent, pid: %d\n", getpid());
 sleep(3);
 exit(0);
 }
 return 0;
}

进程优先级
基本概念
cpu 资源分配的先后顺序,就是指进程的优先权( priority )。
优先权高的进程有优先执行权利。配置进程优先权对多任务环境的 linux很有用,可以改善系统性能。 还可以把进程运行到指定的 CPU 上,这样一来,把不重要的进程安排到某个 CPU,可以大大改善系统整体性能。
查看系统进程
linux 或者 unix 系统中,用 ps –l 命令则会类似输出以下几个内容
我们很容易注意到其中的几个重要信息,有下:
UID : 代表执行者的身份
PID : 代表这个进程的代号
PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
PRI :代表这个进程可被执行的优先级,其值越小越早被执行
NI :代表这个进程的nice值
PRI and NI
PRI 也还是比较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高那 NI ? 就是我们所要说的 nice值了,其表示进程可被执行的优先级的修正数值PRI 值越小越快被执行,那么加入 nice 值后,将会使得 PRI 变为: PRI(new)=PRI(old)+nice
这样,当 nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行所以,调整进程优先级,在 Linux 下,就是调整进程 nice值nice 其取值范围是 -20 19 ,一共 40 个级别。
PRI vs NI
需要强调一点的是,进程的 nice 值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进程的优先级变化。可以理解 nice 值是进程优先级的修正修正数据

环境变量

基本概念
环境变量 (environment variables) 一般是指在操作系统中用来指定操作系统运行环境的一些参数
如:我们在编写 C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性
常见环境变量
PATH : 指定命令的搜索路径
HOME : 指定用户的主工作目录 ( 即用户登陆到 Linux 系统中时 , 默认的目录 )
SHELL : 当前 Shell, 它的值通常是 /bin/bash
查看环境变量方法
echo $NAME //NAME: 你的环境变量名称

程序地址空间

研究背景
kernel 2.6.32
32 位平台
程序地址空间回顾

 

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int g_val = 0;
int main()
{
 pid_t id = fork();
 if(id < 0){
 perror("fork");
 return 0;
 }
 else if(id == 0){ //child
printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
 }else{ //parent
 printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
 }
 sleep(1);
 return 0;
}

 输出

// 与环境相关,观察现象即可
parent[2995]: 0 : 0x80497d8
child[2996]: 0 : 0x80497d8
我们发现,输出出来的变量值和地址是一模一样的,因为子进程按照父进程为模版,父子并没有对变量进行进行任何修改。可是将代码稍加改动 :
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int g_val = 0;
int main()
{
 pid_t id = fork();
 if(id < 0){
 perror("fork");
 return 0;
 }
 else if(id == 0){ //child,子进程肯定先跑完,也就是子进程先修改,完成之后,父进程再读取
 g_val=100;
 printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
 }else{ //parent
 sleep(3);
 printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
 }
 sleep(1);
 return 0;
}
输出结果
// 与环境相关,观察现象即可
child[3046]: 100 : 0x80497e8
parent[3045]: 0 : 0x80497e8
我们发现,父子进程,输出地址是一致的,但是变量内容不一样!能得出如下结论:
变量内容不一样 , 所以父子进程输出的变量绝对不是同一个变量

但地址值是一样的,说明,该地址绝对不是物理地址!
Linux 地址下,这种地址叫做 虚拟地址
我们在用 C/C++ 语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由 OS 统一管理
OS 必须负责将 虚拟地址 转化成 物理地址
进程地址空间
所以之前说 程序的地址空间 是不准确的,准确的应该说成 进程地址空间 ,那该如何理解呢?看图:

 

进程地址空间

  • 使用物理地址不安全,所以使用虚拟地址
  • 每一个进程有自己的pcb
  • 操作系统给每一个进程创造虚拟地址空间

深入理解虚拟地址

  • 虚拟地址会通过映射机制来访问实际的物理内存物理地址(页表)
  • 地址空间不要仅仅理解为是os内部要遵守的,其实编译器也要遵守,即编译器编译代码的时候,就已经给我们形成了,各个区域,并且采用和linux内核一样的编址方式,给每一个变量,每一行代码都进行了编址,所以程序在编译的时候,每一个字段早已经具有了一个虚拟地址
  • Cpu拿到的都是虚拟地址,通过页表跳转读取到物理地址的内容
  • 本质上,因为有地址空间的存在,所以上层申请空间,其实是在地址空间上申请的,而并不是物理内存
  • 只有当你真正的通过虚拟地址传递到cpu再访问物理地址空间的时候(是由操作系统自动完成,用户包括进程完全没有感知),才会执行内存相关的管理算法,帮你申请内存,构建页表映射关系,然后在进行内存的访问

因为在物理内存中理论上可以任意位置加载,那么是不是物理内存的所有数据和代码都是乱序的?

没错,但是因为有页表的存在,它可以将地址空间上的虚拟地址和物理地址进行映射,

那么是不是在进程视角所有内存分布都是有序的?

地址空间+页表的存在,可以将内存分布有序化。

进程要访问的物理内存中的数据和代码,可能并没有在物理内存中,同样也可以让不同的进程映射到不同的物理内存,很容易让进程独立性的实现

进程的独立性可以通过地址空间+页表的方式实现。

因为有地址空间的存在,每一个进程都认为自己拥有4gb的空间,并且各个区域是有序的,进而可以通过页表映射到不同的区域,来实现进程的独立性。

所以每一个进程是不需要知道其他进程的存在的。·

加载本质是创建进程,那么是不是必须非要把所有程序的代码和数据加载到内存中,并且创建内核数据结构建立映射关系?

在最极端的情况下,甚至只有内核结构被创建出来了。

理论上可以实现对程序的分批加载

挂起

进程的数据和代码被换出了,就叫被挂起

创建子进程,不需要将不会被访问的或者只会读取的数据拷贝一份

只有将来会被父或者子进程写入的数据,值得被拷贝。一般来说即便是os,也无法提前知道哪些空间可能会被写入,所以os选择写时拷贝技术,来进行将父子进程的数据分离。

进程终止时,操作系统做了什么

要释放进程申请的相关内核数据结构和对应的数据和代码。本质就是释放系统资源


进程创建

fork 函数初识
linux fork 函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
#include <unistd.h>
pid_t fork(void);
返回值:自进程中返回0,父进程返回子进程id,出错返回-1
进程调用 fork ,当控制转移到内核中的 fork 代码后,内核做:
分配新的内存块和内核数据结构给子进程
将父进程部分数据结构内容拷贝至子进程
添加子进程到系统进程列表当中
fork 返回,开始调度器调度

 

当一个进程调用 fork 之后,就有两个二进制代码相同的进程。而且它们都运行到相同的地方。但每个进程都将可以 开始它们自己的旅程,看如下程序
int main( void )
{
 pid_t pid;
 printf("Before: pid is %d\n", getpid());
if ( (pid=fork()) == -1 )perror("fork()"),exit(1);
 printf("After:pid is %d, fork return %d\n", getpid(), pid);
 sleep(1);
 return 0;
} 
运行结果:
[root@localhost linux]# ./a.out
Before: pid is 43676
After:pid is 43676, fork return 43677
After:pid is 43677, fork return 0
这里看到了三行输出,一行 before ,两行 after 。进程 43676 先打印 before 消息,然后它又打印 after 。另一个 after
消息有 43677 打印的。注意到进程 43677 没有打印before,为什么呢?如下图所示

 

所以, fork 之前父进程独立执行, fork 之后,父子两个执行流分别执行。注意, fork 之后,谁先执行完全由调度器 决定。
fork 函数返回值
子进程返回0,
父进程返回的是子进程的pid 

写时拷贝

通常,父子代码共享,父子再不写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副本。具体见下图
fork 常规用法
一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如,父进程等待客户端请求,生成子进程来处理请求。
一个进程要执行一个不同的程序。例如子进程从fork返回后,调用exec函数。
fork 调用失败的原因
系统中有太多的进程
实际用户的进程数超过了限制

进程终止

进程退出场景
代码运行完毕,结果正确
代码运行完毕,结果不正确
代码异常终止
进程常见退出方法
正常终止(可以通过 echo $? 查看进程退出码):
1. main 返回
2. 调用 exit
3. _exit
异常退出:
ctrl + c ,信号终止

_exit函数

#include <unistd.h>
void _exit(int status);
参数:status 定义了进程的终止状态,父进程通过wait来获取该值
说明:虽然 status int ,但是仅有低 8 位可以被父进程所用。所以 _exit(-1) 时,在终端执行 $?发现返回值是 255

exit函数

#include <unistd.h>
void exit(int status);

exit最后也会调用exit, 但在调用exit之前,还做了其他工作:

1. 执行用户通过 atexit on_exit 定义的清理函数。
2. 关闭所有打开的流,所有的缓存数据均被写入
3. 调用_exit  
int main()
{
 printf("hello");
 exit(0);
}
运行结果:
[root@localhost linux]# ./a.out
hello[root@localhost linux]#
int main()
{
 printf("hello");
 _exit(0);
}
运行结果:
[root@localhost linux]# ./a.out
[root@localhost linux]#

 return退出

return 是一种更常见的退出进程方法。执行 return n 等同于执行 exit(n), 因为调用 main 的运行时函数会将 main的返回值当做 exit 的参数。

进程等待

进程等待必要性
  • 之前讲过,子进程退出,父进程如果不管不顾,就可能造成僵尸进程的问题,进而造成内存泄漏。
  • 另外,进程一旦变成僵尸状态,那就刀枪不入,杀人不眨眼kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。
  • 最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出。
  • 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息
进程等待的方法
wait方法
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);
返回值:
  • 成功返回被等待进程pid,失败返回-1
参数:
输出型参数,获取子进程退出状态 , 不关心则可以设置成为NULL        
waitpid 方法
pid_ t waitpid(pid_t pid, int *status, int options);
返回值:
当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;

参数:

pid
Pid=-1,等待任一个子进程。与wait等效。
Pid>0.等待其进程ID与pid相等的子进程。
status:
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
options:
WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。
如果子进程已经退出,调用 wait/waitpid 时, wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。
如果在任意时刻调用 wait/waitpid ,子进程存在且正常运行,则进程可能阻塞。
如果不存在该子进程,则立即出错返回。

 

获取子进程 status
wait waitpid ,都有一个 status参数,该参数是一 个输出型参数,由操作系统填充。
如果传递 NULL ,表示不关心子进程的退出状态信息。
否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
status 不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究 status低16比特位):
测试代码:
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main( void )
{
 pid_t pid;
 if ( (pid=fork()) == -1 )
 perror("fork"),exit(1);
 if ( pid == 0 ){
 sleep(20);
 exit(10);
 } else {
 int st;
 int ret = wait(&st);
 
 if ( ret > 0 && ( st & 0X7F ) == 0 ){ // 正常退出
 printf("child exit code:%d\n", (st>>8)&0XFF);
 } else if( ret > 0 ) { // 异常退出
 printf("sig code : %d\n", st&0X7F );
 }
 }
}
测试结果:
 [root@localhost linux]# ./a.out #等20秒退出
 child exit code:10 
 [root@localhost linux]# ./a.out #在其他终端kill掉
 sig code : 9

 具体代码实现

进程的阻塞等待方式
int main()
{
 pid_t pid;
 pid = fork();
 if(pid < 0){
 printf("%s fork error\n",__FUNCTION__);
 return 1;
 } else if( pid == 0 ){ //child
 printf("child is run, pid is : %d\n",getpid());
 sleep(5);
 exit(257);
 } else{
 int status = 0;
 pid_t ret = waitpid(-1, &status, 0);//阻塞式等待,等待5S
 printf("this is test for wait\n");
 if( WIFEXITED(status) && ret == pid ){
 printf("wait child 5s success, child return code is :%d.\n",WEXITSTATUS(status));
 }else{
 printf("wait child failed, return.\n");
 return 1;
 }
}
 return 0;
}
运行结果:
[root@localhost linux]# ./a.out
child is run, pid is : 45110
this is test for wait
wait child 5s success, child return code is :1.
进程的非阻塞等待方式:
#include <stdio.h> 
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
 pid_t pid;
 
 pid = fork();
 if(pid < 0){
 printf("%s fork error\n",__FUNCTION__);
 return 1;
 }else if( pid == 0 ){ //child
 printf("child is run, pid is : %d\n",getpid());
 sleep(5);
 exit(1);
 } else{
 int status = 0;
 pid_t ret = 0;
 do
 {
 ret = waitpid(-1, &status, WNOHANG);//非阻塞式等待
 if( ret == 0 ){
 printf("child is running\n");
 }
 sleep(1);
 }while(ret == 0);
 
 if( WIFEXITED(status) && ret == pid ){
 printf("wait child 5s success, child return code is :%d.\n",WEXITSTATUS(status));
 }else{
 printf("wait child failed, return.\n");
 return 1;
 }
 }
 return 0;
}
进程程序替换
替换原理
fork 创建子进程后执行的是和父进程相同的程序 ( 但有可能执行不同的代码分支 ), 子进程往往要调用一种 exec 函数
以执行另一个程序。当进程调用一种 exec 函数时 , 该进程的用户空间代码和数据完全被新程序替换 , 从新程序的启动
例程开始执行。调用 exec 并不创建新进程 , 所以调用 exec 前后该进程的id并未改变

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/66029.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

分享10大自动化测试框架,你用过几个?

软件行业正迈向自主、快速、高效的未来。为了跟上这个高速前进的生态系统的步伐&#xff0c;必须加快应用程序的交付时间&#xff0c;但不能以牺牲质量为代价。快速实现质量是必要的&#xff0c;因此质量保证得到了很多关注。为了满足卓越的质量和更快的上市时间的需求&#xf…

Java基于JSP实验室预约管理系统

科技水平一直是体现一个国家强弱的重要标志&#xff0c;而科技的一点诞生地是实验室&#xff0c;如果能够更好的对实验室进行管理是很多实验室管理人员一直研究的一个问题。只有更加科学和合理化的利用实验室才能够更好的让科技水平有所提高和发展&#xff61; 本项目利用软件工…

毕设项目 - SSM考研信息查询管理系统(含源码+论文)

文章目录1 项目简介2 实现效果2.1 界面展示3 设计方案3.1 概述3.2 系统流程3.2.1 系统开发流程3.2.2 操作流程3.3 系统结构设计4 项目获取1 项目简介 Hi&#xff0c;各位同学好呀&#xff0c;这里是M学姐&#xff01; 今天向大家分享一个今年(2022)最新完成的毕业设计项目作品…

MAX3072EESA+T RS-485/RS-422半双工收发器

MAX3072EESAT 3.3V15kV具有ESD保护、失效保护、热插拔、RS-485/RS-422收发器具备一个驱动器和一个接收器。包含失效保护电路&#xff0c;当接收器输入开路或短路时保证接收器输出逻辑高。当端接总线上的所有发送器被禁止(高阻抗)时&#xff0c;接收器输出逻辑高。MAX3072EESAT具…

springboot基于Java的电影院售票与管理系统毕业设计源码011449

电影院售票与管理系统的设计与实现 摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对电影院售…

自动驾驶:2022 apollo day 观后感(二)

自动驾驶&#xff1a;2022 apollo day 观后感&#xff08;二&#xff09;TOPIC TWO&#xff1a;自动驾驶地图&#xff1a;Apollo的实践和思考&#xff08;黄际洲&#xff09;地图需求升级自动驾驶规划方向轻成本&#xff0c;重体验轻成本重体验安全&#xff1a;舒适度出行效率新…

终于有人把Java面试高分Guide总结得如此系统,堪称傻瓜式笔记总结

纵观今年的技术招聘市场&#xff0c; Java依旧是当仁不让的霸主 &#xff01;即便遭受 Go等新兴语言不断冲击&#xff0c;依旧岿然不动。究其原因&#xff1a; Java有着极其成熟的生态&#xff0c;这个不用我多说&#xff1b;Java在 运维、可观测性、可监 控性方面都有着非常优…

LeetCode简单题之温度转换

题目 给你一个四舍五入到两位小数的非负浮点数 celsius 来表示温度&#xff0c;以 摄氏度&#xff08;Celsius&#xff09;为单位。 你需要将摄氏度转换为 开氏度&#xff08;Kelvin&#xff09;和 华氏度&#xff08;Fahrenheit&#xff09;&#xff0c;并以数组 ans [kelv…

基于web在线餐饮网站的设计与实现——仿Coco线上订奶茶饮料6个页面(HTML+CSS+JavaScript)

&#x1f468;‍&#x1f393;静态网站的编写主要是用HTML DIVCSS JS等来完成页面的排版设计&#x1f469;‍&#x1f393;,常用的网页设计软件有Dreamweaver、EditPlus、HBuilderX、VScode 、Webstorm、Animate等等&#xff0c;用的最多的还是DW&#xff0c;当然不同软件写出的…

用友NC6.5 Linux服务器环境部署

用友NC6.5 Linux服务器环境部署 1.环境配置要求  1.1 操作系统平台 应用服务器操作系统版本&#xff08;补丁&#xff09;中间件类型JDK 版本Linux-RedHat(x64&#xff0c;多核)Enterprise Linux Server release 6.3Websphere 8.5.0.1/UAP/Weblogic11SUN JDK1.7_51/IBM SDK,V…

七周成为数据分析师 | 数据分析思维

为什么思维重要&#xff1f; 不知道问题发生没 不知道问题在哪 不知道为什么 不确定分析对不对 不确定执行结果 不知道老板给不给加薪 一.What&#xff1a;三种核心思维 1.结构化 ①核心论点 它可以是假设&#xff0c;是问题&#xff0c;是预测&#xff0c;是原因 ②…

单片机通信总述——理论部分(CAN、串口、SPI、I2C等)

一、基础概念 1.1 通信方法 并行通信&#xff1a;传输原理&#xff1a;数据各个位同时传输&#xff1b;优点&#xff1a;速度快&#xff1b; 缺点&#xff1a;占用引脚资源多。是指使用 8、16、32 及 64 根或更多的数据线(有多少信号为就需要多少信号位)进行传输的通讯方式&a…

争议不断的AI绘画,靠这个成为了顶流?

今年以来&#xff0c;AIGC迅速崛起。所谓AIGC&#xff0c;即AI-Generated Content&#xff0c;指的是利用人工智能来生成内容&#xff0c;被认为是继专业产出内容&#xff08;PGC&#xff09;、用户产出内容&#xff08;UGC&#xff09;后的新型内容创作方式。不久前掀起热议的…

GitHub上架即下架,《分布式系统人人都是架构师》全彩笔记开源

又来给大家分享好书了&#xff1a;高翔龙老师的 《超大流量分布式系统架构解决方案&#xff1a;人人都是架构师2.0》&#xff0c;我在网上没找见开源的PDF版本所以分享一下&#xff01;小编会在文末附电子版免费方式。 高翔龙是谁&#xff1f; 云集基础架构负责人&#xff0c…

真香!阿里最新公开的200页Spring全家桶进阶指南及视频汇总

最近看了下粉丝们的后台留言以及面试情况&#xff0c;发现很多人知道自己的问题和短板在哪里&#xff0c;对自己的技术水平和能力认知也很清晰&#xff0c;都很迫切想要学习提高&#xff0c;奈何自己盲目学习的过程很费力&#xff0c;效果也不佳&#xff0c;遇到好些困难和阻碍…

2023年新授的IBDP物理/化学/生物课程有何不同?

2023年升读IBDP的小伙伴们将迎来的IB课程改革变化更大&#xff0c;因为IBDP第4科学科目组的主流课程——物理、化学和生物三门课程都将迎来改革。 改革后的IBDP生物、物理和化学课程的学习内容、评估方式、课时分配、主题顺序、考试时间分配、成绩占比等跟现在的IBDP同门课程均…

NLP词向量技术

什么是词向量&#xff1a; 词向量&#xff08;Word Vector&#xff09;是对词语义或含义的数值向量表示&#xff0c;包括字面意义和隐含意义。 词向量可以捕捉到词的内涵&#xff0c;将这些含义结合起来构成一个稠密的浮点数向量&#xff0c;这个稠密向量支持查询和逻辑推理。 …

代码随想录算法训练营第十四天 |二叉树

1.理论基础 二叉树定义 Class TreeNode() {int val;TreeNode left;TreeNode right;TreeNode(){};TreeNode(int val) {this.val val;}TreeNode(int val, TreeNode left, TreeNode right) {this.val val;this.left left;this.right right;} }二叉树种类 在我们解题过程中二叉…

Mysql事务隔离级别是怎么实现的?

文章目录事务隔离级别解析常用命令查看事务隔离级别设置修改隔离级别隔离级别演示Mysql事务操作MVCC实现原理undo log版本链ReadView练习一下案例1案例2问答环节1、Mysql 可重复读到底有没有解决幻读&#xff1f;事务隔离级别 Mysql的事务隔离级别是由Mysql的各种锁以及MVCC机…

基于工业网关的储罐在线监测系统解决方案

储罐可以用来存放油、气、化学原料、工业原料等物资&#xff0c;是石油、化工、粮油、国防、冶金等行业必不可少的基础设施。随着储罐行业和相关行业的发展&#xff0c;越来越多企业开始运用储罐并产生储罐在线监测需求&#xff0c;需要实时了解储罐内的状态&#xff0c;保证重…