Skip navigation.

每一天都是新的开始

每一天都是旧的重复

Linux内核使用的GNU C扩展

GNU CC是一个功能非常强大的跨平台C编译器,它对C语言提供了很多扩展,这些扩展对优化、目标代码布局、更安全的检查等方面提供了很强的支持。本文把支持GNU扩展的C语言称为GNU C。

Linux内核代码使用了大量的GNU C扩展,以至于能够编译Linux内核的唯一编译器是GNU CC,以前甚至出现过编译 Linux内核要使用特殊的GNU CC版本的情况。本文是对Linux内核使用的GNU C扩展的一个汇总,希望当你读内核源码遇到不理解的语法和语义时,能从本文找到一个初步的解答,更详细的信息可以查看gcc.info。

语句表达式
------------------------------------------
GNU C把包含在括号中的复合语句看做是一个表达式,称为语句表达式,它可以出现在任何允许表达式的地方,你可以在语句表达式中使用循环、局部变量等,原本只能在复合语句中使用。例如:
#define min_t(type, x, y) ({ type __x = x;\
type __y = y;\
__x < __y ? __x : __y;})

复合语句的最后一个语句应该是一个表达式,它的值将成为这个语句表达式的值。这里定义了一个安全的求最小值的宏,在标准C中,通常定义为:
#define min(x,y) ((x) < (y) ? (x) : (y))

这个定义计算x和y分别两次(x和y中的小者被计算两次),当参数有副作用时,将产生不正确的结果。 使用语句表达式只计算参数一次,避免了可能的错误。语句表达式通常用于宏定义。


typeof
------------------------------------------
使用前一节定义的宏需要知道参数的类型,利用typeof可以定义更通用的宏,不必事先知道参数的类型,例如:
141: #define min(x,y) ({ \
142: const typeof(x) _x = (x); \
143: const typeof(y) _y = (y); \
144: (void) (&_x == &_y); \
145: _x < _y ? _x : _y; })

这里typeof(x)表示x的值类型,第142行定义了一个与x类型相同的局部常量_x并初使化为x,注意第144行的作用是检查参数x和y的类型是否 相同(如果x和y的类型不同编译器将会发出warning,并不影响后面语句的执行)。typeof可以用在任何类型可以使用的地方,通常用于宏定义。



零长度数组
------------------------------------------
GNU C允许使用零长度数组,在定义变长对象的头结构时,这个特性非常有用。例如:
struct minix_dir_entry {
__u16 inode;
char name[0];
};
结构的最后一个元素定义为零长度数组,它不占结构的空间。在标准C中则需要定义数组长度为1,分配时计算对象大小比较复杂。


可变参数宏
------------------------------------------
在 GNU C中,宏可以接受可变数目的参数,就象函数一样,例如:
#define KERN_DEBUG "<7>"
#define pr_debug(fmt,arg...) printk(KERN_DEBUG fmt,##arg)
这里arg表示其余的参数,可以是零个或多个,这些参数以及参数之间的逗号构成arg的值,在宏扩展时替换arg,例如:
pr_debug("%s:%d",filename,line)
扩展为
printk("<7>" "%s:%d", filename, line)

使用##的原因是处理arg不匹配任何参数的情况。如果arg的值为空,GNU C预处理器在这种特殊情况下,丢弃##之前的逗号,这样
pr_debug("success!\n")
扩展为
printk("<7>" "success!\n")
注意最后没有逗号。


标号元素
------------------------------------------
标准C要求数组或结构变量的初使化值必须以固定的顺序出现, 在GNU C中,通过指定索引或结构域名,允许初始化值以任意顺序出现。指定数组索引的方法是在初始化值前写 '[INDEX] =',要指定一个范围使用 '[FIRST ... LAST] =' 的形式,例如:
static unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL };
将数组的所有元素初使化为 ~0UL,这可以看做是一种简写形式。

要指定结构元素,在元素值前写 'FIELDNAME:',例如:
struct file_operations ext2_file_operations = {
llseek: generic_file_llseek,
read: generic_file_read,
write: generic_file_write,
ioctl: ext2_ioctl,
mmap: generic_file_mmap,
open: generic_file_open,
release: ext2_release_file,
fsync: ext2_sync_file,
};

