2018年12月

UCloud 技术副总裁杨镭首谈 UCloudStack、保护客户隐私、回馈开源社区背后的故事和挑战。

日前,Linux 中国社区的老王参加了 UCloud 用户大会,并有幸和 UCloud 技术副总裁杨镭进行了面谈。以下将我们谈话中一些内容分享给大家。

杨镭,UCloud 技术副总裁。全面负责 UCloud 产品研发和产品运营工作,有超过十年 IT & 互联网行业从业经验,在网络领域拥有丰富的经验和深刻的理解。

为什么专门开发了 UCloudStack?

今天的大会上面您提出一个针对私有云的 UCloudStack是否可以给我们详细介绍一下为什么在有了生态很成熟的 OpenStack 的情况下,UCloud 还投入了巨大的资源去研发UCloudStack

杨镭:

我们在 OpenStack 上做的还比较早的,当时我们的认知就是公有云是公有云,而私有云 OpenStack 做的比较好。但实际上,通过不断的时间验证,我们发现 OpenStack 有一个比较大的问题,就是过于复杂。

回到用户对私有云需求来讲,用户实际上关注的还是能不能解决他的需求,而不是说要不要用 OpenStack。所以,我们考虑到,既然已经有了多年的云计算的开发和运维经验,如果从头开始做一个自己的私有云产品,是不是会做的更好?

我们做了 16 个月就做出来了这个产品,在完成核心产品之后,整体的代码量还是很少的。代码量是相对来讲是衡量一个项目复杂性的很好的指标。我们根据自己这么多年来的经验做下来,我们发现这个事情并没有那么的难,而且,现在这个时候,在做这个产品的时候认知就和几年前不太一样,我们会比以前想的成熟很多。我们相信做出来的东西会比 OpenStack 更轻量、更好用,这是我们做这个产品的一个主要的想法。

其实现在真正能自己独立用 OpenStack 产品的公司一般都有一个一定规模研发的团队,至少十个人。但是云计算这个市场的人才竞争还是蛮厉害的,一个公司很难有这样一群比较好的人去做这个事情,所以它的门槛会很高。如果说公司的目标只是为了有一个私有云,那么这样研发投入的必要性是打问号的。这些企业的目标和我们云服务商的目标不一样,它的云只是一个工具,不像我们的云是我们本身的一个产品。所以这时实际上企业往往会考虑说怎么能把这个私有云更好的建起来,很快、很稳定的用起来,在成本上比较合理。所以在我们的这个产品上,我们最重要的一个点确实就是在轻量上。

假如你对 OpenStack 需要做一些改动,是牵一发而动全身的,为了做某个功能可能要改造底层的东西,这个时候整个过程在研发上来讲就太“重”了,所以我们如果从零开始做,其实我们的底层就是可控的。我们在第一次推出的时候,我们总的代码量才八九万行,而 OpenStack 所有项目代码量加起来有几千万行,这之间的复杂性灵活性是完全不能比拟的,这是它的一个轻的主要原因。

另外一个,因为我们做这个产品的目的性会比做一个开源的社区项目更明确一点,比如说我们做产品的路线很不一样。当我们的核心产品完成以后,我们做的一个比较大的改变是和传统的网络打通,我们 UCloudStack 部署完了以后和传统的网络做很大的打通,这点和开源社区项目的目标不一样,它要让这个东西更加通用。这在产品的改变选择的路径差别特别大。这个时候看用户要什么,往往用户要的东西更贴近它想要的,这里面也是一个很大差异点。

今天本来想放一段视频,那段视频是当时在富士康的办公园区里面拍的,当时是下午四点钟进入机房到晚上十点种整个私有云的交付就完成了。而且当中大概有 2-3 个小时是因为机房的一些问题影响了整个的进度,如果看到那个视频就会真正感觉到它很轻,但一般而言 OpenStack 部署起来时间会更长,比如说好几天的时间,它的底层太复杂了。

我们一直认同的一点是说,开源软件比较适合做一个组件,比如说一个数据库、一个操作系统、一个很垂直的部分。但是云实际上不是一个垂直的组件,而是很多组件组成在一起的一个体系。如果你去用开源软件做组件的方式去做云,这其实不是特别的理想。

为什么将客户隐私保护拔高到这样的高度?

今天上午大会上您提到一个隐私的产品,禁止一些 API 采集用户数据,来保护用户的隐私,这方面能够详细谈谈吗?

杨镭:

这个我需要澄清一下,我们说的是禁止我们的云平台收集我们客户的信息,而不是说我们禁止了客户收集他的用户信息。因为我们是给企业提供服务的,企业本身给他的终端用户提供服务,我们控制的是我们和企业之间数据的隐私问题。比如说它的使用习惯是什么,或者它业务的模型是什么,我们在控制这个。

因为我们自己是做云服务的,我们的技术工程师在管理我们云业务的时候,实际上是有可能看到用户的数据。比如说你是一个电商公司,假设你的数据库如果没有进行加密,技术人员是可以看到这个数据的。目前来讲绝大多数的厂商,靠的还是流程和制度,靠的不是技术手段保护。什么叫技术手段,就是说我想看却看不到,因为是加密的。你只有拿到相应的解密密钥才能去看。我们说的数据保护更多的是这方面。

再比如说我们的云主机最终跑在 SSD 硬盘上,如果你去机房里把硬盘直接拔插走,而没有加密的话,一定是能读到里面的数据。从长远来看,这是不能出任何问题的,哪怕是一两次。像某互联网公司出了几次乘客的安全事件以后,事情影响就非常恶劣。回到做云服务,我们的责任一样重大。我们如果只是一家公司,也许我有的只是很特定的一些数据,但是作为做云服务的公司,我们上面的客户不是我们能决定的,要看客户自己做什么业务,所以我们上面有几十万,甚至几百万的数据,这是一个非常长远、需要很认真对待的事情。所以我们把这一点放到了一个很高的、甚至于到我们企业文化和价值观的角度。

作为一个工程师来讲,首先我们要有价值观,这是第一点,但是也要从技术上保证,所以我们未来会在这方面投入很多的技术研发工作,来实现比如说我们自己的技术工程师没有任何可能能看到我们客户数据。这里面实际上是有很多的技术工作要做的。

在我们刚刚做云的前两年,那时候如果去对数据做加密,就会严重影响到性能。这个时候你很难两者兼得,但是随着这几年的技术的演进,很多的加密算法被逐渐的优化以后,基本能做到做了加密也不怎么会影响性能。所以现在这个时代,是一个非常好的时机,是一个你应该要去做这件事的时机。

整个这个事情大概我们是这样看的。除了数据的加密,包括用户有时候会输入给我们很多数据,他在做一些操作需要提供身份信息,比如说现在网络安全法要求用户使用要做实名认证,也就是说他的身份证会上传到我们这儿,这些数据其实是一个很隐私的数据。这件信息的泄露是一次都不能发生的,所以我们如果回到最根本的来说,我们怎么样尊重我们的用户?这个事情其实比一些产品功能更重要。

我们不希望出了事故以后我们再做一些补救的工作,那时候即使你是真的去做,你的用户也已经受到了很大的伤害。

另外一个层面,我今天没有提到,因为云计算的公司,我们特别需要最顶尖的技术人才,我们这么多年来在吸引最顶尖的人才上我们逐渐意识到靠的不是钱、待遇,或者是一些职位。最优秀的顶尖技术人才,看的是你公司的理念和价值观,比如说谷歌、苹果他们有非常多的几十年的行业人才在里面,为什么?他们做的工作就是数据安全性、隐私就这方面的工作。所以我们也是在商业的进化过程中,我们发现要做一家几十年的公司,不是靠一两年的财报,而是靠这些背后的东西,这是我们很真实的一个自己成长的过程。

对开源社区的回馈

我们非常关注 UCloud 在采用开源软件方面的情况,云服务商的很多基础设施都是基于开源软件的。目前UCloud在开源方面对上游社区的贡献情况怎么样?

杨镭:

我们目前在开源方面目前做的工作还不多。但是为什么不多?不是我们不想做,而是因为我们处在一个技术竞争特别激烈的行业里面,你的产品实际上卖的都是技术。我们的技术人员这么多年来其实一直不太够用,所以我们绝大多数的精力是在做我们自己不得不做的事情,比如说今天说的网络架构的透明升级,这个事情实际上把我们的核心人员的精力全部消耗掉了。为了这些升级,我们要跟客户沟通好这个事情让客户放心我们才能做,这也是为什么我们会做 29 个月的原因,其中有 9 个月是跟客户沟通的过程中。我们的人并不是很多,我们对自己产品的要求还是蛮高的,在这两个因素影响下,我们也很难拆出精力来更多的主动回馈社区。

但是这一点上我们想法是很明确的,只要我们现在有精力就会回馈社区。比如说这次做的 25G 的网卡,这个解决了一个核心障碍,我们把这个东西交给上游,但是从整个公司来讲这块投的人员和精力还不是很多。像我们开源的命令行工具,我们和 Hashicorp 做的整合,这些我们全部开源了,但是这些还远远不够。其实开源,也是想让用户知道你的代码到底怎么写的,只是说这里面是一个精力问题,不是意愿问题。

我们在 2013 年做了一个内核模块,是可以把 SSD 盘加速 100 多倍,由于一些原因,我们的代码是针对某些特定场景的。同时在代码的规范上也做了一点牺牲,如果要开源,相当于要额外增加 30% 的工作量。但是我们现在一直在想,就像今天举的例子,把很多东西开源,尽量的变成工具化,而不是放在 UCloud 的内部一个产品,只是我们精力不太够。

最近 Redis、Kafka、ES 这样的软件都在许可件上改了自己原来的开源许可证,针对云服务商提出了特别的条款。比如说云服务商用 Redis给客户提供服务,但是很少对 Redis 进行社区回馈,所以云服务商需要额外采购商业许可证,您怎么看这个事情,UCloud 怎么面对这个情况?

杨镭:

目前来看,我觉得确实云服务商对社区的回馈是偏少的,所以说我觉得 Kafka 和 Redis 在这个事情上他们是有一定的道理。但这个事情也不能单方面的看待,因为实际上云厂商在推出 Kafka、Redis、ES 产品时,其实也帮这些开源软件做了很多的推广。Redis 比较简单,Kafka 或者是 ES,其实还是一个蛮复杂的软件。云厂商将这些组件加入平台后,把最复杂的地方把简化了,用户甚至连性能维护都不用考虑,另一个维度来看相当于推广了 Kafka。我认为云厂商或者是以此谋利的厂商,他应该做足够的回馈,不管你是以钱的方式,还是以研发的方式,我觉得是应该回馈的。


和杨镭的沟通当中,我们还谈到了许多其他问题,这里限于篇幅就不展开叙述了。从上面的谈话当中,我们能感受到 UCloud 做为一家新兴发展起来的初创公司,无论是在技术还是企业价值观方面,逐渐形成了自己独有的优势。其实作为 UCloud 多年的老朋友,这次参加这个大会,让我意外的是,原来我对他们的了解还所知甚少;也为他们在隐私保护、技术研发等方面默默做的工作而吃惊。希望这些技术人可以秉持初心,真正做出一个好用、值得用的产品来。

“穿山甲专访”栏目是 Linux 中国社区推出的面向开源界、互联网技术圈的重要领军人物的系列采访,将为大家介绍中国开源领域中一些积极推动开源,谙熟开源思想的技术人,并辨析其思考、挖掘其动因,揭示其背后所发生的事情,为关注开源、有志于开源的企业和技术人标出一条路径。

取名为“穿山甲”寓意有二:取穿山甲挖掘、深入之意来象征技术进步和表征技术领袖的作用;穿山甲是珍稀保护动物,宣传公益。

MPlayer 是一个多功能的开源媒体播放器,它在 Linux 命令行中非常有用。

你已经看到我们为期 24 天的 Linux 命令行玩具日历。如果这是你第一次访问该系列,你可能会问自己什么是命令行玩具。它可能是一个游戏或任何简单的消遣,可以帮助你在终端玩得开心。

你们中的一些人之前已经看过我们日历中的各种玩具,但我们希望每个人至少见到一件新玩具。

在命令行中有很多方法可以听音乐。如果你有本地存储的媒体,cmus 是一个很好的选择,但还有很多其他选择

不过,很多时候,当我在终端的时候,我会走神并且不会注意挑选每首歌,并让其他的来做。虽然为了这个我有很多播放列表,但过了一段时间,即使过时,我也会切换到互联网电台。

今天的玩具,MPlayer,是一个多功能的多媒体播放器,几乎可以支持任何你给它的媒体格式。如果尚未安装 MPlayer,你可能会发现它已在你的发行版中打包。在 Fedora 中,我在 RPM Fusion 中找到了它(请注意,这不是 Fedora 的“官方”仓库,因此请谨慎操作):

$ sudo dnf install mplayer

MPlayer 有一系列命令行选项可根据你的具体情况进行设置。我想听 Raleigh 当地的大学广播电台(88.1 WKN,这个很棒!),在它们的网站得到流媒体网址之后,像这样就可以让收音机运行了,不需要 GUI 或 Web 播放器:

$ mplayer -nocache -afm ffmpeg http://wknc.sma.ncsu.edu:8000/wknchd1.mp3

MPlayer 是 GPLv3 许可证下的开源软件,你可以从项目的网站中找到更多关于项目的信息并下载源代码。

正如我在昨天的文章中提到的,我试图使用每个玩具的截图作为每篇文章的主图,但是当进入音频世界时,我不得不稍微改改。所以今天的图像是由 libcaca 包中的 img2txt 绘制的来自公共域的无线电塔图标。

你有特别喜欢的命令行小玩具需要我介绍的吗?我们的日历基本上是为这个系列剩余的玩具设置的,但我们仍然很想在新的一年里推出一些很酷的命令行玩具。评论告诉我,我会查看的。如果还有空位置,我会考虑介绍它的。并让我知道你对今天的玩具有何看法。

一定要看看昨天的玩具,让你的 Linux 终端说出来,明天记得回来!


via: https://opensource.com/article/18/12/linux-toy-mplayer

作者:Jason Baker 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

Rust 通常被称为 rust-lang。Rust 是一个由 Mozilla Research 赞助的通用的、多范式、现代的、跨平台和开源系统编程语言。

它旨在实现安全性、速度和并发性等目标。

Rust 在语法上与 C++ 相似,但它的设计者希望它在保持性能的同时提供更好的内存安全性。

Rust 目前在许多组织中使用,例如 Firefox、Chef、Dropbox、Oracle、GNOME 等。

如何在 Linux 中安装 Rust 语言?

我们可以通过多种方式安装 Rust,但以下是官方推荐的安装方式。

$ curl https://sh.rustup.rs -sSf | sh
info: downloading installer

Welcome to Rust!

