关于Eclipse的一些想法

Eclipse的使用确实可将提高我们的效率,但是不要将所有要做的工作都想集成到eclipse上去,一方面是会使得eclipse很臃肿,启动很慢;另一方面会出现一些莫名其妙的问。
所以只要有独立的运行程序,尽量不要找相应的插件来。
以ant为例,我在eclipse3.4下使用ant1.7运行编写好的build文件,但是一个target死活运行不了,同时eclipse占用CPU 50%;
但是我在命令行下,运行同样的脚本,完全可以正常运行。

内存管理

以下内容摘自高质量C++/C编程指南(作者 林锐 ),之所以放在这里,只是为了以后查找方便。

内存分配方式有三种:

(1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量, static 变量。

(2)
在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

(3) 从堆上分配,亦称动态内存分配。程序在运行的时候用 malloc 或 new 申请任意多少的内存,程序员自己负责在何时用 free 或
delete 释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。

常见的内存错误及其对策

发生内存错误是件非常麻烦的事情。编译器不能自动发现这些错误,通常是在程序运行时才能捕捉到。而这些错误大多没有明显的症状,时隐时现,增加了改错的难度。有时用户怒气冲冲地把你找来,程序却没有发生任何问题,你一走,错误又发作了。

常见的内存错误及其对策如下:

@ 内存分配未成功,却使用了它。

编程新手常犯这种错误,因为他们没有意识到内存分配会不成功。常用解决办法是,在使用内存之前检查指针是否为 NULL 。如果指针 p
是函数的参数,那么在函数的入口处用 assert(p!=NULL) 进行检查。如果是用 malloc 或 new 来申请内存,应该用
if(p==NULL) 或 if(p!=NULL) 进行防错处理。

@ 内存分配虽然成功,但是尚未初始化就引用它。

犯这种错误主要有两个起因:一是没有初始化的观念;二是误以为内存的缺省初值全为零,导致引用初值错误(例如数组)。

内存的缺省初值究竟是什么并没有统一的标准,尽管有些时候为零值,我们宁可信其无不可信其有。所以无论用何种方式创建数组,都别忘了赋初值,即便是赋零值也不可省略,不要嫌麻烦。

@ 内存分配成功并且已经初始化,但操作越过了内存的边界。

例如在使用数组时经常发生下标“多 1 ”或者“少 1 ”的操作。特别是在 for 循环语句中,循环次数很容易搞错,导致数组操作越界。

@ 忘记了释放内存,造成内存泄露。

含有这种错误的函数每被调用一次就丢失一块内存。刚开始时系统的内存充足,你看不到错误。终有一次程序突然死掉,系统出现提示:内存耗尽。

动态内存的申请与释放必须配对,程序中 malloc 与 free 的使用次数一定要相同,否则肯定有错误( new/delete 同理)。

@ 释放了内存却继续使用它。

有三种情况:

( 1 )程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面。

( 2 )函数的 return 语句写错了,注意不要返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被自动销毁。

( 3 )使用 free 或 delete 释放了内存后,没有将指针设置为 NULL 。导致产生“野指针”。

我们发现指针有一些“似是而非”的特征:

( 1 )指针消亡了,并不表示它所指的内存会被自动释放。



2 )内存被释放了, 并不表示指针会消亡或者成了 NULL 指针。



这表明释放内存并不是一件可以草率对待的事。也许有人不服气,一定要找出可以草率行事的理由:

如果程序终止了运行,一切指针都会消亡,动态内存会被操作系统回收。既然如此,在程序临终前,就可以不必释放内存、不必将指针设置为 NULL
了。终于可以偷懒而不会发生错误了吧?

想得美。如果别人把那段程序取出来用到其它地方怎么办?

指针与数组的对比

C++/C 程序中,指针和数组在不少地方可以相互替换着用,让人产生一种错觉,以为两者是等价的。

数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变。

指针可以随时指向任意类型的内存块,它的特征是“可变”,所以我们常用指针来操作动态内存。指针远比数组灵活,但也更危险。

下面以字符串为例比较指针与数组的特性。

7.3.1 修改内容



示例 7-3-1 中,字符数组 a 的容量是 6 个字符,其内容为 hello/0 。 a 的内容可以改变,如 a[0]= ‘X’
。指针 p 指向常量字符串“ world ”(位于静态存储区,内容为 world/0
),常量字符串的内容是不可以被修改的。从语法上看,编译器并不觉得语句 p[0]= ‘X’ 有什么不妥,但是该语句企图修改常量字符串的内容而导致运行错误。

