文章归档

数据结构对齐问题

昨天去了迅雷,有个笔试基础题,结构体对齐相关,sizeof关键字值。回来后感觉不太确信,于是回头翻了翻lcc编译器的代码,看了结构体字段相关的处理过程。 几个基础的数据结构,其中

  • Metrics表示数据类型的大小,对齐值,autofline目前不太清楚
  • 结构体类型是通过type中的u.sym表示的,里面包含一个field类型的链表

typedef struct metrics { unsigned char size, align, outofline; } Metrics; struct type { int op; Type type; int align; int size; union { Symbol sym; struct { unsigned oldstyle:1; Type *proto; } f; } u; Xtype x; }; struct field { char *name; Type type; int offset; short bitsize; short lsb; Field link; };

在x86平台上,基础类型的Metries值(分别是 size align outofline)如下:

Interface x86linuxIR = { 1, 1, 0, /* char */ 2, 2, 0, /* short */ 4, 4, 0, /* int */ 4,

»» 继续阅读全文

some developer tools

Performance tools: ifstat(1), iftop(8), iostat(1), mpstat(1), netstat(1), nfsstat(1), nstat, vmstat(1), xosview(1) Debugging tools: htop(1), lslk(1), lsof(8), top(1) Process tracing: ltrace(1), pmap(1), ps(1), pstack(1), strace(1) Binary debugging: ldd(1), file(1), nm(1), objdump(1), readelf(1) Memory usage tools: free(1), memusage, memusagestat, slabtop(1) Accounting tools: dump-acct, dump-utmp, sa(8) Hardware debugging tools: dmidecode, ifinfo(1), lsdev(1), lshal(1), lshw(1), lsmod(8), lspci(8), lsusb(8), smartctl(8), x86info(1) Application debugging: mailstats(8), qshape(1) Xorg related tools: xdpyinfo(1), xrestop(1) Other useful info: collectl(1), proc(5), procinfo(8)

vim如何处理三元表达式

关于vim script的条件表达式,描述有点问题,在runtime/doc/usr_41.txt文档里:

Borrowed from the C language is the conditional expression: a ? b : c If "a" evaluates to true "b" is used, otherwise "c" is used. Example: > :let i = 4 :echo i > 5 ? "i is big" : "i is small" < i is small ~ The three parts of the constructs are always evaluated first, thus you could see it work as: (a) ? (b) : (c)

有问题那句我红色标出来了,它的意思是说,在计算一个条件表达式的值的时候三部分都是会先被计算的。也就是说,先计算(a)的值,(b)的值,和(c)的值,然后根据(a)是否为true,选择表达式最终的值。 这个做法看起来很有问题,我确实想不出为什么要这么做,至少c这些语言大多编译器的实现,都是最终转化为一个goto语句吧。 如果这么做是可行的,那么是不是说:

:let a = 3 < 5 ? 3 - 1 : getchar()

即使最终的结果是part2,但是getchar()也会被执行?

»» 继续阅读全文

goroutine scheduler

some references

  • Analysis of the Go runtime scheduler
  • Dmitry Vyukov. Scalable Go Scheduler Design Doc

go runtime and go scheduler

The runtime keeps track of each goroutine, and will schedule them to run in turn on a pool of threads belonging to the process. Goroutines are separate from threads but rely upon them to run, and scheduling goroutines onto threads effectively is crucial for the efficient performance of Go programs. The idea behind goroutines is that they are capable of running concurrently, like threads, but are also extremely lightweight in comparison. So, while there might be multiple threads

»» 继续阅读全文

goroutine的实现原理

goroutine的实现主要依赖下面这几个API,linux386平台的实现在runtime/asm_386.s文件里。这里只显示API [c]// void gosave(Gobuf*)

// void gogo(Gobuf*, uintptr) // restore state from Gobuf; longjmp

// void gogocall(Gobuf*, void (*fn)(void)) // restore state from Gobuf but then call fn. // (call fn, returning to state in Gobuf)

// void mcall(void (*fn)(G*)) // Switch to m-&gt;g0's stack, call fn(g). // Fn must never return. It should gogo(&amp;g-&gt;sched) // to keep running g.[/c] 而调度的实现是在runtime/proc.c文件里。

gosave类似于setjmp,但它也保存了当前正在运行的G的状态,PC和SP,gogo类似于longjmp,恢复G的SP和PC。SP和PC是通过一个Gobuf的结构来维护的。gogocall和gogo的作用差不多,但跳转到相应的G之后,会继续执行一个fn函数。mcall稍微复杂点,但也和googcall类似。

通常从M跳转到G的执行,是使用gogo,而从G跳转到M,是使用mcall。这是因为在M的栈上运行的只有一个mstart()函数,而在mstart()函数内主要的工作就是初始化一些数据,然后最后触发调度器schedule(),所以如果是使用gogo从G返回到M,那么M就会执行完毕。

整个过程就是通过一个全局的Sched结构来维护所有的调度状态。比libtask的实现复杂点,和xv6的proc实现也差不多。

lua 5.1虚拟机

  1. The Implementation of Lua 5.0
  2. 手册:http://www.lua.org/manual/5.1/manual.html
  3. lua源码欣赏:http://www.codingnow.com/temp/readinglua.pdf
  4. 指令集:A No Frills Intro To Lua51 VM Instructions.pdf

蛮有用的资料,对vm 5.1的理解很有帮助。

Libtask: c和linux/unix下的协程库

实现协程核心的文件只有几个,其中*-ucontext.h是特定平台上下文切换功能的实现,是几个用户态的API。以386平台为例:

extern int swapcontext(ucontext_t*, const ucontext_t*); extern void makecontext(ucontext_t*, void(*)(), int, ...); extern int getmcontext(mcontext_t*); extern void setmcontext(const mcontext_t*);

task.c/task.h是调度器的实现。其实明白上面这几个API,基本上就可以知道协程是怎么干活的了。下面主要分析task.c代码。

使用libtask构建程序不需要显式地使用main函数,只需一个taskmain即可,因为main函数是在task.c里实现的,也就是libtask的调度。

----- task.c ------ int main(int argc, char **argv) { .... taskcreate(taskmainstart, nil, mainstacksize); taskscheduler(); .... }

taskcreate创建了第一个协程,这个协程就是用户的taskmain函数,将其加入调度队列,然后进入调度函数taskscheduler(),taskscheduler()不会返回

static void taskscheduler(void) { int i; Task *t; for(;;){ if(taskcount == 0) exit(taskexitval); t = taskrunqueue.head; if(t == nil){ fprint(2, "no runnable tasks! %d tasks stalled\n", taskcount); exit(1); } deltask(&taskrunqueue, t); t->ready = 0; taskrunning = t; tasknswitch++; contextswitch(&taskschedcontext, &t->context); taskrunning = nil; if(t->exiting){ if(!t->system) taskcount--; i = t->alltaskslot; alltask[i] = alltask[--nalltask]; alltask[i]->alltaskslot = i; free(t); } } }

contextswitch切换到到协程t去执行了,从t返回的情况有两种,要么是协程执行完毕了,要么是用户主动的yield,然后等待下次继续被执行。

<busying, todo...>