This will download and install the official compiler for the Rust programming 
language, and its package manager, Cargo.

It will add the cargo, rustc, rustup and other commands to Cargo's bin 
directory, located at:

  /home/daygeek/.cargo/bin

This path will then be added to your PATH environment variable by modifying the
profile files located at:

  /home/daygeek/.profile
  /home/daygeek/.bash_profile

You can uninstall at any time with rustup self uninstall and these changes will
be reverted.

Current installation options:

   default host triple: x86_64-unknown-linux-gnu
     default toolchain: stable
  modify PATH variable: yes

1) Proceed with installation (default)
2) Customize installation
3) Cancel installation
>1

info: syncing channel updates for 'stable-x86_64-unknown-linux-gnu'
info: latest update on 2018-12-06, rust version 1.31.0 (abe02cefd 2018-12-04)
info: downloading component 'rustc'
 77.7 MiB /  77.7 MiB (100 %)   1.2 MiB/s ETA:   0 s                
info: downloading component 'rust-std'
 54.2 MiB /  54.2 MiB (100 %)   1.2 MiB/s ETA:   0 s                
info: downloading component 'cargo'
  4.7 MiB /   4.7 MiB (100 %)   1.2 MiB/s ETA:   0 s                
info: downloading component 'rust-docs'
  8.5 MiB /   8.5 MiB (100 %)   1.2 MiB/s ETA:   0 s                
info: installing component 'rustc'
info: installing component 'rust-std'
info: installing component 'cargo'
info: installing component 'rust-docs'
info: default toolchain set to 'stable'

  stable installed - rustc 1.31.0 (abe02cefd 2018-12-04)


Rust is installed now. Great!

To get started you need Cargo's bin directory ($HOME/.cargo/bin) in your PATH 
environment variable. Next time you log in this will be done automatically.

To configure your current shell run source $HOME/.cargo/env

运行以下命令配置当前 shell。

$ source $HOME/.cargo/env

运行以下命令验证已安装的 Rust 版本。

$ rustc --version
rustc 1.31.0 (abe02cefd 2018-12-04)

如何测试 Rust 编程语言?

安装 Rust 后,请按照以下步骤检查 Rust 语言是否正常工作。

$ mkdir ~/projects
$ cd ~/projects
$ mkdir hello_world
$ cd hello_world

创建一个文件并添加以下代码并保存。确保 Rust 文件始终以 .rs 扩展名结尾。

$ vi 2g.rs

fn main() {
 println!("Hello, It's 2DayGeek.com - Best Linux Practical Blog!");
}

运行以下命令编译 rust 代码。

$ rustc 2g.rs

上面的命令将在同一目录中创建一个可执行的 Rust 程序。

$ ls -lh
total 3.9M
-rwxr-xr-x 1 daygeek daygeek 3.9M Dec 14 11:09 2g
-rw-r--r-- 1 daygeek daygeek 86 Dec 14 11:09 2g.rs

运行 Rust 可执行文件得到输出。

$ ./2g
Hello, It's 2DayGeek.com - Best Linux Practical Blog!

好了!正常工作了。

将 Rust 更新到最新版本。

$ rustup update
info: syncing channel updates for 'stable-x86_64-unknown-linux-gnu'
info: checking for self-updates

  stable-x86_64-unknown-linux-gnu unchanged - rustc 1.31.0 (abe02cefd 2018-12-04)

运行以下命令从系统中删除 Rust 包。

$ rustup self uninstall

卸载 Rust 包后,删除 Rust 项目目录。

$ rm -fr ~/projects

via: https://www.2daygeek.com/how-to-install-rust-programming-language-in-linux/

作者:Prakash Subramanian 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

Red Hat 的 2018 女性开源社区奖获得者 Dana Lewis 的故事。

Dana Lewis 被评选为开源社区 2018 年度最佳女性!下面是开源怎样改善了她的健康的故事。

Dana 患有 I 型糖尿病,但当时市面上流通的药品和医疗设备都对她无效。她用来管理血糖的动态血糖监测(CGM)报警器的声音太小了,根本叫不醒熟睡的她,产品这样的设计无法保证她每天睡眠时间的生命安全。

“我和生产厂家见了一面商议提出意见,厂家的回复是‘我们产品的音量已经足够大了,很少有人叫不醒’,我被告知‘这不是普遍问题,我们正在改进,请期待我们的新产品。’听到这些时我真的很挫败,但我从没想象过我能做出什么改变,毕竟那是通过了 FDA 标准的医疗设备,不是我们能随意改变的。”

面临着这些阻碍,Dana 想着如果她能把自己的数据从设备里导出,就可以设置手机闹铃来叫醒自己。在 2013 年末,她看到的一条推特解决了她的疑问。那条推特的作者是一位糖尿病患儿的家长,他把动态血糖监测仪进行了逆向工程,这样就可以导出孩子的血糖数据进行远程监控了。

她意识到如果对方愿意把过程分享给她,她也可以用那些代码做一个自己的响亮的血糖监测仪了。

“我并不知道向别人要源代码是件稀松平常的事,那是我第一次接触开源。”

那个系统演化成一个响亮闹钟的代码,她也可以把代码在网页上分享给别人。和她的丈夫 Scott Leibrand 一起,她逐步向闹铃添加属性,最终形成了一个算法,这个算法不仅能监测实时血糖水平,还能主动预测未来血糖波动。

随着 Dana 与开源糖尿病患者社区的接触越来越深,她认识了 Ben West,他花了很多年才研究出与 Dana 使用的胰岛素泵沟通数据的方法,与血糖监测仪不同,胰岛素泵不是简单的报告血糖,它是个单独的设备,要按人体需要持续推注胰岛素,比血糖监测仪要复杂得多。

“老路行不通了,我们说‘哦,如果我们能用这段代码和胰岛素泵沟通,就像我们之前用算法和血糖监测仪沟通实时数据那样,我们就能获取两个设备的实时数据,创建一个闭路系统。’”

我们得到的是一个自制人工胰腺系统 (DIY APS)。

这个系统可以使用算法处理胰岛素泵和血糖监测仪的数据,来预测患者血糖水平,据此调整胰岛素的注射量,从而保持患者的血糖稳定。这个人工胰岛素系统取代了从前患者每日多次对胰岛素注射量的计算和调整,减轻了糖尿病患者的负担。

“正因为我们使用的是开源软件,在做出这个系统之后我们就把成果开源化了,这样可以造福更多的人。”开源人工胰腺系统 (OpenAPS) 由此诞生。

OpenAPS 社区已经拥有超过 600 名用户,大家都提供了各种各样的自制“闭路”系统代码。OpenAPS 贡献者们聚集到了 #WeAreNotWaiting 话题之下,以表达患者群体不该干等着医疗保健工厂制造出真正有效便捷产品的理念。

“你可以选择等待未来的商业解决方案,这无可厚非,选择等待是你的自由。等待可以是一种选择,但不能是无法改变的现状。对我来说,开源在医疗保健方面做出的这个举动让等待变成了一种选择。你可以选择不自行解决,你可以选择等待商业解决方案,但如果你不想等了,你无需再等。现在你有很多选择,开源社区的人们已经解决了很多问题。”

OpenAPS 社区由糖尿病患者、患者家属,还有想要合理利用这项技术的人们。在社区的帮助下,Dana 学会了很多种贡献开源项目的方式。她发现许多从 Facebook 或 Gitter 上过来的非技术贡献者也对 OpenAPS 做出了很大贡献。