char a[] = “ hello ” ;

a[0] = ‘ X ’ ;

cout << a << endl;

char *p = “ world ” ; // 注意 p 指向常量字符串

p[0] = ‘ X ’ ; // 编译器不能发现该错误

cout << p << endl;


示例 7-3-1 修改数组和指针的内容

7.3.2 内容 复制与比较



不能对数组名进行直接复制与比较。示例 7-3-2 中,若想把数组 a 的内容复制给数组 b ,不能用语句 b = a
,否则将产生编译错误。应该用标准库函数 strcpy 进行复制。同理,比较 b 和 a 的内容是否相同,不能用 if(b==a)
来判断,应该用标准库函数 strcmp 进行比较。

语句 p = a 并不能把 a 的内容复制指针 p ,而是把 a 的地址赋给了 p 。要想复制 a 的内容,可以先用库函数
malloc 为 p 申请一块容量为 strlen(a)+1 个字符的内存,再用 strcpy 进行字符串复制。同理,语句 if(p==a)
比较的不是内容而是地址,应该用库函数 strcmp 来比较。

// 数组 …

char a[] = “hello”;

char b[10];

strcpy(b, a); // 不能用 b = a;

if(strcmp(b, a) == 0) // 不能用 if (b == a)


// 指针 …

int len = strlen(a);

char p = (char )malloc(sizeof(char)*(len+1));

strcpy(p,a); // 不要用 p = a;

if(strcmp(p, a) == 0) // 不要用 if (p == a)

示例 7-3-2 数组和指针的内容复制与比较







7.3.3 计算内存容量



用运算符 sizeof 可以计算出数组的容量(字节数)。示例 7-3-3 ( a )中, sizeof(a) 的值是 12 (注意别忘了
’ /0 ’ )。指针 p 指向 a ,但是 sizeof(p) 的值却是 4 。这是因为 sizeof(p)
得到的是一个指针变量的字节数,相当于 sizeof(char*) ,而不是 p 所指的内存容量。 C++/C
语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。

注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。 示例 7-3-3 ( b )中,不论数组 a 的容量是多少,
sizeof(a) 始终等于 sizeof(char *) 。

char a[] = “hello world”;

char *p = a;

cout << sizeof(a) << endl; // 12 字节

cout<< sizeof(p) << endl; // 4 字节


示例 7-3-3 ( a ) 计算数组和指针的内存容量

void Func(char a[100])

{

cout<< sizeof(a) << endl; // 4 字节而不是 100 字节

}


示例 7-3-3 ( b ) 数组退化为指针

杜绝“野指针”

“野指针”不是 NULL 指针,是指向“垃圾”内存的指针。人们一般不会错用 NULL 指针,因为用 if
语句很容易判断。但是“野指针”是很危险的, if 语句对它不起作用。

“野指针”的成因主要有两种:

( 1 )指针变量没有被初始化。任何指针变量刚被创建时不会自动成为 NULL
指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为 NULL ,要么让它指向合法的内存。例如

char *p = NULL;

char str = (char ) malloc(100);

( 2 )指针 p 被 free 或者 delete 之后,没有置为 NULL ,让人误以为 p 是个合法的指针。参见 7.5
节。

( 3 )指针操作超越了变量的作用范围。这种情况让人防不胜防,示例程序如下:

class A

{

public:

void Func(void){ cout << “ Func of class A ” << endl; }

};

void Test(void)

{

A *p;

{

A a;

p = &a; // 注意 a 的生命期

}

p->Func(); // p 是“野指针”

}

函数 Test 在执行语句 p->Func() 时 ,对象 a 已经消失,而 p 是指向 a 的,所以 p 就成了
“野指针”。但奇怪的是我运行这个程序时居然没有出错,这可能与编译器有关。

引用与指针的比较

引用是 C++ 中的概念,初学者容易把引用和指针混淆一起。一下程序中, n 是 m 的一个引用( reference ), m
是被引用物( referent )。

int m;

int &n = m;

n 相当于 m 的别名(绰号),对 n 的任何操作就是对 m
的操作。例如有人名叫王小毛,他的绰号是“三毛”。说“三毛”怎么怎么的,其实就是对王小毛说三道四。所以 n 既不是 m 的拷贝,也不是指向 m
的指针,其实 n 就是 m 它自己。

引用的一些规则如下:

( 1 )引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。

