介绍

在Linux内核中使用了大量的C语言编程的技巧,例如C语言中的静态变量、静态函数、内联函数、指针等技巧。这些技巧可以在大规模实现底层基础设施中使用,优化整体架构的耦合和提升代码执行的效率

Linux Kernel C Language Skill

static变量

static静态全局变量仅仅在某个C程序文件中访问,具有文件的作用域,这样可以降低耦合性。

如果普通变量在函数中定义和初始化则这个变量作用域仅仅限于这个函数内;如果函数的变量申明为static这个变量则是static局部变量,它不会暴露给其他的函数使用。

static变量有一个特性就是整个生命周期内只会被初始化一次,在设计模式中中单例模式在C语言实现中可以借助static变量来实现。

static函数

C语言中普通函数只要导出符号表都可以被其他的函数访问;如果函数申明的是static函数,那么仅仅可以在定义所在的文件内访问。在c++私有的成员函数和static函数在大部分设计中有很多相似的地方,具体看设计和实现的细节。

inline函数

C语言的inline关键字定义内联函数,它可以用来替代C语言中的宏定义,inline关键字定义的函数在编译期就会被内联展开调用的地方。宏是在编译预处理时候被替换,出错时候不容易排查。

函数指针

指针的本质是指向一个内存地址,这个地址存储一段代码的首地址。一般函数指针的有2个用途,(1)直接使用函数指针调用函数,比如linux内核的vfs中的inode,inode会有2个操作一个是file操作一个是dir操作.比如在下面例子ext4_dir_inode_operations用于目录的操作。当用户态发起mkdir到了内核的vfs层,在vfs层调用的是函数指针.mkdir.,而这个函数实际指向的是ext4_dir_inode_operations对应的ext4_rmdir这个函数。


const struct inode_operations ext4_dir_inode_operations = {
	.create		= ext4_create,
	.lookup		= ext4_lookup,
	.link		= ext4_link,
	.unlink		= ext4_unlink,
	.symlink	= ext4_symlink,
	.mkdir		= ext4_mkdir,
	.rmdir		= ext4_rmdir,
	.mknod		= ext4_mknod,
	.tmpfile	= ext4_tmpfile,
	.rename		= ext4_rename2,
	.setattr	= ext4_setattr,
	.getattr	= ext4_getattr,
	.listxattr	= ext4_listxattr,
	.get_acl	= ext4_get_acl,
	.set_acl	= ext4_set_acl,
	.fiemap         = ext4_fiemap,
	.fileattr_get	= ext4_fileattr_get,
	.fileattr_set	= ext4_fileattr_set,
};

第二个用途则是回调,如果函数指针作为参数传递给其他函数,则函数指针关联的函数称为回调函数,简称为callback.通常函数指针把函数接口和实现分开,调用函数在定义时候仅仅需要知道被调用函数或者调用函数的原型即可不需要知道具体由哪个函数实现。回调函数机制比较好的实现例子stdlib.h/cstdlib.h中的qsort和bsearch函数


void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*);

void *bsearch( const void *key, const void *array, size_t n, size_t size, int (*compare)(const void *, const void *));

双重指针

Linux Kernel C Language Skill

内核开发中C语言技巧

双重指针本质是指向指针的指针使用双重指针一般有字符串数组或者指针数组、存储指针的地址和作为函数参数返回指针这几种情况。比如字符串数组最佳的例子在main函数。


int main(int argc, char **argv)
{
	// todo
}

C语言中函数参数是按照传值的方式,比如参数是指针,在函数内修改这个指针生效仅仅是在这个函数中,函数指针完毕后这个指针修改会reset.如果传递是的双重指针,在函数内针对双重指针的引用进行修改,则修改的是双重指针指向的内容,即使这个函数执行结束,双重指针指向的内容变更也是生效的。


// 函数执行完毕后,修改的是ptr的副本,ptr自身没有修改
void self_malloc(int *ptr){
   ptr = malloc(1);
}

// 这样修改的是双重指针指针的内容,函数执行完毕后*ptr得到的是malloc的内存地址
void self_malloc2(int **ptr){
   *ptr = malloc(1);
}