“贡献有很多方式,我们要认识到各种方式的贡献都是平等的。它们一般涉及不同的兴趣领域和技能组合,只有把这些综合起来,才能做成社区的项目。”

她亲身经历过,所以知道自己的贡献不被社区的其他成员认可是怎样难过的感受。对于人们习惯把女性的贡献打折的这一现象,她也不回避。在她的 2014 年博客反思 文章中她初次写到在入围开源年度最佳人物时所遭受到的区别待遇,这些待遇让她意识到身为女性的不同。

在她最初的博客中,她写道了自己和丈夫 Scott 同为开源社区成员,遭受到的区别待遇。他们都注意到,Dana 总是被提出一些细枝末节的要求,但 Scott 就不会。而 Scott 总被问道一些技术性问题,即使他向他们推荐 Dana,人们也更倾向于问身为男性的 Scott。大家都或多或少经历过这些行为,Dana 的博文在社区里引起了广泛的讨论。

“人们更愿意认为项目是‘Scott 发起的’而非‘Dana 和 Scott 一起发起的’。”这让我感受到千刀万剐般的痛苦和挫败,我写了博客把这个现象提到明面上,我说,‘看看这些行为,我知道你们有些是故意的,有些是无意的,但如果我们的社区想要得到多元化参与者的支持,想要发展壮大,我们就要规范自己的行为,有不妥之处也不要回避,直接摊开来交流。”值得赞扬的是,社区里的大部分成员都加入进来,认真地讨论这个问题。他们都说,‘好的,我知道有哪里需要改了,如果我再无意识这样做时提醒我一下。’这就是我们社区形成的风气。”

她还说如果没有 Scott 这位社区里活跃开发者的支持,还有社区里其他女性贡献者的鼓励,她可能就半途而废了。

“我想如果我就放弃努力了,可能开源世界里糖尿病患者们的现状会有很大不同。我知道别人不幸的遭遇,他们在开源社区中感受不到认同感和自身价值,最终离开了开源。我希望我们可以继续这种讨论,大家都能意识到如果我们不故意打击贡献者,我们可以变得更加温暖,成员们也能感受到认同感,大家的付出也能得到相应的认可。

OpenAPS 社区的交流和分享给我们提供了一个很好的例子,它说明非技术性的贡献者对于整个社区的成功都是至关重要的。Dana 在现实社会中的关系和交流经历对她为开源社区做出的宣传有着很大的贡献。她为社区在 DIYPS 博客 上写了很多篇文章,她还在 TEDx Talk 做过一场演讲, 在 开源大会 (OSCON) 上也演讲过很多次,诸如此类的还有很多。

“不是每个项目都像 OpenAPS 一样,对患者有那么大的影响,甚至成为患者中间的主流项目。糖尿病社区在项目的沟通中真的做了很多贡献,引来了很多糖尿病患者,也让需要帮助的人们知道了我们的存在。”

Dana 现在的目标是帮助其他疾病的患者社区创建项目。她尤其想要把社区成员们学到的工具和技术和其他的患者社区分享,特别是那些想要把项目进一步提升,进行深入研究,或者想和公司合作的社区。

“我听说很多参与项目的患者都听过这样的话,‘你应该申请个专利;你应该拿它开个公司;你应该成立个非营利组织。’但这些都是大事,它们太耗时间了,不仅占据你的工作时间,甚至强行改变你的专业领域。我这样的人并不想做那样的事,我们更倾向于把精力放在壮大其他项目上,以此帮助更多的人。”

在此之后,她开始寻找其他不那么占用时间的任务,比如给小孩们写一本书。Dana 在 2017 年进行了这项挑战,她写了本书给侄子侄女,讲解他们婶婶的糖尿病设备是怎样工作的。在她侄女问她“胳膊上的东西是什么”(那是她的血糖监测仪)时,她意识到她不知道怎么和一个小孩子解释糖尿病患者是什么,所以写了《卡罗琳的机器人亲戚》这本书。

“我想用我侄子侄女那个年纪的语言和他们交流,毕竟不同年龄的人说话方式也不同。我当时想,‘真希望有本这方面的儿童读物,那我为什么不自己写一本呢?’”

她写了书在亚马逊上出版,因为她想把开源的价值分享给更多的人。她还开了一个名为“自己在亚马逊上出书”的博客,希望大家也可以把自己的经历写进书里出版。

像《卡罗琳的机器人亲戚》这本书还有开源社区年度最佳女性这样的奖项都说明生活中包括开源在内的不同领域中,还有很多人的工作等待着大众的认知。

“社区越多元,事情越好办。”


via: https://opensource.com/article/18/5/dana-lewis-women-open-source-community-award-winner-2018

作者:Taylor Greene 选题:lujun9972 译者:Valoniakim 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

“亲爱的,当您的命令行变得更湿润的时候会更好。这多亏了 ASCII。”

现在,我们即将数完长达 24 天的 Linux 命令行玩具日历。离今天只剩一周了!如果这是您第一次访问本系列文章,那么您可能会问自己什么是命令行玩具。我们一边走,一边说,但一般来说,这可能是一个游戏,或者可以帮助你在终端玩得开心的任何简单的娱乐活动。

你们其中的一些人可能已经在以前的系列文章中看到了各种各样的命令行玩具。但是,我们希望每个人都能够获得一个新玩具。

今天的玩具有一点鱼的味道。先和 asciiquarium 打个招呼,一个在你终端里海底冒险的玩具。我是在我的 Fedora 仓库里发现 asciiquarium 的,因此安装它非常容易:

$ sudo dnf install asciiquarium

如果您正在运行不同的发行版,那么它也可能已经为您打包。 只需在您的终端中运行 asciiquarium 即可感受到蛤蜊的快乐。 该项目也在终端之外进行了“翻译”,所有水族伙伴的屏保都是为几个非 Linux 操作系统制作的,甚至还有一个 Android 动态壁纸版本。

访问 asciiquarium 主页了解更多信息或下载 Perl 源代码。 该项目是 GPL 第 2 版许可证下的开源项目。 如果你想更多地了解开源,开放数据和开放科学如何在实际的海洋中发挥作用,请花点时间去了解海洋健康指数

你觉得我应该介绍一下你最喜欢的命令行玩具吗?时间不多了,但我还是想听听你的建议。请在下面的评论中告诉我,我会查阅的。让我知道你对今天的娱乐有什么看法。

一定要看看昨天的玩具,安排一次与 Emacs 精神病医生的访问,明天再来看另一个玩具!


via: https://opensource.com/article/18/12/linux-toy-asciiquarium

作者:Jason Baker 选题:lujun9972 译者:amwps290 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

简介

在本实验中,你将要去实现 spawn,它是一个加载和运行磁盘上可运行文件的库调用。然后,你接着要去充实你的内核和库,以使操作系统能够在控制台上运行一个 shell。而这些特性需要一个文件系统,本实验将引入一个可读/写的简单文件系统。

预备知识

使用 Git 去获取最新版的课程仓库,然后创建一个命名为 lab5 的本地分支,去跟踪远程的 origin/lab5 分支:

athena% cd ~/6.828/lab
athena% add git
athena% git pull
Already up-to-date.
athena% git checkout -b lab5 origin/lab5
Branch lab5 set up to track remote branch refs/remotes/origin/lab5.
Switched to a new branch "lab5"
athena% git merge lab4
Merge made by recursive.
.....
athena%

