乐正

Actions speak louder than words.

Redis 基础教程

简介

Redis 是一个键值存储仓库,经常被称为 NoSQL 数据库。键值存储仓库的本质是有能力按 照一个键映射一个值的方式存储一些数据,然后你可以只通过这个键寻找到你之前通过这个 键存储的值。我们可以使用命令SET将值『fido』存储在键『server:name』中:

1
SET server:name "fido"

Redis 将会把我们的数据永久存储。于是,我们可以假设这样询问 Redis 数据库:键 server:name 对应的值是什么? 然后,Redis 会返回『fido』。

1
GET server:name # => "fido"

下面列出了一些其他常用的命令:

  • DEL 根据给定的键,删除相应的键值关系
  • SETNX 当且仅当给定键没有指定值的时候,才设定相应的键值对
  • INCR 将数字递增
1
2
3
4
5
SET  connection 10
INCR connection # => 11
INCR connection # => 12
DEL  connection
INCR connection # => 1

递增

对于 INCR 命令,我们有一些特别的事情要说明。Redis 为什么会提供一个自己很简单就 能实现的功能呢?就像下面这么简单:

1
2
3
x = GET count
x += 1
SET count x

然而问题是,这种递增操作只能用于单客户端上。看一下,如果两个客户端同时执行这样 的操作会发生什么:

  1. 客户端 A 读取值 x 为10
  2. 客户端 B 读取值 x 为10
  3. 客户端 A 写 x 的值为11
  4. 客户端 B 写 x 的值为11

我们希望 x 的值为12,但是真实的 x 的值仅仅是11,这是因为你自己定义的递增操作不是 一个原子性操作。使用 Redis 的 INCR 命令可以防止这样的事情发生, 因为它是一个原 子性操作。Redis 为许多不同类型的数据提供了类似的原子性操作。

过期

Redis 可以使用命令 EXPIRETTL,能让一个键值对只存在于指定的时间段内。

1
2
SET resource:lock "Redis Demo"
EXPIRE resource:lock 120

这会导致键 resource:lock 会在120s 后被删除,你可以使用 TTL 去查看一个键还能存 在多少时间:

1
2
3
4
TTL resource:lock # => 120

# after 122s later
TTL resource:lock # => -2

这里的 -2 是指 resource:lock 已经不存在了,如果返回值是 -1 说明这个键永远不会过 期。注意:当你使用 SET 重新设置一个键, 它对应的 TTL 就会被重置。

1
2
3
4
5
SET resource:lock "Redis demo 1"
EXPIRE resource:lock 120
TTL resource:lock # => 119
SET resoource.lock "Redis demo 2"
TTL resource:lock # => -1

列表

此外,Redis 也支持一些更复杂的数据结构。我们第一个会看的是列表。一个列表是一系列 有序的值。与数组有关的一系列操作是:RPUSH, LPUSH, LLEN, LRANGE, LPOPRPOP。列表和普通的值一样,可以被直接使用。

  • RPUSH 将值添加到列表的末尾
1
2
RPUSH friends "Alice"
RPUSH friedns "Joe"
  • LPUSH 将值添加到列表的开始
1
LPUSH friends "Sam"
  • LRANGE是从列表中去一个指定范围的子集。它通过你想取的范围的第一个元素的下标和 最后一个元素的下标作为参数。将 -1 作为参数意味着取值到列表的最后。
1
2
3
LRANGE friends 0 -1 # => 1) "Sam", 2) "Alice", 3) "Joe"
LRANGE friends 0  1 # => 1) "Sam", 2) "Alice"
LRANGE friends 1  2 # => 1) "Alice", 2) "Joe"
  • LLEN 返回指定列表的长度
1
LLEN friends # => 3
  • LPOP 从列表中删除第一个元素,并将它作为返回值
1
LPOP friends # => "Sam"
  • RPOP 从列表中删除最后一个元素,并将它作为返回值
