Posted on 2015-01-26 14:58:26 c
摘要:协程,即协作式程序,其思想是,一系列互相依赖的协程间依次使用CPU,每次只有一个协程工作,而其他协程处于休眠状态。协程可以在运行期间的某个点上暂停执行,并在恢复运行时从暂停的点上继续执行。 协程已经被证明是一种非常有用的程序组件,不仅被python、lua、ruby等脚本语言广泛采用,而且被新一代面向多核的编程语言如golang rust-lang等采用作为并发的基本单位。 协程可以被认为是一种用户空间线程,与传统的线程相比,有2个主要的优点:
与线程不同,协程是自己主动让出CPU,并交付他期望的下一个协程运行,而不是在任何时候都有可能被系统调度打断。因此协程的使用更加清晰易懂,并且多数情况下不需要锁机制。 与线程相比,协程的切换由程序控制,发生在用户空间而非内核空间,因此切换的代价非常小。
协程可以认为是一种用户态的线程,与系统提供的线程不同点是,它需要主动让出CPU时间,而不是由系统进行调度,即控制权在程序员手上。既然看成是用户态线程,那必然要求程序员自己进行各个协程的调度,这样就必须提供一种机制供编写协程的人将当前协程挂起,即保存协程运行场景的一些数据,调度器在其他协程挂起时再将此协程运行场景的数据恢复,以便继续运行。这里我们将协程运行场景的数据称为上下文。
阅读全文Posted on 2015-01-25 18:37:38 c
对一个应用程序员来讲,了解汇编不是必需的,更少有手写纯汇编的需求。但是如果能了解些基本的汇编知识,对程序调试和一些语言特性的理解是大有裨益的。本文介绍 AT&T 语法的汇编的要点以及 GCC 使用的内联汇编(inline assembly)的使用。
AT&T 汇编是 GCC 所采用的语法,要点:
通用寄存器(x86_64):
X86_64 下 ABI 调用约定:
内联汇编允许在 C/C++ 代码中嵌入汇编代码,以优化关键代码或者使用架构特有的指令。内联汇编的基本格式如下:
asm [volatile] ( <assembler template>
: ["constraints"(var)] [,"constraints"(var)] /* output operands */
: ["constraints"(var)] [,"constraints"(var)] /* input operands */
: ["register"] [,"register"] [,"memory"] /* clobbered registers */
);
中括号中为可选部分,尖括号为必选部分。圆括号内由 ‘:’ 分割为四个部分:
常用的 constraints 为:
输出 constraints 中需要下面至少一个『修饰符』(constraints modifier)作为前缀:
下面看几个示例:
asm ("":::); //~ nothing
asm ("incl %%eax\n\t":::"eax"); //~ access register directly
asm ("movq $1, %0\n\t" : "=m"(var)); //~ write 1 to var
asm ("mov %0, %%eax\n\t" : : "m"(var)); //~ read from var to eax
//~ read a to eax, read b to either ebx|ecx|edx|edi|esi, add it to eax, write back eax to a
asm ("addl %1, %0\n\t" : "+a"(a) : "r"(b));
asm ("incq global_var\n\t" :::"memory"); //~ access global_var directly
asm ("incl %0\n\t" : "+q"(var)); //~ read var to either eax|ebx|ecx|edx, increase it, write it back to var
asm ("incl %0\n\t" : "=q"(var) : "0"(var)) //~ the same as above, constraint 0 means using the same register
asm ("incl %[__var__]\n\t" : [__var__]"+q"(var)); //~ use user-defined placeholder
最后一个示例使用了用户自定义的占位符,通常在输入输入变量较多的情况下使用,省得逐个地对应。
在汇编中调用 printf:
#include <stdio.h>
int
main()
{
char *fmt = "Hello, %s\n";
char *s = "World";
int ret = 0;
asm (" callq printf\n\t"
: "=a"(ret)
: "D"(fmt), "S"(s));
printf("ret: %d\n", ret);
return 0;
}
在汇编中进行系统调用:
int
sys_write(int fd, const char *buf, size_t n)
{
int ret;
asm (
"syscall\n\t"
: "=a"(ret)
: "0"(1), "D"(fd), "S"(buf), "d"(n)
);
return ret;
}
int
main()
{
char *s = "Hello, World\n";
printf("%d\n", sys_write(fileno(stdout), s, strlen(s)));
return 0;
}
Posted on 2015-01-14 05:23:36 life
如果天空总是黑暗的,那就摸黑生存;如果发出声音是危险的,那就保持沉默;如果自觉无力发光,那就蜷伏于墙角。 但不要习惯了黑暗就为黑暗辩护;也不要为自己的苟且而得意;不要嘲讽那些比自己更勇敢的人们。我们可以卑微如尘土,但不可扭曲如蛆虫。 –曼德拉
Posted on 2014-10-04 03:51:00 golang
摘要:首先需要注意的是:golang1.3之后的版本,对于支持gdb调试存在很大的问题。产生这个问题的原因是,golang的runtime没有完整的被gdb支持。
最新比较完整支持gdb调试的版本是golang 1.2.2,但是也有个别问题存在。
为什么会出现以上种种问题,golang官网给出的解释是:
GDB does not understand Go programs well. The stack management, threading, and runtime contain aspects that differ enough from the execution model GDB expects that they can confuse the debugger, even when the program is compiled with gccgo. As a consequence, although GDB can be useful in some situations, it is not a reliable debugger for Go programs, particularly heavily concurrent ones. Moreover, it is not a priority for the Go project to address these issues, which are difficult. In short, the instructions below should be taken only as a guide to how to use GDB when it works, not as a guarantee of success.
翻译一下:
GDB不能很好的理解GO程序。堆栈管理,线程,而且runtime包含了非常不一样的执行模式,这不是GDB期望的,他们会扰乱调试器,即使go程序是使用gccgo编译的。结果就是,虽然GDB在某些场合下是有用的,但是对go程序来说并不是一个可靠的调试器。尤其是在大量并发的时候。而且,这不是Golang项目优先考虑的事情,这很困难。总而言之,下面的操作手册,只是当GDB正常工作的时候,引导你如何使用GDB,不能保证总是成功。
并且从google group讨论组和stackoverflow中,可以看到golang的多个版本对于GDB的支持都有这样那样的问题。 不过既然官方的手册都这么说了,我们也只有在合适的场合使用GDB吧。
默认情况下,编译过的二进制文件已经包含了 DWARFv3 调试信息,只要 GDB7.1 以上版本都可以进行调试。 在OSX下,如无法执行调试指令,可尝试用sudo方式执行gdb。
在编译go程序的时候,需要关闭内联优化:** -gcflags “-N -l”**。可以在go get/build/test
的时候指定这个参数。
有两种方式可以下断点: