顯示包含「Linux」標籤的文章。顯示所有文章
顯示包含「Linux」標籤的文章。顯示所有文章

2013年7月18日星期四

C optimisation tutorial

C optimisation tutorial

Introduction

This document has evolved over time and contains a number of the best ways to hand-optimise your C-code. Compilers are good, but they can't do everything, and here I hope to help you squeeze the best performance out of your code. This is not intended for beginners, but for more experienced programmers. 

Disclaimer

Depending on your particular hardware and compiler, some of these techniques may actually slow down your code. Do some timings with and without them, as modern compilers may well be able to do things better at a low level. Improving the overall algorithm used will often produce better results than localised code tweaking. This document was originally written as a set of personal notes for myself - do not consider it to be an authoritative paper on the subject of optimisation. I may have made mistakes! If you have anything to add to this, or just have some constructive criticism (flames ignored), please contact me at the address below.

Coding for speed

How to squeeze those last few T-states out of your C code. This only applies where you are more concerned with maximum speed and are less concerned with readability of code.
No error-checking is shown here as this article is only concerned with the fundamentals. By the time the application gets down to the low-level routines, you should have filtered out any bad data already.

Using array indices

If you wished to set a variable to a particular character, depending upon the value of something, you might do this :
switch ( queue ) {
case 0 :   letter = 'W';
   break;
case 1 :   letter = 'S';
   break;
case 2 :   letter = 'U';
   break;
}
or maybe
if ( queue == 0 )
  letter = 'W';
else if ( queue == 1 )
  letter = 'S';
else
  letter = 'U';
A neater ( and quicker ) method is to simply use the value as an index into a character array, eg.
static char *classes="WSU";

letter = classes[queue];

Aliases

Consider the following:
void func1( int *data )
{
    int i;

    for(i=0; i<10; i++)
    {
           somefunc2( *data, i);
    }
}
Even though "*data" may never change, the compiler does not know that somefunc2() did not alter it, and so the program must read it from memory each time it is used - it may be an alias for some other variable that is altered elsewhere. If you know it won't be altered, you could code it like this instead:
void func1( int *data )
{
    int i;
    int localdata;

    localdata = *data;
    for(i=0; i<10; i++)
    {
           somefunc2( localdata, i);
    }
}
This gives the compiler better opportunity for optimisation. 

Integers

Use unsigned ints instead of ints if you know the value will never be negative. Some processors can handle unsigned integer arithmetic considerably faster than signed ( this is also good practise, and helps make for self-documenting code). 
So the best declaration for an int variable in a tight loop would be
register unsigned int   var_name;
(although it is not guaranteed that the compiler will take any notice of "register", and "unsigned" may make no difference to the processor.) 
Remember, integer arithmetic is much faster than floating-point arithmetic, as it can be usually be done directly by the processor, rather than relying on external FPUs or floating point maths libraries. If you only need to be accurate to two decimal places (e.g. in a simple accounting package), scale everything up by 100, and convert it back to floating point as late as possible. 

Loop jamming

Never use two loops where one will suffice:
for(i=0; i<100; i++)
{
    stuff();
}

for(i=0; i<100; i++)
{
    morestuff();
}
It would be better to do:
for(i=0; i<100; i++)
{
    stuff();
    morestuff();
}
Note, however, that if you do a lot of work in the loop, it might not fit into your processor's instruction cache. In this case, two separate loops may actually be faster as each one can run completely in the cache. 

Loop Unrolling and Dynamic Loop Unrolling

This can make a BIG difference. 
It is well known that unrolling loops can produce considerable savings, e.g.
for(i=0; i<3; i++)
{
    something(i);
}
is less efficient than
something(0);
something(1);
something(2);
because the code has to check and increment the value of i each time round the loop. Compilers will often unroll simple loops like this, where a fixed number of iterations is involved, but something like
for(i=0;i<limit;i++){ ... }
is unlikely to be unrolled, as we don't know how many iterations there will be. It is, however, possible to unroll this sort of loop and take advantage of the speed savings that can be gained. A good example of this was given in the "Graphic Gems" series of books, as a way of speeding up the display of pixels in a scanline during graphics rendering, but can also be applied to any situation which involves the same operation being applied to a large amount of data. 
The following code (listing 1) is obviously much larger than a simple loop, but is much more efficient. The block-size of 8 was chosen just for demo purposes, as any suitable size will do - you just have to repeat the "loop-contents" the same amount. In this example, the loop-condition is tested once every 8 iterations, instead of on each one. If you know that you will working with arrays of a certain size, you could make the blocksize the same size as (or divisible into the size of) the array. I find 8 to be an adequate size though. I tried some performance tests using this technique on a Sun Sparcstation, and block-sizes of 8 and 16 worked fine, but when I went up to 32, it slowed right down again (again, I suspect this was to do with the size of the machines cache). 
I have used code similar to this in an image-processing application, to negate a large monochrome bitmap (of about 3000x2000 pixels), and it was significantly faster than using a simple for() type loop, as the processor was able to get on with the job of processing the data rather than incrementing counters and testing the loop conditions.

Listing 1

#include<stdio.h> 

#define BLOCKSIZE (8) 

void main(void)
{ 
int i = 0; 
int limit = 33;  /* could be anything */ 
int blocklimit; 

/* The limit may not be divisible by BLOCKSIZE, 
 * go as near as we can first, then tidy up.
 */ 
blocklimit = (limit / BLOCKSIZE) * BLOCKSIZE; 

/* unroll the loop in blocks of 8 */ 
while( i < blocklimit ) 
{ 
    printf("process(%d)\n", i); 
    printf("process(%d)\n", i+1); 
    printf("process(%d)\n", i+2); 
    printf("process(%d)\n", i+3); 
    printf("process(%d)\n", i+4); 
    printf("process(%d)\n", i+5); 
    printf("process(%d)\n", i+6); 
    printf("process(%d)\n", i+7); 

    /* update the counter */ 
    i += 8; 

} 

/* 
 * There may be some left to do.
 * This could be done as a simple for() loop, 
 * but a switch is faster (and more interesting) 
 */ 

if( i < limit ) 
{ 
    /* Jump into the case at the place that will allow
     * us to finish off the appropriate number of items. 
     */ 

    switch( limit - i ) 
    { 
        case 7 : printf("process(%d)\n", i); i++; 
        case 6 : printf("process(%d)\n", i); i++; 
        case 5 : printf("process(%d)\n", i); i++; 
        case 4 : printf("process(%d)\n", i); i++; 
        case 3 : printf("process(%d)\n", i); i++; 
        case 2 : printf("process(%d)\n", i); i++; 
        case 1 : printf("process(%d)\n", i); 
    }
} 

}
Another simple trick to use with loops is to count down, instead of up. Look at this code, which will go through the values of i=0 to i=9 :
for(i=0; i<10; i++)
{
  do stuff...
}
If the order in which the loop contents are executed does not matter, you can do this instead:
for( i=10; i--; )
which will step through i=9, down to i=0.It is important to use "i--" rather than "--i", otherwise the loop will terminate early. 

Faster for() loops

