2017年7月

Python 是 Linux 中一种最流行的编程语言。它被写成了各种工具和库。除此之外,Python 在开发者之间很流行因为它非常简单,并且实际很容易掌握。如果你安装了 Linux 系统,正在学习 Python 并想要使用最新的版本的话,那么这篇文章就是为你而写的。现在我已经安装好了 Linux Mint 18。默认安装的版本是 2.7 和 3.5。你可以用这个命令检查:

$ python -V
$ python2 -V
$ python3 -V

安装最新的 Python 3.6 到 Linux 中:

$ sudo add-apt-repository ppa:jonathonf/python-3.6
$ sudo apt update
$ sudo apt install python3.6

检查已安装的 Python 3.6 版本

$ python3.6 -V

请注意旧版本仍然还在,它仍然可以通过 python3 可用,新的版本可以通过命令 python3.6。如果你想要默认使用这个版本而不是 3.5 运行所有的程序,这有个工具叫 update-alternatives。但是如果你尝试获取可能的列表,我们会得到错误:

Python 3.6  - install latest version into Linux Mint

这是正常的,你首先需要为那个问题设置文件,因为维护者没有设置这个:

$ sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.5 1
$ sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.6 2

现在再次查看:

$ update-alternatives --list python3

Python 3.6  - install latest version into Linux Mint

现在我们选择需要的版本并按需切换。对于设置使用配置命令:

$ sudo update-alternatives --config python3

Python 3.6  - install latest version into Linux Mint

在提示符中,你需要指定默认使用的编号。

选择版本时要小心,不要去动 python(python2),只使用我说的 python3,Python 2.7 编写了各种系统工具,如果你尝试用错误的解释器版本运行它们,可能就不会工作。

愿原力与你同在,好运!!!


via: https://mintguide.org/other/794-python-3-6-install-latest-version-into-linux-mint.html

作者: Shekin 译者:geekpi 校对:wxy

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

A Java Nightmare

周末带着儿子去了一个不一样的迪尼斯乐园——Java 大世界。

公园的门口,有两个 Java 吉祥物 Duke,只是左边那个好像是戴着发带的女 Duke。看见没有,公园大门最顶部的标志是著名的咖啡杯——基本上是公众所熟知的 Java 语言的形象了,相对来说,Duke 的知名度不如咖啡杯。公园门口的标语上写着“堆”满了乐趣(“堆”,即 heap,是一种 Java 等语言用于操作数据的内存结构)。

驶过道路上的标牌,上面分别写着:

  • “麦格王国”,麦格——mageek,可能影射的是 Majava.A 安全漏洞,这是一个攻击 JRE 漏洞的恶意文件,此处嘲讽 Java 的安全缺陷。
  • “热点中心”,热点——Hotspot,是 Java 一个较新的虚拟机。
  • “极演播室”,极——JEE,即 Java EE,是 J2EE 的一个新名称,面向企业的一种应用框架/标准。

进入公园看到的大型广告牌上写着:只需要写出来——即可运行。这里隐喻 Java 的跨平台特性。

远处的街道上,有大大的 Duke 充气人偶,而孩子手中的气球上画着咖啡杯,这真是一个 Java 的世界啊。

根据指示牌,有通往:

  • 小小新世界,“hello world” 是各种编程语言教学中通常学生们接触到的第一个程序例子。
  • 拼图馆,拼图——Jigsaw,是 OpenJDK 项目下的一个子项目,旨在为 Java SE 平台设计实现一个标准的模块系统,并应用到该平台和 JDK 中。
  • 汤姆猫小岛,汤姆猫——Tomcat,Tomcat 服务器是一个自由开源的 Web 应用服务器,用于 JSP 程序。

孩子兴奋极了,要去那个“神秘阀门”去玩。我觉得也可以去汤姆猫小岛看看,此处用 servlets 指代了服务项目。而 servlet 是一种 Java 应用。

正在这时,广播发出警告,“异常抛出”——Exception Thrown 是 Java 等语言用于处理异常情况的机制。得赶紧疏散,而可怜的娃还不明白发生了什么。跑吧!

结果爸爸被触手怪抓去了——触手怪的下面写着“内存溢出”,一定是因为这个才导致触手怪出现的!