1
RPOP friends # => "Joe"

注意看现在的列表:

1
2
LLEN friends # => 1
LRANGE friends 0 -1 # => 1) "Alice"

集合

接下来我们要看的数据结构是集合。集合和列表类似,但是集合中元素是无序且不能重复的。 和集合有关的一些重要的命令是:SADD, SREM, SISMEMBER, SMEMBERSSUNION.

  • SADD 将给定的值添加到集合中
1
2
3
SADD superpowers "flight"
SADD superpowers "x-ray vision"
SADD superpowers "reflexes"
  • SREM 从集合中移除指定的值
1
SREM superpowers "reflexes"
  • SISMEMBER 检查一个值是否在集合中,返回0不在,返回1在。
1
2
SISMEMBER superpowers "flight" # => 1
SISMEMBER superpowers "reflexes" # => 0
  • SMEMBERS 返回集合中所有的元素
1
SMEMBERS superpowers # => 1) "flight", 2) "x-ray vision"
  • SUNION 合并两个或者更多个集合,并且将所有的元素返回。
1
2
3
SADD birdpowers "pecking"
SADD birdpowers "flight"
SUNION superpowers birdpowers # => 1) "pecking", 2) "flight", 3) "x-ray vision"

可排序集合

集合是一个非常有用的数据类型,但是因为它是无序的,所以因此会导致很多的问题。因此 Redis 1.2 开始添加了可排序集合。可排序集合和标准的集合类似,只是添加了一个分数和 集合中的元素相关联。这个分数用来给元素排序。

1
2
3
4
5
6
ZADD hackers 1940 "Alan Kay"
ZADD hackers 1906 "Grace Hopper"
ZADD hackers 1954 "Wang Zhi He"
ZADD hackers 1988 "Li Feng"

ZRANGE hackers 1, 3 # => 1) "Alan Kay", 2) "Grace Hopper", 3) "Wang Zhi He"

哈希表

除了字符串、列表、集合之外,Redis 还能储存一种类型的数据————哈希表。哈希表将两个 字符串类型的值映射在一起,它是最好的用来表示对象的数据结构。

1
2
3
HSET user:1000 name "John Smith"
HSET user:1000 email "john.smith@google.com"
HSET user:1000 password "public"

使用命令 HGETALL 获得保存的数据

1
HGETALL user:1000

我们也可以一起行设置多个域

1
HMSET user:1001 name "Zack Lee" email "zack.lee@facebook.com" password "public"

我们也可以只获取特定域的值:

1
HGET user:1001 name # => "Zack Lee"

数字类型的值在哈希表里面有一些方便的原子性的递增方法:

1
2
3
4
5
HSET user:1000 visits 10
HINCRBY user:1000 visits 1  # => 11
HINCRBY user:1000 visits 10 # => 21
HDEL    user:1000 visits
HINCRBY user:1000 visits 1  # => 1

关于哈希表的完整命令列表,请查看官方文档

更多关于 Redis 文档:

将你的Vim 打造成轻巧强大的IDE

Vim和Emacs一个称为神之编辑器一个被称为编辑器之神,固然很是夸张,但也足以说明这两 款软件的优秀和在程序员界的地位。但是它们都已漫长的学习曲线让人望而生畏,阻止了大 多数人进入。作为一名几乎完全使用Vim写各种代码、文档的人,我想把我自己平时使用的 插件和配置整理下来,方便自己的总结和归纳,如果能有幸帮助到一些想学习Vim但是又不知 道如何入门的人来说,那就再荣幸不过了

在下面的内容中,我会介绍我使用的插件、Vim的配置,最后如果你觉得这些配置手动太麻烦 的话,我推荐你看我的另一篇文章(从零搭建和配置OSX开发环境), 在那篇文章的末尾,我给出了一个自动化配置和管理Vim的方法

先贴一张我的Vim的截图:

vim as ide