Simple but effective. 
Ordinarily, you would code a simple for() loop like this:
for( i=0;  i<10;  i++){ ... }
i loops through the values 0,1,2,3,4,5,6,7,8,9
If you don't care about the order of the loop counter, you can do this instead:
for( i=10; i--; ) { ... }
Using this code, i loops through the values 9,8,7,6,5,4,3,2,1,0, and the loop should be faster. 
This works because it is quicker to process "i--" as the test condition, which says "is i non-zero? If so, decrement it and continue.". 
For the original code, the processor has to calculate "subtract i from 10. Is the result non-zero? if so, increment i and continue.". In tight loops, this make a considerable difference.
The syntax is a little strange, put is perfectly legal. The third statement in the loop is optional (an infinite loop would be written as "for( ; ; )" ). The same effect could also be gained by coding:
for(i=10; i; i--){}
or (to expand it further)
for(i=10; i!=0; i--){}
The only things you have to be careful of are remembering that the loop stops at 0 (so if you wanted to loop from 50-80, this wouldn't work), and the loop counter goes backwards.It's easy to get caught out if your code relies on an ascending loop counter.


 

switch() instead of if...else...

For large decisions involving if...else...else..., like this:
if( val == 1)
    dostuff1();
else if (val == 2)
    dostuff2();
else if (val == 3)
    dostuff3();
it may be faster to use a switch:
switch( val )
{
    case 1: dostuff1(); break;

    case 2: dostuff2(); break;

    case 3: dostuff3(); break;
}
In the if() statement, if the last case is required, all the previous ones will be tested first. The switch lets us cut out this extra work. If you have to use a big if..else.. statement, test the most likely cases first. 

Pointers

Whenever possible, pass structures by reference ( ie. pass a pointer to the structure ), otherwise the whole darn thing will be copied onto the stack and passed, which will slow things down a tad (I've seen programs that pass structures several megabytes in size by value, when a simple pointer will do the same thing). 
Functions receiving pointers to structures as arguments should declare them as pointer to const if the function is not going to alter the contents of the structure, e.g.
void print_data( const bigstruct  *data_pointer)
{
    ...printf contents of structure...
}
This example informs the compiler that the function does not alter the contents (pointer to constant structure) of the external structure, and does not need to keep re-reading the contents each time they are accessed. It also ensures that the compiler will trap any accidental attempts by your code to write to the read-only structure. 

Early loop breaking

It is often not necessary to process the entirety of  a loop. For example, if you are searching an array for a particular item, break out of the loop as soon as you have got what you need. 
Example: 
This loop searches a list of 10000 numbers to see if there is a -99 in it.
found = FALSE;
for(i=0;i<10000;i++)
{
    if( list[i] == -99 )
    {
        found = TRUE;
    }
}
if( found ) printf("Yes, there is a -99. Hooray!\n");
This works well, but will process the entire array, no matter where the search item occurs in it.
A better way is to abort the search as soon as you've found the desired entry.
found = FALSE;
for(i=0; i<10000; i++)
{
    if( list[i] == -99 )
    {
        found = TRUE;
        break;
    }
}
if( found ) printf("Yes, there is a -99. Hooray!\n");
If the item is at, say position 23, the loop will stop there and then, and skip the remaining 9977 iterations.

Misc.

  • In general, savings can be made by trading off memory for speed. If you can cache any often used data rather than recalculating or reloading it, it will help. Examples of this would be sine/cosine tables, or tables of pseudo-random numbers (calculate 1000 once at the start, and just reuse them if you don't need truly random numbers).
  • Avoid using ++ and -- etc. within loop expressions, eg. while(n--){}, as this can sometimes be harder to optimise.
  • Minimize the use of global variables.
  • Declare anything within a file (external to functions) as static, unless it is intended to be global.
  • Use word-size variables if you can, as the machine can work with these better ( instead of char, short, double, bitfields etc. ).
  • Don't use recursion. Recursion can be very elegant and neat, but creates many more function calls which can become a large overhead.
  • Avoid the sqrt() square root function in loops - calculating square roots is very CPU intensive.
  • Single dimension arrays are faster than multi-dimensioned arrays.
  • Compilers can often optimise a whole file - avoid splitting off closely related functions into separate files, the compiler will do better if can see both of them together (it might be able to inline the code, for example).
  • Single precision maths may be faster than double precision - there is often a compiler switch for this.
  • Floating point multiplication is often faster than division - use val * 0.5 instead of val / 2.0.
  • Addition is quicker than multiplication - use val + val + val instead of val * 3
  • puts() is quicker than printf(), although less flexible.
  • Use #defined macros instead of commonly used tiny functions - sometimes the bulk of CPU usage can be tracked down to a small external function being called thousands of times in a tight loop. Replacing it with a macro to perform the same job will remove the overhead of all those function calls, and allow the compiler to be more aggressive in it's optimisation..
  • Binary/unformatted file access is faster than formatted access, as the machine does not have to convert between human-readable ASCII and machine-readable binary. If you don't actually need to read the data in a file yourself, consider making it a binary file.
  • If your library supports the mallopt() function (for controlling malloc), use it. The MAXFAST setting can make significant improvements to code that does a lot of malloc work.If a particular structure is created/destroyed many times a second, try setting the mallopt options to work best with that size.
  • Last but definitely not least - turn compiler optimisation on! Seems obvious, but is often forgotten in that last minute rush to get the product out on time. The compiler will be able to optimise at a much lower level than can be done in the source code, and perform optimisations specific to the target processor.


Last updated July 1998
If you find any of this helps to dramatically increase the performance of your software, please let me know. 

Now Available - Vatican Approved Debugger

Debug your code by faith alone 

Check out the Amiga Programmers Web Site 

中文:http://just-study.blogbus.com/logs/37238127.html

2013年7月2日星期二

BASH 的基本语法

BASH 的基本语法 source
最简单的例子 —— Hello World!
关于输入、输出和错误输出
BASH 中对变量的规定(与 C 语言的异同)
BASH 中的基本流程控制语法
函数的使用
2.1     最简单的例子 —— Hello World!
几乎所有的讲解编程的书给读者的第一个例子都是 Hello World 程序,那么我们今天也就从这个例子出发,来逐步了解 BASH。
用 vi 编辑器编辑一个 hello 文件如下:

#!/bin/bash
# This is a very simple example
echo Hello World
这样最简单的一个 BASH 程序就编写完了。这里有几个问题需要说明一下:
一,第一行的 #! 是什么意思
二,第一行的 /bin/bash 又是什么意思
三,第二行是注释吗
四,echo 语句
五,如何执行该程序
#! 是说明 hello 这个文件的类型的,有点类似于 Windows 系统下用不同文件后缀来表示不同文件类型的意思(但不相同)。Linux 系统根据 "#!" 及该字串后面的信息确定该文件的类型,关于这一问题同学们回去以后可以通过 "man magic"命令 及 /usr/share/magic 文件来了解这方面的更多内容。在 BASH 中 第一行的 "#!" 及后面的 "/bin/bash" 就表明该文件是一个 BASH 程序,需要由 /bin 目录下的 bash 程序来解释执行。BASH 这个程序一般是存放在 /bin 目录下,如果你的 Linux 系统比较特别,bash 也有可能被存放在 /sbin 、/usr/local/bin 、/usr/bin 、/usr/sbin 或 /usr/local/sbin 这样的目录下;如果还找不到,你可以用 "locate bash" "find / -name bash 2> /dev/null" 或 "whereis bash" 这三个命令找出 bash 所在的位置;如果仍然找不到,那你可能需要自己动手安装一个 BASH 软件包了。
第二行的 "# This is a ..." 就是 BASH 程序的注释,在 BASH 程序中从“#”号(注意:后面紧接着是“!”号的除外)开始到行尾的多有部分均被看作是程序的注释。的三行的 echo 语句的功能是把 echo 后面的字符串输出到标准输出中去。由于 echo 后跟的是 "Hello World" 这个字符串,因此 "Hello World"这个字串就被显示在控制台终端的屏幕上了。需要注意的是 BASH 中的绝大多数语句结尾处都没有分号。
如何执行该程序呢?有两种方法:一种是显式制定 BASH 去执行:
$ bash hello 或
$ sh hello (这里 sh 是指向 bash 的一个链接,“lrwxrwxrwx 1 root root 4 Aug 20 05:41 /bin/sh -> bash”)
或者可以先将 hello 文件改为可以执行的文件,然后直接运行它,此时由于 hello 文件第一行的 "#! /bin/bash" 的作用,系统会自动用/bin/bash 程序去解释执行 hello 文件的:
$ chmod u+x hello
$ ./hello
此处没有直接 “$ hello”是因为当前目录不是当前用户可执行文件的默认目录,而将当前目录“.”设为默认目录是一个不安全的设置。
需要注意的是,BASH 程序被执行后,实际上 Linux 系统是另外开设了一个进程来运行的。

2.2     关于输入、输出和错误输出
在字符终端环境中,标准输入/标准输出的概念很好理解。输入即指对一个应用程序 或命令的输入,无论是从键盘输入还是从别的文件输入;输出即指应用程序或命令产生的一些信息;与 Windows 系统下不同的是,Linux 系统下还有一个标准错误输出的概念,这个概念主要是为程序调试和系统维护目的而设置的,错误输出于标准输出分开可以让一些高级的错误信息不干扰正常的输出 信息,从而方便一般用户的使用。
在 Linux 系统中:标准输入(stdin)默认为键盘输入;标准输出(stdout)默认为屏幕输出;标准错误输出(stderr)默认也是输出到屏幕(上面的 std 表示 standard)。在 BASH 中使用这些概念时一般将标准输出表示为 1,将标准错误输出表示为 2。下面我们举例来说明如何使用他们,特别是标准输出和标准错误输出。
输入、输出及标准错误输出主要用于 I/O 的重定向,就是说需要改变他们的默认设置。先看这个例子:
$ ls > ls_result
$ ls -l >> ls_result
上面这两个命令分别将 ls 命令的结果输出重定向到 ls_result 文件中和追加到 ls_result 文件中,而不是输出到屏幕上。">"就是输出(标准输出和标准错误输出)重定向的代表符号,连续两个 ">" 符号,即 ">>" 则表示不清除原来的而追加输出。下面再来看一个稍微复杂的例子:
$ find /home -name lost* 2> err_result
这个命令在 ">" 符号之前多了一个 "2","2>" 表示将标准错误输出重定向。由于 /home 目录下有些目录由于权限限制不能访问,因此会产生一些标准错误输出被存放在 err_result 文件中。大家可以设想一下 find /home -name lost* 2>>err_result 命令会产生什么结果?
如果直接执行 find /home -name lost* > all_result ,其结果是只有标准输出被存入 all_result 文件中,要想让标准错误输出和标准输入一样都被存入到文件中,那该怎么办呢?看下面这个例子:
$ find /home -name lost* > all_result 2>& 1
上面这个例子中将首先将标准错误输出也重定向到标准输出中,再将标准输出重定向到 all_result 这个文件中。这样我们就可以将所有的输出都存储到文件中了。为实现上述功能,还有一种简便的写法如下:
$ find /home -name lost* >& all_result

如果那些出错信息并不重要,下面这个命令可以让你避开众多无用出错信息的干扰:
$ find /home -name lost* 2> /dev/null
同学们回去后还可以再试验一下如下几种重定向方式,看看会出什么结果,为什么?
$ find /home -name lost* > all_result 1>& 2
$ find /home -name lost* 2> all_result 1>& 2
$ find /home -name lost* 2>& 1 > all_result
另外一个非常有用的重定向操作符是 "-",请看下面这个例子:
$ (cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xvfp -)
该命令表示把 /source/directory 目录下的所有文件通过压缩和解压,快速的全部移动到 /dest/directory 目录下去,这个命令在 /source/directory 和 /dest/directory 不处在同一个文件系统下时将显示出特别的优势。
下面还几种不常见的用法:
n<&- 表示将 n 号输入关闭
<&- 表示关闭标准输入(键盘)
n>&- 表示将 n 号输出关闭
>&- 表示将标准输出关闭
2.3     BASH 中对变量的规定(与 C 语言的异同)
好了下面我们进入正题,先看看 BASH 中的变量是如何定义和使用的。对于熟悉 C 语言的程序员,我们将解释 BASH 中的定义和用法与 C 语言中有何不同。
2.3.1. BASH 中的变量介绍
我们先来从整体上把握一下 BASH 中变量的用法,然后再去分析 BASH 中变量使用与 C 语言中的不同。BASH 中的变量都是不能含有保留字,不能含有 "-" 等保留字符,也不能含有空格。
2.3.1.1 简单变量
在 BASH 中变量定义是不需要的,没有 "int i" 这样的定义过程。如果想用一个变量,只要他没有在前面被定义过,就直接可以用,当然你使用该变量的第一条语句应该是对他赋初值了,如果你不赋初值也没关 系,只不过该变量是空( 注意:是 NULL,不是 0 )。不给变量赋初值虽然语法上不反对,但不是一个好的编程习惯。好了我们看看下面的例子:
首先用 vi 编辑下面这个文件 hello2:
#!/bin/bash
# give the initialize value to STR
STR="Hello World"
echo $STR

在上面这个程序中我们需要注意下面几点:
一,变量赋值时,'='左右两边都不能有空格;
二,BASH 中的语句结尾不需要分号(";");
三,除了在变量赋值和在FOR循环语句头中,BASH 中的变量使用必须在变量前加"$"符号,同学们可以将上面程序中第三行改为 "echo STR" 再试试,看看会出什么结果。==>output: STR
四,由于 BASH 程序是在一个新的进程中运行的,所以该程序中的变量定义和赋值不会改变其他进程或原始 Shell 中同名变量的值,也不会影响他们的运行。
更细致的文档甚至提到以但引号括起来的变量将不被 BASH 解释为变量,如 '$STR' ,而被看成为纯粹的字符串。而且更为标准的变量引用方式是 ${STR} 这样的,$STR 自不过是对 ${STR} 的一种简化。在复杂情况下(即有可能产生歧义的地方)最好用带 {} 的表示方式。
BASH 中的变量既然不需要定义,也就没有类型一说,一个变量即可以被定义为一个字符串,也可以被再定义为整数。如果对该变量进行整数运算,他就被解释为整数;如果对他进行字符串操作,他就被看作为一个字符串。请看下面的例子:
#!/bin/bash
x=1999
let "x = $x + 1"
echo $x
x="olympic'"$x
echo $x
关于整数变量计算,有如下几种:" + - * / % ",他们的意思和字面意思相同。整数运算一般通过 let 和 expr 这两个指令来实现,如对变量 x 加 1 可以写作:let "x = $x + 1" 或者 x=`expr $x + 1`
在比较操作上,整数变量和字符串变量各不相同,详见下表:
对应的操作
整数操作
字符串操作
相同
-eq
=
不同
-ne
!=
大于
-gt
>
小于
-lt
<
大于或等于
-ge

小于或等于
-le

为空

-z
不为空

-n

比如:
比较字符串 a 和 b 是否相等就写作:if [ $a = $b ]
判断字符串 a 是否为空就写作: if [ -z $a ]
判断整数变量 a 是否大于 b 就写作:if [ $a -gt $b ]
更细致的文档推荐在字符串比较时尽量不要使用 -n ,而用 ! -z 来代替。(其中符号 "!" 表示求反操作)
BASH 中的变量除了用于对 整数 和 字符串 进行操作以外,另一个作用是作为文件变量。BASH 是 Linux 操作系统的 Shell,因此系统的文件必然是 BASH 需要操作的重要对象,如 if [ -x /root ] 可以用于判断 /root 目录是否可以被当前用户进入。下表列出了 BASH 中用于判断文件属性的操作符:
运算符
含义( 满足下面要求时返回 TRUE )
-e file
文件 file 已经存在
-f file
文件 file 是普通文件
-s file
文件 file 大小不为零
-d file
文件 file 是一个目录
-r file
文件 file 对当前用户可以读取
-w file
文件 file 对当前用户可以写入
-x file
文件 file 对当前用户可以执行
-g file
文件 file 的 GID 标志被设置
-u file
文件 file 的 UID 标志被设置
-O file
文件 file 是属于当前用户的
-G file
文件 file 的组 ID 和当前用户相同
file1 -nt file2
文件 file1 比 file2 更新
file1 -ot file2
文件 file1 比 file2 更老

注意:上表中的 file 及 file1、file2 都是指某个文件或目录的路径。
2.3.1.1. 关于局部变量
在 BASH 程序中如果一个变量被使用了,那么直到该程序的结尾,该变量都一直有效。为了使得某个变量存在于一个局部程序块中,就引入了局部变量的概念。BASH 中,在变量首次被赋初值时加上 local 关键字就可以声明一个局部变量,如下面这个例子:
#!/bin/bash
HELLO=Hello
function hello {
local HELLO=World
echo $HELLO
}
echo $HELLO
hello
echo $HELLO
该程序的执行结果是:
Hello
World
Hello
这个执行结果表明全局变量 $HELLO 的值在执行函数 hello 时并没有被改变。也就是说局部变量 $HELLO 的影响只存在于函数那个程序块中。
2.3.2. BASH 中的变量与 C 语言中变量的区别
这里我们为原来不熟悉 BASH 编程,但是非常熟悉 C 语言的程序员总结一下在 BASH 环境中使用变量需要注意的问题。
1,BASH 中的变量在引用时都需要在变量前加上 "$" 符号( 第一次赋值及在For循环的头部不用加 "$"符号 );
2,BASH 中没有浮点运算,因此也就没有浮点类型的变量可用;
3,BASH 中的整形变量的比较符号与 C 语言中完全不同,而且整形变量的算术运算也需要经过 let 或 expr 语句来处理;
2.4     BASH 中的基本流程控制语法
BASH 中几乎含有 C 语言中常用的所有控制结构,如条件分支、循环等,下面逐一介绍。
2.4.1 if...then...else
if 语句用于判断和分支,其语法规则和 C 语言的 if 非常相似。其几种基本结构为:
if [ expression ]
then
statments
fi
或者
if [ expression ]
then
statments
else
statments
fi
或者
if [ expression ]
then
statments
else if [ expression ]
then
statments
else
statments
fi
或者
if [ expression ]
then
statments
elif [ expression ]
then
statments
else
statments
fi
值得说明的是如果你将 if 和 then 简洁的写在一行里面,就必须在 then 前面加上分号,如:if [ expression ]; then ... 。下面这个例子说明了如何使用 if 条件判断语句:
#!/bin/bash

if [ $1 -gt 90 ]
then
echo "Good, $1"
elif [ $1 -gt 70 ]
then
echo "OK, $1"
else
echo "Bad, $1"
fi

exit 0
上面例子中的 $1 是指命令行的第一个参数,这个会在后面的“BASH 中的特殊保留字”中讲解。
2.4.2 for
for 循环结构与 C 语言中有所不同,在 BASH 中 for 循环的基本结构是:
for $var in

do
statments
done
其中 $var 是循环控制变量,
是 $var 需要遍历的一个集合,do/done 对包含了循环体,相当于 C 语言中的一对大括号。另外如果do 和 for 被写在同一行,必须在 do 前面加上 ";"。如: for $var in ; do 。下面是一个运用 for 进行循环的例子:
#!/bin/bash

for day in Sun Mon Tue Wed Thu Fri Sat
do
echo $day
done

# 如果列表被包含在一对双引号中,则被认为是一个元素
for day in "Sun Mon Tue Wed Thu Fri Sat"
do
echo $day
done

exit 0
注意上面的例子中,在 for 所在那行的变量 day 是没有加 "$" 符号的,而在循环体内,echo 所在行变量 $day 是必须加上 "$" 符号的。另外如果写成 for day 而没有后面的 in
部分,则 day 将取遍命令行的所有参数。如这个程序:
#!/bin/bash

for param
do
echo $param
done

exit 0
上面这个程序将列出所有命令行参数。for 循环结构的循环体被包含在 do/done 对中,这也是后面的 while、until 循环所具有的特点。
2.4.3 while
while 循环的基本结构是:
while [ condition ]
do
statments
done
这个结构请大家自己编写一个例子来验证。
2.4.4 until
until 循环的基本结构是:
until [ condition is TRUE ]
do
statments
done
这个结构也请大家自己编写一个例子来验证。
2.4.5 case
BASH 中的 case 结构与 C 语言中的 switch 语句的功能比较类似,可以用于进行多项分支控制。其基本结构是:
case "$var" in
condition1 )
statments1;;
condition2 )
statments2;;
...
* )
default statments;;
esac
下面这个程序是运用 case 结构进行分支执行的例子:
#!/bin/bash

