当我听到在不远的将来,Linux内核会支持Rust语言的这个消息时,我心潮澎湃,欣喜若狂。作为一名用了十几年C语言Linux开发的员来说,早就体会到了指针带来快乐与痛苦,也享受了C的门槛带来的系统知识的提升和调试诊断经验的积累。每当遇到内存踩踏的问题,总要深深的口气,哎又要被蹂躏几个月了。这么多年来自己倒是也思考和总结了诊断内存问题的方法、技巧,也开发了一些诊断工具。但是这些工具还是不能预防,也不能解决普遍的问题,只能在遇到问题后case by case的分析和应对,为次也付出了漫长的资源和时间。所以说在大型的系统中,C的维护成本极高极高,虽然开发效率也很低。
Rust语言的设计者针对C/C++的痛点,并且博采众高级语言的特长,设计了内存安全的、性能卓越的编程语言。
下面分享下C语言编程中常常遇到的内存相关的问题,及Rust是如何能避免这些问题的。
buffer overflow
char array[4] = {1,2,3,4};
array[4] = 0; //valid indexes are 0, 1,2,3.
简简单单的两行代码,一不小心就会铸成大错。换成Rust,会是什么结果呢?
let mut array:[u32;4] = [1,2,3,4];
array[4] = 0;
//^^^^^^^^ index out of bounds: the len is 4 but the index is 4
Rust编译的时候,会报错out of bounds。 从array的声明可以看到,array不仅仅保存了数据,还包含了数据的类型和数据的个数,因此编译器在编译的时候可以检查出错误。
memory leak
在C语言中,函数在返回前,忘记释放str,会导致内存泄露。
int fun()
{
char *str = strdup("hello world\n"); //alloced memory for string
printf("%s\n", str);
return 0; //missed to free memory
}
在Rust语言中,str的作用域在“}”结束后,然后会被自动drop。
fn fun(){
let str = String::from("hello world");
}
double free
在内核编程时经常会遇到类似的情况:
(1) Slab为程序分配了100字节内存,假设地址为0xc000 1000;
(2) 函数退出时,释放了0xc000 1000这个地址。
(3)case1 :在2-3之间,如果函数被中断或者调度;然后其他程序被分配了0xc000 1000这一片区域的内存。程序再次释放0xc000 1000这块内存,会导致其他程序运行出错。case 2: 重复释放同一块内存,Slab会报错。
1 int handle_data()
2 {
3 char *data = kalloc(100, GFP_KERNEL); (1)
4 char *p = data;
5
6 //...
7
8 exit:
9 if (data)
10 free(data); (2)
11
12 if (p) (3)
13 free(p);
14 }
在rust中,内存的owner只有一个,指针的赋值叫做转移,新指针或者ownership,老指针失效。新指针离开作用域时会自动释放内存。不会有double free的问题。
use after free
在C语言里,指针就是地址,是地址就可以操作,管你释放了没有。而Rust有作用域和生命周期的概念, game over了就不能用,你用的话就编不过。
wild pointer
在C语言里,野指针是最烦人的,没有之一。而在Rust语言里面,这都不是事。
Null pointer
在C中,有很多的代码是做空指针的检查的,尽管如此,很大一部分的crash都是有NULL指针导致的。我一直在问自己,难道gcc不能加个空指针的检查吗? 在Rust中,压根没有空指针,哈哈哈。有option,而且编译器管的很宽,保证你的代码处理了所有的option。