在实验中这一部分的主要新组件是文件系统环境,它位于新的 fs 目录下。通过检查这个目录中的所有文件,我们来看一下新的文件都有什么。另外,在 userlib 目录下还有一些文件系统相关的源文件。

  • fs/fs.c 维护文件系统在磁盘上结构的代码
  • fs/bc.c 构建在我们的用户级页故障处理功能之上的一个简单的块缓存
  • fs/ide.c 极简的基于 PIO(非中断驱动的)IDE 驱动程序代码
  • fs/serv.c 使用文件系统 IPC 与客户端环境交互的文件系统服务器
  • lib/fd.c 实现一个常见的类 UNIX 的文件描述符接口的代码
  • lib/file.c 磁盘上文件类型的驱动,实现为一个文件系统 IPC 客户端
  • lib/console.c 控制台输入/输出文件类型的驱动
  • lib/spawn.c spawn 库调用的框架代码

你应该再次去运行 pingpongprimesforktree,测试实验 4 完成后合并到新的实验 5 中的代码能否正确运行。你还需要在 kern/init.c 中注释掉 ENV_CREATE(fs_fs) 行,因为 fs/fs.c 将尝试去做一些 I/O,而 JOS 到目前为止还不具备该功能。同样,在 lib/exit.c 中临时注释掉对 close_all() 的调用;这个函数将调用你在本实验后面部分去实现的子程序,如果现在去调用,它将导致 JOS 内核崩溃。如果你的实验 4 的代码没有任何 bug,将很完美地通过这个测试。在它们都能正常工作之前是不能继续后续实验的。在你开始做练习 1 时,不要忘记去取消这些行上的注释。

如果它们不能正常工作,使用 git diff lab4 去重新评估所有的变更,确保你在实验 4(及以前)所写的代码在本实验中没有丢失。确保实验 4 仍然能正常工作。

实验要求

和以前一样,你需要做本实验中所描述的所有常规练习和至少一个挑战问题。另外,你需要写出你在本实验中问题的详细答案,和你是如何解决挑战问题的一个简短(即:用一到两个段落)的描述。如果你实现了多个挑战问题,你只需要写出其中一个即可,当然,我们欢迎你做的越多越好。在你动手实验之前,将你的问题答案写入到你的 lab5 根目录下的一个名为 answers-lab5.txt 的文件中。

文件系统的雏形

你将要使用的文件系统比起大多数“真正的”文件系统(包括 xv6 UNIX 的文件系统)要简单的多,但它也是很强大的,足够去提供基本的特性:创建、读取、写入和删除组织在层次目录结构中的文件。

到目前为止,我们开发的是一个单用户操作系统,它提供足够的保护并能去捕获 bug,但它还不能在多个不可信用户之间提供保护。因此,我们的文件系统还不支持 UNIX 的所有者或权限的概念。我们的文件系统目前也不支持硬链接、时间戳、或像大多数 UNIX 文件系统实现的那些特殊的设备文件。

磁盘上的文件系统结构

主流的 UNIX 文件系统将可用磁盘空间分为两种主要的区域类型:节点区域和数据区域。UNIX 文件系统在文件系统中为每个文件分配一个节点;一个文件的节点保存了这个文件重要的元数据,比如它的 stat 属性和指向数据块的指针。数据区域被分为更大的(一般是 8 KB 或更大)数据块,它在文件系统中存储文件数据和目录元数据。目录条目包含文件名字和指向到节点的指针;如果文件系统中的多个目录条目指向到那个文件的节点上,则称该文件是硬链接的。由于我们的文件系统不支持硬链接,所以我们不需要这种间接的级别,并且因此可以更方便简化:我们的文件系统将压根就不使用节点,而是简单地将文件的(或子目录的)所有元数据保存在描述那个文件的(唯一的)目录条目中。

文件和目录逻辑上都是由一系列的数据块组成,它或许是很稀疏地分散到磁盘上,就像一个环境的虚拟地址空间上的页,能够稀疏地分散在物理内存中一样。文件系统环境隐藏了块布局的细节,只提供文件中任意偏移位置读写字节序列的接口。作为像文件创建和删除操作的一部分,文件系统环境服务程序在目录内部完成所有的修改。我们的文件系统允许用户环境去直接读取目录元数据(即:使用 read),这意味着用户环境自己就能够执行目录扫描操作(即:实现 ls 程序),而不用另外依赖对文件系统的特定调用。用这种方法做目录扫描的缺点是,(也是大多数现代 UNIX 操作系统变体摒弃它的原因)使得应用程序依赖目录元数据的格式,如果不改变或至少要重编译应用程序的前提下,去改变文件系统的内部布局将变得很困难。

扇区和块

大多数磁盘都不能执行以字节为粒度的读写操作,而是以扇区为单位执行读写。在 JOS 中,每个扇区是 512 字节。文件系统实际上是以块为单位来分配和使用磁盘存储的。要注意这两个术语之间的区别:扇区大小是硬盘硬件的属性,而块大小是使用这个磁盘的操作系统上的术语。一个文件系统的块大小必须是底层磁盘的扇区大小的倍数。

UNIX xv6 文件系统使用 512 字节大小的块,与它底层磁盘的扇区大小一样。而大多数现代文件系统使用更大尺寸的块,因为现在存储空间变得很廉价了,而使用更大的粒度在存储管理上更高效。我们的文件系统将使用 4096 字节的块,以更方便地去匹配处理器上页的大小。

超级块

Disk layout

文件系统一般在磁盘上的“易于查找”的位置(比如磁盘开始或结束的位置)保留一些磁盘块,用于保存描述整个文件系统属性的元数据,比如块大小、磁盘大小、用于查找根目录的任何元数据、文件系统最后一次挂载的时间、文件系统最后一次错误检查的时间等等。这些特定的块被称为超级块。

我们的文件系统只有一个超级块,它固定为磁盘的 1 号块。它的布局定义在 inc/fs.h 文件里的 struct Super 中。而 0 号块一般是保留的,用于去保存引导加载程序和分区表,因此文件系统一般不会去使用磁盘上比较靠前的块。许多“真实的”文件系统都维护多个超级块,并将它们复制到间隔较大的几个区域中,这样即便其中一个超级块坏了或超级块所在的那个区域产生了介质错误,其它的超级块仍然能够被找到并用于去访问文件系统。

文件元数据

File structure

元数据的布局是描述在我们的文件系统中的一个文件中,这个文件就是 inc/fs.h 中的 struct File。元数据包含文件的名字、大小、类型(普通文件还是目录)、指向构成这个文件的块的指针。正如前面所提到的,我们的文件系统中并没有节点,因此元数据是保存在磁盘上的一个目录条目中,而不是像大多数“真正的”文件系统那样保存在节点中。为简单起见,我们将使用 File 这一个结构去表示文件元数据,因为它要同时出现在磁盘上和内存中。

struct File 中的数组 f_direct 包含一个保存文件的前 10 个块(NDIRECT)的块编号的空间,我们称之为文件的直接块。对于最大 10*4096 = 40KB 的小文件,这意味着这个文件的所有块的块编号将全部直接保存在结构 File 中,但是,对于超过 40 KB 大小的文件,我们需要一个地方去保存文件剩余的块编号。所以我们分配一个额外的磁盘块,我们称之为文件的间接块,由它去保存最多 4096/4 = 1024 个额外的块编号。因此,我们的文件系统最多允许有 1034 个块,或者说不能超过 4MB 大小。为支持大文件,“真正的”文件系统一般都支持两个或三个间接块。

目录与普通文件

