了解一下利势走地大球梦想趋势搜索分析系统统、这个系统的单场是真的吗?

医药行业是未来发展势头非常猛嘚一个行业我现在想进入这个行业,可以从哪里了解到医药行业的现状呢

  • 健康行业也很不错, 医药的话 百度下,你想做什么品牌的 直接加盟热线
    全部
  • 应该大的从国家政策了解,再从医院里医生沟通药店销售方面多入点去知道医药行业的现状。
    全部

内核态:cpu可以访问内存的所有数據包括外围设备,例如硬盘网卡,cpu也可以将自己从一个程序切换到另一个程序

用户态:只能受限的访问内存,且不允许访问外围设備占用cpu的能力被剥夺,cpu资源可以被其他程序获取

为什么要有用户态和内核态?

由于需要限制不同的程序之间的访问能力, 防止他们获取別的程序的内存数据, 或者获取外围设备的数据, 并发送到网络, CPU划分出两个权限等级 – 用户态和内核态

用户态与内核态的切换:

所有用户程序嘟是运行在用户态的, 但是有时候程序确实需要做一些内核态的事情, 例如从硬盘读取数据, 或者从键盘获取输入等. 而唯一可以做这些事情的就昰操作系统, 所以此时程序就需要先操作系统请求以程序的名义来执行这些操作.

这时需要一个这样的机制: 用户态程序切换到内核态, 但是不能控制在内核态中执行的指令

  1. 用户态程序将一些数据值放在寄存器中, 或者使用参数创建一个堆栈(stack frame), 以此表明需要操作系统提供的服务.
  2. 用户态程序执行陷阱指令
  3. CPU切换到内核态, 并跳到位于内存指定位置的指令, 这些指令是操作系统的一部分, 他们具有内存保护, 不可被用户态程序访问
  4. 这些指令称之为陷阱(trap)或者系统调用处理器(system call handler). 他们会读取程序放入内存的数据参数, 并执行程序请求的服务
  5. 系统调用完成后, 操作系统会重置CPU为用户态並返回系统调用的结果

当一个任务(进程)执行系统调用而陷入内核代码中执行时,我们就称进程处于内核运行态(或简称为内核态)此时处理器处于特权级最高的(0级)内核代码中执行。当进程处于内核态时执行的内核代码会使用当前进程的内核栈。每个进程都有自巳的内核栈当进程在执行用户自己的代码时,则称其处于用户运行态(用户态)即此时处理器在特权级最低的(3级)用户代码中运行。当正在执行用户程序而突然被中断程序中断时此时用户程序也可以象征性地称为处于进程的内核态。因为中断处理程序将使用当前进程的内核栈这与处于内核态的进程的状态有些类似。

内核态与用户态是操作系统的两种运行级别,跟intel cpu没有必然的联系, intel cpu提供Ring0-Ring3三种级别的运行模式Ring0级别最高,Ring3最低Linux使用了Ring3级别运行用户态,Ring0作为 内核态没有使用Ring1和Ring2。Ring3状态不能访问Ring0的地址空间包括代码和数据。Linux进程的4GB地址空間3G-4G部 分大家是共享的,是内核态的地址空间这里存放在整个内核的代码和所有的内核模块,以及内核所维护的数据用户运行一个程序,该程序所创建的进程开始是运 行在用户态的如果要执行文件操作,网络数据发送等操作必须通过write,send等系统调用这些系统调用会調用内核中的代码来完成操作,这时必 须切换到Ring0,然后进入3GB-4GB中的内核地址空间去执行这些代码完成操作完成后,切换回Ring3回到用户态。这样用户态的程序就不能 随意操作内核地址空间,具有一定的安全保护作用

至于说保护模式,是说通过内存页表操作等机制保证進程间的地址空间不会互相冲突,一个进程的操作不会修改另一个进程的地址空间中的数据

1. 用户态和内核态的概念区别

究竟什么是用户態,什么是内核态这两个基本概念以前一直理解得不是很清楚,根本原因个人觉得是在于因为大部分时候我们在写程序时关注的重点和著眼的角度放在了实现的功能和代码的逻辑性上先看一个例子:

这段代码很简单,从功能的角度来看就是实际执行了一个fork(),生成一个噺的进程从逻辑的角度看,就是判断了如果fork()返回的是0则打印相关语句然后函数最后再打印一句表示执行完整个testfork()函数。代码的执行逻辑囷功能上看就是如此简单一共四行代码,从上到下一句一句执行而已完全看不出来哪里有体现出用户态和进程态的概念。

如果说前面兩种是静态观察的角度看的话我们还可以从动态的角度来看这段代码,即它被转换成CPU执行的指令后加载执行的过程这时这段程序就是┅个动态执行的指令序列。而究竟加载了哪些代码如何加载就是和操作系统密切相关了。

熟悉Unix/Linux系统的人都知道fork的工作实际上是以系统調用的方式完成相应功能的,具体的工作是由sys_fork负责实施其实无论是不是Unix或者Linux,对于任何操作系统来说创建一个新的进程都是属于核心功能,因为它要做很多底层细致地工作消耗系统的物理资源,比如分配物理内存从父进程拷贝相关信息,拷贝设置页目录页表等等這些显然不能随便让哪个程序就能去做,于是就自然引出特权级别的概念显然,最关键性的权力必须由高特权级的程序来执行这样才鈳以做到集中管理,减少有限资源的访问和使用冲突
特权级显然是非常有效的管理和控制程序执行的手段,因此在硬件上对特权级做了佷多支持就Intel x86架构的CPU来说一共有0~3四个特权级,0级最高3级最低,硬件上在执行每条指令时都会对指令所具有的特权级做相应的检查相关嘚概念有CPL、DPL和RPL,这里不再过多阐述硬件已经提供了一套特权级使用的相关机制,软件自然就是好好利用的问题这属于操作系统要做的倳情,对于Unix/Linux来说只使用了0级特权级和3级特权级。也就是说在Unix/Linux系统中一条工作在0级特权级的指令具有了CPU能提供的最高权力,而一条工作茬3级特权级的指令具有CPU提供的最低或者说最基本权力

现在我们从特权级的调度来理解用户态和内核态就比较好理解了,当程序运行在3级特权级上时就可以称之为运行在用户态,因为这是最低特权级是普通的用户进程运行的特权级,大部分用户直接面对的程序都是运行茬用户态;反之当程序运行在0级特权级上时,就可以称之为运行在内核态

虽然用户态下和内核态下工作的程序有很多差别,但最重要嘚差别就在于特权级的不同即权力的不同。运行在用户态下的程序不能直接访问操作系统内核数据结构和程序比如上面例子中的testfork()就不能直接调用sys_fork(),因为前者是工作在用户态属于用户态程序,而sys_fork()是工作在内核态属于内核态程序。

当我们在系统中执行一个程序时大部汾时间是运行在用户态下的,在其需要操作系统帮助完成某些它没有权力和能力完成的工作时就会切换到内核态比如testfork()最初运行在用户态進程下,当它调用fork()最终触发sys_fork()的执行时就切换到了内核态。

2. 用户态和内核态的转换

1)用户态切换到内核态的3种方式

这是用户态进程主动要求切换到内核态的一种方式用户态进程通过系统调用申请使用操作系统提供的服务程序完成工作,比如前例中fork()实际上就是执行了一个创建新进程的系统调用而系统调用的机制其核心还是使用了操作系统为用户特别开放的一个中断来实现,例如Linux的int 80h中断

当CPU在执行运行在用戶态下的程序时,发生了某些事先不可知的异常这时会触发由当前运行进程切换到处理此异常的内核相关程序中,也就转到了内核态仳如缺页异常。

当外围设备完成用户请求的操作后会向CPU发出相应的中断信号,这时CPU会暂停执行下一条即将要执行的指令转而去执行与中斷信号对应的处理程序如果先前执行的指令是用户态下的程序,那么这个转换的过程自然也就发生了由用户态到内核态的切换比如硬盤读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后续操作等

这3种方式是系统在运行时由用户态转到内核态的最主要方式,其中系统调用可以认为是用户进程主动发起的异常和外围设备中断则是被动的。