眼看电锯就要切到脑袋上了——啊,吓死我了,原来是又一个 Java 的噩梦啊!


via: http://turnoff.us/geek/a-java-nightmare/

作者:Daniel Stori 译者:wxy

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

在 Collabora 公司,我们所做的许多工作之一就是为客户构建包括 32 位和 64 位 ARM 系统在内的各种架构的 Debian 衍生版。就像 Debian 做的那样,我们的 OBS 系统建立在原生系统而不是仿真器上。

幸运的是随着几年前 ARM 服务器系统的出现,为这些系统原生构建不再像以前那么痛苦了。对于 32 位的 ARM,我们一直依赖 Calxeda 刀片服务器,然而不幸的是 Calxeda 在不久前淘汰,硬件开始显露其年龄(尽管幸运的是 Debian Stretch 还支持它,因此至少软件还是新的)。

在 64 位 ARM 方面,我们运行在基于 Gigabyte MP30-AR1 的服务器上,该服务器可以运行 32 位的 ARM 代码(与之相反,比如基于 ThunderX 的服务器只能运行 64 位代码)。像这样在它们之上运行 armhf 虚拟机作为 从构建服务器 build slaves 似乎是一个很好的选择,但是设置起来可能会需要更多东西的介入。

第一个陷阱是 Debian 中没有标准的 bootloader 或者 boot 固件来启动 qemu 仿真的 “virt” 设备(我不想使用真实机器的仿真)。这也意味着在启动时客户机内没有任何东西会加载内核,也不会从客户机网络引导,这意味着需要直接的内核引导。

第二个陷阱是当前的 Debian Stretch 的 armhf 内核并不支持 qemu 虚拟机所提供的通用 PCI 主机控制器,这意味着客户机中不会出现存储器和网络。希望这会被尽快解决(Debian bug 864726),并出现在 Stretch 更新中,那在之前需要使用 bug 报告中附带的补丁的自定义内核,但在这篇文章中我不想进一步说。

高兴的假设我们有一个可用的内核,剩下的挑战是很好地管理直接内核加载。或者更具体地说,如何确保主机启动客户机通过标准 apt 工具安装的内核,而不必在客户机/主机之间复制内核,这本质上归结于客户机将 /boot 暴露给主机。我们选择的方案是使用 qemu 的 9pfs 支持来从主机共享一个文件夹,并将其用作客户机的 /boot。对于 9p 文件夹,似乎需要 “mapped” 安全模式,因为 “none” 模式对 dpkg 有问题(Debian bug 864718)。

由于我们使用 libvirt 作为我们的虚拟机管理器,剩下的事情就是如何将它们组合到一起。

第一步是安装系统,基本和平常一样。可以直接引导进入由普通的 Stretch armhf netboot 安装程序提供的 vmlinuzinitrd.gz(下载到如 /tmp 中)。 设置整体来说很直接,会有一些小的调整:

  • /srv/armhf-vm-boot 设置为 9p 共享文件夹(这个应该存在,并且由 libvirt-qemu 拥有),这之后会被用于共享 /boot
  • 内核参数中在 root= 后面加上 VM 中的 root 分区,根据你的情况调整。
  • 镜像文件使用 virtio 总线,这似乎不是默认的。

除了这些调整,最后的示例命令与 virt-install 手册页中的相似。

virt-install --name armhf-vm --arch armv7l --memory 512 \
  --disk /srv/armhf-vm.img,bus=virtio
  --filesystem /srv/armhf-vm-boot,virtio-boot,mode=mapped \
  --boot=kernel=/tmp/vmlinuz,initrd=/tmp/initrd.gz,kernel_args="console=ttyAMA0,root=/dev/vda1"

按照通常的方式运行安装。到最后安装程序可能会提示它不知道如何安装引导程序,这个没什么问题。只要在结束安装和重启之前,切换到 shell 并以某种方式将目标系统中的 /boot/vmlinuz/boot/initrd.img 复制到主机中(比如 chroot 进入 /target 并在已安装的系统中使用 scp)。 这是必需的,因为安装程序不支持 9p,但是要启动系统,需要 initramfs 以及能够挂载根文件系统的模块,这由已安装的 initramfs 提供。这些完成后,安装就可以完成了。

接下来,引导已安装的系统。调整 libvirt 配置(比如使用 virsh 编辑并调整 xml)来使用从安装程序复制过来的内核以及 initramfs,而不只是使用它提供的。再次启动虚拟机,它应该就能愉快地进入安装的 Debian 系统中了。

要在客户机这一侧完成,/boot 应该移动到共享的 9pfs 中,/boot 的新 fstab 条目看上去应该这样:

virtio-boot /boot  9p trans=virtio,version=9p2000.L,x-systemd.automount 0 0

有了这一步,这只是将 /boot 中的文件混到新的文件系统里面,并且客户机完事了(确保 vmlinuz/initrd.img 保持符号链接)。内核可以如常升级,并对主机可见。

这时对于主机端,有另外一个问题需要跨过,由于客户机使用 9p 映射安全模式,客户机的符号链接对主机而言将是普通的包含链接目标的文件。为了解决这个问题,我们在客户机启动前使用 libvirt qemu 的 hook 支持来设置合适的符号链接。作为一个例子,下面是我们最终使用的脚本(/etc/libvirt/hooks/qemu):

vm=$1
action=$2
bootdir=/srv/${vm}-boot

if [ ${action} != "prepare" ] ; then
  exit 0
fi

if [ ! -d ${bootdir} ] ; then
  exit 0
fi

ln -sf $(basename $(cat ${bootdir}/vmlinuz))  ${bootdir}/virtio-vmlinuz
ln -sf $(basename $(cat ${bootdir}/initrd.img))  ${bootdir}/virtio-initrd.img

有了这个,我们可以简单地定义 libvirt 使用 /srv/${vm}-boot/virtio-{vmlinuz,initrd.img} 作为机器的内核 / initramfs,并且当 VM 启动时,它会自动获取客户机安装的最新内核 / initramfs

只有最后一个边缘情况了,当从 VM libvirt 重启会让 qemu 处理它而不是重启 qemu。如果这不幸发生的话,意味着重启不会加载新内核。所以现在我们通过配置 libvirt 来解决这个问题,从而在重启时停止虚拟机。由于我们通常只在升级内核(安装)时重启 VM,虽然这有点乏味,但这避免了重启加载的是旧内核 / initramfs 而不是预期的。


via: https://www.collabora.com/news-and-blog/blog/2017/06/20/debian-armhf-vm-on-arm64-server/

作者:Sjoerd Simons 译者:geekpi 校对:wxy

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

上一篇博文中我们给调试器添加了一个简单的地址断点。这次,我们将添加读写寄存器和内存的功能,这将使我们能够使用我们的程序计数器、观察状态和改变程序的行为。

系列文章索引

随着后面文章的发布,这些链接会逐渐生效。

  1. 准备环境
  2. 断点
  3. 寄存器和内存
  4. Elves 和 dwarves
  5. 源码和信号
  6. 源码级逐步执行
  7. 源码级断点
  8. 调用栈展开
  9. 读取变量
  10. 下一步

注册我们的寄存器

在我们真正读取任何寄存器之前,我们需要告诉调试器一些关于我们的目标平台的信息,这里是 x8664 平台。除了多组通用和专用目的寄存器,x8664 还提供浮点和向量寄存器。为了简化,我将跳过后两种寄存器,但是你如果喜欢的话也可以选择支持它们。x86\_64 也允许你像访问 32、16 或者 8 位寄存器那样访问一些 64 位寄存器,但我只会介绍 64 位寄存器。由于这些简化,对于每个寄存器我们只需要它的名称、它的 DWARF 寄存器编号以及 ptrace 返回结构体中的存储地址。我使用范围枚举引用这些寄存器,然后我列出了一个全局寄存器描述符数组,其中元素顺序和 ptrace 中寄存器结构体相同。

enum class reg {
    rax, rbx, rcx, rdx,
    rdi, rsi, rbp, rsp,
    r8,  r9,  r10, r11,
    r12, r13, r14, r15,
    rip, rflags,    cs,
    orig_rax, fs_base,
    gs_base,
    fs, gs, ss, ds, es
};