将结构ext2_file_operations的元素llseek初始化为generic_file_llseek,元素read 初始化genenric_file_read,依次类推。我觉得这是GNU C 扩展中最好的特性之一,当结构的定义变化以至元素的偏移改变时,这种初始化方法仍然保证已知元素的正确性。对于未出现在初始化中的元素,其初值为 0。



case范围
------------------------------------------
GNU C 允许在一个 case 标号中指定一个连续范围的值,例如:
case '0' ... '9': c -= '0'; break;
case 'a' ... 'f': c -= 'a'-10; break;
case 'A' ... 'F': c -= 'A'-10; break;

case '0' ... '9':
相当于
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':




声明的特殊属性
------------------------------------------
GNU C允许声明函数、变量和类型的特殊属性,以便手工的代码优化和更仔细的代码检查。要指定一个声明的属性,在声明后写__attribute__ (( ATTRIBUTE )),其中ATTRIBUTE是属性说明,多个属性以逗号分隔。GNU C支持十几个属性,这里介绍最常用的:

* noreturn
属性noreturn用于函数,表示该函数从不返回。这可以让编译器生成稍微优化的代码,最重要的是可以消除不必要的警告信息比如未初使化的变量。例如:
# define ATTRIB_NORET __attribute__((noreturn)) ....
asmlinkage NORET_TYPE void do_exit(long error_code)
ATTRIB_NORET;

* format(ARCHETYPE, STRING-INDEX, FIRST-TO-CHECK)
属性format用于函数,表示该函数使用printf, scanf 或strftime风格的参数,使用这类函数最容易犯的错误是格式串与参数不匹配,指定format属性可以让编译器根据格式串检查参数类型。例如:
asmlinkage int printk(const char * fmt, ...)
__attribute__ ((format (printf, 1, 2)));
表示第一个参数是格式串,从第二个参数起根据格式串检查参数。

* unused
属性unused用于函数和变量,表示该函数或变量可能不使用,这个属性可以避免编译器产生警告信息。

* section ("section-name")
属性section用于函数和变量,通常编译器将函数放在.text节,变量放在.data或 .bss 节,使用section属性,可以让编译器将函数或变量放在指定的节中。例如:
#define __init __attribute__ ((__section__ (".text.init")))
#define __exit __attribute__ ((unused, __section_.text.exit))
#define __initdata __attribute__ ((__section__ (".data.init")))
#define __exitdata __attribute__ ((unused, __section__ (".data.exit")))
#define __initsetup __attribute__ ((unused,__section__ (".setup.init")))
#define __init_call __attribute__ ((unused,__section__ (".initcall.init")))
#define __exit_call __attribute__ ((unused,__section__ (".exitcall.exit")))
连接器可以把相同节的代码或数据安排在一起,Linux内核很喜欢使用这种技术,例如系统的初始化代码被安排在单独的一个节,在初始化结束后就可以释放这部分内存。

* aligned (ALIGNMENT)
属性aligned用于变量、结构或联合类型,指定变量、结构域、结构或联合的对齐量,以字节为单位,例如:
struct i387_fxsave_struct {
unsigned short cwd;
unsigned short swd;
unsigned short twd;
unsigned short fop;
} __attribute__ ((aligned (16)));
表示该结构类型的变量以16字节对齐。通常编译器会选择合适的对齐量,显示指定对齐通常是由于体系限制、优化等原因。

* packed
属性packed用于变量和类型,用于变量或结构域时表示使用最小可能的对齐,用于枚举、结构或联合类型时表示该类型使用最小的内存。例如:
struct Xgt_desc_struct {
unsigned short size;
unsigned long address __attribute__((packed));
};
域address将紧接着size分配。属性packed的用途大多是定义硬件相关的结构,使元素之间没有因对齐而造成的空洞。