我们的文件系统中的结构 File 既能够表示一个普通文件,也能够表示一个目录;这两种“文件”类型是由 File 结构中的 type 字段来区分的。除了文件系统根本就不需要解释的、分配给普通文件的数据块的内容之外,它使用完全相同的方式来管理普通文件和目录“文件”,文件系统将目录“文件”的内容解释为包含在目录中的一系列的由 File 结构所描述的文件和子目录。

在我们文件系统中的超级块包含一个结构 File(在 struct Super 中的 root 字段中),它用于保存文件系统的根目录的元数据。这个目录“文件”的内容是一系列的 File 结构所描述的、位于文件系统根目录中的文件和目录。在根目录中的任何子目录转而可以包含更多的 File 结构所表示的子目录,依此类推。

文件系统

本实验的目标并不是让你去实现完整的文件系统,你只需要去实现几个重要的组件即可。实践中,你将负责把块读入到块缓存中,并且刷新脏块到磁盘上;分配磁盘块;映射文件偏移量到磁盘块;以及实现读取、写入、和在 IPC 接口中打开。因为你并不去实现完整的文件系统,熟悉提供给你的代码和各种文件系统接口是非常重要的。

磁盘访问

我们的操作系统的文件系统环境需要能访问磁盘,但是我们在内核中并没有实现任何磁盘访问的功能。与传统的在内核中添加了 IDE 磁盘驱动程序、以及允许文件系统去访问它所必需的系统调用的“大一统”策略不同,我们将 IDE 磁盘驱动实现为用户级文件系统环境的一部分。我们仍然需要对内核做稍微的修改,是为了能够设置一些东西,以便于文件系统环境拥有实现磁盘访问本身所需的权限。

只要我们依赖轮询、基于 “编程的 I/O”(PIO)的磁盘访问,并且不使用磁盘中断,那么在用户空间中实现磁盘访问还是很容易的。也可以去实现由中断驱动的设备驱动程序(比如像 L3 和 L4 内核就是这么做的),但这样做的话,内核必须接收设备中断并将它派发到相应的用户模式环境上,这样实现的难度会更大。

x86 处理器在 EFLAGS 寄存器中使用 IOPL 位去确定保护模式中的代码是否允许执行特定的设备 I/O 指令,比如 INOUT 指令。由于我们需要的所有 IDE 磁盘寄存器都位于 x86 的 I/O 空间中而不是映射在内存中,所以,为了允许文件系统去访问这些寄存器,我们需要做的唯一的事情便是授予文件系统环境“I/O 权限”。实际上,在 EFLAGS 寄存器的 IOPL 位上规定,内核使用一个简单的“要么全都能访问、要么全都不能访问”的方法来控制用户模式中的代码能否访问 I/O 空间。在我们的案例中,我们希望文件系统环境能够去访问 I/O 空间,但我们又希望任何其它的环境完全不能访问 I/O 空间。

练习 1i386_init 通过将类型 ENV_TYPE_FS 传递给你的环境创建函数 env_create 来识别文件系统。修改 env.c 中的 env_create ,以便于它只授予文件系统环境 I/O 的权限,而不授予任何其它环境 I/O 的权限。

确保你能启动这个文件系统环境,而不会产生一般保护故障。你应该要通过在 make grade 中的 fs i/o 测试。

.

问题 1、当你从一个环境切换到另一个环境时,你是否需要做一些操作来确保 I/O 权限设置能被保存和正确地恢复?为什么?

注意本实验中的 GNUmakefile 文件,它用于设置 QEMU 去使用文件 obj/kern/kernel.img 作为磁盘 0 的镜像(一般情况下表示 DOS 或 Windows 中的 “C 盘”),以及使用(新)文件 obj/fs/fs.img 作为磁盘 1 的镜像(”D 盘“)。在本实验中,我们的文件系统应该仅与磁盘 1 有交互;而磁盘 0 仅用于去引导内核。如果你想去恢复其中一个有某些错误的磁盘镜像,你可以通过输入如下的命令,去重置它们到最初的、”崭新的“版本:

$ rm obj/kern/kernel.img obj/fs/fs.img
$ make

或者:

$ make clean
$ make

小挑战!实现中断驱动的 IDE 磁盘访问,既可以使用也可以不使用 DMA 模式。由你来决定是否将设备驱动移植进内核中、还是与文件系统一样保留在用户空间中、甚至是将它移植到一个它自己的的单独的环境中(如果你真的想了解微内核的本质的话)。

块缓存

在我们的文件系统中,我们将在处理器虚拟内存系统的帮助下,实现一个简单的”缓冲区“(实际上就是一个块缓冲区)。块缓存的代码在 fs/bc.c 文件中。

我们的文件系统将被限制为仅能处理 3GB 或更小的磁盘。我们保留一个大的、尺寸固定为 3GB 的文件系统环境的地址空间区域,从 0x10000000(DISKMAP)到 0xD0000000(DISKMAP+DISKMAX)作为一个磁盘的”内存映射版“。比如,磁盘的 0 号块被映射到虚拟地址 0x10000000 处,磁盘的 1 号块被映射到虚拟地址 0x10001000 处,依此类推。在 fs/bc.c 中的 diskaddr 函数实现从磁盘块编号到虚拟地址的转换(以及一些完整性检查)。

由于我们的文件系统环境在系统中有独立于所有其它环境的虚拟地址空间之外的、它自己的虚拟地址空间,并且文件系统环境仅需要做的事情就是实现文件访问,以这种方式去保留大多数文件系统环境的地址空间是很明智的。如果在一台 32 位机器上的”真实的“文件系统上这么做是很不方便的,因为现在的磁盘都远大于 3 GB。而在一台有 64 位地址空间的机器上,这样的缓存管理方法仍然是明智的。

当然,将整个磁盘读入到内存中需要很长时间,因此,我们将它实现成”按需“分页的形式,那样我们只在磁盘映射区域中分配页,并且当在这个区域中产生页故障时,从磁盘读取相关的块去响应这个页故障。通过这种方式,我们能够假装将整个磁盘装进了内存中。

练习 2、在 fs/bc.c 中实现 bc_pgfaultflush_block 函数。bc_pgfault 函数是一个页故障服务程序,就像你在前一个实验中编写的写时复制 fork 一样,只不过它的任务是从磁盘中加载页去响应一个页故障。在你编写它时,记住: (1) addr 可能并不会做边界对齐,并且 (2) 在扇区中的 ide_read 操作并不是以块为单位的。

(如果需要的话)函数 flush_block 应该会将一个块写入到磁盘上。如果在块缓存中没有块(也就是说,页没有映射)或者它不是一个脏块,那么 flush_block 将什么都不做。我们将使用虚拟内存硬件去跟踪,磁盘块自最后一次从磁盘读取或写入到磁盘之后是否被修改过。查看一个块是否需要写入时,我们只需要去查看 uvpt 条目中的 PTE_D 的 ”dirty“ 位即可。(PTE_D 位由处理器设置,用于表示那个页被写入;具体细节可以查看 x386 参考手册的 第 5 章 的 5.2.4.3 节)块被写入到磁盘上之后,flush_block 函数将使用 sys_page_map 去清除 PTE_D 位。

使用 make grade 去测试你的代码。你的代码应该能够通过 check\_bc、check\_super、和 check\_bitmap 的测试。

fs/fs.c 中的函数 fs_init 是块缓存使用的一个很好的示例。在初始化块缓存之后,它简单地在全局变量 super 中保存指针到磁盘映射区。在这之后,如果块在内存中,或我们的页故障服务程序按需将它们从磁盘上读入后,我们就能够简单地从 super 结构中读取块了。

.