constexpr std::size_t n_registers = 27;

struct reg_descriptor {
    reg r;
    int dwarf_r;
    std::string name;
};

const std::array<reg_descriptor, n_registers> g_register_descriptors {{
    { reg::r15, 15, "r15" },
    { reg::r14, 14, "r14" },
    { reg::r13, 13, "r13" },
    { reg::r12, 12, "r12" },
    { reg::rbp, 6, "rbp" },
    { reg::rbx, 3, "rbx" },
    { reg::r11, 11, "r11" },
    { reg::r10, 10, "r10" },
    { reg::r9, 9, "r9" },
    { reg::r8, 8, "r8" },
    { reg::rax, 0, "rax" },
    { reg::rcx, 2, "rcx" },
    { reg::rdx, 1, "rdx" },
    { reg::rsi, 4, "rsi" },
    { reg::rdi, 5, "rdi" },
    { reg::orig_rax, -1, "orig_rax" },
    { reg::rip, -1, "rip" },
    { reg::cs, 51, "cs" },
    { reg::rflags, 49, "eflags" },
    { reg::rsp, 7, "rsp" },
    { reg::ss, 52, "ss" },
    { reg::fs_base, 58, "fs_base" },
    { reg::gs_base, 59, "gs_base" },
    { reg::ds, 53, "ds" },
    { reg::es, 50, "es" },
    { reg::fs, 54, "fs" },
    { reg::gs, 55, "gs" },
}};

如果你想自己看看的话,你通常可以在 /usr/include/sys/user.h 找到寄存器数据结构,另外 DWARF 寄存器编号取自 System V x86\_64 ABI

现在我们可以编写一堆函数来和寄存器交互。我们希望可以读取寄存器、写入数据、根据 DWARF 寄存器编号获取值,以及通过名称查找寄存器,反之类似。让我们先从实现 get_register_value 开始:

uint64_t get_register_value(pid_t pid, reg r) {
    user_regs_struct regs;
    ptrace(PTRACE_GETREGS, pid, nullptr, &regs);
    //...
}

ptrace 使得我们可以轻易获得我们想要的数据。我们只需要构造一个 user_regs_struct 实例并把它和 PTRACE_GETREGS 请求传递给 ptrace

现在根据要请求的寄存器,我们要读取 regs。我们可以写一个很大的 switch 语句,但由于我们 g_register_descriptors 表的布局顺序和 user_regs_struct 相同,我们只需要搜索寄存器描述符的索引,然后作为 uint64_t 数组访问 user_regs_struct 就行。(你也可以重新排序 reg 枚举变量,然后使用索引把它们转换为底层类型,但第一次我就使用这种方式编写,它能正常工作,我也就懒得改它了。)

        auto it = std::find_if(begin(g_register_descriptors), end(g_register_descriptors),
                               [r](auto&& rd) { return rd.r == r; });

        return *(reinterpret_cast<uint64_t*>(&regs) + (it - begin(g_register_descriptors)));

uint64_t 的转换是安全的,因为 user_regs_struct 是一个标准布局类型,但我认为指针算术技术上是 未定义的行为 undefined behavior 。当前没有编译器会对此产生警告,我也懒得修改,但是如果你想保持最严格的正确性,那就写一个大的 switch 语句。

set_register_value 非常类似,我们只是写入该位置并在最后写回寄存器:

void set_register_value(pid_t pid, reg r, uint64_t value) {
    user_regs_struct regs;
    ptrace(PTRACE_GETREGS, pid, nullptr, &regs);
    auto it = std::find_if(begin(g_register_descriptors), end(g_register_descriptors),
                           [r](auto&& rd) { return rd.r == r; });

    *(reinterpret_cast<uint64_t*>(&regs) + (it - begin(g_register_descriptors))) = value;
    ptrace(PTRACE_SETREGS, pid, nullptr, &regs);
}

下一步是通过 DWARF 寄存器编号查找。这次我会真正检查一个错误条件以防我们得到一些奇怪的 DWARF 信息。