( 2 )不能有 NULL 引用,引用必须与合法的存储单元关联(指针则可以是 NULL )。

( 3 )一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。

以下示例程序中, k 被初始化为 i 的引用。语句 k = j 并不能将 k 修改成为 j 的引用,只是把 k 的值改变成为 6
。由于 k 是 i 的引用,所以 i 的值也变成了 6 。

int i = 5;

int j = 6;

int &k = i;

k = j; // k 和 i 的值都变成了 6;

上面的程序看起来象在玩文字游戏,没有体现出引用的价值。引用的主要功能是传递函数的参数和返回值。 C++
语言中,函数的参数和返回值的传递方式有三种:值传递、指针传递和引用传递。

以下是“值传递”的示例程序。由于 Func1 函数体内的 x 是外部变量 n 的一份拷贝,改变 x 的值不会影响 n, 所以 n
的值仍然是 0 。

void Func1(int x)

{

x = x + 10;

}

int n = 0;

Func1(n);

cout << “n = ” << n << endl; // n = 0

以下是“指针传递”的示例程序。由于 Func2 函数体内的 x 是指向外部变量 n 的指针,改变该指针的内容将导致 n 的值改变,所以 n
的值成为 10 。

void Func2(int *x)

{

( x) = ( x) + 10;

}

int n = 0;

Func2(&n);

cout << “n = ” << n << endl; // n = 10

以下是“引用传递”的示例程序。由于 Func3 函数体内的 x 是外部变量 n 的引用, x 和 n 是同一个东西,改变 x
等于改变 n ,所以 n 的值成为 10 。

void Func3(int &x)

{

x = x + 10;

}

int n = 0;

Func3(n);

cout << “n = ” << n << endl; // n = 10

对比上述三个示例程序,会发现“引用传递”的性质象“指针传递”,而书写方式象“值传递”。实际上“引用”可以做的任何事情“指针”也都能够做,为什么还要“引用”这东西?

答案是“用适当的工具做恰如其分的工作”。

指针能够毫无约束地操作内存中的如何东西,尽管指针功能强大,但是非常危险。就象一把刀,它可以用来砍树、裁纸、修指甲、理发等等,谁敢这样用?

如果的确只需要借用一下某个对象的“别名”,那么就用“引用”,而不要用“指针”,以免发生意外。比如说,某人需要一份证明,本来在文件上盖上公章的印子就行了,如果把取公章的钥匙交给他,那么他就获得了不该有的权利。

C++ 拷贝构造函数的调用

拷贝构造函数主要在以下三种情况下起初始化作用:

1. 在声明语句中用一个对象初始化另一个对象;

2. 将一个对象作为参数按值调用方式传递给另一个对象时生成对象副本;

3. 生成一个临时对象作为函数的返回结果。(这种情况只有在windwos下是真的,我只是在windos xp + VC 6.0)

以下面的代码为例:

  1. // Tt.cpp : Defines the entry point for the console application.
  2. //
  3. #include <iostream.h>

  4. class CopyClassTest{

  5. public :
  6. CopyClassTest(){
  7. cout << “Constructor” << endl;
  8. }
  9. CopyClassTest(CopyClassTest const &a){

  10. cout << “Copy Constructor” << endl;
  11. }
  12. };
  13. void processCall(CopyClassTest a){

  14. cout << “process call 1” << endl;
  15. }
  16. CopyClassTest processCall2(){

  17. cout << “process call 2” << endl;
  18. CopyClassTest temp;
  19. return temp;
  20. }
  21. int main(){

  22. CopyClassTest one;
  23. CopyClassTest two = one;
  24. processCall(one);

  25. processCall2();
  26. return 0;
  27. }

在windows xp + VC 6.0的环境下,结果是:
Constructor //对应的是main函数中的 CopyClassTest one;
Copy Constructor//对应的是main函数中的 CopyClassTest two = one;
Copy Constructor//对应的是main函数中的 processCall(one); 函数调用要生成一个临时变量,所以调用了copy
constructor
process call 1
process call 2
Constructor//对应的是 processCall2中的 CopyClassTest temp;
Copy Constructor//对应的是 processCall2中的 return temp;

在Linux localhost.localdomain 2.6.9-78.ELsmp #1 SMP Wed Jul 9 15:39:47 EDT 2008
i686 i686 i386 GNU/Linux + gcc version 3.4.6 20060404 (Red Hat
3.4.6-10)环境下,结果是:
Constructor
Copy Constructor
Copy Constructor
process call 1
process call 2
Constructor

