*NIX & Hacking —— 第2期

做一本我感兴趣的杂志,就这么简单!

Debug

Introduction to Debuggers

Docker

DOCKER MACHINE 0.3.0 DEEP DIVE

Git

First aid git

Golang

go-lang-cheat-sheet

Kernel

Kernel and userspace tracing: with LTTng and his friends

Regular Expression

Five Invaluable Techniques to Improve Regex Performance

Rust

First Rust Program Pain (So you can avoid it…)

Scala

Various ways to run Scala code
Why does the same scala code work OK in command line while not in Intellij?

Unix

A little collection of cool unix terminal/console/curses tools
How big is the pipe buffer?
Stack Smashing On A Modern Linux System
Unix history repository

Easter egg

DMA (Direct Memory Access)
Get Unix Jobs
Here are 13 tech jobs that pay at least $130,000 across the country

如何运行scala脚本

搭建Scala开发环境一文中可以看到不加任何参数直接运行scala命令时,会启动REPL(Read,Eval,Print,Loop)环境,然后就可以在这个环境里交互地执行Scala代码。本文介绍如何运行Scala脚本(script)。

首先创建一个简单的Scala脚本(hello.scala):

print("Hello world!\n")

a)直接运行scala hello.scala

[root@Fedora scala]# scala hello.scala
Hello world!

b)在REPL环境用load命令运行hello.scala

[root@Fedora scala]# scala
Welcome to Scala version 2.10.4 (OpenJDK 64-Bit Server VM, Java 1.8.0_45).
Type in expressions to have them evaluated.
Type :help for more information.

scala> :load hello.scala
Loading hello.scala...
Hello world!

c)如果想把脚本编译成JVM byte code (一组*.class文件),可以使用scalac程序(命令行选项是-Xscript <object><object>是“main class”的名字,也即Java程序的入口):

[root@Fedora scala]# scalac -Xscript hello hello.scala
[root@Fedora scala]# ls
hello$$anon$1.class  hello.class  hello$.class  hello.scala
[root@Fedora scala]# scala hello
Hello world!

也可以用scalap这个逆向分析工具分析一下hello.class

[[email protected] scala]# scalap -cp . hello
object hello extends scala.AnyRef {
  def this() = { /* compiled code */ }
  def main(argv : scala.Array[scala.Predef.String]) : scala.Unit = { /* compiled code */ }
}

*NIX & Hacking —— 创刊号

做一本我感兴趣的杂志,就这么简单!

C

Docker

Golang

Kernel

Lua

Rust

Unix

Vim

X86

Easter egg

Lua笔记(7)——luaL_loadfile和luaL_dofile的区别

luaL_loadfile()会加载和编译Lua脚本,但不会运行。而luaL_dofile不仅运行编译后的脚本,运行结束后还会把脚本pop出栈。看下面这个例子:

一个简单的脚本(test.lua):

print "Hello World!"

首先看调用luaL_loadfile()的程序:

#include <stdio.h>
#include <stdlib.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"

static void stackDump(lua_State *L)
{
    int i = 0;
    int type = 0;
    int top = lua_gettop(L);

    printf("There is %d elements in stack: ", top);
    for (i = 1; i <= top; i++)
    {
        type = lua_type(L, i);
        switch (type)
        {
            case LUA_TSTRING:
            {
                printf("'%s'", lua_tostring(L, i));
                break;
            }
            case LUA_TBOOLEAN:
            {
                printf(lua_toboolean(L, i) ? "true" : "false");
                break;
            }
            case LUA_TNUMBER:
            {
                printf("%g", lua_tonumber(L, i));
                break;
            }
            default:
            {
                printf("Element type is %s", lua_typename(L, type));
                break;
            }
        }
        printf(" ");
    }
    printf("\n");
    return;
}

static void bail(lua_State *L)
{
    fprintf(stderr, "\nFATAL ERROR:%s\n\n", lua_tostring(L, -1));
    exit(1);
}
int main(void)
{   
    lua_State *L = luaL_newstate();

    luaL_openlibs(L);

    if (luaL_loadfile(L, "test.lua"))
    {
        bail(L);
    }

    stackDump(L);

    lua_close(L);
    return 0;
}