uint64_t get_register_value_from_dwarf_register (pid_t pid, unsigned regnum) {
    auto it = std::find_if(begin(g_register_descriptors), end(g_register_descriptors),
                           [regnum](auto&& rd) { return rd.dwarf_r == regnum; });
    if (it == end(g_register_descriptors)) {
        throw std::out_of_range{"Unknown dwarf register"};
    }

    return get_register_value(pid, it->r);
}

就快完成啦,现在我们已经有了寄存器名称查找:

std::string get_register_name(reg r) {
    auto it = std::find_if(begin(g_register_descriptors), end(g_register_descriptors),
                           [r](auto&& rd) { return rd.r == r; });
    return it->name;
}

reg get_register_from_name(const std::string& name) {
    auto it = std::find_if(begin(g_register_descriptors), end(g_register_descriptors),
                           [name](auto&& rd) { return rd.name == name; });
    return it->r;
}

最后我们会添加一个简单的帮助函数用于导出所有寄存器的内容:

void debugger::dump_registers() {
    for (const auto& rd : g_register_descriptors) {
        std::cout << rd.name << " 0x"
                  << std::setfill('0') << std::setw(16) << std::hex << get_register_value(m_pid, rd.r) << std::endl;
    }
}

正如你看到的,iostreams 有非常精确的接口用于美观地输出十六进制数据(啊哈哈哈哈哈哈)。如果你喜欢你也可以通过 I/O 操纵器来摆脱这种混乱。

这些已经足够支持我们在调试器接下来的部分轻松地处理寄存器,所以我们现在可以把这些添加到我们的用户界面。

显示我们的寄存器

这里我们要做的就是给 handle_command 函数添加一个命令。通过下面的代码,用户可以输入 register read raxregister write rax 0x42 以及类似的语句。

    else if (is_prefix(command, "register")) {
        if (is_prefix(args[1], "dump")) {
            dump_registers();
        }
        else if (is_prefix(args[1], "read")) {
            std::cout << get_register_value(m_pid, get_register_from_name(args[2])) << std::endl;
        }
        else if (is_prefix(args[1], "write")) {
            std::string val {args[3], 2}; //assume 0xVAL
            set_register_value(m_pid, get_register_from_name(args[2]), std::stol(val, 0, 16));
        }
    }

接下来做什么?

设置断点的时候我们已经读取和写入内存,因此我们只需要添加一些函数用于隐藏 ptrace 调用。

uint64_t debugger::read_memory(uint64_t address) {
    return ptrace(PTRACE_PEEKDATA, m_pid, address, nullptr);
}

void debugger::write_memory(uint64_t address, uint64_t value) {
    ptrace(PTRACE_POKEDATA, m_pid, address, value);
}

你可能想要添加支持一次读取或者写入多个字节,你可以在每次希望读取另一个字节时通过递增地址来实现。如果你需要的话,你也可以使用 process_vm_readvprocess_vm_writev/proc/<pid>/mem 代替 ptrace

现在我们会给我们的用户界面添加命令:

    else if(is_prefix(command, "memory")) {
        std::string addr {args[2], 2}; //assume 0xADDRESS

        if (is_prefix(args[1], "read")) {
            std::cout << std::hex << read_memory(std::stol(addr, 0, 16)) << std::endl;
        }
        if (is_prefix(args[1], "write")) {
            std::string val {args[3], 2}; //assume 0xVAL
            write_memory(std::stol(addr, 0, 16), std::stol(val, 0, 16));
        }
    }

continue_execution 打补丁

在我们测试我们的更改之前,我们现在可以实现一个更健全的 continue_execution 版本。由于我们可以获取程序计数器,我们可以检查我们的断点映射来判断我们是否处于一个断点。如果是的话,我们可以停用断点并在继续之前跳过它。

为了清晰和简洁起见,首先我们要添加一些帮助函数:

uint64_t debugger::get_pc() {
    return get_register_value(m_pid, reg::rip);
}

void debugger::set_pc(uint64_t pc) {
    set_register_value(m_pid, reg::rip, pc);
}

然后我们可以编写函数来跳过断点:

void debugger::step_over_breakpoint() {
    // - 1 because execution will go past the breakpoint
    auto possible_breakpoint_location = get_pc() - 1;

    if (m_breakpoints.count(possible_breakpoint_location)) {
        auto& bp = m_breakpoints[possible_breakpoint_location];

        if (bp.is_enabled()) {
            auto previous_instruction_address = possible_breakpoint_location;
            set_pc(previous_instruction_address);

            bp.disable();
            ptrace(PTRACE_SINGLESTEP, m_pid, nullptr, nullptr);
            wait_for_signal();
            bp.enable();
        }
    }
}

首先我们检查当前程序计算器的值是否设置了一个断点。如果有,首先我们把执行返回到断点之前,停用它,跳过原来的指令,再重新启用断点。

wait_for_signal 封装了我们常用的 waitpid 模式:

void debugger::wait_for_signal() {
    int wait_status;
    auto options = 0;
    waitpid(m_pid, &wait_status, options);
}

最后我们像下面这样重写 continue_execution

void debugger::continue_execution() {
    step_over_breakpoint();
    ptrace(PTRACE_CONT, m_pid, nullptr, nullptr);
    wait_for_signal();
}

测试效果

现在我们可以读取和修改寄存器了,我们可以对我们的 hello world 程序做一些有意思的更改。类似第一次测试,再次尝试在 call 指令处设置断点然后从那里继续执行。你可以看到输出了 Hello world。现在是有趣的部分,在输出调用后设一个断点、继续、将 call 参数设置代码的地址写入程序计数器(rip)并继续。由于程序计数器操纵,你应该再次看到输出了 Hello world。为了以防你不确定在哪里设置断点,下面是我上一篇博文中的 objdump 输出:

0000000000400936 <main>:
  400936:   55                      push   rbp
  400937:   48 89 e5                mov    rbp,rsp
  40093a:   be 35 0a 40 00          mov    esi,0x400a35
  40093f:   bf 60 10 60 00          mov    edi,0x601060
  400944:   e8 d7 fe ff ff          call   400820 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
  400949:   b8 00 00 00 00          mov    eax,0x0
  40094e:   5d                      pop    rbp
  40094f:   c3                      ret

你要将程序计数器移回 0x40093a 以便正确设置 esiedi 寄存器。

在下一篇博客中,我们会第一次接触到 DWARF 信息并给我们的调试器添加一系列逐步调试的功能。之后,我们会有一个功能工具,它能逐步执行代码、在想要的地方设置断点、修改数据以及其它。一如以往,如果你有任何问题请留下你的评论!

你可以在这里找到这篇博文的代码。


via: https://blog.tartanllama.xyz/c++/2017/03/31/writing-a-linux-debugger-registers/

作者:TartanLlama 译者:ictlyh 校对:jasminepeng

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

Android 5.0 Lollipop——有史以来最重要的安卓版本

2014 年 11 月,谷歌发布了 安卓 5.0 棒棒糖。很多系统更新都被称作“有史以来最大的”,但那些陈词滥调在安卓 5.0 棒棒糖的身上成真了。首先,它改变了安卓发布的方式。谷歌与这个版本的安卓一同开始了“开发者预览”计划,可以以 beta 的形式提前几个月看到新版本。由于版本代号和版本号现在被用作市场营销工具,最终的名称在 beta 期间始终保密,仅以字母指代。在 2014 年的 Google I/O 大会,谷歌宣布了“Android L 开发者预览”。

给开发者(以及全世界)四个月的时间接触学习这个版本肯定是有必要的。安卓 L 包含了大范围的变动,它们在系统中都是首次亮相,直到今天还能感受到这些变动。棒棒糖引入了 Material Design 设计理念,作为修改安卓每个界面的指南。一个新的运行时环境“ART”,它代表着驱动安卓应用引擎的完全革新。谷歌的“OK Google”语音命令系统升级到能在任意界面生效,在一些设备上它甚至可以在设备锁定的时候工作。多用户系统从平板来到了手机上。棒棒糖还铺设了 Android for Work 的基础,这是一个关注企业与个人双重应用的特性。

Material Design 给了安卓(以及谷歌的所有产品)一个统一形象

当 Matias Duarte 踏上 I/O 大会的舞台并宣布 Material Design 时,它公布的不仅仅是一个安卓的统一设计蓝图,还是谷歌的所有产品以及第三方应用生态的统一设计蓝图。这个想法是安卓、iOS、Chrome OS,以及 Web 版的谷歌产品,都应该有一致的界面风格、字体,以及行为。它们在不需要在不同尺寸的屏幕上有相同的布局,Material Design 提供了有一致行为的构建元素,它们能够基于屏幕尺寸自适应布局。