echo "Hit a key, then hit return."
read Keypress

case "$Keypress" in
[a-z] ) echo "Lowercase letter";;
[A-Z] ) echo "Uppercase letter";;
[0-9] ) echo "Digit";;
* ) echo "Punctuation, whitespace, or other";;
esac

exit 0
上面例子中的第四行 "read Keypress" 一句中的 read 语句表示从键盘上读取输入。这个命令将在本讲义的 BASH 的其他高级问题中讲解。
2.4.6 break/continue
熟悉 C 语言编程的都很熟悉 break 语句和 continue 语句。BASH 中同样有这两条语句,而且作用和用法也和 C 语言中相同,break 语句可以让程序流程从当前循环体中完全跳出,而 continue 语句可以跳过当次循环的剩余部分并直接进入下一次循环。
2.5     函数的使用
BASH 是一个相对简单的脚本语言,不过为了方便结构化的设计,BASH 中也提供了函数定义的功能。BASH 中的函数定义很简单,只要向下面这样写就可以了:
function my_funcname {
code block
}
或者
my_funcname() {
code block
}
上面的第二种写法更接近于 C 语言中的写法。BASH 中要求函数的定义必须在函数使用之前,这是和 C 语言用头文件说明函数方法的不同。
更进一步的问题是如何给函数传递参数和获得返回值。BASH 中函数参数的定义并不需要在函数定义处就制定,而只需要在函数被调用时用 BASH 的保留变量 $1 $2 ... 来引用就可以了;BASH 的返回值可以用 return 语句来指定返回一个特定的整数,如果没有 return 语句显式的返回一个返回值,则返回值就是该函数最后一条语句执行的结果(一般为 0,如果执行失败返回错误码)。函数的返回值在调用该函数的程序体中通过 $? 保留字来获得。下面我们就来看一个用函数来计算整数平方的例子:
#!/bin/bash