执行结果:

[root@Fedora test]# ./a
There is 1 elements in stack: Element type is function

可以看到,并没有打印“Hello World!”,而且栈里还有一个类型为function的元素。

接下来看调用luaL_dofile()的程序:

#include <stdio.h>
#include <stdlib.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"

static void stackDump(lua_State *L)
{
    int i = 0;
    int type = 0;
    int top = lua_gettop(L);

    printf("There is %d elements in stack: ", top);
    for (i = 1; i <= top; i++)
    {
        type = lua_type(L, i);
        switch (type)
        {
            case LUA_TSTRING:
            {
                printf("'%s'", lua_tostring(L, i));
                break;
            }
            case LUA_TBOOLEAN:
            {
                printf(lua_toboolean(L, i) ? "true" : "false");
                break;
            }
            case LUA_TNUMBER:
            {
                printf("%g", lua_tonumber(L, i));
                break;
            }
            default:
            {
                printf("Element type is %s", lua_typename(L, type));
                break;
            }
        }
        printf(" ");
    }
    printf("\n");
    return;
}

static void bail(lua_State *L)
{
        fprintf(stderr, "\nFATAL ERROR:%s\n\n", lua_tostring(L, -1));
        exit(1);
}
int main(void)
{
    lua_State *L = luaL_newstate();

    luaL_openlibs(L);

    if (luaL_dofile(L, "test.lua"))
    {
        bail(L);
    }

    stackDump(L);

    lua_close(L);
    return 0;
}

执行结果:

[root@Fedora test]# ./a
Hello World!
There is 0 elements in stack:

可以看到,不仅打印了“Hello World!”,而且栈也变成了空。

参考资料:
lual_dofile(); wont load script with C++ and Lua

Lua笔记(6)——栈(stack)

C程序和Lua库之间通过栈(stack)来进行数据交换,并且栈中的每个槽位(slot)都能存放任意的Lua数据类型值。栈如下图所示:

                            |________|    <--  (-1)
                            |________|    <--  (-2)
                            |________|
                            | ...... |
                            |________|    <--  (2)
                            |________|    <--  (1)

栈底以1为起始索引,而栈顶则以-1作为起始索引。lua_gettop()函数可以返回当前栈中的元素个数,同时也是栈顶元素的索引值。如果是空栈,则 lua_gettop()返回0

Lua C API的核心就集中在对栈的操作上:当想要运行某个Lua脚本时,需要调用luaL_dofile()函数。而想执行Lua脚本的某个函数时,则首先要把函数push进栈(lua_getglobal()),如果函数需要参数,则参数也要相应地进栈(lua_pushXX())。接下来执行函数(比如lua_pcall()函数)。当函数退出时,返回值同样push进栈,C程序就可以使用lua_toXX()函数获得结果。请看下面这个例子:

一个简单的Lua脚本(test.Lua):

function add (a, b)
    return (a+b)
end

C程序如下:

#include <stdio.h>
#include <stdlib.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"

static void bail(lua_State *L)
{
    fprintf(stderr, "\nFATAL ERROR:%s\n\n", lua_tostring(L, -1));
    exit(1);
}
int main(void)
{   
    lua_State *L = luaL_newstate();

    luaL_openlibs(L);

    if (luaL_dofile(L, "test.lua"))
    {
        bail(L);
    }

    lua_getglobal(L, "add");


    lua_pushnumber(L, 1);
    lua_pushnumber(L, 2);

    if (lua_pcall(L, 2, 1, 0))
    {
        bail(L);
    }

    printf("%g\n", lua_tonumber(L, -1));

    lua_close(L);
    return 0;
}

执行结果如下:

3

Go语言数组浅析

先看一个简单的数组定义:

var array [3]int

上面语句定义了变量array,这个变量是一个包含3个整数的数组类型。可以使用Go语言内置的len函数得到数组长度:

package main

