深入理解Linux kernel(内核)中的signal函数

在Linux内核代码中,有一个信号处理绑定器函数signal,它到原型定义如下,通过man 2 signal可以查看其原型。
#include <signal.h>
typedef void (*sighandler_t)(int);    (1)
sighandler_t signal(int signum,sighandler_t handler);    (2)
第一句是包含头文件,第二句是类型定义,第三句才是signal函数到真正声明。扩展声明后,真正到声明为
void (*signal(int signum,void (*sighandler)(int)))(int);    (3)
第(3)看起来比第(1)和(2)句更难理解,首先从(1)(2)下手。
(1)表示sighandler_t是自定义类型,它其实是一种函数指针类型,它指向到函数到形参为int,返回值为void。
(2)表示signal函数到形参有一个int型signum,以及一个sighandler_t型到形参handler,返回一个sighandler_t型到值,由(1)可以知道:sighandler_t是函数指针类型,所以signal到第二个形参是一个函数指针,并且signal函数返回一个函数指针。它们指向到函数类型都是具有一个int形参,返回为void到函数。
    迷惑:对于signal函数,对于初学者来说,不仅是语法上到迷惑,还有应用上到迷惑,下面结合我自己到理解,举例说明怎样理解signal函数。
代码think.c-----------------------------------------------------------------------------------
#include<stdio.h>
/*定义一个函数*/
void func(int a){
    printf("in func a=%d/n",a);
}
/*声明一个函数指针*/
void (*pfunc)(int);
/*定义我们自己到my_signal函数*/
void (*my_signal(int sig,void (*handler)(int h)))(int c){
    printf("in my_signal/n");
    /*想一下下面一句,对吗?*/
    return handler;
}
int main(){
    printf("before invoke 'void func(int)'/n");
    func(1);        /*(4)*/
    pfunc=func;
    printf("before invoke 'void func(int) by function pointer'/n");
    pfunc(2);        /*(5)*/
    printf("before invoke 'my_signal(int sig,void (*handler)(int h)))'/n");
    pfunc=my_signal(2,func);    /*(6)*/
    printf("before invoke 'the function pointer return by my_signal'/n");
    pfunc(4);        /*(7)*/
    printf("/nbefore invoke 'my_signal(int sig,void (*handler)(int h)))(int c)'/n");
    my_signal(5,func)(6);        /*(8)*/
    /*    (9)
        printf("the return of my_signal(6,func)(6) is %X/n",(unsigned)&my_signal(5,func)(6));
    */
    return 0;
}
上面到代码在gcc4.4.1中编译运行都没有错误。
    迷惑1:从(2)和(3)对比来看,二者在语法上完全等价,但是从调用上看呢?
先看声明(2):
(2)的感觉就像是一个很普通到函数调用,我们将函数指针换成一个指向基本类型到指针如int *,(2)就变成了(2a)int *signal(int signum,int *handler);
(2a)是个很普通到函数,所以调用它到时候自然而然就是signal(1,pointer);也就是think.c中main函数到调用(6)。
再看声明(3):
(3)完全使用基本类型定义来表示到函数,所以看起来颇复杂,拨开整个函数,将signal(...)省略为signal(),那么(3)变成了(3a)void (*signal())(int);进一步,我们直接使用一个指针来表示signal()函数返回到指针,那么就变成了(3b)void (*psignal)(int);这样就简单了,这个函数的调用极其简单了,也就类似think.c中到调用(8)。
现在问题出来了,发现调用(6)和(8)截然不同了吗?
而且,看看函数my_signal的定义方式,一眼看上去还感觉不对劲呢,函数不是void吗?怎么还返回一个函数指针?
其实,当你把(3)简化到(3b)到时候,就会发现,其实signal不是最外层到函数,而整个my_signal函数到定义并不是最外层函数,它返回的指针是指向一个函数的,所以my_signal函数会返回一个指针!而不是void。而当你以方式(6)调用my_signal函数的时候,它会返回一个函数指针,而当你用方式(8)调用函数的时候,这意味着你不仅调用了signal函数,而且还调用了signal返回到指针指向到函数,最后(8)的结果肯定就是一个void了,所以假设语句(9)存在,那么肯定编译不能通过,因为my_signal(5,func)(6)到返回值是一个void,不可能由%X输出,而且也不能由其它格式化输出。
    迷惑2:如果在my_signal函数到定义为