square() {
let "res = $1 * $1"
return $res
}

square $1
result=$?
echo $result

exit 0

BASH 中的特殊保留字
保留变量
随机数
运算符
变量的特殊操作
3.1     保留变量
BASH 中有一些保留变量,下面列出了一些:

$IFS  这个变量中保存了用于分割输入参数的分割字符,默认识空格。
$HOME  这个变量中存储了当前用户的根目录路径。
$PATH  这个变量中存储了当前 Shell 的默认路径字符串。
$PS1  表示第一个系统提示符。
$PS2  表示的二个系统提示符。
$PWD  表示当前工作路径。
$EDITOR 表示系统的默认编辑器名称。
$BASH  表示当前 Shell 的路径字符串。
$0, $1, $2, ...
表示系统传给脚本程序或脚本程序传给函数的第0个、第一个、第二个等参数。
$#   表示脚本程序的命令参数个数或函数的参数个数。
$$   表示该脚本程序的进程号,常用于生成文件名唯一的临时文件。
$?   表示脚本程序或函数的返回状态值,正常为 0,否则为非零的错误号。
$*   表示所有的脚本参数或函数参数。
$@   和 $* 涵义相似,但是比 $* 更安全。
$!   表示最近一个在后台运行的进程的进程号。

3.2    随机数
随机数是经常要用到的,BASH 中也提供了这个功能,请看下面这个程序:
#!/bin/bash

# Prints different random integer from 1 to 65536

a=$RANDOM
echo $a

exit 0
这个程序可以在每次执行的时候随机的打印出一个大小在 1 到 65536 之间的整数。
3.3     运算符
算术运算符
+ - * / % 表示加减乘除和取余运算
+= -= *= /= 同 C 语言中的含义
位操作符
<< <<= >> >>= 表示位左右移一位操作
& &= | |= 表示按位与、位或操作
~ ! 表示非操作
^ ^= 表示异或操作
关系运算符
< > <= >= == != 表示大于、小于、大于等于、小于等于、等于、不等于操作
&& || 逻辑与、逻辑或操作
3.4     变量的特殊操作
BASH 中还有一些对变量的简洁、快速的操作,大家还记得 "${var}" 和 "$var" 同样是对变量的引用吧,对 ${var} 进行一些变化就可以产生一些新功能:
${var-default} 表示如果变量 $var 还没有设置,则保持 $var 没有设置的状态,并返回后面的默认值 default。
${var=default} 表示如果变量 $var 还没有设置,则取后面的默认值 default。
${var+otherwise} 表示如果变量 $var 已经设置,则返回 otherwise 的值,否则返回空( null )。
${var?err_msg} 表示如果变量 $var 已经设置,则返回该变量的值,否则将后面的 err_msg 输出到标准错误输出上。
请同学们自己尝试下面的例子:
#!/bin/bash

echo ${var?There is an error}