Duarte 和他的团队在蜂巢中尝试了“电子”风格审美,以及在果冻豆中的“卡片”主题风格,但 Material Design 最终成了被选中的设计系统,应用到谷歌的所有产品线。Material Design 已经超出了一个用户界面设计指南的概念,成为了谷歌作为一个公司的一致性代表。

Material Design 主要的比喻是“纸和墨”。所有的用户界面是“纸”的片层,漂浮于最底端平面之上。阴影用来提供界面的层次结构——用户界面的每个层在 Z 轴上拥有一个位置,并在其之下的元素上投射一个阴影。这是在安卓 4.1 的 Google Now 中使用的“卡片”风格更清晰的进化版本。“墨”是用来指代谷歌推荐开发者在界面的重要项目上使用的泼色。这个概念也引用了真实世界的物体概念,与 Windows 8 和 iOS 7 带来的反拟物化“不惜一切代价扁平化”的趋势相违。

动画也是一个重点,思想是任何东西都不应该“弹出”到屏幕上。组件应该滑进,缩小,或放大。“纸”表面和真实世界中的纸还有点不同,它们可以缩小,扩展,合并以及增大。为了让动画系统协同图像资源工作,阴影不像之前版本中那样整合进用户界面组件——实际上谷歌创造了一个实时的 3D 阴影系统,这样阴影就能够在这些动画和转换中正确渲染。

棒棒糖的层状界面和阴影的夸张侧视图。

为了将 Material Design 带到谷歌的其它地方以及应用生态系统,谷歌创建并持续维护着一个统一的设计指南,描述了一切设计的规范。那里有应该与不应该做的,基线、基线网格、色板、堆叠图解、字体、库、布局建议以及更多内容。谷歌甚至开始定期举办针对设计的会议,来听取设计者的声音以及给他们教学,还成立了 Material Design 奖项。在发布 Material Design 后不久,Duarte 离开了安卓团队并成为了谷歌的 Material Design 总监,创建了一个完全专注于设计的部门。


Ron Amadeo / Ron 是 Ars Technica 的评论编缉,专注于安卓系统和谷歌产品。他总是在追寻新鲜事物,还喜欢拆解事物看看它们到底是怎么运作的。@RonAmadeo


via: http://arstechnica.com/gadgets/2016/10/building-android-a-40000-word-history-of-googles-mobile-os/27/

作者:RON AMADEO 译者:alim0x 校对:wxy

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

编辑:在写完这个这篇文章之后,我在树莓派 3 上基于 Debian 开始打造 64 位的系统。你可以在这里找到

树莓派 3 配有 Broadcom BCM2837 64 位 ARMv8 四核 Cortex A53 处理器,它是一个 64 位 CPU。如果你有一块,运行以下命令可能会让你感到惊讶:

uname -a
Linux raspberrypi 4.4.34-v7+ #930 SMP Wed Nov 23 15:20:41 GMT 2016 armv7l GNU/Linux

是的,这是一个 32 位内核。这是因为树莓派基金会还没有为官方的树莓派系统 Raspbian 提供 64 位版本。然而你可以构建一个,多亏了 Electron752 提供的许多补丁。

构建内核

树莓派基金会维护着它们自己的 Linux 内核分支,它为它们的设备特别裁剪过,同时定期地从上游合并。

我们将会遵照这个页面的指导来构建一个 64 位内核