import (
    "fmt"
)

func main() {
    var array [3]int
    fmt.Println(len(array))
}

执行以上程序结果:

3

需要注意的是,在Go语言中,数组的长度是数组类型的一部分,它必须是一个常量表达式,即长度的值在程序编译时是可以得到的。请看下面这个程序:

package main

import (
    "fmt"
)

func print_array(array []int) {
    fmt.Printf("In function, array is %v\n", array)
}

func main() {
    var array [3]int

    print_array(array )
}

编译这个程序会产生下面的错误:

main.go:14: cannot use array (type [3]int) as type []int in argument to print_array

Go语言要求传入函数的形参和实参的类型必须是完全匹配的,而array的类型是[3]int,不是[]int,所以编译会出错。

Go语言函数参数都是传值调用,所以如果函数的参数是数组的话,那么实际传入的将是原数组的拷贝。请看这个例子:

package main

import (
    "fmt"
)

func change_array(array [3]int) {
    for i, _ := range array {
        array[i] = 1
    }
    fmt.Printf("In function, array address is %p, value is %v\n", &array, array)
}

func main() {
    var array [3]int

    fmt.Printf("Original array address is %p, value is %v\n", &array, array)
    change_array(array)
    fmt.Printf("Changed array address is %p, value is %v\n", &array, array)
}

执行结果:

Original array address is 0xc082006560, value is [0 0 0]
In function, array address is 0xc0820065e0, value is [1 1 1]
Changed array address is 0xc082006560, value is [0 0 0]

可以看到,在change_array函数中操作的数组地址并不是main函数中的数组地址,而main函数中的数组的值在调用change_array函数前后也没有发生变化。

如果数组的元素类型是可比较的,那么数组类型就是可比较的,即可以使用“==”和“!=”运算符对数组进行运算。 请看下例:

package main
import "fmt"

func main()  {
    a := [...]int{2, 1}
    b := [...]int{1, 2}
    fmt.Println(a == b, a != b)
}

执行结果如下:

false true

 

如何编译使用Lua库的C程序

编写使用LuaC程序后,除了需要链接Lua库以外,还需要链接libmlibdl。以下面程序为例(test.c):

#include <stdio.h>
#include <stdlib.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"

int main(void)
{
    char buf[256] = {0};
    int error = 0;

    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    while (fgets(buf, sizeof(buf), stdin) != NULL)
    {
        error = luaL_loadstring(L, buf) || lua_pcall(L, 0, 0, 0);
        if (error)
        {
            fprintf(stderr, "Error is %s.\n", lua_tostring(L, -1));
            lua_pop(L, 1);
        }
    }
    lua_close(L);
    return 0;
}

编译:

[root@Fedora test]# gcc -g -o test test.c -llua -lm -ldl

执行:

[root@Fedora test]# ./test
print("hello")
hello
^C
[root@Fedora test]#

什么是coroutine(协程)?

Coroutine isn't thread!(协程不是线程!)

Coroutinethread有许多共同点:每个coroutine(或thread)都有一组指令执行序列,私有的栈,私有的局部变量,私有的程序计数器(program counter);同时各个coroutine(或thread)之间共享全局变量等等信息。

Coroutinethread的不同在于:在多核系统上,一个进程的多个thread是可以并行运行的(in parallel),即在一个时间点上,多个thread同时在运行。而coroutine之间是合作式的(collaborative),在任何一个时间点,只有一个coroutine在运行。Coroutine之间的调度是非抢占式的(non-preemptive):只有运行的coroutine主动地放弃执行权,其它coroutine才可以获得执行机会。

“Orthogonality”的含义

Orthogonality(正交性)的含义通俗来讲是指“改变A不会改变B”。一个“Orthogonality”系统的例子是收音机:换台不会改变音量;而不是“Orthogonality”系统的例子则是直升飞机:改变速度也会改变方向。

在编程语言中,Orthogonality指执行一条指令时,只有这条指令被执行,不会有其它任何副作用(side-effect)。

参考资料:
What is “Orthogonality”?