exit 0
还有下面一些用法,这些用法主要用于从文件路径字符串中提取有用信息:
${var#pattern}, ${var##pattern} 用于从变量 $var 中剥去最短(最长)的和 pattern 相匹配的最左侧的串。
${var%pattern}, ${var%%pattern} 用于从变量 $var 中剥去最短(最长)的和 pattern 相匹配的最右侧的串。
另外 BASH 2 中还加入下面一些操作:
${var:pos} 表示去掉变量 $var 中前 pos 个字符。
${var:pos:len} 表示变量 $var 中去掉前 pos 个字符后的剩余字符串的前 len 个字符。
${var/pattern/replacement} 表示将变量 $var 中第一个出现的 pattern 模式替换为 replacement 字符串。
${var//pattern/replacement} 表示将变量 $var 中出现的所有 pattern 模式全部都替换为 replacment 字符串。

BASH 中的其他高级问题
BASH 中对返回值的处理
用 BASH 设计简单用户界面
在 BASH 中读取用户输入
一些特殊的惯用法
BASH 程序的调试
关于 BASH2
4.1     BASH 中对返回值的处理
无论是在 Shell 中对 BASH 脚本返回值的处理,还是在脚本中对函数返回值的处理,都是通过 "$?" 系统变量来获得。BASH 要求返回值必须为一个整数,不能用 return 语句返回字符串变量。

4.2     用 BASH 设计简单用户界面
BASH 中提供了一个小的语句格式,可以让程序快速的设计出一个字符界面的用户交互选择的菜单,该功能就是由 select 语句来实现的,select 语句的语法为:
select var in

do
statments use $var
done
上面的语法结构在执行后,BASH 会将
中的所有项加上数字列在屏幕上等待用户选择,在用户作出选择后,变量 $var 中就包含了那个被选中的字符串,然后就可以对该变量进行需要的操作了。我们可以从下面的例子中更直观的来理解这个功能:
#!/bin/bash

OPTIONS="Hello Quit"
select opt in $OPTIONS; do
if [ "$opt" = "Quit" ]; then
echo done
exit
elif [ "$opt" = "Hello" ]; then
echo Hello World
else
clear
echo bad option
fi
done

exit 0
大家可以试着执行上面的程序,看看是什么执行结果。
4.3     在 BASH 中读取用户输入
BASH 中通过 read 函数来实现读取用户输入的功能,如下面这段程序:
#!/bin/bash

echo Please enter your name
read NAME
echo "Hi! $NAME !"

exit 0
上面这个脚本读取用户的输入,并回显在屏幕上。
另外 BASH 中还提供另外一种称为 here documents 的结构????,可以将用户需要通过键盘输入的字符串改为从程序体中直接读入,如密码。下面的小程序演示了这个功能:
#!/bin/bash

passwd="aka@tsinghua"
ftp -n localhost <<FTPFTP
user anonymous $passwd
binary
bye
FTPFTP

exit 0
这个程序在用户需要通过键盘敲入一些字符时,通过程序内部的动作来模拟键盘输入。请注意 here documents 的基本结构为:
command <<SOMESPECIALSTRING
statments
...
SOMESPECIALSTRING
这里要求在需要键盘输入的命令后,直接加上 <<符号,然后跟上一个特别的字符串,在该串后按顺序输入本来应该由键盘输入的所有字符,在所有需要输入的字符都结束后,重复一遍前面 <<符号后的“特别的字符串”即表示该输入到此结束。
4.4 一些特殊的惯用法
在 BASH 中 () 一对括号一般被用于求取括号中表达式的值或命令的执行结果,如:(a=hello; echo $a) ,其作用相当于 `...` 。
: 有两个含义,一是表示空语句,有点类似于 C 语言中的单个 ";" 。表示该行是一个空命令,如果被用在 while/until 的头结构中,则表示值 0,会使循环一直进行下去,如下例:
while :
do
operation-1
operation-2
...
operation-n
done
另外 : 还可以用于求取后面变量的值,比如:
#!/bin/bash

: ${HOSTNAME?} {USER?} {MAIL?}
echo $HOSTNAME
echo $USER
echo $MAIL

exit 0
在 BASH 中 export 命令用于将系统变量输出到外层的 Shell 中了。
4.5 BASH 程序的调试
用 bash -x bash-script 命令,可以查看一个出错的 BASH 脚本到底错在什么地方,可以帮助程序员找出脚本中的错误。
另外用 trap 语句可以在 BASH 脚本出错退出时打印出一些变量的值,以供程序员检查。trap 语句必须作为继 "#!/bin/bash" 后的第一句非注释代码,一般 trap 命令被写作: trap 'message $checkvar1 $checkvar2' EXIT 。
4.6 关于 BASH2
使用 bash -version 命令可以看出当前你正在使用的 BASH 是什么版本,一般版本号为1.14或其他版本。而现在机器上一般还安装了一个版本号为 2.0 的 BASH 。该 BASH 也在 /bin 目录下。BASH2 提供了一些新功能,有兴趣的同叙可以自己去看相关资料,或直接 man bash2 即可。

2013年1月24日星期四

Compile gcc-4.1.2 on Ubuntu 12.04 64bit


Compile gcc-4.1.2 on Ubuntu 12.04 64bit

ftp://ftp.gnu.org/gnu/gcc/gcc-4.1.2/

Create a folder where to build the gcc version

mkdir ~/gcc412

Extract the downloaded gcc release to that folder

tar -C ~/gcc412/ -xzvf /path/to/the/file/gcc-4.1.2.tar.gz

Make a build folder:

mkdir ~/gcc412/gcc-build/

At this stage we should be ready to build but first we need to be sure that we've got all the dependencies needed to compile. We need the linux-headers gcc-multilib, zlib1g, zlib1g-dev, zlibc

sudo apt-get install linux-headers-$(uname -r) gcc-multilib zlib1g zlib1g-dev zlibc

Then we need also to fix some file in the gcc folder.

First of all you need to edit the file ~/gcc412/gcc-4.1.2/gcc/config/i386/t-linux64.

At line 9 you should find:
MULTILIB_OSDIRNAMES = ../lib64 ../lib

You need to modify it as follows:
MULTILIB_OSDIRNAMES = ../lib64 ../lib32

Open the file ~/gcc412/gcc-4.1.2/libstdc++-v3/configure and at line 8284 replace:
sed -e 's/GNU ld version \([0-9.][0-9.]*\).*/\1/'`

with:

sed -e 's/GNU ld (GNU Binutils for Ubuntu) \([0-9.][0-9.]*\).*/\1/'`

Last thing to do is to link some library otherwise the compiler won't find it.
cd /usr/lib
sudo ln -s x86_64-linux-gnu/crt1.o crt1.o
sudo ln -s x86_64-linux-gnu/crti.o crti.o
sudo ln -s x86_64-linux-gnu/crtn.o crtn.o
At this point we are ready to compile gcc.
Go in the build folder:
cd ~/gcc412/gcc-build

run the configure as autodesk suggests:
../configure --prefix=/opt/gcc412 --program-suffix=412 --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --disable-libunwind-exceptions --enable-__cxa_atexit --enable-languages=c,c++

and then compile it:
make -j 2 bootstrap MAKEINFO=makeinfo

As soon as the compiling process ends without throwing errors you will be able to run:
sudo make install

Setup remote debug gdb/gdbserver



cd /usr/local
mkdir arm-gdb

#download and build gdb for host "http://ftp.gnu.org/gnu/gdb/"
cd gdb-6.7
./configure --target=arm-linux-uclibc --prefix=/usr/local/arm-gdb --disable-werror
make
make install

#build gdbserver for target
cd ./gdb/gdbserver

#before building the gdbserver for target, the target toolchain should be ready first
./configure --target=arm-linux-uclibc --host=arm-linux-uclibc --prefix=/usr/local/arm-gdb/gdbserver
export PATH=$PATH:/root/workspace/skyhawk_trunk/build_arm/staging_dir/bin
make CC=arm-linux-uclibc-gcc
make install

#if using uclibc and need debug multi-thread program, make sure the PTHREADS_DEBUG_SUPPORT is enabled when building the library

Using Eclipse Juno Service Release 1 (Setting for debug with gdb/gdbserver)
Debug Configurations > C/C++ Application > change to GDB(DSF) Remote System Process Launcher>
Main (Tab):
  • input the execute file which run on target
Debugger (Tab):
  • select the custom build gdb debugger
  • Force thread list update on suspend (turn on for showing source when break inside thread)
  • .gdbinit (in project root)
    • set solib-absolute-prefix "paths of the .so that used in the project" (path1:path2)
    • set solib-search-path "paths of the .so that used in the project" (path1:path2)
    • handle SIG32 pass noprint nostop
    • set can-use-hw-watchpoints 0
  • Shared Libraries: input the paths of .so that used in the project (search from first line)
  • Connection: set the ip and port of the target which gdbserver is running at
Source(Tab):
  • input the path of the source file, gdb search from the first line

#Debugging steps
cross compile the program for target (disable strip if it exist in makefile)

copy all updated execute and .so file to target

$./gdbserver ip:port execute  (on target)

then start Debug from eclipse

problem not solved yet:
  • Cannot 'step into' the function inside a .so, but it can pause at the breakpoint preset in the .so function, if compiled the execute with .a , 'step into' is ok
  • if set breakpoint inside a function of .so, need to set again in every session of debug

Reference:
http://crasseux.com/books/ctutorial/Building-a-library.html

http://blog.csdn.net/hlchou/article/details/7425492
http://www.study-area.org/cyril/opentools/opentools/x1265.html


Some useful commands:
arm-linux-uclibc-ar -t libmylib.a      -list out the functions name inside the static library
nm --demangle libmylib.a/libmylib.so      -list out the functions name inside the static/shared library

2013年1月20日星期日

TFTP server for Ubuntu 12.04


TFTP server for Ubuntu 12.04 source

用apt-get直接裝TFTP server套件。

    sudo apt-get install tftpd-hpa

Default options放在 /etc/default/tftp-hpa

# /etc/default/tftpd-hpa

TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/var/lib/tftpboot"
TFTP_ADDRESS="0.0.0.0:69"
TFTP_OPTIONS="--secure"

TFTP_DIRECTORY 是TFTP的檔案存放目錄.,我是將它改成rw for all,不知有沒有甚麼副作用?

    sudo chmod a+rw /var/lib/tftpboot

在Ubuntu上面直接裝的TFTP server套件不會在開機時執行,精確一點說應該是開機時跑了卻沒有起來。這是個老毛病,修一下就好。
編輯 /etc/init/tftpd-hpa.conf ,修改它錯誤的初始條件。

#start on runlevel [2345] # the original line which is wrong
start on (filesystem and net-device-up IFACE!=lo)

service tftpd-hpa restart

2013年1月18日星期五

install FreeNX on Ubuntu


How to install FreeNX on Ubuntu 12.04 Precise Pangolin source

Ask anyone who knows me and they will tell you that I am a vocal (and perhaps tireless) advocate of FOSS/GNU/Linux. I loves me some FOSS and GNU/Linux and I really like to help others with it as well!

So after writing my post: How to install NX Free Edition on Ubuntu 12.04 Precise Pangolin, I will follow it up with a story about FOSS. In this post, I will guide you through some easy instructions for installing the GPL,FOSS NX server variant called FreeNX, and the FOSS "qtnx" client that is used to connect to the FreeNX server.

The main difference is that, unlike NX Free Edition, which is licensed as proprietary ( 2 connections limit), FreeNX and qtnx are completely Free Open Source Software (FOSS, GPL)! Sounds good right? It's music to my ears. Afterall, this FOSS/GNU/Linux stuff makes the Technical world go around for everyone.

Update 01/02/13: Want to try something easier? Simply install xrdp:
1.) sudo apt-get install xrdp
2.) Add this to the ~ of the user you plan on logggin in with:
echo "gnome-session --session=ubuntu-2d" > ~/.xsession
3.) sudo /etc/init.d/xrdp restart
4.) Connect to your xrdp host from GNU/Linux with rdesktop , or from win with windows-key+R, mstsc /v:

So here are the simple instructions (and a couple of tweaks) that I used to install FreeNX on Ubuntu 12.04 GNU/Linux:

First, you need to add the freenx-team PPA for Ubuntu 12.04 GNU/Linux. Hit CTRL + ALT + t to get your Terminal, then type or paste in the command below, then hit Enter, then hit Enter to confirm the addition of the new source:
sudo add-apt-repository ppa:freenx-team
Next, update your sources list, then install the FreeNX server software (there are two commands below, the 2nd only runs if 1st is successful). After verifying that no important packages will beremoved, hit Y then enter to install FreeNX server:

sudo apt-get update && sudo apt-get install freenx

Next, as noted in the community documentation for installing FreeNX - download the missing nxsetup script, untar it, then copy it to /usr/lib/nx (the command below is one entire line that runs 3 commands and ends with /usr/lib/nx):
wget https://bugs.launchpad.net/freenx-server/+bug/576359/+attachment/1378450/+files/nxsetup.tar.gz && tar xvf nxsetup.tar.gz && sudo cp nxsetup /usr/lib/nx
Now, run the nxserver setup script. I chose to use the parameter to install the default NoMachine provided encryption keys during this command so the NoMachine win-clients can connect as well as qtnx:
sudo /usr/lib/nx/nxsetup --install --setup-nomachine-key
At this point, you have FreeNX server installed, but now you'll want to configure the FreeNX server to configure clients to use ubuntu-2d session:
echo -e "\n#Use unity 2d for client sessions\nCOMMAND_START_GNOME='gnome-session --session=ubuntu-2d'"|sudo tee -a /etc/nxserver/node.conf


Next, restart the FreeNX server to ensure it takes in the .conf file:
sudo /etc/init.d/freenx-server restart
That's it for the FreeNX server, now let's move on to the client. First, install the 'qtnx' package on Ubuntu 12.04 so we can have a client application to access the FreeNX server. You'll have to launch the Ubuntu Software Center, then click 'Edit > Software Sources' from the top menu. Then place a check by "Community-maintained free and open-source software (universe). Also, uncheck "Cdrom with Ubuntu 12.04" if it's checked. Then close the Software Sources dialog and the Ubuntu Software Center.
Now, run the commands to update your sources and install the qtnx application from the terminal (CTRL + ALT + t):
sudo apt-get update && sudo apt-get install qtnx
Ok, from the Unity menu or the CLI, start the 'qtnx' application. Enter the username/password for a user on the server, set the speed to LAN, then click configure. On the configuration dialog, set a name for the prfile, the hostname(or IP address) for the FreeNX server machine, the client resolution (I used 1024x768), network speed (LAN), and set the platform type to GNOME (see the example screenshot below).


Note: For the non-GNU/Linux clients, you can use nomachine.com's NX Client Free to connect to the server. Just use GNOME as the session.

That's it! Get your FreeNX connection on!
As for the version I tested with, it's: nxserver --version 3.2.0-74-SVN OS (GPL, using backend: 3.5.0)

Extra Tip: If for some reason your client wouldn't connect after testing, try deleting the entries in the ~/.nx directory. I'm not sure why this would help at all in most cases but it seemed to work for me. 



Extra Tip 2: If your clients are seeing the Network Manager 'Edit' buttons as greyed out while connected - have a look at this workaround: http://ubuntuforums.org/showthread.php?t=1616355




Install FreeNX server on Ubuntu 10.04 (Lucid), 10.10 (Maverick) or 11.04 (Natty)

  1. Enable the repository:
    sudo add-apt-repository ppa:freenx-team
  2. If you're using 10.10 Maverick, run
    sudo sed -i 's/maverick/lucid/g' /etc/apt/sources.list.d/freenx-team-ppa-maverick.list
  3. If you're using 11.04 Natty, run
    sudo sed -i 's/natty/lucid/g' /etc/apt/sources.list.d/freenx-team-ppa-natty.list
  4. Update the repository:
    sudo apt-get update
  5. Install FreeNX:
    sudo apt-get install freenx
  6. Download the setup script:
    cd /tmp;
    wget https://bugs.launchpad.net/freenx-server/+bug/576359/+attachment/1378450/+files/nxsetup.tar.gz;
    tar zxvf nxsetup.tar.gz
  7. Install the setup script:
    sudo cp nxsetup /usr/lib/nx/nxsetup
  8. Run the setup script:
    sudo /usr/lib/nx/nxsetup --install


2013年1月17日星期四

Using GCC to create static and shared library .so


[轉貼]用GCC自製Library source

引用: PTT看板: LinuxDev (作者: cole945)


Library可分成三種,static、shared與dynamically loaded。

1. Static libraries

Static 程式庫用於靜態連結,簡單講是把一堆object檔用ar(archiver)
包裝集合起來,檔名以 `.a' 結尾。優點是執行效能通常會比後兩者快,
而且因為是靜態連結,所以不易發生執行時找不到library或版本錯置而
無法執行的問題。缺點則是檔案較大,維護度較低;例如library如果發
現bug需要更新,那麼就必須重新連結執行檔。

1.1 編譯

編譯方式很簡單,先例用 `-c' 編出 object 檔,再用 ar 包起來即可。

____ hello.c ____
#include
void hello(){ printf("Hello "); }

____ world.c ____
#include
void world(){ printf("world."); }

____ mylib.h ____
void hello();
void world();

$ gcc -c hello.c world.c /* 編出 hello.o 與 world.o */
$ ar rcs libmylib.a hello.o world.o /* 包成 limylib.a */

這樣就可以建出一個檔名為 libmylib.a 的檔。輸出的檔名其實沒有硬性規定,
但如果想要配合 gcc 的 '-l' 參數來連結,一定要以 `lib' 開頭,中間是你要
的library名稱,然後緊接著 `.a' 結尾。

1.2 使用

____ main.c ____
#include "mylib.h"
int main() {
hello();
world();
}

使用上就像與一般的 object 檔連結沒有差別。

$ gcc main.c libmylib.a

也可以配合 gcc 的 `-l' 參數使用

$ gcc main.c -L. -lmylib

`-Ldir' 參數用來指定要搜尋程式庫的目錄,`.' 表示搜尋現在所在的目錄。
通常預設會搜 /usr/lib 或 /lib 等目錄。
`-llibrary' 參數用來指定要連結的程式庫 ,'mylib' 表示要與mylib進行連結
,他會搜尋library名稱前加`lib'後接`.a'的檔案來連結。

$ ./a.out
Hello world.


2. Shared libraries

Shared library 會在程式執行起始時才被自動載入。因為程式庫與執行檔
是分離的,所以維護彈性較好。有兩點要注意,shared library是在程式起始
時就要被載入,而不是執行中用到才載入,而且在連結階段需要有該程式庫
才能進行連結。

首先有一些名詞要弄懂,soname、real name與linker name。

soname 用來表示是一個特定 library 的名稱,像是 libmylib.so.1 。
前面以 `lib' 開頭,接著是該 library 的名稱,然後是 `.so' ,接著
是版號,用來表名他的介面;如果介面改變時,就會增加版號來維護相容度。

real name 是實際放有library程式的檔案名稱,後面會再加上 minor 版號與
release 版號,像是 libmylib.so.1.0.0 。

一般來說,版號的改變規則是(印象中在 APress-Difinitive Guide to GCC中有
提到,但目前手邊沒這本書),最尾碼的release版號用於程式內容的修正,
介面完全沒有改變。中間的minor用於有新增加介面,但相舊介面沒改變,所以
與舊版本相容。最前面的version版號用於原介面有移除或改變,與舊版不相容
時。

linker name是用於連結時的名稱,是不含版號的 soname ,如: libmylib.so。
通常 linker name與 real name是用 ln 指到對應的 real name ,用來提供
彈性與維護性。


2.1 編譯
shared library的製作過程較複雜。

$ gcc -c -fPIC hello.c world.c

