Go语言实践技巧(4)——goroutine之间切换的时间点

以下列出了goroutine之间切换的主要的时间点:

(1)Channel发送和接收操作(如果这些操作是阻塞的);
(2)执行go语句,虽然不能保证新的goroutine马上被调度执行;
(3)阻塞的系统调用,像文件操作,网络操作等等;
(4)停下来进入垃圾回收周期以后。

换句话讲,在goroutine不能继续进行运算以后(需要更多数据,更多空间,等等),都会进行切换。

参考资料:
Performance without the event loop

Linux kernel IOMMU代码分析笔记(7)——intel_enable_irq_remapping(1)

看一下intel_enable_irq_remapping的代码:

static int __init intel_enable_irq_remapping(void)
{
    struct dmar_drhd_unit *drhd;
    struct intel_iommu *iommu;
    bool x2apic_present;
    int setup = 0;
    int eim = 0;

    x2apic_present = x2apic_supported();

    if (parse_ioapics_under_ir() != 1) {
        printk(KERN_INFO "Not enable interrupt remapping\n");
        goto error;
    }

    if (x2apic_present) {
        pr_info("Queued invalidation will be enabled to support x2apic and Intr-remapping.\n");

        eim = !dmar_x2apic_optout();
        if (!eim)
            printk(KERN_WARNING
                "Your BIOS is broken and requested that x2apic be disabled.\n"
                "This will slightly decrease performance.\n"
                "Use 'intremap=no_x2apic_optout' to override BIOS request.\n");
    }

    for_each_iommu(iommu, drhd) {
        /*
         * If the queued invalidation is already initialized,
         * shouldn't disable it.
         */
        if (iommu->qi)
            continue;

        /*
         * Clear previous faults.
         */
        dmar_fault(-1, iommu);

        /*
         * Disable intr remapping and queued invalidation, if already
         * enabled prior to OS handover.
         */
        iommu_disable_irq_remapping(iommu);

        dmar_disable_qi(iommu);
    }

    /*
     * check for the Interrupt-remapping support
     */
    for_each_iommu(iommu, drhd) {
        if (!ecap_ir_support(iommu->ecap))
            continue;

        if (eim && !ecap_eim_support(iommu->ecap)) {
            printk(KERN_INFO "DRHD %Lx: EIM not supported by DRHD, "
                   " ecap %Lx\n", drhd->reg_base_addr, iommu->ecap);
            goto error;
        }
    }

    /*
     * Enable queued invalidation for all the DRHD's.
     */
    for_each_iommu(iommu, drhd) {
        int ret = dmar_enable_qi(iommu);

        if (ret) {
            printk(KERN_ERR "DRHD %Lx: failed to enable queued, "
                   " invalidation, ecap %Lx, ret %d\n",
                   drhd->reg_base_addr, iommu->ecap, ret);
            goto error;
        }
    }

    /*
     * Setup Interrupt-remapping for all the DRHD's now.
     */
    for_each_iommu(iommu, drhd) {
        if (!ecap_ir_support(iommu->ecap))
            continue;

        if (intel_setup_irq_remapping(iommu, eim))
            goto error;

        setup = 1;
    }

    if (!setup)
        goto error;

    irq_remapping_enabled = 1;

    /*
     * VT-d has a different layout for IO-APIC entries when
     * interrupt remapping is enabled. So it needs a special routine
     * to print IO-APIC entries for debugging purposes too.
     */
    x86_io_apic_ops.print_entries = intel_ir_io_apic_print_entries;

    pr_info("Enabled IRQ remapping in %s mode\n", eim ? "x2apic" : "xapic");

    return eim ? IRQ_REMAP_X2APIC_MODE : IRQ_REMAP_XAPIC_MODE;

error:
    /*
     * handle error condition gracefully here!
     */

    if (x2apic_present)
        pr_warn("Failed to enable irq remapping.  You are vulnerable to irq-injection attacks.\n");

    return -1;
}