Test/Test2/Test3/Test4(内存问题)

  1. #include <stdio.h>
  2. #include <malloc.h>
  3. #include <string.h>
  4. #include <iostream.h>
  5. /*
    • Test.cpp
  6. *
    • Created on: Oct 22, 2008
    • Author: root
  7. */
    1. char *GetMemory( void ) {
  8. char p[] = “hello world” ;
  9. return p;
  10. }
    1. void Test( void ) {
  11. char *str = NULL;
  12. str = GetMemory();
  13. printf(str);
  14. }
    1. void GetMemory2( char *p) {
  15. p = ( char *) malloc(100);
  16. }
    1. void Test2( void ) {
  17. char *str = NULL;
  18. GetMemory2(str);
  19. strcpy(str, “hello world” );
  20. printf(str);
  21. }
    1. void Test3( void ) {
  22. char str = ( char ) malloc(100);
  23. strcpy(str, “hello” );
  24. free(str);
  25. if (str != NULL){
  26. strcpy(str, “world” );
  27. printf(str);
  28. }
  29. }
    1. void GetMemory4( char **p, int num) {
  30. p = ( char ) malloc(num);
  31. }
    1. void Test4( void ) {
  32. char *str = NULL;
  33. GetMemory4(&str, 100);
  34. strcpy(str, “hello” );
  35. printf(str);
  36. }
  37. void testSizeOf(){
  38. cout << endl << “ sizeof(char) “ << sizeof ( char ) << endl;
  39. cout << “ ssizeof(int) “ << sizeof ( int ) << endl;
  40. cout << “ sizeof(unsigned int) “ << sizeof (unsigned int ) << endl;
  41. cout << “ sizeof(long) “ << sizeof ( long ) << endl;
  42. cout << “ sizeof(unsigned long) “ << sizeof (unsigned long ) << endl;
  43. cout << “ sizeof(float) “ << sizeof ( float ) << endl;
  44. cout << “ sizeof(double) “ << sizeof ( double ) << endl;
  45. cout << “ sizeof(void ) “ << sizeof ( void ) << endl;
  46. }
  47. int main() {
  48. //Test();
  49. //Test2();
  50. //Test3();

  51. Test4();

  52. testSizeOf();
  53. return 0;
  54. }

上面的代码来自高质量C++/C编程指南。

Test()的运行结果是未知,因为GetMemory返回的是指向“栈内存”的指针,该指针的地址不是
NULL,但其原现的内容已经在函数退出后被清除,新内容不可知。

Test2()的运行结果是程序崩溃。因为GetMemory2并不能传递动态内存(编译器总是要为函数的每个参数制作临时副本,指针参数p的副本是 _p,编译器使
_p = p。传入的是一个指针,在本例中,_p申请了新的内存,只是把_p所指的内存地址改变了,但是p丝毫未变),Test函数中的 str一直都是
NULL。strcpy(str, “hello world”);将使程序崩溃。

Test4 ()的运行结果是hello。因为传入的是指针的指针,所以_p的改变,也就是改变传入的参数,故str被正确的复制。

Test3()的运行结果是输出world。篡改动态内存区的内容,后果难以预料,非常危险。因为free(str);之后,str成为野指针,但是str并没有置成空,所以if(str
!= NULL)语句不起作用。

Linux's boot process explained

Short history of the UNIX operating system

Linux is an implementation of the UNIX operating system concept. UNIX was
derived from AT&T’s ”Sys V” (System 5). The initialization process is meant to
control the starting and ending of services and/or daemons in a system, and
permits different start-up configurations on different execution levels (”run
levels”).

Some Linux distribution, like SlackWare, use the BSD init system, developed at
the University of California, Berkeley.

Sys V uses a much more complex set of command files and directives to
determine which services are available at different levels of execution, than
the BSD’s do.

Booting the Linux operating system

The first thing a computer does on start-up is a primer test (POST - Power On
Self Test). This way several devices are tested, including the processor,
memory, graphics card and the keyboard. Here is tested the boot medium (hard
disk, floppy unit, CD-ROMs). After POST, the loader from a ROM loads the boot
sector, which in turn loads the operating system from the active partition.

The boot blocks is always at the same place: track 0, cylinder 0, head 0 of
the device from which we’re booting. This block contains a program called
loader, which in Linux’s case is LiLo (Linux Loader), or Grub (GNU Grub
Unified Boot Loader), which actually boots the operating system. These loaders
in Linux , in case of a multi-boot configuration (more operating systems on a
computer), permit the selection of the operating system to be booted. Lilo and
Grub are installed or at the MBR (Master Boot Record), or at the first sector
of the active partition.