編譯時要加上 -fPIC 用來產生 position-independent code。也可以用 -fpic
參數。 (不太清楚差異,只知道 -fPIC 較通用於不同平台,但產生的code較大
,而且編譯速度較慢)。

$ gcc -shared -Wl,-soname,libmylib.so.1 -o libmylib.so.1.0.0 \
hello.o world.o

-shared 表示要編譯成 shared library
-Wl 用於參遞參數給linker,因此-soname與libmylib.so.1會被傳給linker處理。
-soname用來指名 soname 為 limylib.so.1
library會被輸出成libmylib.so.1.0.0 (也就是real name)

若不指定 soname 的話,在編譯結連後的執行檔會以連時的library檔名為
soname,並載入他。否則是載入soname指定的library檔案。

可以利用 objdump 來看 library 的 soname。

$ objdump -p libmylib.so | grep SONAME
SONAME libmylib.so.1

若不指名-soname參數的話,則library不會有這個欄位資料。

在編譯後再用 ln 來建立 soname 與 linker name 兩個檔案。
$ ln -s libmylib.so.1.0.0 libmylib.so
$ ln -s libmylib.so.1.0.0 libmylib.so.1


2.2 使用

與使用 static library 同。

$ gcc main.c libmylib.so

以上直接指定與 libmylib.so 連結。

或用

$ gcc main.c -L. -lmylib

linker會搜尋 libmylib.so 來進行連結。

如果目錄下同時有static與shared library的話,會以shared為主。
使用 -static 參數可以避免使用shared連結。

$ gcc main.c -static -L. -lmylib

此時可以用 ldd 看編譯出的執行檔與shared程式庫的相依性
$ldd a.out
linux-gate.so.1 => (0xffffe000)
libmylib.so.1 => not found
libc.so.6 => /lib/libc.so.6 (0xb7dd6000)
/lib/ld-linux.so.2 (0xb7f07000)
輸出結果顯示出該執行檔需要 libmylib.so.1 這個shared library。
會顯示 not found 因為沒指定該library所在的目錄,所找不到該library。

因為編譯時有指定-soname參數為 libmylib.so.1 的關係,所以該執行檔會
載入libmylib.so.1。否則以libmylib.so連結,執行檔則會變成要求載入
libmylib.so

$ ./a.out
./a.out: error while loading shared libraries: libmylib.so.1:
cannot open shared object file: No such file or directory

因為找不到 libmylib.so.1 所以無法執行程式。
有幾個方式可以處理。

a. 把 libmylib.so.1 安裝到系統的library目錄,如/usr/lib下
b. 設定 /etc/ld.so.conf ,加入一個新的library搜尋目錄,並執行ldconfig
更新快取
c. 設定 LD_LIBRARY_PATH 環境變數來搜尋library
這個例子是加入目前的目錄來搜尋要載作的library
$ LD_LIBRARY_PATH=. ./a.out
Hello world.


3. Dynamically loaded libraries

Dynamicaaly loaded libraries 才是像 windows 所用的 DLL ,在使用到
時才載入,編譯連結時不需要相關的library。動態載入庫常被用於像plug-ins
的應用。

3.1 使用方式
動態載入是透過一套 dl function來處理。
#include
void *dlopen(const char *filename, int flag);
開啟載入 filename 指定的 library。
void *dlsym(void *handle, const char *symbol);
取得 symbol 指定的symbol name在library被載入的記憶體位址。
int dlclose(void *handle);
關閉dlopen開啟的handle。
char *dlerror(void);
傳回最近所發生的錯誤訊息。

____ dltest.c ____
#include
#include
#include
int main() {
void *handle;
void (*f)();
char *error;

/* 開啟之前所撰寫的 libmylib.so 程式庫 */
handle = dlopen("./libmylib.so", RTLD_LAZY);
if( !handle ) {
fputs( dlerror(), stderr);
exit(1);
}

/* 取得 hello function 的 address */
f = dlsym(handle, "hello");
if(( error=dlerror())!=NULL) {
fputs(error, stderr);
exit(1);
}
/* 呼叫該 function */
f();
dlclose(handle);
}

編譯時要加上 -ldl 參數來與 dl library 連結
$ gcc dltest.c -ldl
結果會印出 Hello 字串
$ ./a.out
Hello

關於dl的詳細內容請參閱 man dlopen

--
參考資料:

Creating a shared and static library with the gnu compiler [gcc]
http://www.adp-gmbh.ch/cpp/gcc/create_lib.html

Program Library HOWTO
http://tldp.org/HOWTO/Program-Library-HOWTO/index.html

APress - Definitive Guide to GCC

------------------------------------------------------------------------------------------------------------------

Makefile



GCC=gcc  
  CFLAGS=-Wall   -ggdb   -fPIC  
  #CFLAGS=  
   
  all:   libfunc  
   
  libfunc:func.o   func1.o  
                  $(GCC)   -shared   -Wl,-soname,libfunc.so.1   -o   libfunc.so.1.1   $<  
                  ln   -sf   libfunc.so.1.1   libfunc.so.1  
                  ln   -sf   libfunc.so.1   libfunc.so  
   
  %.o:%.c  
                  $(GCC)   -c   $(CFLAGS)   -o   $@   $<  
   
  clean:  
                  rm   -fr   *.o  
                  rm   -fr   *.so*

-------------------------------------------------------------------------------------------------------------------

2013年1月3日星期四

setup git + gitolite + gitweb, add user + repo


Workstation setup

#Install ssh
$sudo apt-get install openssh-server

#Install git-core
$sudo apt-get install python-software-properties 
$sudo add-apt-repository ppa:git-core/ppa 
$sudo apt-get update 
$sudo apt-get install git

$git config --global user.email "Email"

$git config --global user.name "Your Name"


$ssh-keygen -t rsa (and rename ~/.ssh/id_rsa.pub to YourName.pub )

#send YourName.pub to the person which have admin rights to the git server



Git Server Setup

#Adduser git
$sudo useradd git
#edit /etc/sudoers, add user 'git' as sudoer

#Install ssh
$sudo apt-get install openssh-server

#Install git-core
$sudo apt-get install python-software-properties 
$sudo add-apt-repository ppa:git-core/ppa 
$sudo apt-get update 
$sudo apt-get install git

#Install gitolite 
Make sure ~/.ssh/authorized_keys is empty or non-existent



Run this from your workstation (this workstation will have admin rights of the git server)

$ssh-keygen -t rsa (and rename ~/.ssh/id_rsa.pub to YourName.pub )

copy the ssh public key from your workstation to git server $HOME/YourName.pub 

Run the following commands:

git clone git://github.com/sitaramc/gitolite
mkdir -p $HOME/bin
gitolite/install -to $HOME/bin
gitolite setup -pk YourName.pub

If the last command doesn't run perhaps 'bin' in not in your 'PATH'.

You can either add it (add this line to ~/.bashrc), or just run:

$HOME/bin/gitolite setup -pk YourName.pub


Adding Users and repos

Do NOT add new repos or users manually on the server.  Gitolite users, repos, and access rules are maintained by making changes to a special repo called 'gitolite-admin' and pushing those changes to the server.


To administer your gitolite installation, start by doing this on your workstation, which this workstation have added public key to git server and have RW access rights to 'gitolite-admin' repo


$git clone git@host:gitolite-admin


**NOTE**: if you are asked for a password, something has gone wrong. 

