请教linux内核中goto语句的问题!

发表于:2007-07-04来源:作者:点击数: 标签:
在linux内核中goto出现的频率很高,书上说是考虑到内核的执行效率。 但在下面的函数中,如果将goto换成return,那不更直接? 请大虾指教! asmlinkageintsys_execve(structpt_regsregs) interror; char*filename; filename=getname((char*)regs.ebx); error=P

在linux内核中goto出现的频率很高,书上说是考虑到内核的执行效率。
但在下面的函数中,如果将goto换成return,那不更直接?
请大虾指教!
asmlinkage int sys_execve(struct pt_regs regs)
{
int error;
char * filename;

filename = getname((char *) regs.ebx);
error = PTR_ERR(filename);
if (IS_ERR(filename))
goto out;
error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, ®s);
if (error == 0)
current->ptrace &= ~PT_DTRACE;
putname(filename);
out:
return error;
}

 e4gle 回复于:2003-05-14 10:14:17
当然不是!内核中编程和应用层大大不一样,你还忘了内核中为什么要大量用goto语句的另一个重要原因,就是内核中经常会牵涉到分配内存空间,而内核页的大小是有限的,内核堆栈空间也仅仅只有8k字节而已,如果已经分配了的空没有释放就会引起大量内存泄漏问题,在你这个例子中,我们分析一下:
asmlinkage int sys_execve(struct pt_regs regs) 

int error; 
char * filename; 

filename = getname((char *) regs.ebx); //仔细看这句,getname函数是在内核中分配空间,然后用strncpy_from_user宏从应用层把regs.ebx传进内核
error = PTR_ERR(filename); 
if (IS_ERR(filename)) 
goto out; //仔细想想如果你这里直接return了,那么getname函数分配的空间谁来释放?这样做会引起系统不稳定,严重会导致当机
error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, &s); 
if (error == 0) 
current->ptrace &= ~PT_DTRACE; 
putname(filename);//这句相当重要,putname函数是一个宏,他释放前面getname函数分配的内核空间 
out: 
return error; 
}


明白了么?所以千万不要以应用层的观点来编写内核程序,在内核写程序一定要严谨,而且要对内核非常熟悉,内核中还牵涉到很多的锁,如果你加了锁,然后在解锁之前就return了也会出问题,所以一定要借助goto语句来实现这个严谨的特性。

 arcsiny 回复于:2003-05-14 18:33:47
谢谢大虾的指教,在你给的例子里goto-->out:后面跟有解锁的操作---这个当然应该goto了,它是goto完了之后的事情。但实际上内核中还有不少out处(我提问的那个函数里面)后面直接跟return error,这样的话got out:不就直接等于return error了吗?这中间有地方进行内存释放?由于小生初学,请不吝赐教。

 arcsiny 回复于:2003-05-14 12:52:41
十分感谢!
goto能实现内存释放?

 e4gle 回复于:2003-05-14 14:44:13
倒,看来你还没明白,goto不能释放内存,但是利用goto语句可以使程序严谨,你仔细看看上面的例子,用了goto,不论程序走什么分支,分配了的内存都得到了释放。如果直接return,那么前面分配的空间就在没释放就退出程序了,你自己想想吧

 e4gle 回复于:2003-05-14 14:58:26
我们看看我写的这个程序片断,这是在内核中的一个遍历双向循环链表的操作,在内核中
我们操作链表一定要加锁,以防止其他进程占用这块内存引起kernel panic。
[code:1:ad92ecfe1f]
...
spin_lock(&list_lock);
list_for_each(p,&user_aclearcase/" target="_blank" >ccess_list){
struct k_user_access *t = list_entry(p,struct k_user_access,a_list);
if(getpath(file,f))
{
printk("Can not get full pathname\n");
ret= -EPERM;/*我们看看这个程序分支,如果进入if这个分支,我们要返回一个
      错误-EPERM,如果这里我们直接用return -EPERM;的话,程序就
      退出了,我看看前面的spin_lock(&list_lock);这个自旋锁是不
      是还没解开?所以这是很危险的。*/
goto out;/*我们用goto语句,让程序到out的地方执行,看看我们解锁了然后再return*/
}
}
ret = 0;
out:
spin_unlock(&list_lock);
return ret;
[/code:1:ad92ecfe1f]
你可以看看这个程序任何一个分支都是不会出现上了锁而没解锁就退出程序的现象,这就goto语句的
作用,我们分配内存释放内存的道理也是一样的。如果你还没明白,那我就没办法啦。

 e4gle 回复于:2003-05-15 10:33:10
对,并不是所有的goto语句都一定为了释放内存或者解锁,有的则是为了程序便于维护和阅读。

 arcsiny 回复于:2003-05-15 11:29:20
十分感谢 

 rdd 回复于:2003-05-15 11:35:05
呵呵,大鹰讲述的真是深入浅出啊。顶一下

 sakulagi 回复于:2003-05-15 12:43:14
[quote:eb668b6234="e4gle"]对,并不是所有的goto语句都一定为了释放内存或者解锁,有的则是为了程序便于维护和阅读。[/quote:eb668b6234]大鹰,那么arcsiny提出的那段代码里面因为out:是在putname()的后面,是不是getname()请求的空间实际上并没有得到释放?

 e4gle 回复于:2003-05-15 12:46:36
是的,他提出的例子确实是这样,在他的例子中goto语句仅仅是为了便于维护代码,我一开始看错了,主要是他贴代码没用[code],所以我以为error也是一个goto呢,但goto语句在内核中大量运用非常重要的原因之一就是我所说的为了程序的严谨

 sakulagi 回复于:2003-05-15 12:51:30
感谢大鹰(这么快就回复了?)

 a9711 回复于:2003-05-16 08:38:54
不错,又学到一点。。

 HopeCao 回复于:2003-05-16 09:40:31
有意思!

 observertwo 回复于:2003-05-16 14:04:45
呵呵,学到一点,讲解得很细致,不错

 arcsiny 回复于:2003-05-16 18:59:02
[code:1:40094816a9]嘿嘿 不好意思 开始不会贴code[/code:1:40094816a9]

 homeend0 回复于:2003-05-19 21:11:06
不错。

原文转自:http://www.ltesting.net