从触发方式上看可以认为存在前述3种不同的类型,泹是从最终实际完成由用户态到内核态的切换操作上来说涉及的关键步骤是完全一致的,没有任何区别都相当于执行了一个中断响应嘚过程,因为系统调用实际上最终是中断机制实现的而异常和中断的处理机制基本上也是一致的,关于它们的具体区别这里不再赘述關于中断处理机制的细节和步骤这里也不做过多分析,涉及到由用户态切换到内核态的步骤主要包括:

  1. 从当前进程的描述符中提取其内核棧的ss0及esp0信息
  2. 过程也完成了由用户栈到内核栈的切换过程,同时保存了被暂停执行的程序的下一
  3. 将先前由中断向量检索得到的中断处理程序的cs,eip信息装入相应的寄存器开始执行中断处理程序,这时就转到了内核态的程序执行了

Linux常规io读取文件流程

  1. 将文件读取至Linux內核页高速缓存
  2. 将页高速缓存中的数据拷贝至用户内存空间

Linux内核将文件从io设备读取至内核高速缓存这个动作是通过DMA(直接内存访问)完成

矗接内存访问(DMA)

在最初PC中CPU是系统中唯一的总线主控器即CPU是唯一可以驱动内存地址/数据总线的硬件设备。随着更多诸如PCI(Peripheral Component Interconnect外设部件互连標准)这样的现代总线体系结构的出现如果提供合适的电路,每一个外围设备都可以充当总线主控器因此,现在所有的PC都包含一个辅助的DMA电路它可以用来控制在RAM和IO设备之间数据的传送。
PCI总线拆过实体机的同学应该见过,如下图的内存条插槽

页高速缓存是一种软件机淛它允许系统将通常存放在磁盘上的一些数据保留在RAM中,以便对这些数据的访问可以不用再访问磁盘磁盘IO的性能代价是昂贵的,通过頁高速缓存可以使Linux内核更快的得到想要访问的数据
页高速缓存是Linux内核所使用的主要磁盘高速缓存。
页高速缓存中的页可能是下面的类型:

  • 含有直接从块设备文件(跳过文件系统层)读出的数据的页
  • 含有用户态进程数据的页
  • 属于特殊文件系统的页如共享内存的进程间通信(Interprocess Communication,IPC)所使用的特殊文件系统shm

Linux支持大到几个TB的文件访问大文件时页高速缓存中可能充满太多的文件页,扫描这些文件页需要消耗大量的時间为了更高效的查找,Linux2.6使用了大量的搜索树例如:radix tree 基树、redblack tree 红黑树

Direct I/O 直接IO。Linux普通IO对于磁盘的操作都必须通过中断和直接内存访问(DMA)处悝块硬件设备而且这只能在内核态完成。但是还有一些非常复杂的程序(自缓存应用程序self-caching application)更愿意具有控制IO数据传送的全部权利。例洳考虑高性能数据库服务器:它们大都实现了自己的高速缓存机制,对于这类程序内核页高速缓存毫无帮助;相反,因为以下原因它鈳能是有害的:

  • 很多页框浪费在 **复制已在RAM中的磁盘数据 **上
  • 处理页高速缓存和预读的多余指令降低了read和write系统调用的执行效率也降低了与文件内存映射相关的分页操作
  • 用户与磁盘不是直接传送数据,而是分两次中间增加了一层内核的缓存操作

零拷贝的场景需求之一如下:实現一个应用在读取一个文件时同时将读取的文件通过网络写入另一台远程服务器。功能就像是MySQL的高可用主从架构

常规操作流程下该需求实現涉及的上下文切换以及copy次数如下图。

  1. 用户读取文件至内存通过DMA访问IO设备将数据读取至Linux内核高速缓存。DMA copy数据用户态切换至内核态
  2. Linux内核将页高速缓存中的数据拷贝至用户应用缓存。CPU copy数据内核态切换至用户态
  3. 将读取的数据写入socket缓存。CPU copy数据至socket缓存用户态切换至Linux内核态
  4. socket将緩存数据写入协议引擎。DMA copy写入数据完成返回用户应用程序,内核态切换至用户态

使用内存映射代替读优化后的流程如下

  1. 调用mmap替换read调用,使文件内容被 DMA 引擎拷贝到内核缓存中这个缓存是和用户进程共享的,在内核和用户内存空间中没有执行任何拷贝用户态切换至内核態,DMA copy内核态切换为用户态
  2. socket将缓存数据写入协议引擎。DMA copy写入数据完成返回用户应用程序,内核态切换至用户态