(check if this workstation's ssh public key is added to the git server or not)

Now if you 'cd gitolite-admin', you will see two subdirectories in it: 'conf' and 'keydir'.

To add new users alice, bob, and carol, obtain their public keys and add them to 'keydir' as alice.pub, bob.pub, and carol.pub respectively.

    To add a new repo 'foo' and give different levels of access to these

    users, edit the file 'conf/gitolite.conf' and add lines like this:

        repo foo

            RW+         =   alice
            RW          =   bob
            R           =   carol

    See the 'ACCESS RULES' section later for more details.


    Once you have made these changes, do something like this:


        $git add conf

        $git add keydir
        $git commit -m 'added foo, gave access to alice, bob, carol'
        $git push

When the push completes, gitolite will add the new users to ~/.ssh/authorized_keys on the server, as well as create a new, empty, repo called 'foo'.


Install gitweb

$apt-get install apache2 gitweb

$sudo vim /etc/gitweb.conf
change $projectroot to /home/git/repositorieschange $projects_list to /home/git/projects.list
$chmod g+r /home/git/projects.list
$chmod -R g+rx /home/git/repositories

$sudo vim /home/git/.gitolite.rc
# change $REPO_UMASK = 0077; to $REPO_UMASK = 0027; # gets you 'rwxr-x---'

#add authentication to the web
$sudo htpasswd -cb /etc/appach2/gitweb.htpasswd ID PASSWD


$sudo vim /etc/apache2/conf.d/gitweb
Alias /gitweb /usr/share/gitweb<Directory /usr/share/gitweb>Options FollowSymLinks +ExecCGIAddHandler cgi-script .cgiAuthUserFile /etc/apache2/gitweb.htpasswdAuthName "GitWeb"AuthType Basicrequire valid-user</Directory>
$sudo /etc/init.d/apache2 restart #restart to take effect

Vsftpd on ubuntu server 12.04


Vsftpd on ubuntu server 12.04

After install vsftpd, then sudo rm /etc/pam.d/vsftpd (fix the 530 error)

500 OOPS: vsftpd: refusing to run with writable root inside chroot ()
Vsftpd v2.3.5 restriction, when chroot() is enable for the user, the user cannot have write permission to its root.

So downgrade to v2.3.2 instead. Use below link.

Loading Modules on Demand


Loading Modules on Demand
To make it easier for users to load and unload modules, to avoid wasting kernel memory by keeping drivers in core when they are not in use, and to allow the creation of "generic'' kernels that can support a wide variety of hardware, Linux offers support for automatic loading and unloading of modules. To exploit this feature, you need to enable kmod support when you configure the kernel before you compile it; most kernels from distributors come with kmod enabled. This ability to request additional modules when they are needed is particularly useful for drivers using module stacking.

Setup new gitolite user and repo

Setup new gitolite user 
#Create the user's public key
  • $ssh-keygen -t rsa (default output path ~/.ssh/id_rsa.pub, rename it to new_user.pub)
  • git config --global user.email "Email"
  • git config --global user.name "Your Name"

#Add the key to gitolite (gitolite recommends to use method 2)
  • Method 1 - server side (@git~)
    • copy new_user.pub to server's /home/git/publickeys
    • $gitolite setup -pk publickeys/new_user.pub

  • Method 2 - client side (a client which is already added to the git server, and have read rights to gitolite-admin)
    • $git clone git@git.actatekrd.com:gitolite-admin
    • $git add keydir/new_user.pub
    • $git commit -m 'add users: new_user'
    • $git push
#Remove a user
  • $git rm keydir/new_user.pub



Setting up a remote repository and doing an initial “push”
#Create the new repo directory 

ssh to git server (from a client which had NOT added key git server) 
$ssh git@git.actatekrd.com (password:git)

or direct run the below commands on git server
  • $cd /home/git/repositories
  • $mkdir my_project.git
  • $cd my_project.git
  • $git init --bare
  • $git update-server-info (If planning to serve via HTTP)
#Add access rights of the new repo to users
From a client which is already added to the git server and have read rights to gitolite-admin
  • git clone git@git.actatekrd.com:gitolite-admin
  • edit gitolite-admin/conf/gitolite.conf, add access rights of the new repo to users
#Initial push from client(From a client which has RW rights to the new repo)
  • cd my_project
  • git init
  • git add *
  • git commit -m "My initial commit message"
  • git remote add origin git@git.actatekrd.com:my_project.git
  • git push -u origin master
(Optional) can use 'git'  to replace 'git@git.actateked.com' in the future
create ‘config’ file under ~/.ssh
host git
user git
hostname git.actatekrd.com

2013年1月2日星期三

about origen quad

http://wiki.ebuntu.co.kr/mediawiki/index.php/Ubuntu_for_4412

http://git.linaro.org/gitweb/landing-teams/working/samsung/kernel.git

http://forum.insignal.co.kr/viewtopic.php?f=6&t=67


# install jdk
http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html


mv jdk1.7.0_11 /usr/lib/jvm

update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/jdk1.7.0_11/bin/javac 1
update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk1.7.0_11/bin/java 1
update-alternatives --install /usr/bin/javaws javaws /usr/lib/jvm/jdk1.7.0_11/bin/javaws 1

update-alternatives --config javac
update-alternatives --config java
update-alternatives --config javaws

java -version

2013年1月1日星期二

acta3 original compile

apt-get install subversion
rm ~/.gnome2/keyrings/login.keyring   "svn和gnome的一个key冲突了,只要删除就可以了"

svn co http://svn.actatekrd.com/svn/skyhawk/trunk/ skyhawk_trunk
svn co http://svn.actatekrd.com/svn/skyhawk/downloads/ skyhawk_downloads

cd skyhawk_trunk
ln -s ../skyhawk_downloads dl
cp SKYHAWK_buildroot.config .config

#for ubuntu 10.04
apt-get install gcc-4.1
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.4 40
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.1 30
update-alternatives --config gcc

apt-get install git-core gnupg flex bison gperf build-essential zip curl zlib1g-dev libc6-dev lib32ncurses5-dev ia32-libs x11proto-core-dev libx11-dev lib32readline5-dev lib32z-dev libgl1-mesa-dev g++-multilib mingw32 tofrodos python-markdown libxml2-utils xsltproc

getlibs package/config/conf
https://launchpad.net/~jcollins/+archive/jaminppa/+build/1482994/+files/getlibs_2.06-0ubuntu1~ppa2_all.deb

make oldconfig

apt-get install gettext texinfo uboot-mkimage intltool

make

Install and Setup Gcc 4.1(g++4.1) in Ubuntu 10.04


How to Install and Setup Gcc 4.1(g++4.1) in Ubuntu 10.04/10.10

Published in Posted by Meilin in General


The default gcc version in Ubuntu 10.04 LTS and 10.10 is gcc 4.4. However some programs depend on gcc 4.1.1 or gcc 4.1.2. Here? how to install and setup gcc 4.1.3 in Ubuntu 10.04/10.10 which also works if one program need gcc 4.1.1/4.1.2 to run.

Install Gcc 4.1

gcc 4.1.3 is available default in Ubuntu 10.04/10.10 official repository, just run the command in Applications -> Accessories -> Terminal:

sudo apt-get install gcc-4.1
If you absolutely want gcc 4.1.1, refer to ubuntuforums.org.

Set gcc 4.1 as default version

After that, use this command to list installed gcc in your Ubuntu:

ls /usr/bin/gcc*
and use this command to check which is default:

gcc -v
1.) Now, add alternatives for gcc:

sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.4 40
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.1 30
run this command which lists available versions, then type a number to set default.

sudo update-alternatives --config gcc
There are 2 choices for the alternative gcc (providing /usr/bin/gcc).

Selection Path Priority Status

0 /usr/bin/gcc-4.4 40 auto mode
* 1 /usr/bin/gcc-4.1 30 manual mode
2 /usr/bin/gcc-4.4 40 manual mode

2.) add alternatives for g++, i486-linux-gnu-gcc, and i486-linux-gnu-g++ and set 4.1 as default:

update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.4 40
update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.1 30
update-alternatives --config g++
update-alternatives --install /usr/bin/i486-linux-gnu-gcc i486-linux-gnu-gcc /usr/bin/i486-linux-gnu-gcc-4.4 40
update-alternatives --install /usr/bin/i486-linux-gnu-gcc i486-linux-gnu-gcc /usr/bin/i486-linux-gnu-gcc-4.1 30
update-alternatives --config i486-linux-gnu-gcc
update-alternatives --install /usr/bin/i486-linux-gnu-g++ i486-linux-gnu-g++ /usr/bin/i486-linux-gnu-g++-4.4 40
update-alternatives --install /usr/bin/i486-linux-gnu-g++ i486-linux-gnu-g++ /usr/bin/i486-linux-gnu-g++-4.1 30
update-alternatives --config i486-linux-gnu-g++

source

2012年12月30日星期日

Useful command / sites

export LD_LIBRARY_PATH=/lib

ldd       #find dependencies

getlibs   #auto install missing libraries
https://launchpad.net/~jcollins/+archive/jaminppa/+build/1482994/+files/getlibs_2.06-0ubuntu1%7Eppa2_all.deb

du  #Size of a directory & Free disk space
source

ps -eO user -H

will show the process tree along with the process status (to determine if it is a zombie -Z- or an uninterruptable sleep -D-)

strace -p pid

pstree

xkill

要強制手動釋放或清除Linux中的Cache Memory可以使用下面的指令
echo 3 > /proc/sys/vm/drop_caches

https://code.google.com/p/nekodrive/    (Mount NFS on windows)

http://tldp.org/LDP/tlk/ipc/ipc.html    IPC

http://tw.myblog.yahoo.com/hughes-blog/article?mid=105   IPC - fork and shared memory

http://developer.android.com/tools/help/proguard.html    Android, prevent reverse engineering

Solve ^M problem
If you get a “dos2unix: not found” error on Ubuntu 10.04 Lucid Lynx, there is the solution:
Install a “tofrodos” package and add two symlinks
root@ramune:/usr/bin# ln -s fromdos dos2unix
root@ramune:/usr/bin# ln -s todos unix2dos