你看的到的插件

从上面那种截图中肉眼能看到的插件说起,把整个界面按照左窗口、主窗口、右窗口和下窗 口命名,依次介绍出现在这个窗口中的主要插件。

主窗口

作为一款主要用于书写代码的文本编辑器,一个足够舒服、靓丽的配色当然是首要考虑的。 我使用的配色主题是molokai官方地址),在 你安装好了这个插件之后,你需要下面几行配置应用它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
" Switch syntax highlighting on, when the terminal has colors
if (t_Co > 2 || has("gui_running")) && !exists("syntax_on")
  syntax on
endif

" Javascript syntax hightlight
syntax enable

" Set syntax highlighting for specific file types
autocmd BufRead,BufNewFile Appraisals set filetype=ruby
autocmd BufRead,BufNewFile *.md set filetype=markdown
autocmd Syntax javascript set syntax=jquery

" Color scheme
colorscheme molokai
highlight NonText guibg=#060606
highlight Folded  guibg=#0A0A0A guifg=#9090D0

另外一个推荐的vim主题是solarized(官方地址)。

在选定了一个适合自己的主题之后,就需要一些配置去解决排版的问题,比如字符编码和缩 进等问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
" Backspace deletes like most programs in insert mode
set backspace=2
" Show the cursor position all the time
set ruler
" Display incomplete commands
set showcmd
" Set fileencodings
set fileencodings=utf-8,bg18030,gbk,big5

filetype plugin indent on

" Softtabs, 2 spaces
set tabstop=2
set shiftwidth=2
set shiftround
set expandtab

" Display extra whitespace
set list listchars=tab:»·,trail:·

" Make it obvious where 80 characters is
set textwidth=80
set colorcolumn=+1

" Numbers
set number
set numberwidth=5

set matchpairs+=<:>
set hlsearch

在第68行,水平和垂直方向分别有一条高亮条,这是用来表示我当 前光标所处于的行和列 用的。实现它,只需要几行简单的配置就可以了:

1
2
3
4
" Highlight current line
au WinLeave * set nocursorline nocursorcolumn
au WinEnter * set cursorline cursorcolumn
set cursorline cursorcolumn
关于代码补全

有些人可能已经发现了,在我的主窗口中没有演示代码补全的功能,我需要对此做一个说明。 我本人不喜欢过于强大的代码补全,所以默认的对于我来说已经完全足够了,如果你需要使 用更强大的代码补全,我推荐你使用YouCompleteMe官方地址)。

YouCompleteMe

左窗口

左窗口是一个用于浏览目录结构的插件nerdtree官方地址)。 同样一些简单的配置之后,它便能为你提供一个方便够用的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
" NERD tree
let NERDChristmasTree=0
let NERDTreeWinSize=35
let NERDTreeChDirMode=2
let NERDTreeIgnore=['\~$', '\.pyc$', '\.swp$']
let NERDTreeShowBookmarks=1
let NERDTreeWinPos="left"
" Automatically open a NERDTree if no files where specified
autocmd vimenter * if !argc() | NERDTree | endif
" Close vim if the only window left open is a NERDTree
autocmd bufenter * if (winnr("$") == 1 && exists("b:NERDTreeType") && b:NERDTreeType == "primary") | q | endif
" Open a NERDTree
nmap <F5> :NERDTreeToggle<cr>

右窗口

在我的截图中,右窗口陈列出了我当前打开的rb文件中申明的类、变量及方法等等。这是一 款名叫tagbar的插件,它为我们提供了一个简单的方式去浏览当前文件的结构,并且支持 在各个标签之间快捷的跳转。同理,安装之后,需要一些配置:

1
2
3
4
" Tagbar
let g:tagbar_width=35
let g:tagbar_autofocus=1
nmap <F6> :TagbarToggle<CR>

如果你发现默认的Tagbar不能支持你的语言,比如Css, Clojure, Markdown等等,你可以 参照这篇文章为它提供额外的支持。