(1)

x2apic_present = x2apic_supported();

查看系统是否支持x2APIC模式。

(2)

if (parse_ioapics_under_ir() != 1) {
    printk(KERN_INFO "Not enable interrupt remapping\n");
    goto error;
}

parse_ioapics_under_ir函数如下:

/*
 * Finds the assocaition between IOAPIC's and its Interrupt-remapping
 * hardware unit.
 */
static int __init parse_ioapics_under_ir(void)
{
    struct dmar_drhd_unit *drhd;
    struct intel_iommu *iommu;
    int ir_supported = 0;
    int ioapic_idx;

    for_each_iommu(iommu, drhd)
        if (ecap_ir_support(iommu->ecap)) {
            if (ir_parse_ioapic_hpet_scope(drhd->hdr, iommu))
                return -1;

            ir_supported = 1;
        }

    if (!ir_supported)
        return 0;

    for (ioapic_idx = 0; ioapic_idx < nr_ioapics; ioapic_idx++) {
        int ioapic_id = mpc_ioapic_id(ioapic_idx);
        if (!map_ioapic_to_ir(ioapic_id)) {
            pr_err(FW_BUG "ioapic %d has no mapping iommu, "
                   "interrupt remapping will be disabled\n",
                   ioapic_id);
            return -1;
        }
    }

    return 1;
}

ir_parse_ioapic_hpet_scope函数的作用是解析device scope类型是IOAPICHPET (High Precision Event Timer)

static int ir_parse_ioapic_hpet_scope(struct acpi_dmar_header *header,
                      struct intel_iommu *iommu)
{
    struct acpi_dmar_hardware_unit *drhd;
    struct acpi_dmar_device_scope *scope;
    void *start, *end;

    drhd = (struct acpi_dmar_hardware_unit *)header;

    start = (void *)(drhd + 1);
    end = ((void *)drhd) + header->length;

    while (start < end) {
        scope = start;
        if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_IOAPIC) {
            if (ir_ioapic_num == MAX_IO_APICS) {
                printk(KERN_WARNING "Exceeded Max IO APICS\n");
                return -1;
            }

            printk(KERN_INFO "IOAPIC id %d under DRHD base "
                   " 0x%Lx IOMMU %d\n", scope->enumeration_id,
                   drhd->address, iommu->seq_id);

            ir_parse_one_ioapic_scope(scope, iommu);
        } else if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_HPET) {
            if (ir_hpet_num == MAX_HPET_TBS) {
                printk(KERN_WARNING "Exceeded Max HPET blocks\n");
                return -1;
            }

            printk(KERN_INFO "HPET id %d under DRHD base"
                   " 0x%Lx\n", scope->enumeration_id,
                   drhd->address);

            ir_parse_one_hpet_scope(scope, iommu);
        }
        start += scope->length;
    }

    return 0;
}

ir_parse_one_ioapic_scope为例(ir_parse_one_hpet_scope类似):

static void ir_parse_one_ioapic_scope(struct acpi_dmar_device_scope *scope,
                      struct intel_iommu *iommu)
{
    struct acpi_dmar_pci_path *path;
    u8 bus;
    int count;

    bus = scope->bus;
    path = (struct acpi_dmar_pci_path *)(scope + 1);
    count = (scope->length - sizeof(struct acpi_dmar_device_scope))
        / sizeof(struct acpi_dmar_pci_path);

    while (--count > 0) {
        /*
         * Access PCI directly due to the PCI
         * subsystem isn't initialized yet.
         */
        bus = read_pci_config_byte(bus, path->device, path->function,
                       PCI_SECONDARY_BUS);
        path++;
    }

    ir_ioapic[ir_ioapic_num].bus   = bus;
    ir_ioapic[ir_ioapic_num].devfn = PCI_DEVFN(path->device, path->function);
    ir_ioapic[ir_ioapic_num].iommu = iommu;
    ir_ioapic[ir_ioapic_num].id    = scope->enumeration_id;
    ir_ioapic_num++;
}