In the following we will refer to LiLO as boot loader. This is usually
installed in the boot sector, also known as MBR. If the user decides to boot
Linux, LiLo will try to load the kernel. Now I will present step-by-step
LiLo’s attempt to load the operating system.

1. In case of a multi-boot config, LiLo permits the user two choose an
operating system from the menu. The LiLo settings are stored at
/etc/lilo.conf. System administrators use this file for a very detailed
finement of the loader. Here can be manually set what operating systems are
installed, as well as the method for loading any of them. If on the computer
there is only Linux, LiLo can be set to load directly the kernel, and skip the
selection menu.

2. The Linux kernel is compressed, and contains a small bit, which will
decompress it. Immediately after the first step begins the decompression and
the loading of the kernel.

3. If the kernel detects that your graphics card supports more complex text
modes, Linux allows the usage of them - this can be specified or during the
recompilation of the kernel, or right inside Lilo, or other program, like
rdev.

4. The kernel verifies hardware configuration (floppy drive, hard disk,
network adapters, etc) and configures the drivers for the system. During this
operation, several informative messages are shown to the user.

5. The kernel tries to mount the file system and the system files. The
location of system files is configurable during recompilation, or with other
programs - LiLo and rdev. The file system type is automatically detected. The
most used file systems on Linux are ext2 and ext3. If the mount fails, a so-
called kernel panic will occur, and the system will ”freeze”.

System files are usually mounted in read-only mode, to permit a verification
of them during the mount. This verification isn’t indicated if the files were
mounted in read-write mode.

6. After these steps, the kernel will start init, which will become process
number 1, and will start the rest of the system.

The init process

It’s Linux’s first process, and parent of all the other processes. This
process is the first running process on any Linux/UNIX system, and is started
directly by the kernel. It is what loads the rest of the system, and always
has a PID of 1.

The initialization files in /etc/inittab

First time the initialization process (init) examines the file /etc/inittab to
determine what processes have to be launched after. This file provides init
information on runlevels, and on what process should be launched on each
runlevel.

After that, init looks up the first line with a sysinit (system
initialization) action and executes the specified command file, in this case
/etc/rc.d/rc.sysinit. After the execution of the scripts in
/etc/rc.d/rc.sysinit, init starts to launch the processes associated with the
initial runlevel.

The next few lines in /etc/inittab are specific to the different execution
(run-) levels. Every line runs as a single script (/etc/rc.d/rc), which has a
number from 1 to 6 as argument to specify the runlevel.

The most used action in /etc/inittab is wait, which means init executes the
command file for a specified runlevel, and then waits until that level is
terminated.

The files in /etc/rc.d/rc.sysinit

The commands defined in /etc/inittab are executed only once, by the init
process, every time when the operating system boots. Usually these scripts are
running as a succession of commands, and usually realise the following:

1. Determine whether the system takes part of a network, depending on the
content of /etc/sysconfig/network

2. Mount /proc, the file system used in Linux to determine the state of the
diverse processes.

3. Set the system time in fuction to the BIOS settings, as well as realises
other settings (setting of time zone, etc), stabilized and configured during
the installation of the system.

4. Enables virtual memory, activating and mounting the swap partition,
specified in /etc/fstab (File System Table)

5. Sets the host name for the network and system wide authentication, like
NIS (Network Information Service), NIS+ (an improved version of NIS), and so
on.

6. Verifies the root fily system, and if no problems, mounts it.

7. Verifies the other file systems specified in /etc/fstab.

8. Identifies, if case of, special routines used by the operating system to
recognize installed hardware to configure Plug’n’Play devices, and to activate
other prime devices, like the sound card, for example.

9. Verifies the state of special disk devices, like RAID (Redundant Array of
Inexpensive Disks)

10. Mounts all the specified file systems in /etc/fstab.

11. Executes other system-specific tasks.

The /etc/rc.d/init.d directory

The directory /etc/rc.d/init.d contains all the commands which start or stop
services which are associated with all the execution levels.

All the files in /etc/rc.d/init.d have a short name which describes the
services to which they’re associated. For example, /etc/rc.d/init.d/amd starts
and stops the auto mount daemon, which mounts the NFS host and devices anytime
when needed.

The login process

