6) SIGABRT
1. man手册上说是由abort函数产生的,先介绍一下abort函数:
函数原型 | void abort(void); |
头文件 | stdlib.h |
功能 | 给自己发送SIGABRT信号 |
参数 | 无 |
返回值 | 空 |
2. 事实上不止是abort函数能产生SIGABRT信号,assert也可以,assert实际上并不是一个函数,它是一个宏,但用的时候当成一个函数来用:
原型 | void assert(scalar expression); |
头文件 | assert.h |
功能 | 判断传入的参数是否为真,如果为真则程序继续运行,否则发送SIGABRT信号 |
参数 | [in] expression:可以传入表达式,如果表达式为真,则程序继续运行,否则发送SIGABRT信号,默认情况将导致程序退出 |
返回 | 无 |
实际上assert还是调用了abort,所以assert导致的SIGABRT信号其实也是abort发出的。
3. 另外,如果使用malloc申请内存空间,多次使用free释放也会产生SIGABRT信号。测试例程如下:
1 /** 2 * filename: signal_6.c 3 * author: Suzkfly 4 * date: 2021-02-15 5 * platform: Ubuntu 6 * 操作步骤: 7 * 分别将MODE的值置为1,2,3,观察执行结果 8 */ 9 #include <stdio.h> 10 #include <signal.h> 11 #include <stdlib.h> 12 #include <assert.h> 13 #include <string.h> 14 15 #define MODE 3 16 17 /* 自定义信号处理函数 */ 18 void func(int sig) 19 { 20 switch (sig) { 21 case SIGABRT : 22 printf("SIGTRAP signal captured. value = %d\n", sig); 23 exit(0); 24 break; 25 } 26 } 27 28 int main(int argc, const char *argv[]) 29 { 30 void (*p)(int); /* 定义一个函数指针 */ 31 char *p_malloc = NULL; 32 33 p = signal(SIGABRT, func); 34 printf("p = %p\n", p); 35 36 printf("MODE = %d\n", MODE); 37 #if (MODE == 1) 38 abort(); 39 #elif (MODE == 2) 40 assert(0); 41 #elif (MODE == 3) 42 p_malloc = malloc(100); 43 free(p_malloc); 44 free(p_malloc); 45 #endif 46 while (1) { 47 sleep(1); 48 } 49 50 return 0; 51 }
测试结果:
7) SIGBUS
一般来说导致SIGBUS是由于硬件错误导致的,但想办法让硬件出错还是有点难,不过想引发SIGBUS还有别的方法,比如将一个int型的指针赋值为不是4的整数倍,然后来操作该地址空间,具体代码如下:
1 /** 2 * filename: signal_7.c 3 * author: Suzkfly 4 * date: 2021-02-16 5 * platform: Ubuntu 6 * 操作步骤: 7 * 执行程序,经测试,该程序在i386架构和x86架构的平台上都不会产生SIGBUS信号。 8 */ 9 #include <stdio.h> 10 #include <signal.h> 11 #include <stdlib.h> 12 13 /* 自定义信号处理函数 */ 14 void func(int sig) 15 { 16 switch (sig) { 17 case SIGBUS : 18 printf("SIGBUS signal captured. value = %d\n", sig); 19 exit(0); 20 break; 21 } 22 } 23 24 int main(int argc, const char *argv[]) 25 { 26 void (*p)(int); /* 定义一个函数指针 */ 27 int i[2] = { 0 }; 28 int *p_i = NULL; 29 30 p = signal(SIGBUS, func); 31 printf("p = %p\n", p); 32 33 printf("&i[0] = %p\n", i); 34 p_i = (int *)((char *)i + 1); 35 36 printf("p_i = %p\n", p_i); 37 *p_i = 0x12345678; /* 赋值(不按4字节对齐) */ 38 printf("*p_i = %#x\n", *p_i); 39 printf("i[0] = %#x\n", i[0]); 40 printf("i[1] = %#x\n", i[1]); 41 42 return 0; 43 }
测试结果:
从测试结果看出来,p_i是一个int型的地址,其并非是4的整数倍,但是赋值行为并没有产生SIGBUS信号,我在网上看到很多人进行这样的操作是会产生SIGBUS信号的,原因应该是我们用的平台不一样,我在i386架构和x86架构的Ubuntu的平台上测试都没有产生SIGBUS信号。
但是通过另外一种方法产生了SIGBUS信号,代码如下:
1 /** 2 * filename: signal_7.c 3 * author: Suzkfly 4 * date: 2021-02-16 5 * platform: Ubuntu 6 * 操作步骤: 7 * 运行可执行程序。 8 * 如果执行open时,1.txt不存在,则会引发SIGBUS信号; 9 * 如果执行open时,1.txt存在,并且长度为0,也会引发SIGBUS信号; 10 * 如果执行open时,1.txt存在,但是长度不为0,则不会引发SIGBUS信号。 11 */ 12 #include <stdio.h> 13 #include <fcntl.h> 14 #include <sys/mman.h> 15 #include <sys/stat.h> 16 #include <sys/types.h> 17 #include <stdlib.h> 18 #include <string.h> 19 #include <signal.h> 20 21 /* 自定义信号处理函数 */ 22 void func(int sig) 23 { 24 switch (sig) { 25 case SIGBUS : 26 printf("SIGBUS signal captured. value = %d\n", sig); 27 exit(0); 28 break; 29 } 30 } 31 32 int main(int argc,char *argv[]) 33 { 34 int fd = 0; 35 char *p = NULL; 36 int i = 0; 37 38 signal(SIGBUS, func); 39 40 //如果加入O_TRUNC,则一定会引发SIGBUS信号 */ 41 //fd = open("1.txt", O_RDWR | O_CREAT | O_TRUNC, 0666); 42 fd = open("1.txt", O_RDWR | O_CREAT, 0666); 43 if (fd < 0) { 44 printf("open failed\n"); 45 return 0; 46 } 47 48 p = mmap(NULL, /* 映射区的开始地址,为NULL表示由系统决定映射区的起始地址 */ 49 5, /* 映射区大小 */ 50 PROT_READ | PROT_WRITE, /* 内存保护标志(可读可写) */ 51 //MAP_PRIVATE, /* 映射对象类型(不与其他进程共享) */ 52 MAP_SHARED, /* 映射对象类型(与其他进程共享) */ 53 fd, /* 有效的文件描述符 */ 54 0); /* 被映射内容的偏移量 */ 55 if (p == MAP_FAILED) { 56 printf("mmap failed\n"); 57 return 0; 58 } 59 printf("p = %p\n",p); 60 61 *p = 'H'; 62 //strcpy(p, "0123456789"); 63 64 munmap(p, 5); 65 close(fd); 66 67 return 0; 68 }
运行结果:
代码分析:
该程序有2个关键点,第1是打开的1.txt必须是不存在或者是空的,第2是要通过mmap映射地址。我的理解是这样的,通过mmap从1.txt的文件描述符里映射出来一个地址,这个地址就相当于是1.txt这个文件,但是这个文件的大小又是0,因此实际上映射出来的这个地址可用空间大小就是0,就好像有人告诉你3号房间到5号房间都归你管,但这几个房间压根就是没有的,对它写数据就造成了SIGBUS信号。
题外话:
在测试过程中发现了两个我认为奇怪的现象:
1)1.txt这个文件能修改的部分取决于文件原本有多大,而与mmap传入的第2个参数无关。比如1.txt原来的长度就是8个字节,通过mmap分配的长度为5个字节,而使用strcpy写入10个字节,最后的结果是前8个字节可以写成功,最后文件大小仍为8个字节,而且这个操作并不会引发信号。
2)如果mmap传入的第4个参数为MAP_PRIVATE,那么对p进行赋值并不会改变文件内容,也不会引发信号。
由于本人对mmap的机制并不清楚,因此暂时无法解释这个现象,希望有大神可以解答。