可以看到,实际上是通过递归访问path得到IOAPIC信息的过程:bus好,对应的iommu设备单元,等等。

for (ioapic_idx = 0; ioapic_idx < nr_ioapics; ioapic_idx++) {
    int ioapic_id = mpc_ioapic_id(ioapic_idx);
    if (!map_ioapic_to_ir(ioapic_id)) {
        pr_err(FW_BUG "ioapic %d has no mapping iommu, "
               "interrupt remapping will be disabled\n",
               ioapic_id);
        return -1;
    }
}

这段代码则是检查IOAPIC是否都有对应的IOMMU

(3)

if (x2apic_present) {
        pr_info("Queued invalidation will be enabled to support x2apic and Intr-remapping.\n");

        eim = !dmar_x2apic_optout();
        if (!eim)
            printk(KERN_WARNING
                "Your BIOS is broken and requested that x2apic be disabled.\n"
                "This will slightly decrease performance.\n"
                "Use 'intremap=no_x2apic_optout' to override BIOS request.\n");
    }  

dmar_x2apic_optout函数实现如下:

static int __init dmar_x2apic_optout(void)
{
    struct acpi_table_dmar *dmar;
    dmar = (struct acpi_table_dmar *)dmar_tbl;
    if (!dmar || no_x2apic_optout)
        return 0;
    return dmar->flags & DMAR_X2APIC_OPT_OUT;
}

这个函数的返回值表示系统是否使用X2APIC功能(1表示不使用,0表示使用)。

参考资料:
"BIOS Considerations" in *Intel ® Virtualization Technology for Directed I/Oc

Linux kernel IOMMU代码分析笔记(6)——intel_irq_remapping_supported

看一下intel_irq_remapping_supported的代码:

static int __init intel_irq_remapping_supported(void)
{
    struct dmar_drhd_unit *drhd;
    struct intel_iommu *iommu;

    if (disable_irq_remap)
        return 0;
    if (irq_remap_broken) {
        printk(KERN_WARNING
            "This system BIOS has enabled interrupt remapping\n"
            "on a chipset that contains an erratum making that\n"
            "feature unstable.  To maintain system stability\n"
            "interrupt remapping is being disabled.  Please\n"
            "contact your BIOS vendor for an update\n");
        add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK);
        disable_irq_remap = 1;
        return 0;
    }

    if (!dmar_ir_support())
        return 0;

    for_each_iommu(iommu, drhd)
        if (!ecap_ir_support(iommu->ecap))
            return 0;

    return 1;
}

disable_irq_remapirq_remap_broken定义在irq_remapping.c中,用来判断是否支持IRQ remapping这个功能。

dmar_ir_support定义在dmar.c中:

/*
 * Check interrupt remapping support in DMAR table description.
 */
int __init dmar_ir_support(void)
{
    struct acpi_table_dmar *dmar;
    dmar = (struct acpi_table_dmar *)dmar_tbl;
    if (!dmar)
        return 0;
    return dmar->flags & 0x1;
}

flags的第0位标示平台是否支持IRQ remapping这个功能。

    for_each_iommu(iommu, drhd)
        if (!ecap_ir_support(iommu->ecap))
            return 0;

这段代码检查DMA Remapping Hardware Unit的所代表的IOMMU单元是否支持IRQ remapping这个功能。

如果检查都通过了,返回1,表示支持IRQ remapping这个功能。

参考资料:
"BIOS Considerations" in *Intel ® Virtualization Technology for Directed I/O

Git rebase简介

假设当前git repository的情况是这样的:

5-3

git rebase <new-base>命令可以把当前branchcommit ID移到<new-base> branch的后面。执行下面两条命令:

git checkout about
git rebase master

git repository变为:

5-4

 

可以看到,同“3-way merge”相比(参考:Git branch简介),不用产生一个新的commit ID