下窗口

下窗口包含了两个部分:一个是用于全局搜索的窗口和一个状态条。

全局搜索是一个基于文件名的搜索功能,可以快速定位一个文件。这是ctrlp这个插件提 供的功能。下面是ctrlp的一些配置:

1
2
3
" ctrap
set wildignore+=*/tmp/*,*.so,*.swp,*.zip,*.png,*.jpg,*.jpeg,*.gif " MacOSX/Linux
let g:ctrlp_custom_ignore = '\v[\/]\.(git|hg|svn)$'

ctrlp默认会使用grep进行搜索,效率低且慢。所以,我使用了ag去替换默认的搜索 功能。ag是一款轻量级的搜索工具,速度非常快。为了集成ag,需要添加下列配置:

1
2
3
4
5
6
7
8
if executable('ag')
  " Use Ag over Grep
  set grepprg=ag\ --nogroup\ --nocolor
  " Use ag in CtrlP for listing files.
  let g:ctrlp_user_command = 'ag %s -l --nocolor -g ""'
  " Ag is fast enough that CtrlP doesn't need to cache
  let g:ctrlp_use_caching = 0
endif

下面状态条中会依次显示:当前模式、Git分支、文件路径、文件是否保存以及当前所载行和 列的信息。这是通过vim-powerline来实现的。其中显示Git信息需要配合vim-fugitive 插件一些使用。

1
2
set laststatus=2 " Always display the status line
set statusline+=%{fugitive#statusline()} "  Git Hotness

小结

通过以上的配置,你就可以拥有一些如第一张图所示的那样,看起来还不错的编辑器。当然, Vim之所以如此倍受推崇,只是依靠这些还是远远不够的。接下来,我要介绍一些看不见的插 件来实实在在的提升Vim体验。

看不见的实用插件

现代化的插件管理

在我的另一篇文章中(从零搭建和配置OSX开发环境) ,我已经详细介绍过Vundle这个管理Vim插件的一个软件,这里不做过多介绍。

在Vim中执行你想要运行的命令

vim-run-interactive让你可以在Vim中执行几乎任何你想要在命令行中执行的命令。举例 来说,假设你有条git update的自定义命令,你可以通过:RunInInteractiveShell git update来执行它,而不需要退出Vim。添加一条配置,可以简化这个步骤:

1
2
" Run commands that require an interactive shell
nnoremap <Leader>r :RunInInteractiveShell<space>

如此一来,你可以通过<Leader> + r + 命令键来激活执行命令。如果你不知道什么是Leader 键,你可以去百度或者Google一下。

Vim的语法检查

Vim中有个很强大的语法检查插件,它支持几乎所有常用的语言的语法检测[syntastic(https://github.com/scrooloose/syntastic)]。 附上一张来自官方的截图:

syntastic plugin

为了让它更好的工作,同样需要一些配置:

1
2
3
4
5
6
7
8
9
" configure syntastic syntax checking to check on open as well as save
let g:syntastic_check_on_open=1
let g:syntastic_html_tidy_ignore_errors=[" proprietary attribute \"ng-"]
let g:syntastic_always_populate_loc_list = 1
let g:syntastic_auto_loc_list = 1
let g:syntastic_check_on_wq = 0
set statusline+=%#warningmsg#
set statusline+=%{SyntasticStatuslineFlag()}
set statusline+=%*

Rails集成开发套件

我是一名Ruby的爱好者,所以Vim中少不了针对Ruby的一系列插件。我作为一名Web开发者, Rails这个大名鼎鼎的框架自然也是有所涉猎。所以在我的Vim中有着针对它们开发的一套插 件。

插件列表:

  • ruby-vim:在快速的在module, class, method中跳跃。
  • vim-bundler:在Vim中集成Bundler
  • vim-endwise:自动补全end关键字。
  • vim-rails:它的功能很多,可以说是用 Vim开发Rails不可缺少的一个插件。更详细的信息,可以前往它的官方网站获取。
  • vim-rspec:在Vim中执行Rspec测试。
1
2
3
4
5
6
7
8
9
" Cucumber navigation commands
autocmd User Rails Rnavcommand step features/step_definitions -glob=**/* -suffix=_steps.rb
autocmd User Rails Rnavcommand config config -glob=**/* -suffix=.rb -default=routes

" RSpec.vim mappings
map <Leader>t :call RunCurrentSpecFile()<CR>
map <Leader>s :call RunNearestSpec()<CR>
map <Leader>l :call RunLastSpec()<CR>
map <Leader>a :call RunAllSpecs()<CR>

更多好用的工具

还有很多好用的插件,如果每个都一一说明,那么篇幅再长一倍怕也是不够。所以,我这里 就把一些好用的插件列出来,有兴趣的可以自己看看。

备注

有更多的一些插件我没有都列出来,它们一般用于特定语法的开发,不一定适合所有人。你 可以参考从零搭建和配置OSX开发环境 这篇文章,自动管理、配置你的Vim环境。

在上文中,我有一些遗漏的或者错误的地方,希望朋友发现后可以在下方留言指正。如果你 有一些更好的插件、配置,也恳请你在下方留言。谢谢。

进程描述和控制

进程概念是现在操作系统的基本概念,已经成为计算机科学中的一大成就。

什么是进程?

进程的出现,是为了是操作系统可以以一种有序的方式管理应用的执行,以达到以下目的:

  • 资源对多个应用程序是可用的
  • 物理处理器在多个应用程序之间切换以保证所有程序都在执行中
  • 处理器和I/O设备能得到充分利用

所有现在操作系统采用的方法都是依据一个或者多个进程存在的应用程序执行的一种模型。 到底什么是进程呢?

进程是一组元素组成的实体,它可以是一个正在执行中的程序,也可以是一个能分配给处理 器并由处理器执行的实体。进程的两个基本元素是:程序代码(program code)和代码相 关联的数据集(set of data)。在进程执行时,任意给定一个时间,进程都可以唯一地 表征为以下元素:

  • 标识符:进程的唯一标识符,用来区别其他进程
  • 状态:进程在不同的生命周期有着不同的状态
  • 优先级:相对于其他进程的优先级
  • 程序计数器:程序中即将被执行的下一条指令的地址
  • 内存指针:包含程序代码和进程相关数据的指针,还有和其他进程共享内存块的指针
  • I/O状态信息:包括显示的I/O请求、分配给进程的I/O设备和被进程使用的文件列表等
  • 记账信息:可能包括处理器时间总和、使用的时钟数总和、时间限制、记账号等

上述的列表信息被存放在一个称为进程控制块的数据结构中,该控制块由操作系统创建 和管理。

进程状态

在任何时刻,进程可以处于以下两种状态之一:运行态和未运行态,这是最简单的两状态模 型。在这个模型中,会有一个调度器(dispatcher),使处理器从一个进程切换到另外一 个进程。

'内存状态转换'

由于存在着一些处于非运行状态但已经就绪等待执行的进程,而同时存在另外一些处于堵塞 状态等待I/O操作结束的进程。因此,解决这一问题比较自然的方法是使用五状态模型: 运行态、就绪态、堵塞/等待态、新建态和退出态

'五状态模型'

被挂起的进程

上述的基本状态提供了一种为进程建立系统模型的方法,并指导系统的实现。但是,往这个 模型中添加其他状态也是合理的。

由于处理器的运行速度远大于I/O,以至于内存中所有的进程都在等待I/O的情况也是很常见 的。因此,即使是多道程序设计,大多数处理器仍然可能处于空闲状态。

一种解决方案是增大内存,使得内存中可以存在更多的进程。然而这种方案显然是治标不治 本的。

另外一种解决方案是交换(swapping)。当内存中没有处于就绪状态的进程时,操作系统 就把被阻塞的进程换出到磁盘中的挂起队列(suspend queue)。操作系统在此之后取出 挂起队列中的另一个进程,或者接受一个新进程,将其加载到内存中运行。这时,在进程状 态模型中添加了另外一个状态:挂起态

'单挂起态模型'

当操作系统从挂起队列中取出一个依然阻塞的进程是毫无意义的,因为它仍然没有准备好执 行。所以为了区分被挂起的进程哪些是可以取出的,需要设计另外一种挂起模型:

'双挂起态模型'

为了区分,需要四个状态:

  • 就绪态:进程在内存中并可以执行
  • 阻塞态:进程在进程中并等待一个事件
  • 阻塞/挂起态:进程在外存中并等待一个事件
  • 就绪/挂起态:进程在外存中,但是只要被载入内存就可以执行

总结一下挂起的进程的概念:

  1. 进程不能被立即执行。
  2. 进程可能是或不是正在等待一个事件。如果是,阻塞条件不依赖于挂起条件,阻塞事件的 的发生不会使进程立即执行。
  3. 为组织进程的执行,可以通过代理把这个进程置于挂起状态,代理可以是进程自己,也 可以是父进程或者操作系统。
  4. 除非代理显示的命令操作系统进行状态转换,否则进程无法从这个状态中转移。

除了因为提供更多的内存空间,进程还会因为什么原因被挂起呢?

事件 说明
交换 操作系统需要释放更多的内存空间,以调入并执行处于就绪状态的进程
其他OS原因 操作系统可能挂起后台进程或工具程序进程,或者怀疑导致问题的进程
用户请求 用户可能希望挂起一个程序的执行,目的是为了调试或者与一个资源连接
定时 一个进程可能会周期性的执行,而且可能在等待下一个时间间隔时被挂起
父进程请求 父进程请求挂起后代进程,以检查或者修改挂起的进程

在所有这些导致进程挂起的情况中,挂起进程的活动都是由最初请求挂起的代理请求的。

进程描述

操作系统控制计算机系统内部的事件,它为处理器执行进程而进行调度「schedule」和分派 「dispatch」,给进程分配资源,并响应用户程序的基本服务请求。因此,操作系统可以被 视为管理系统资源的实体。

操作系统为了控制进程和管理资源需要哪些信息呢?

操作系统的控制结构

为了管理进程和资源,操作系统构造并维护它所管理的每个实体的信息表。

'操作系统控制表的通用结构'

操作系统维护四种不同类型的表:内存、I/O、文件和进程

内存表「memory tables」用于跟踪内存和外存。内存表必须包括一下信息:

  • 分配给进程的内存
  • 分配给进程的外存
  • 内存块或者虚拟内存块的保护属性
  • 管理虚拟内存所需要的任何信息

I/O表「I/O tables」用于管理计算机系统中的I/O设备和通道。在任何给定的时刻,一个I/O 设备或者是可用的,或者是已分配给某个特定的进程。如果正在进行I/O操作,则操作系统需 要知道I/O操作的状态和作为I/O传送的源与目标的内存单元。

文件表「file tables」用于提供关于文件是否存在、文件在外存中的位置、当前状态和属性 的信息。

进程表「process tables」为了管理和操作进程所必须使用的表。

进程控制结构

操作系统在管理和控制进程时,首先必须知道进程的位置,然后,它必须知道在管理时所必 需的进程的属性(如进程ID、进程状态)。

进程位置

想一个最基本的问题:进程的物理表示是什么?

回想之前关于进程的定义,进程至少包括一个或者一组被执行的程序,与这些程序相关联的 局部变量、全局变量和任何已定义常量的数据单元。因此,一个进程至少包括足够的内存空 间,以保存该进程的程序和数据;此外,程序的执行通常设计用于跟踪过程调用和过程间参 数传递的栈。最后,与每个进程相关联的还有操作系统用于控制进程的许多属性,也就是进 程控制块。程序、数据、栈和属性的集合称为进程映像「process image」

在最简单的情况下,进程映像保存在邻近的活连续的存储块中。因此,操作系统必须知道每 个进程在磁盘中的位置;对于在内存中的进程,需要知道其在内存中的位置。

现代操作系统嘉定分页硬件允许用不连续的物理内存来支持部分常驻内存的程序。在任何给 定的时刻,进程映像的一部分可以在内存中,剩余部分可以在外存中。因此,操作系统维护 的进程表必须表明每个进程映像中每页的位置。

进程属性

操作系统所需要的每个进程信息的简单分类:

  • 进程标识信息
  • 进程状态信息
  • 进程控制信息

'虚拟内存中的用户进程'

所有的操作系统中,每个进程都分配了唯一的一个数字来表示进程标识符。除此之外, 还分配一个用户标识符,用于表明拥有该进程的用户。

处理器状态信息包括处理器寄存器的内容。当进程被中断时,所有寄存器中的信息必须 被保存起来,使得进程恢复执行时,这些信息可以被恢复。

进程控制块中的第三类主要信息是进程控制信息,用于操作系统控制和协调各种活动进 程所需要的额外信息。

进程控制块中可能还包含构造信息,包括将进程控制块链接起来的指针。

'进程链表结构'

进程控制块的作用

进程控制块是操作系统中最重要的数据结构。操作系统中的每个模块,包括那些设计调度、 资源分配、中断处理、性能检测和分析的模块,都可能读取或者修改进程控制块。

进程控制

执行模式

为了保护操作系统和重要的操作系统表不受用户程序的干涉,操作系统通常使用两种模式管 理进程:特权模式『也称为系统模式(system mode)、控制模式(control mode)或者内核模式 (kernel mode)』,和用户模式。

在内核模式下,软件具有对处理器及所有指令、寄存器和内存的控制能力,这一级的控制对 用户程序不是必需的,并且为了安全也不是用户程序可以访问的。

进程创建

操作系统一般安装以下步骤创建进程:

  1. 给进程分配一个唯一的进程标识符。此时,主进程表中增加一条新表项,其对应该进程。
  2. 给进程分配空间。包括进程映像中的所有元素。
  3. 初始化进程控制块。进程控制信息部分的初始化基于标准默认值和为该进程所请求的属 性。
  4. 设置正确的连接。
  5. 创建或者扩充其他数据结构。

进程切换

关于进程切换,有着一些问题。

  • 进程什么时候进程切换?
  • 执行模式切换和进程切换之间有什么区别?
  • 进程切换时,操作系统必须对它控制的各种数据结构做什么?
何时切换进程

通常,下列原因可能造成进程切换。

机制 原因 用例
中断 当前指令的外部执行 对异步外部事件的反映
陷阱 与当前指令的执行有关 处理一个错误或者异常
系统调用 显式请求 调用操作系统函数
模式切换

如果存在一个未处理的中断,处理器会做以下工作:

  • 把程序计数器置成中断处理程序的开始地址。
  • 从用户模式切换到特权模式,使得中断处理代码可以包含有特权的指令。
进程的状态变化

如果当前正在运行的进程被转换到另外一个状态(就绪、挂起等),则操作系统必须使其环 境发生实质性的变化:

  1. 保存处理器的上下文环境,包括程序计数器和其他寄存器。
  2. 更新当前处于运行态进程的进程控制块,包括将进程状态改变到另外一个状态。
  3. 将进程的进程控制块移到相应的队列。
  4. 选择另一个进程执行。
  5. 更新所算则进程的进程控制块。
  6. 更新内存管理的数据结构。
  7. 恢复处理器在被选择的进程最近一次切换出运行状态时的上下文环境。