After the init process executes all the commands, files and scripts, the last
few processes are the /sbin/mingetty ones, which shows the banner and log-in
message of the distribution you have installed. The system is loaded and
prepared so the user could log in.

Linux’s execution levels

The execution levels represent the mode in which the computer operates. They
are defined by a set of available services at any time they are started. The
execution levels represent different ways Linux uses to be available to you,
the user, or eventually the administrator.

As daily user you don’t have to bother with the execution levels, although the
multi-user level makes the services which you need while using Linux in a
network (though in a transparent mode) available.

In the next few sentences I’ll present the execution levels, one by one:

0: Halt (stops all running processes and executes shutdown)

1: Known under the name ”Single-user mode”. In this case the system runs with
a reduced set of services and daemons. The root file system is mounted read-
only. This runlevel is used when the others fail while booting.

2: On this level run the most of the services, with the exception of network
services (httpd, named, nfs, etc). This execution level is ideal for the debug
of network services, keeping the file system shared.

3: Complete multi-user mode, with network support enabled.

4: Unused, in most of the distributions. In Slackware this level is equivalent
with 3, the only difference is that this has graphic login enabled.

5: Complete multi-user mode, with network and graphic subsystem support
enabled.

6: Reboot. Stops all running processes and reboots the system to the initial
execution level.

Modification of execution levels

The most used facility of init, and maybe the most confusing one, is the
ability to move from an execution level to an other.

The system boots into a runlevel specified in /etc/inittab, or to a level
specified at the LiLo prompt. To change the execution level, use the command
init. For example, to change the execution level to 3, type

init 3

This stops most of the processes and takes the system into a multi-user mode
with networking enabled. Attention, changing the init level might force
several daemons used at the moment to stop!

The directories of execution levels

Every execution level has a directory with a symbolic links (symlinks)
pointing to the corresponding scripts in /etc/rc.d/init.d. These directories
are:

/etc/rc.d/rc0.d

/etc/rc.d/rc1.d

/etc/rc.d/rc2.d

/etc/rc.d/rc3.d

/etc/rc.d/rc4.d

/etc/rc.d/rc5.d

/etc/rc.d/rc6.d

The name of the symlinks are semnificative. It specifies which service has to
be stopped, started and when. The links starting with an ”S” are programmed to
start in various execution levels. The links also have a number in their name
(01-99). Now some examples of symlinks in the directory /etc/rc.d/rc2.d:

K20nfs -> ../init.d/nfs

K50inet -> ../init.d/inet

S60lpd -> ../init.d/lpd

S80sendmail -> ../init.d/sendmail

When operating systems change the execution level, init compares the list of
the terminated processes (links which start with ”K”) from the directory of
the current execution level with the list of processes which have to be
started (starting with ”S”), found in the destination directory.

Example:

When the system boots into runlevel 3, will execute all the corresponding
links starting with ”S”, in an order accorind to their number:

/etc/rc.d/rc3.d/S60lpd start

/etc/rc.d/rc3.d/S80sendmail start

(and so on)

If the system now changes to runlevel 1, will execute:

/etc/rc.d/rc3.d/K20nfs stop

/etc/rc.d/rc3.d/K50inet stop

(presuming that nfs and inet are NOT in /etc/rc.d/rc1.d)

After that it will start all the processes mentioned in /etc/rc.d/rc1.d except
which are already running. In this example there’s a single one only:

/etc/rc.d/rc1.d/S00single

Changing the current execution level

To change the current execution level for example to level 3, edit
/etc/inittab in a text editor, and edit the following line:

id:3:initdefault:

(do not change the initial runlevel to 0 or 6!)

Booting into an alternative execution level

At the LiLo prompt you have to write the number of the wanted execution level,
before booting the operating system. This way to boot into the third level,
type for example:

linux 3

Eliminating a service from an execution level

To disable a service from a runlevel, you might simply delete or modify the
corresponding symlink.

For example, to disable pcmcia, and don’t start in the future, type:

rm /etc/rc.d/rc3.d/S45pcmcia

Adding a service to an execution level

To add a service, it is needed to create a symlink pointing to the
corresponding scripts in /etc/rc.d/init.d. After the symlink is created, be
sure to assign it a number, so it would be started in the right time:

To add ”lpd” to runlevel 3, type:

ln -s /etc/rc.d/init.d/lpd /etc/rc.d/rc3.d/S64lpd

P.S.: This article has been originally written by Ovidiu T. He gave me the
permission to translate and publish it, and since I found the article
interesting, I did that way as well, although I don’t treat myself responsible
about the content of it.