当前函数名
------------------------------------------
GNU CC预定义了两个标志符保存当前函数的名字,__FUNCTION__保存函数在源码中的名字;__PRETTY_FUNCTION__保存带语言特色的名字。在C函数中,这两个名字是相同的,在C++函数中,__PRETTY_FUNCTION__包括函数返回类型等额外信息,Linux内核只使用了__FUNCTION__。
void ext2_update_dynamic_rev(struct super_block *sb) {
struct ext2_super_block *es = EXT2_SB(sb)->s_es;
if (le32_to_cpu(es->s_rev_level) > EXT2_GOOD_OLD_REV)
return;
ext2_warning(sb, __FUNCTION__,
"updating to rev %d because of new feature flag, "
"running e2fsck is recommended",
EXT2_DYNAMIC_REV);
这里__FUNCTION__将被替换为字符串 "ext2_update_dynamic_rev"。虽然__FUNCTION__看起来类似于标准 C中的__FILE__,但实际上__FUNCTION__是被编译器替换的,不象__FILE__ 被预处理器替换。



内建函数
------------------------------------------
GNU C提供了大量的内建函数,其中很多是标准C库函数的内建版本,例如memcpy,它们与对应的C库函数功能相同,本文不讨论这类函数,其他内建函数的名字通常以__builtin开始。
* __builtin_return_address (LEVEL)
内建函数__builtin_return_address返回当前函数或其调用者的返回地址,参数LEVEL指定在栈上搜索框架的个数,0表示当前函数的返回地址,1表示当前函数的调用者的返回地址,依此类推。例如:
printk( KERN_ERR "schedule_timeout: wrong timeout "
"value %lx from %p\n", timeout,
__builtin_return_address(0));

* __builtin_constant_p(EXP)
内建函数__builtin_constant_p用于判断一个值是否为编译时常数,如果参数EXP的值是常数,函数返回 1,否则返回 0。例如:
#define test_bit(nr,addr) \
(__builtin_constant_p(nr) ? \
constant_test_bit((nr),(addr)) : \
variable_test_bit((nr),(addr)))
很多计算或操作在参数为常数时有更优化的实现,在GNU C中用上面的方法可以根据参数是否为常数,只编译常数版本或非常数版本,这样既不失通用性,又能在参数是常数时编译出最优化的代码。

* __builtin_expect(EXP, C)
内建函数__builtin_expect用于为编译器提供分支预测信息,其返回值是整数表达式EXP的值,C的值必须是编译时常数。例如:
13: #define likely(x) __builtin_expect((x),1)
14: #define unlikely(x) __builtin_expect((x),0)

564: if (unlikely(in_interrupt())) {
565: printk("Scheduling in interrupt\n");
566: BUG();
567: }
这个内建函数的语义是 EXP 的预期值是 C,编译器可以根据这个信息适当地重排语句块的顺序,使程序在预期的情况下有更高的执行效率。上面的例子表示处于中断上下文是很少发生的,第 565-566 行的目标码可能会放在较远的位置,以保证经常执行的目标码更紧凑。

http://hi.baidu.com/shlongli/blog/item/22120f38c8bfe820b8998fca.html

list all files in a directory (ocaml)

The easiest way is to use Sys.readdir, which takes the name of the
directory and returns an array containing the names of the files in
the directory (excluding "." and ".."). eg:

$ ocaml
Objective Caml version 3.08.2

# Sys.readdir ".";;
- : string array =
[|".bashrc"; ".bash_profile"; ".Xauthority"; (* etc *) |]

If you want a list, use Array.to_list (Sys.readdir ".").

A more flexible, but more convoluted approach is to use the Unix-
module functions Unix.opendir, Unix.readdir and Unix.closedir.
(Despite the name, they should work on Windows too).

Note that neither of these functions will necessarily return the names
in alphabetical order. You may want to sort the filenames if you're
going to present results back to the user.

Rich.

别对我嘟嘟叫啦!(转)

,

Introduction
当电脑对我嘟嘟嚷的时候,我真的觉得很讨厌。我常常在 shell 里面使用 Tab-补全来节省大量的输入时间,但是我受不了扬声器没完没了的嘟嘟声!

下面就为大家介绍如何快速去除讨厌的叫声。在 shell 里面,你可以按下 crtl-g 来测试一下这个嘟嘟声是否已经去掉。
关掉所有的提示音
在 Linux 控制台下(没有 X11),你可以使用一下命令:

setterm -blength 0

#alternatively you can change the frequency of the beep to a
#very low value:

setterm -bfreq 10

而在 X11 下面(不管是 KDE、Gnome、XFCE 或者……) 你可以:

xset b off


对每种 shell 操作
作为一种可能的选择,你可以直接关掉某种 shell 里的提示音。

对 Bash:

# has to go into /etc/inputrc or .inputrc
# It will not work in a .bashrc file!
set bell-style none



对 Tcsh:

# put this into your .tcshrc file

# just tab completion beep off:
set matchbeep = never
# any beep off:
set nobeep = 1


结论
为避免误解,特此声明:以上操作只是关掉了(蜂鸣器的)嘟嘟声,你仍然可以在你的电脑上自在的听歌。

世界清静了……

Notice what no one else notices, and you will know what no one else knows.

Pay close attention to everything - everything you see, notice what no one else notices, and you will know what no one else knows. What you get is what you get, what you do with what you get, that's more the point.

使用Java操作文本文件的方法详解

  最初Java是不支持对文本文件的处理的,为了弥补这个缺憾而引入了Reader和Writer两个类,这两个类都是抽象类,Writer中write(char[] ch,int off,int length),flush()和close()方法为抽象方法,Reader中read(char[] ch,int off,int length)和close()方法是抽象方法。子类应该分别实现他们。

  当我们读写文本文件的时候,采用 Reader是非常方便的,比如FileReader,InputStreamReader和BufferedReader。其中最重要的类是 InputStreamReader,它是字节转换为字符的桥梁。你可以在构造器重指定编码的方式,如果不指定的话将采用底层操作系统的默认编码方式,例如GBK等。当使用FileReader读取文件的时候。

FileReader fr = new FileReader("ming.txt");
int ch = 0;
while((ch = fr.read())!=-1 )
{
 System.out.print((char)ch);
}


  其中read()方法返回的是读取得下个字符。当然你也可以使用read(char[] ch,int off,int length)这和处理二进制文件的时候类似,不多说了。如果使用InputStreamReader来读取文件的时候

while((ch = isr.read())!=-1)
{
 System.out.print((char)ch);
}


  这和FileReader并没有什么区别,事实上在FileReader中的方法都是从InputStreamReader中继承过来的。read() 方法是比较好费时间的,如果为了提高效率我们可以使用BufferedReader对Reader进行包装,这样可以提高读取得速度,我们可以一行一行的读取文本,使用readLine()方法。

BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("ming.txt")));
String data = null;
while((data = br.readLine())!=null)
{
 System.out.println(data);
}


  当你明白了如何用Reader来读取文本文件的时候那么用Writer写文件同样非常简单。有一点需要注意,当你写文件的时候,为了提高效率,写入的数据会先放入缓冲区,然后写入文件。因此有时候你需要主动调用flush()方法。与上面对应的写文件的方法为:

FileWriter fw = new FileWriter("hello.txt");
String s = "hello world";
fw.write(s,0,s.length());
fw.flush();

OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("hello2.txt"));
osw.write(s,0,s.length());
osw.flush();

PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream("hello3.txt")),true);
pw.println(s);


  不要忘记用完后关闭流!下面是个小例子,帮助新手理解。其实有的时候java的IO系统是需要我们多记记的,不然哪天就生疏了。

hello world i like java language

import java.io.*;

public class TestFile2
{
 public static void main(String[] args) throws IOException
 {
  FileReader fr = new FileReader("ming.txt");
  char[] buffer = new char[1024];
  int ch = 0;
  while((ch = fr.read())!=-1 )
  {
   System.out.print((char)ch);
  }

  InputStreamReader isr = new InputStreamReader(new FileInputStream("ming.txt"));
  while((ch = isr.read())!=-1)
  {
   System.out.print((char)ch);
  }

  BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("ming.txt")));
  String data = null;
  while((data = br.readLine())!=null)
  {
   System.out.println(data);
  }

  FileWriter fw = new FileWriter("hello.txt");
  String s = "hello world";
  fw.write(s,0,s.length());
  fw.flush();

  OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("hello2.txt"));
  osw.write(s,0,s.length());
  osw.flush();

  PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream("hello3.txt")),true);
  pw.println(s);

  fr.close();
  isr.close();
  br.close();
  fw.close();
  osw.close();
  pw.close();
 }
}
December 2009
S M T W T F S
November 2009January 2010
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31