以下列出了goroutine
之间切换的主要的时间点:
(1)Channel
发送和接收操作(如果这些操作是阻塞的);
(2)执行go
语句,虽然不能保证新的goroutine
马上被调度执行;
(3)阻塞的系统调用,像文件操作,网络操作等等;
(4)停下来进入垃圾回收周期以后。
换句话讲,在goroutine
不能继续进行运算以后(需要更多数据,更多空间,等等),都会进行切换。
以下列出了goroutine
之间切换的主要的时间点:
(1)Channel
发送和接收操作(如果这些操作是阻塞的);
(2)执行go
语句,虽然不能保证新的goroutine
马上被调度执行;
(3)阻塞的系统调用,像文件操作,网络操作等等;
(4)停下来进入垃圾回收周期以后。
换句话讲,在goroutine
不能继续进行运算以后(需要更多数据,更多空间,等等),都会进行切换。
看一下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
类型是IOAPIC
和HPET (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
看一下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_remap
和irq_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 repository
的情况是这样的:
git rebase <new-base>
命令可以把当前branch
的commit ID
移到<new-base> branch
的后面。执行下面两条命令:
git checkout about
git rebase master
则git repository
变为:
可以看到,同“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
保持不变。但是新生成的3
个commit
都会有新的commit ID
。这表明不仅仅是合并commit
这么简单,而是git repository
的history
完全被改写了。如下图所示:
另外,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
处理到第2
个commit
时,会停下来做一下“amending
”:
处理完后,运行下面命令,提交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
用户的观点来看,module
就是一段可以通过require
加载的代码(C
或Lua
),并且生成和返回一个table
。Lua
中所有的标准库都是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
查找module
的path
是一系列用“;
”分开的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
对每个template
,require
用module
名字代替?
,看是否存在这个文件。找到就返回,没有就尝试下一个template
。
require
查找Lua
文件的path
是package.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
语言关键字range
可以作用在array
,string
,slice
,map
和channel
五种数据结构上。
(1)一个文件夹下的所有文件必须属于同一个package
,一个好的规则是package
和文件夹名字相同。此外,要注意import
语句所指定的是寻找package
的path
,而不是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
在第二种情况下,m
是package
名字;第三种情况下,使用math package
可以省略package
名字。
(2)
import (
_ "github.com/xxx"
)
这种做法可以保证”github.com/xxx
“这个package
的初始化操作完成(这个package
中所有init
函数都执行),即使在当前文件中没有显式地使用到这个package
。
Go
语言变量定义的一个好的实践技巧是:
(1)如果变量声明时会被赋给数据类型的非“0
”值,则使用“:=
”这种方式:
a := 3
b := fun1()
(2)否则,因为所有变量声明时都会初始化为数据类型的“0
”值,则使用var
关键字定义这种方式:
var a int;
git branch
命令列出repository
中所有的branch
:
[root@localhost git_repo]# git branch
* master
git branch <branch-name>
会从当前working directory
中fork
出一个新的branch
:
[root@localhost git_repo]# git branch crazy
git checkout <branch-name>
会切换到指定branch
的working directory
:
[root@localhost git_repo]# git checkout crazy
Fast-forwarding merge
:把fork
出来的branch merge
回原branch
时,如果原branch
和fork
出来的branch
没有分叉,也即还在fork
出来的branch
的revision history
里,则直接让原branch
的HEAD
指向现在fork
出来的branch的
snapshot
即可。如下图所示:
与fast-forwarding merge
相对应的是3-way merge
:两个branch
之间存在着分叉,这时merge
回branch
需要产生一个新的commit
:
参考资料:
Branches, Part I;
Branches, Part II。
使用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.
如果想取消最后一次commit
(commit ID
是4a95041
),使用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 directory
和staged snapshot
的操作,而git revert
是取消committed snapshots
的操作。如下图所示:
参考资料:
Undoing Changes。