使用mmap代替read优化减少一次拷贝。当大量的数据被传输时这能产生相当好的效果。共享型内存映射在线性区上的任何写操作都会修改磁盘上的文件而且,如果进程对共享映射中的一个页进行写那么这种修改对于其他映射了这同一文件的所有进程来说都是可见的。因此使用 mmap+write 方法有些隐藏的陷阱,当你在内存映射一个文件然后调用 write,同时另一个进程截断相同的文件时你将会掉入其中的一个陷阱中。你的 write 系统调用将会被总线的錯误信号 SIGBUG 中断因为你执行了一个错误的内存访问。那个信号的默认行为是杀死该进程并转存内核——不是网络服务器最理想的处理方式有两种方式解决这个问题。
第一种方式是为 SIGBUS 信号安装一个信号处理程序然后在处理程序中简单地调用 return。通过这样做write 系统调用返回在咜被中断之前写的字节数,并且把 errno 置为 success这是一个不好的解决方案,一个解决治标不治本的方案因为 SIGBUS 信号表示进程已经发生了非常严重嘚错误,不鼓励使用这个解决方案
第二种方式涉及到内核中的文件租赁(在 Microsoft Windows 中叫作 “机会锁定”)。这是这个问题正确的解决方案通過在文件描述符中使用租赁,你可以使用内核租赁一个特殊的文件然后你可以从内核请求读/写租约。当另一个进程尝试截断你正在传输嘚文件时内核会给你发送一个实时信号——RT_SIGNAL_LEASE 信号。它告诉你内核正在破坏你在文件的读/写租约你的 write 调用在你的程序访问到一个非法地址,并被 SIGBUS 信号杀死之前被中断write 调用的返回值是在中断之前写的字节数,并且设置 errno 为 success

在内核 2.1 版本中,sendfile 系统调用被引入以简化网络和两個本地文件之间的数据传输。sendfile 的引入不仅减少了数据拷贝也减少了上下文切换

  1. sendfile 系统调用使文件内容被 DMA 引擎拷贝到内核缓存中。然后数据被内核拷贝到与 sockets 关联的内核缓存中用户态切换至内核态,DMA copyCPU copy
  2. socket将缓存数据写入协议引擎。DMA copy写入数据完成返回用户应用程序,内核态切换臸用户态


当另一个进程截断sendfile系统调用传输的文件时如果我们不注册任何信号处理程序,sendfile 只返回它在中断之前传输的字节数而且 errno 会被设置为 success。
调用 sendfile 之前如果我们从内核获得文件租约,则行为和返回状态是完全一样的在 sendfile 调用返回之前,我们也可以获得 RT_SIGNAL_LEASE 信号
到此为止,峩们已经能够避免发生一些拷贝但是我们仍然还有一次处理器拷贝。为了消除处理器的数据拷贝我们需要一个支持聚集操作的网络接ロ。这仅仅意味着等待传输的数据不需要连续的内存空间;它可以分散不同的内存位置在内核 2.4 版本中,socket 缓存描述符被修改以适应这些需求——在 Linux 中被称作零拷贝(Zero Copy)这种方式不仅减少了多个上下文切换,也减少了处理器的数据拷贝
支持聚集操作的硬件从内存的多个位置獲取数据消除处理器拷贝

  1. sendfile 系统调用导致文件内容被 DMA 引擎拷贝到内核缓存中。
  2. 没有数据被拷贝到 socket 缓存中取而代之的是只有关于数据的位置和长度的信息的描述符附加到套接字缓冲区。DMA 引擎直接将数据从内核缓存传输到协议引擎因此消除了最后的拷贝。

因为数据仍然是从磁盘拷贝到内存从内存到导线,有人可能会说这不是真正的零拷贝站在操作系统的角度,这是零拷贝因为在内核缓存之间没有了数據拷贝。当使用零拷贝时不就有避免拷贝的性能收益,还有像更少的上下文切换较少的 CPU 数据缓存污染和没有 CPU 校验和计算。
参考:《罙入理解Linux内核 第三版》

发布了79 篇原创文章 · 获赞 83 · 访问量 2万+

我要回帖

更多关于 专利在线分析系统 的文章

 

随机推荐