git rebase -i <new-base>是交互式地进行rebase操作:可以对每个commit进行操作。举例如下:

git rebase -i master

执行完以后,会打开一个文本编辑器,列出所有需要操作的commit。修改如下:

pick 5cf316e Add empty page in about section
squash 964e013 Add contents to about page
pick 89db9ab Add HTML page for personal bio
squash 2bda8e5 Add empty HTML page for Mary's bio
pick 915466f Add link to about section in home page

前两个commit会合并成一个commit,接下来两个commit会合并成一个commit,最后一个commit保持不变。但是新生成的3commit都会有新的commit ID。这表明不仅仅是合并commit这么简单,而是git repositoryhistory完全被改写了。如下图所示:

5-5

 

另外,git rebase -i <new-base>还可以改写某个snapshot。举例如下:

git rebase -i master

然后edit中间的commit

pick 58dec2a Create the about page
edit 6ac8a9f Begin creating bio pages
pick 51c958c Add link to about section in home page

git处理到第2commit时,会停下来做一下“amending”:

5-6

处理完后,运行下面命令,提交commit

git add about/mary.html
git status
git commit --amend

--amend告诉git使用staged snapshot替代存在的commit而不是新创建一个commit
接下来继续rebase

git rebase --continue

如果rebase操作中间想放弃这次操作,可以使用“git rebase --abort”操作。

 

Lua笔记(13)——module简介

Lua用户的观点来看,module就是一段可以通过require加载的代码(CLua),并且生成和返回一个tableLua中所有的标准库都是module

通过require命令加载module的步骤:
(1)查看module是否已经在package.loaded table中了,如果已经在了,就返回table中的值;否则转向步骤(2);
(2)查找以module命名的Lua文件。找到就使用loadfile加载,会得到一个称之为“loader”的函数。否则转向步骤(3);
(3)查找以module命名的C库文件。找到就使用package.loadlib加载,寻找luaopen_modname函数,也就是“loader”的函数。

有了loader以后,require会调用loader函数,并传入模块名和得到loader的文件名作为参数(很多module会忽略参数)。loader的返回值会被存入package.loaded table中,如果没有返回值,require表现会像loader返回了true,这样可以防止多次加载同一module

有时,为了避免module名冲突,需要重命名module(比如,加载同一module的不同版本)。Lua module没有把名字固定在内部,所以简单地重命名Lua文件就好了。但是对binary文件,由于无法改luaopen_*函数,所以需要一个trick:当module名有“-”时,require期待的open函数是使用”-“以后的名字。举个例子,如果一个module名字是v1-mod,则require期待的open函数就是luaopen_mod。所以只要把module前面加上版本和”-“以进行区分就可以了。

require查找modulepath是一系列用“;”分开的template,例如:

 /usr/local/share/lua/5.3/?.lua;/usr/local/share/lua/5.3/?/init.lua;/usr/local/lib/lua/5.3/?.lua;/usr/local/lib/lua/5.3/?/init.lua;./?.lua;./?/init.lua

对每个templaterequiremodule名字代替?,看是否存在这个文件。找到就返回,没有就尝试下一个template

require查找Lua文件的pathpackage.path变量里的值。这个值是这样得到的:
1)首先看是否定义环境变量LUA_PATH_5_2(取决于具体的Lua版本,这里只是个例子),如果有,则把这个值赋给package.path,没有转向2);
2)如果定义了环境变量LUA_PATH,则把这个值赋给package.path,没有转向3);
3)使用预定义的default path的值赋给package.path)。
需要注意的是,当使用环境变量定义的值时,如果有“;;”,则要把它替换成default path。举例来说,如果LUA_PATH_5_2的值是“mydir/?.lua;;”,则最后得到package.path应该是mydir/?.lua后面跟上default path
查找C库是package.cpath变量里的值。类似地,也有LUA_CPATH_5_2(同样取决于具体的`Lua`版本)和LUA_CPATH