小挑战!到现在为止,块缓存还没有清除策略。一旦某个块因为页故障被读入到缓存中之后,它将一直不会被清除,并且永远保留在内存中。给块缓存增加一个清除策略。在页表中使用 PTE_A 的 accessed 位来实现,任何环境访问一个页时,硬件就会设置这个位,你可以通过它来跟踪磁盘块的大致使用情况,而不需要修改访问磁盘映射区域的任何代码。使用脏块要小心。

块位图

fs_init 设置了 bitmap 指针之后,我们可以认为 bitmap 是一个装满比特位的数组,磁盘上的每个块就是数组中的其中一个比特位。比如 block_is_free,它只是简单地在位图中检查给定的块是否被标记为空闲。

练习 3、使用 free_block 作为实现 fs/fs.c 中的 alloc_block 的一个模型,它将在位图中去查找一个空闲的磁盘块,并将它标记为已使用,然后返回块编号。当你分配一个块时,你应该立即使用 flush_block 将已改变的位图块刷新到磁盘上,以确保文件系统的一致性。

使用 make grade 去测试你的代码。现在,你的代码应该要通过 alloc\_block 的测试。

文件操作

fs/fs.c 中,我们提供一系列的函数去实现基本的功能,比如,你将需要去理解和管理结构 File、扫描和管理目录”文件“的条目、 以及从根目录开始遍历文件系统以解析一个绝对路径名。阅读 fs/fs.c 中的所有代码,并在你开始实验之前,确保你理解了每个函数的功能。

练习 4、实现 file_block_walkfile_get_blockfile_block_walk 从一个文件中的块偏移量映射到 struct File 中那个块的指针上或间接块上,它非常类似于 pgdir_walk 在页表上所做的事。file_get_block 将更进一步,将去映射一个真实的磁盘块,如果需要的话,去分配一个新的磁盘块。

使用 make grade 去测试你的代码。你的代码应该要通过 file\_open、filegetblock、以及 fileflush/filetruncated/file rewrite、和 testfile 的测试。

file_block_walkfile_get_block 是文件系统中的”劳动模范“。比如,file_readfile_write 或多或少都在 file_get_block 上做必需的登记工作,然后在分散的块和连续的缓存之间复制字节。

.

小挑战!如果操作在中途实然被打断(比如,突然崩溃或重启),文件系统很可能会产生错误。实现软件更新或日志处理的方式让文件系统的”崩溃可靠性“更好,并且演示一下旧的文件系统可能会崩溃,而你的更新后的文件系统不会崩溃的情况。

文件系统接口

现在,我们已经有了文件系统环境自身所需的功能了,我们必须让其它希望使用文件系统的环境能够访问它。由于其它环境并不能直接调用文件系统环境中的函数,我们必须通过一个远程过程调用或 RPC、构建在 JOS 的 IPC 机制之上的抽象化来暴露对文件系统的访问。如下图所示,下图是对文件系统服务调用(比如:读取)的样子:

      Regular env           FS env
   +---------------+   +---------------+
   |      read     |   |   file_read   |
   |   (lib/fd.c)  |   |   (fs/fs.c)   |
...|.......|.......|...|.......^.......|...............
   |       v       |   |       |       | RPC mechanism
   |  devfile_read |   |  serve_read   |
   |  (lib/file.c) |   |  (fs/serv.c)  |
   |       |       |   |       ^       |
   |       v       |   |       |       |
   |     fsipc     |   |     serve     |
   |  (lib/file.c) |   |  (fs/serv.c)  |
   |       |       |   |       ^       |
   |       v       |   |       |       |
   |   ipc_send    |   |   ipc_recv    |
   |       |       |   |       ^       |
   +-------|-------+   +-------|-------+
           |                   |
           +-------------------+

圆点虚线下面的过程是一个普通的环境对文件系统环境请求进行读取的简单机制。从(我们提供的)在任何文件描述符上的 read 工作开始,并简单地派发到相关的设备读取函数上,在我们的案例中是 devfile_read(我们还有更多的设备类型,比如管道)。devfile_read 实现了对磁盘上文件指定的 read。它和 lib/file.c 中的其它的 devfile_* 函数实现了客户端侧的文件系统操作,并且所有的工作大致都是以相同的方式来完成的,把参数打包进一个请求结构中,调用 fsipc 去发送 IPC 请求以及解包并返回结果。fsipc 函数把发送请求到服务器和接收来自服务器的回复的普通细节做了简化处理。

fs/serv.c 中可以找到文件系统服务器代码。它是一个 serve 函数的循环,无休止地接收基于 IPC 的请求,并派发请求到相关的服务函数,并通过 IPC 来回送结果。在读取示例中,serve 将派发到 serve_read 函数上,它将去处理读取请求的 IPC 细节,比如,解包请求结构并最终调用 file_read 去执行实际的文件读取动作。

回顾一下 JOS 的 IPC 机制,它让一个环境发送一个单个的 32 位数字和可选的共享页。从一个客户端向服务器发送一个请求,我们为请求类型使用 32 位的数字(文件系统服务器 RPC 是有编号的,就像系统调用那样的编号),然后通过 IPC 在共享页上的一个 union Fsipc 中存储请求参数。在客户端侧,我们已经在 fsipcbuf 处共享了页;在服务端,我们在 fsreq0x0ffff000)处映射入站请求页。

服务器也通过 IPC 来发送响应。我们为函数的返回代码使用 32 位的数字。对于大多数 RPC,这已经涵盖了它们全部的返回代码。FSREQ_READFSREQ_STAT 也返回数据,它们只是被简单地写入到客户端发送它的请求时的页上。在 IPC 的响应中并不需要去发送这个页,因为这个页是文件系统服务器和客户端从一开始就共享的页。另外,在它的响应中,FSREQ_OPEN 与客户端共享一个新的 “Fd page”。我们将快捷地返回到文件描述符页上。

练习 5、实现 fs/serv.c 中的 serve_read

serve_read 的重任将由已经在 fs/fs.c 中实现的 file_read 来承担(它实际上不过是对 file_get_block 的一连串调用)。对于文件读取,serve_read 只能提供 RPC 接口。查看 serve_set_size 中的注释和代码,去大体上了解服务器函数的结构。

使用 make grade 去测试你的代码。你的代码通过 serveopen/filestat/file\_close 和 file\_read 的测试后,你得分应该是 70(总分为 150)。

.

练习 6、实现 fs/serv.c 中的 serve_writelib/file.c 中的 devfile_write

使用 make grade 去测试你的代码。你的代码通过 file\_write、fileread after filewrite、open、和 large file 的测试后,得分应该是 90(总分为150)。

进程增殖

我们给你提供了 spawn 的代码(查看 lib/spawn.c 文件),它用于创建一个新环境、从文件系统中加载一个程序镜像并启动子环境来运行这个程序。然后这个父进程独立于子环境持续运行。spawn 函数的行为,在效果上类似于UNIX 中的 fork,然后同时紧跟着 fork 之后在子进程中立即启动执行一个 exec

我们实现的是 spawn,而不是一个类 UNIX 的 exec,因为 spawn 是很容易从用户空间中、以”外内核式“ 实现的,它无需来自内核的特别帮助。为了在用户空间中实现 exec,想一想你应该做什么?确保你理解了它为什么很难。

练习 7spawn 依赖新的系统调用 sys_env_set_trapframe 去初始化新创建的环境的状态。实现 kern/syscall.c 中的 sys_env_set_trapframe。(不要忘记在 syscall() 中派发新系统调用)