void (*my_signal(int sig,void (*handler)(int h)))(int c){
    printf("in my_signal/n");
}
那么编译think.c能通过吗?因为没有返回值。think.c如果能够通过,那么生成到程序运行会有错吗?
1)think.c能够通过编译,因为在C语言中,函数到返回类型如果没有声明,那么就默认是int,如果没有返回(return)那么也默认是返回int,当然你也可以使用一个变量来捕捉函数返回到值,但是这个值通常是一个未定义的。
2)think.c编译生成的程序,运行到时候肯定会发生段错误,这个段错误也就是由于my_signal没有返回正确到函数指针导致的,因为没有明确返回一个合法到函数指针,所以my_signal运行后会返回一个为定义的值,而当运行到main中调用(7)的时候,显然,这里会发生段错误,因为本程序根本不能访问那段内存空间!


    最后,在Linux Kernel中signal函数绑定信号处理函数后,确实会返回一个函数指针,它返回到是以前这个信号到处理函数指针,所以如果你存储了这个指针,那么当你处理完成以后就可以恢复以前的信号绑定。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
深入理解Linux内核第三版》为读者提供了深入了解Linux内核的绝佳机会。全书分为十一个章节,内容涵盖了Linux内核的各个方面,例如进程管理、内存管理、文件系统以及驱动程序等。本书适合有一定编程和Linux基础的读者。 本书的作者是一位经验丰富的Linux内核专家,他将复杂的内核概念以清晰易懂的方式呈现给读者,让读者可以系统地掌握Linux内核的核心知识。本书的目的不是教授读者如何编写应用程序,而是帮助读者理解Linux内核的独特设计和实现原理。 本书的特点在于对Linux内核各个部分的深入分析,涉及的知识点很丰富,包括系统初始化、进程调度、内存管理、文件系统、网络协议栈等。书也包含了大量的实例代码和内核源代码分析,有助于读者通过实际代码理解Linux内核的运行机制。 读者通过阅读本书,可以逐步理解Linux内核的各个部分是如何协同工作的,以及它们是如何影响系统的整体性能和稳定性的。此外,本书还对Linux内核的发展历程和未来趋势进行了展望,对于想要深入研究Linux内核的读者来说,是一本不可多得的参考书籍。 总之,本书内容全面,深入浅出,适合那些渴望深入了解Linux内核的读者。通过本书的学习,读者可以更好地理解Linux系统的运行原理,为系统调优和性能提升提供了宝贵的参考。 "Understanding the Linux Kernel, Third Edition" provides readers with a great opportunity to dive deep into the Linux kernel. The book is divided into eleven chapters, covering various aspects of the Linux kernel, such as process management, memory management, file systems, and drivers. This book is suitable for readers with a basic understanding of programming and Linux. The author of this book is an experienced Linux kernel expert who presents complex kernel concepts in a clear and understandable manner, allowing readers to grasp the core knowledge of the Linux kernel systematically. The purpose of this book is not to teach readers how to write applications, but to help them understand the unique design and implementation principles of the Linux kernel. The highlight of this book is its in-depth analysis of various parts of the Linux kernel, covering a wide range of knowledge, including system initialization, process scheduling, memory management, file systems, network protocol stack, and more. The book also includes a large number of sample codes and kernel source code analysis, which helps readers understand the operating mechanism of the Linux kernel through actual code. By reading this book, readers can gradually understand how each part of the Linux kernel works together and how they affect the overall performance and stability of the system. In addition, the book also provides an outlook on the development history and future trends of the Linux kernel, making it an invaluable reference for those who want to delve into the Linux kernel. In conclusion, this book is comprehensive and easy to understand, making it suitable for readers who are eager to understand the Linux kernel in depth. Through studying this book, readers can better understand the operating principles of the Linux system, providing valuable references for system tuning and performance improvement.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值