我们不能使用“本地构建”的方法,因为它需要一块 64 位的树莓派,这个我们明显还没有。因此我们需要交叉编译它,Ubuntu 是推荐的系统。我个人没有 Ubuntu,因此我在一个有 2 个 CPU 的 Ubuntu 16.04 Digital Ocean 实例上构建,这应该花费我 $0.03。如果你也想这么做,你可以通过[这个链接](https://m.do.co/c/8ef9c5832a9c)得到 $10 的免费额度。或者你可以通过使用 Virtualbox 中的 Ubuntu VM 作为实例。

首先,我们需要一些构建工具以及** aarch64 交叉编译器**:

apt-get update
apt-get install -y bc build-essential gcc-aarch64-linux-gnu git unzip

接着我们可以下载 Linux 内核源码

git clone –depth=1 -b rpi-4.8.y https://github.com/raspberrypi/linux.git

进入到创建的 git 目录。另外你可以为你的内核添加额外的版本标签,可以通过编辑 Makefile 的开始几行完成:

VERSION = 4
PATCHLEVEL = 8
SUBLEVEL = 13
EXTRAVERSION = +bilal

为了构建它,运行下面的命令:

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- bcmrpi3_defconfig
make -j 3 ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu-

第一个应该很快。第二个则会完全不同,我没有精确计时,但是对我来说大概要半个小时。根据你的 CPU 数(nproc * 1.5)调整 -j 标志。

选择一个 Linux 发行版

在内核编译的时候,我们可以开始准备它的 Linux 发行版了。在本教程中为了简单我使用 Raspbian,即使这是一个只有 32 位的发行版。

如果你想要一直用 64 位系统,你应该选一个有 aarch64 支持的发行版,Debian 有一个健壮的 ARM64 移植版。得到它基本有三种方式:

  • 下载一个预构建的根文件系统,这很可能会如页面中提到的那样给你一个过期的版本。
  • 如果你熟悉 debootstrap,用它构建你自己的(这回比较棘手,因为它需要一些手工调整,它最初的目的是在已经运行的主机上进行 chroot,而不是为其他机器构建根文件系统)
  • 我建议使用 multistrap,这里有一个很好的教程:http://free-electrons.com/blog/embdebian-with-multistrap/

回到 Raspbian,我们现在可以下载官方系统,并开始准备了。

打开一个新的 shell 会话并运行下面的命令:

wget -O raspbian.zip https://downloads.raspberrypi.org/raspbian_lite_latest
unzip raspbian.zip

我们用下面的命令审查:

fdisk -l 2016-11-25-raspbian-jessie-lite.img
Disk 2016-11-25-raspbian-jessie-lite.img: 1.3 GiB, 1390411776 bytes, 2715648 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x244b8248

Device Boot Start End Sectors Size Id Type
2016-11-25-raspbian-jessie-lite.img1 8192 137215 129024 63M c W95 FAT32 (LBA)
2016-11-25-raspbian-jessie-lite.img2 137216 2715647 2578432 1.2G 83 Linux

我们可以看到它有两个分区。第一个是启动分区,它主要包含了 bootloader、Linux 内核以及少量配置文件。第二个是根分区

我们可以在我们的文件系统上挂载这些分区,从根分区开始:

mount -o loop,offset=70254592 2016-11-25-raspbian-jessie-lite.img /mnt

offset 取决于扇区大小(512):70254592 = 512 * 137216

接着是启动分区

mount -o loop,offset=4194304,sizelimit=66060288 2016-11-25-raspbian-jessie-lite.img /mnt/boot

offset :4194304 = 512 * 8192,sizelimit:66060288 = 512 * 129024 。

树莓派系统现在应该可以在 /mnt 中看到了。我们基本要完成了。

打包内核

内核编译完成后,最后一步包括复制 Linux 内核以及设备树到启动分区中:

cp arch/arm64/boot/Image /mnt/boot/kernel8.img
cp arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b.dtb /mnt/boot/

调整 config.txt :

echo “kernel=kernel8.img” >> /mnt/boot/config.txt

安装内核模块 :

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu INSTALL_MOD_PATH=/mnt modules_install
umount /mnt/boot
umount /mnt

就是这样了,用于树莓派 3 的** ARM64 Linux 内核**诞生了!

现在你可以压缩镜像,通过 scp 下载下来,并按照标准的步骤放到你的 SD 卡中。

最后你会得到:

uname -a
Linux raspberrypi 4.8.13+bilal-v8+ #1 SMP Wed Dec 14 14:09:38 UTC 2016 aarch64 GNU/Linux

via: https://devsidestory.com/build-a-64-bit-kernel-for-your-raspberry-pi-3/

作者:Bilal Amarni 译者:geekpi 校对:wxy

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