Go语言实践技巧(2)——package定义和初始化

(1)一个文件夹下的所有文件必须属于同一个package,一个好的规则是package和文件夹名字相同。此外,要注意import语句所指定的是寻找packagepath,而不是package的名字。关于这个话题讨论,请参见Is the package name must same with name folder name?

另外,要注意import的语法(参考这里):

import packagename importpath

举例如下:

Import declaration          Local name of Sin

import   "lib/math"         math.Sin
import m "lib/math"         m.Sin
import . "lib/math"         Sin

在第二种情况下,mpackage名字;第三种情况下,使用math package可以省略package名字。

 

(2)

import (
_ "github.com/xxx"
)

这种做法可以保证”github.com/xxx“这个package的初始化操作完成(这个package中所有init函数都执行),即使在当前文件中没有显式地使用到这个package

Go语言实践技巧(1)——变量定义

Go语言变量定义的一个好的实践技巧是:

(1)如果变量声明时会被赋给数据类型的非“0”值,则使用“:=”这种方式:

a := 3
b := fun1()

(2)否则,因为所有变量声明时都会初始化为数据类型的“0”值,则使用var关键字定义这种方式:

var a int;

Git branch简介

git branch命令列出repository中所有的branch

[root@localhost git_repo]# git branch
* master

3-1

git branch <branch-name>会从当前working directoryfork出一个新的branch

[root@localhost git_repo]# git branch crazy

git checkout <branch-name>会切换到指定branchworking directory

[root@localhost git_repo]# git checkout crazy

3-3

Fast-forwarding merge:把fork出来的branch merge回原branch时,如果原branchfork出来的branch没有分叉,也即还在fork出来的branchrevision history里,则直接让原branchHEAD指向现在fork出来的branchsnapshot即可。如下图所示:

3-8

3-10

3-11

fast-forwarding merge相对应的是3-way merge:两个branch之间存在着分叉,这时mergebranch需要产生一个新的commit

4-1

参考资料:
Branches, Part I
Branches, Part II

Git中取消操作的方法

使用git revert命令取消已经committed的操作。举例如下:

[root@localhost git_repo]# git log --oneline
4a95041 Add a crazzzy experiment
5f08b5f Add navigation links
7f9fa70 Create blue and orange pages
7807520 Commit the first version of index.html.

如果想取消最后一次commitcommit ID4a95041),使用git revert 4a95041命令:

[root@localhost git_repo]# git revert 4a95041
[master 0b959a0] Revert "Add a crazzzy experiment"
 Committer: root <root@localhost.localdomain>
Your name and email address were configured automatically based
on your username and hostname. Please check that they are accurate.
You can suppress this message by setting them explicitly. Run the
following command and follow the instructions in your editor to edit
your configuration file:

    git config --global --edit

After doing this, you may fix the identity used for this commit with:

    git commit --amend --reset-author

 1 file changed, 14 deletions(-)
 delete mode 100644 crazy.html
[root@localhost git_repo]# git log --oneline
0b959a0 Revert "Add a crazzzy experiment"
4a95041 Add a crazzzy experiment
5f08b5f Add navigation links
7f9fa70 Create blue and orange pages
7807520 Commit the first version of index.html.

现在第三次和第五次commit代表相同的snapshot。并且第四次commit还在revision history中,以后也可以恢复到第四次commit的内容。要注意,git revert命令使用的commit ID是要取消操作的commit ID,不是要“回滚”到的commit ID

取消还没committed的操作分以下两种情况:

(1)Tracked文件:git reset --hard命令会把所有tracked文件恢复到最近一次committed状态。加上--hard选项会真正更新文件,否则只是unstage文件,但文件的内容还是变化了。

(2)Untracked文件:git clean -f会删除所有untracked文件。

git reset取消working directorystaged snapshot的操作,而git revert是取消committed snapshots的操作。如下图所示:

2-5

参考资料:
Undoing Changes