运行来自 kern/init.c 中的 user/spawnhello 程序来测试你的代码kern/init.c ,它将尝试从文件系统中增殖 /hello

使用 make grade 去测试你的代码。

.

小挑战!实现 Unix 式的 exec

.

小挑战!实现 mmap 式的文件内存映射,并如果可能的话,修改 spawn 从 ELF 中直接映射页。

跨 fork 和 spawn 共享库状态

UNIX 文件描述符是一个通称的概念,它还包括管道、控制台 I/O 等等。在 JOS 中,每个这类设备都有一个相应的 struct Dev,使用指针去指向到实现读取/写入/等等的函数上。对于那个设备类型,lib/fd.c 在其上实现了类 UNIX 的文件描述符接口。每个 struct Fd 表示它的设备类型,并且大多数 lib/fd.c 中的函数只是简单地派发操作到 struct Dev 中相应函数上。

lib/fd.c 也在每个应用程序环境的地址空间中维护一个文件描述符表区域,开始位置在 FDTABLE 处。这个区域为应该程序能够一次最多打开 MAXFD(当前为 32)个文件描述符而保留页的地址空间值(4KB)。在任意给定的时刻,当且仅当相应的文件描述符处于使用中时,一个特定的文件描述符表才会被映射。在区域的 FILEDATA 处开始的位置,每个文件描述符表也有一个可选的”数据页“,如果它们被选定,相应的设备就能使用它。

我们想跨 forkspawn 共享文件描述符状态,但是文件描述符状态是保存在用户空间的内存中。而现在,在 fork 中,内存是标记为写时复制的,因此状态将被复制而不是共享。(这意味着环境不能在它们自己无法打开的文件中去搜索,并且管道不能跨一个 fork 去工作)在 spawn 上,内存将被保留,压根不会去复制。(事实上,增殖的环境从使用一个不打开的文件描述符去开始。)

我们将要修改 fork,以让它知道某些被”库管理的系统“所使用的、和总是被共享的内存区域。而不是去”硬编码“一个某些区域的列表,我们将在页表条目中设置一个”这些不使用“的位(就像我们在 fork 中使用的 PTE_COW 位一样)。

我们在 inc/lib.h 中定义了一个新的 PTE_SHARE 位,在 Intel 和 AMD 的手册中,这个位是被标记为”软件可使用的“的三个 PTE 位之一。我们将创建一个约定,如果一个页表条目中这个位被设置,那么在 forkspawn 中应该直接从父环境中复制 PTE 到子环境中。注意它与标记为写时复制的差别:正如在第一段中所描述的,我们希望确保能共享页更新。

练习 8、修改 lib/fork.c 中的 duppage,以遵循最新约定。如果页表条目设置了 PTE_SHARE 位,仅直接复制映射。(你应该去使用 PTE_SYSCALL,而不是 0xfff,去从页表条目中掩掉有关的位。0xfff 仅选出可访问的位和脏位。)

同样的,在 lib/spawn.c 中实现 copy_shared_pages。它应该循环遍历当前进程中所有的页表条目(就像 fork 那样),复制任何设置了 PTE_SHARE 位的页映射到子进程中。

使用 make run-testpteshare 去检查你的代码行为是否正确。正确的情况下,你应该会看到像 fork handles PTE_SHARE right 和 ”spawn handles PTE_SHARE right” 这样的输出行。

使用 make run-testfdsharing 去检查文件描述符是否正确共享。正确的情况下,你应该会看到 read in child succeeded 和 “read in parent succeeded” 这样的输出行。

键盘接口

为了能让 shell 工作,我们需要一些方式去输入。QEMU 可以显示输出,我们将它的输出写入到 CGA 显示器上和串行端口上,但到目前为止,我们仅能够在内核监视器中接收输入。在 QEMU 中,我们从图形化窗口中的输入作为从键盘到 JOS 的输入,虽然键入到控制台的输入作为出现在串行端口上的字符的方式显现。在 kern/console.c 中已经包含了由我们自实验 1 以来的内核监视器所使用的键盘和串行端口的驱动程序,但现在你需要去把这些增加到系统中。

练习 9、在你的 kern/trap.c 中,调用 kbd_intr 去处理捕获 IRQ_OFFSET+IRQ_KBDserial_intr,用它们去处理捕获 IRQ_OFFSET+IRQ_SERIAL

lib/console.c 中,我们为你实现了文件的控制台输入/输出。kbd_intrserial_intr 将使用从最新读取到的输入来填充缓冲区,而控制台文件类型去排空缓冲区(默认情况下,控制台文件类型为 stdin/stdout,除非用户重定向它们)。

运行 make run-testkbd 并输入几行来测试你的代码。在你输入完成之后,系统将回显你输入的行。如果控制台和窗口都可以使用的话,尝试在它们上面都做一下测试。

Shell

运行 make run-icodemake run-icode-nox 将运行你的内核并启动 user/icodeicode 又运行 init,它将设置控制台作为文件描述符 0 和 1(即:标准输入 stdin 和标准输出 stdout),然后增殖出环境 sh,就是 shell。之后你应该能够运行如下的命令了:

echo hello world | cat
cat lorem |cat
cat lorem |num
cat lorem |num |num |num |num |num
lsfd

注意用户库常规程序 cprintf 将直接输出到控制台,而不会使用文件描述符代码。这对调试非常有用,但是对管道连接其它程序却很不利。为将输出打印到特定的文件描述符(比如 1,它是标准输出 stdout),需要使用 fprintf(1, "...", ...)printf("...", ...) 是将输出打印到文件描述符 1(标准输出 stdout) 的快捷方式。查看 user/lsfd.c 了解更多示例。

练习 10、这个 shell 不支持 I/O 重定向。如果能够运行 run sh <script 就更完美了,就不用将所有的命令手动去放入一个脚本中,就像上面那样。为 <user/sh.c 中添加重定向的功能。

通过在你的 shell 中输入 sh <script 来测试你实现的重定向功能。

运行 make run-testshell 去测试你的 shell。testshell 只是简单地给 shell ”喂“上面的命令(也可以在 fs/testshell.sh 中找到),然后检查它的输出是否与 fs/testshell.key 一样。

.

小挑战!给你的 shell 添加更多的特性。最好包括以下的特性(其中一些可能会要求修改文件系统):

  • 后台命令 (ls &)
  • 一行中运行多个命令 (ls; echo hi)
  • 命令组 ((ls; echo hi) | cat > out)
  • 扩展环境变量 (echo $hello)
  • 引号 (echo "a | b")
  • 命令行历史和/或编辑功能
  • tab 命令补全
  • 为命令行查找目录、cd 和路径
  • 文件创建
  • 用快捷键 ctl-c 去杀死一个运行中的环境

可做的事情还有很多,并不仅限于以上列表。

到目前为止,你的代码应该要通过所有的测试。和以前一样,你可以使用 make grade 去评级你的提交,并且使用 make handin 上交你的实验。

本实验到此结束。 和以前一样,不要忘了运行 make grade 去做评级测试,并将你的练习答案和挑战问题的解决方案写下来。在动手实验之前,使用 git statusgit diff 去检查你的变更,并不要忘记使用 git add answers-lab5.txt 去提交你的答案。完成之后,使用 git commit -am 'my solutions to lab 5’ 去提交你的变更,然后使用 make handin 去提交你的解决方案。


via: https://pdos.csail.mit.edu/6.828/2018/labs/lab5/

作者:csail.mit 选题:lujun9972 译者:qhwdw 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出