可以参考:
http://www.ibm.com/developerworks/cn/linux/kernel/startup/index.html

linux 软连接和硬链接的区别

(1)软连接可以 跨文件系统 ,硬连接不可以 。实践的方法就是用共享文件把windows下的 aa.txt文本文档连接到linux下/root目录 下
bb. ln -s aa.txt /root/bb 连接成功 。ln aa.txt /root/bb 失败 。
(2)关于 I节点的问题 。硬连接不管有多少个,都指向的是同一个I节点,会把 结点连接数增加 ,只要结点的连接数不是 0,文件就一直存在
,不管你删除的是源文件还是 连接的文件 。只要有一个存在 ,文件就 存在 (其实也不分什么 源文件连接文件的 ,因为他们指向都是同一个 I节点)。
当你修改源文件或者连接文件任何一个的时候 ,其他的 文件都会做同步的修改 。软链接不直接使用i节点号作为文件指针,而是使用文件路径名作为指针。所以
删除连接文件 对源文件无影响,但是 删除 源文件,连接文件就会找不到要指向的文件 。软链接有自己的inode,并在磁盘上有一小片空间存放路径名.
(3)软连接可以对一个不存在的文件名进行连接 。
(4)软连接可以对目录进行连接

编码规范(自己的要求啦)

1 . 为什么要有编码规范

编码规范对于程序员而言尤为重要,有以下几个原因:

- 一个软件的生命周期中, 80% 的花费在于维护;

- 几乎没有任何一个软件,在其整个生命周期中,均由最初的开发人员来维护;

- 编码规范可以改善软件的可读性,可以让程序员尽快并彻底地理解新的代码;

- 如果你将源码作为产品发布,就需要确任它是否被很好的打包并且清晰无误;

2. 编码规范

package java.test;

import java.test.test1.TestClass;

/**

  • Class description goes here.

  • @version 1.82 18 Mar 1999

  • @author Firstname Lastname

*/

public class Son extends TestClass {

/* classVar1 documentation comment /

public static int classVar1;

/**

  • classVar2 documentation comment that happens to be

  • more than one line long

*/

private static Object classVar2;

/* instanceVar1 documentation comment /

public Object instanceVar1;

/* instanceVar2 documentation comment /

protected int instanceVar2;

/* instanceVar3 documentation comment /

private Object[] instanceVar3;

/**

  • …constructor Son documentation comment…

*/

public Son () {

// …implementation goes here…

}

/**

  • …method doSomething documentation comment…

*/

public void doSomething() {

// …implementation goes here…

}

/**

  • …method doSomethingElse documentation comment…

  • @param someParam description

*/

public void doSomethingElse(Object someParam) {

// …implementation goes here…

}

}

3. 编程实践

- 若没有足够理由,不要把实例变量或类变量声明为公有;

- 类变量 名 不要以下划线开头;

- 最小化变量的作用域;

- 尽量不要使用静态变量;

- 所有的变量名 / 方法名都应该是见明知意的,不要使用 temp , l , x 等变量名;

- 变量定义的时候一定要初始化;

- 不要硬编码,即使它只使用了一次;

- 在含有多种运算符的表达式中使用圆括号来避免运算符优先级问题;

- 完成代码的时候,一定要和式样一致;

- 更改的画面一定要在至少在中、日和英文系统(各一个)的真实环境下进行测试;

- 容器类( map 或 list )在使用之前一定要保证它的正确初始化(里面没有垃圾数据);

- 提交代码之前要充分的 CDI ,尽量进行交叉 review ,而且 review 者最好是不懂改造点的人;

- 提交代码之前要比较差分,确保提交的 source 是正确的;

- 不要将测试数据提交到数据库中;

- 在做 GUI 的时候,只要有按钮点击,并出现别的画面的时候,必须考虑多重启动的问题;

- 如果编写的是数据类的话,要完成它的 toString 方法和 clone 方法;

- 代码在 20 行左右或者关键的地方必须出现注释;

- 不要出现 3 重或者 3 重以上的循环;

- 方法尽量控制在 20 到 100 行之内;

- 一行的字符个数不要超过 80 个;

- 使用空行来分割或组织代码,推荐在方法中使用 1 行,方法见 2 行,不同部分 3 到 4 行;

- 不要使用 tab 键,如果使用的话,在 eclipse 中将 tab 映射成 4 个 space ;

- 尽量不要 copy/paste ,尽量使用继承或重构的方式共享相同的代码;

- 要是 copy/paste ,注意更新注释;

- 在类中, public 方法必须写 javadoc ,还要注意方法改变后同时更新注释或 javadoc ;

- 方法的制限和约定一定要在 javadoc 中写清楚;

- 注释一定要简明扼要,不要只是代码的重复;

- 不要编写只有自己能看懂的代码,简单就是最好的;

- 不要使用代码生成工具生成的代码并改造它;

- 程序的关键部分和异常都要出好 trace ;

- 一个函数只使用一个 return , if else 中的各个分支不要遗漏, case 不要忘记 break ;

- 一定不要将重要的东西保存在桌面上;

- 不要出现 e.printStackTrace()/System.out.println() ,可将其输出到 log 中;

- 无用的代码一定要删除;

- 一定要处理异常;

- 一定要在资源使用完后将其释放,不如 file , socket ,数据库连接等;

- for , while 等语句后面都要加上大括号;

- 给自己的代码编写单元测试项,编写的时候不要改动被测试项的代码,如果要改,使用反射;

- 方法开始的时候,一定要检查参数的有效性;

- 返回零长度的数组比返回 null 好;

- 注意按引用传递和按值传递的区别;

参考资料:

1. http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html

2. http://www.huihoo.org/code/java_code_conventions.html#b1

杂项

一定不要将东西保存在桌面上,如果重装系统的话,你的东西就没有了;

不懂的事情不要胡说或者是“我猜,我认为。。。”;

不会的事情不要zheng很长时间,停下来查查资料或者问问别人;

如果在linux下安装firefox时(就是下一个压缩包,解压,运行里面的firefox就好了),如果完成后还是启动不了,可能是因为firefox需要的系统需求不够,这时可以换一个旧版本的firefox试一下,应该没有问题了;

在linux下面如果没有firefox,不要使用konqueror,原因有两个,一个是就算设置了java的路径让它来运行applet,但是速度很慢;一个是代理的设置时,它的exclude的描述不太好,很容易使人误解;

在vmware(5.5)上安装windows 2008后,windows不能找到vmware的网卡,这时安装一下6.0版本的vmware
tool,windows就可以找到网卡;

代码中多处出现的常量,请务必将之放到constant文件中;

有代码了,不管代码是对还是少,就要进行review(包括很有效的交叉review,进行交叉的对象最好是没有看过要review 代码的人);

List中的remove(Object o)
,使用的是传进来的类的equals方法来判断两个类是否相等的,如果只是重写了传进来的类的compare方法的话,调用List的remove方法会失效;

如果map中的value或者key放的某个类数组的话,然后通过map的方法取出value时,它们将会是Object[]数组;

在java中使用JFrame或者JDialog时,应该注意的地方:
手工调用dispose方法,不会调用注册的Closing方法,只会调用注册的Closed方法;
如果想人为的同时调用Closing和Closed方法,只要使用
WindowEvent ee = new WindowEvent(this, WindowEvent.WINDOW_CLOSING);
Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(ee);
就好了;(在使用Closing或Closed方法时,最好将它们的DefaultCloseOperation设置成:
DO_NOTHING_ON_CLOSE,然后调用dispose方法)

在做程序的时候,只要有按钮点击,并出现别的画面的时候,必须考虑多重启动的问题。因为在cpu高负荷的情况下,如果不进行处理的话,会出现多个相同的画面;

能不使用静态变量的,就不要使用静态变量,应该使用的地方也要尽量回避;

提交代码之前要充分的CDI,不管是自己还是找别人CDI,因为每个人的思维都有局限;

ArrayList在使用之前一定要保证他的正确初始化,否则的话,里面的多余数据会影响程序的结果;

使用Map的时候,一定要注意取出来的是key 还是value,如果两者弄反了的话,程序出的问题更是莫名其妙。注意了啦;

测试英文message的时候,一定要在英文环境下调试,否则的话,调试出来的大小和位置是不对的。我就吃过这样的亏,通过将日文资源文件的内容清空,来测试英文message,最后拿到真正的英文环境下测试的时候,内容显示不下了。

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

IA64/x64/IA32

IPF ( Intel ® Itanium® Processor Family ) = IA64;
EM64T ( Extended Memory 64 Technology ) = x64;
{

AMD calls the resulting architecture “AMD64”,

Intel calls it “EM64T”,

Microsoft calls it “x64”,

and various others call it “x86-64”.

}
IA32 ( 32 bits Intel Architecture )