免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 5224 | 回复: 13
打印 上一主题 下一主题

[函数] [见鬼了系列]关于sigaction 和段错误相关! [复制链接]

论坛徽章:
1
2015年亚洲杯之巴林
日期:2015-02-05 20:34:47
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2012-11-06 21:03 |只看该作者 |倒序浏览
最近看《unix网络编程》看到第五章,想试验一下那个主进程wait子进程结束信号SIGCHLD这个信号,用书上的例子,在测试过程中出现一个问题,不知道怎么解析,想了一天也没想通。流程很简单:
1、服务器运行(等待客户端连接)。
2、客户端运行,输入一行,按回车(服务器里面返回客户端输入的内容)。
3、客户端ctrl + D 结束。
4、服务端处理完SIGCHLD信号后段错误。(正常是处理完SIGCHLD信号,就继续阻塞ACCEPT)。

一下是代码。
客户端(这个没有问题,不看也可以),问题在服务端的代码里面,有注释。
  1. #include "unp.h"

  2. void str_cli(FILE *fp, int fd)
  3. {
  4.         char send[MAXLINE], recv[MAXLINE];

  5.         while(fgets(send, MAXLINE, fp) != NULL)
  6.         {
  7.                 int n = writen(fd, send, strlen(send));
  8.                 //if (readn(fd, recv, MAXLINE) == 0)
  9.                 if (readline(fd, recv, MAXLINE) == 0)
  10.                 {
  11.                         printf("readn return 0,then close");
  12.                         close(fd);
  13.                         exit(0);
  14.                 }

  15.                 fputs(recv, stdout);
  16.         }
  17. }

  18. int main(int argc, char *argv[])
  19. {
  20.         int fd = socket(AF_INET, SOCK_STREAM, 0);
  21.        
  22.         struct sockaddr_in ser;
  23.        
  24.         if (argc < 2)
  25.         {
  26.                 printf("usage app ip\n");
  27.                 return 1;
  28.         }

  29.         bzero(&ser, sizeof(sockaddr_in));
  30.         ser.sin_family = AF_INET;
  31.         ser.sin_port = htons(5005);
  32.         inet_pton(AF_INET, argv[1], &ser.sin_addr);
  33.        
  34.         socklen_t len = sizeof(ser);
  35.         if (connect(fd, (struct sockaddr *)&ser, len) != 0)
  36.         {
  37.                 printf("connect error:%s.\n", strerror(errno));
  38.                 return 1;
  39.         }
  40.         else printf("connect success.\n");

  41.         str_cli(stdin, fd);

  42.         exit(1);
  43.         return 1;
  44. }
复制代码
服务端:(这个是有问题的)
  1. #include "unp.h"

  2. void sig_handle(int sig)
  3. {
  4.                 pid_t pid;
  5.                 int status;
  6.                 pid = wait(&status);
  7.                 printf("child %d terminated\n", pid);
  8.         return;
  9. }

  10. void str_echo(int fd)
  11. {
  12.         ssize_t n;
  13.         char buf[MAXLINE];

  14.         memset(buf, '\0', sizeof(buf));
  15.         again:
  16.                 while((n = read(fd, buf, MAXLINE)) > 0) writen(fd, buf, n);

  17.         if (n < 0 && errno == EINTR)
  18.         {
  19.                 printf("got again\n");
  20.                 goto again;
  21.         }
  22.         else if (n < 0)
  23.         {
  24.                 printf("read error...........\n");
  25.         }
  26. }

  27. int main(int argc, char *argv[])
  28. {
  29.         int fd = socket(AF_INET, SOCK_STREAM, 0);
  30.         struct sockaddr_in ser, cli;

  31.         struct sigaction act, oact;
  32.         act.sa_handler = sig_handle;
  33.         sigemptyset(&act.sa_mask);
  34.         //act.sa_flags = 0;
  35.         if (sigaction(SIGCHLD, &act, &oact) < 0)
  36.         {
  37.                 printf("sigaction error\n");
  38.         }
  39.         else
  40.         {
  41.                 printf("sigact succ.\n");
  42.         }

  43.         bzero(&ser, sizeof(sockaddr_in));
  44.         bzero(&cli, sizeof(sockaddr_in));

  45.         ser.sin_family = AF_INET;
  46.         ser.sin_port = htons(5005);
  47.         ser.sin_addr.s_addr = htonl(INADDR_ANY);

  48.         socklen_t len = sizeof(sockaddr_in);
  49.         if (bind(fd, (struct sockaddr *)&ser, len) != 0)
  50.         {
  51.                 printf("bind error:%s.\n", strerror(errno));
  52.                 return 1;
  53.         }
  54.         else printf("bind success.\n");

  55.         listen(fd, 5);

  56.         while(true)
  57.         {
  58.                 int cfd = accept(fd, (struct sockaddr *)&cli, &len);
  59.                 if (cfd < 0 && errno == EINTR)
  60.                 {
  61.                         printf("accept intrrupt by intr,continue.\n");
  62.                         continue;
  63.                 }
  64.                 else
  65.                 {
  66.                         char c = 'A';//问题在这里,如果没有这个无关的定义就会断错误。
  67.                 }

  68.                 pid_t pid = 0;
  69.                 if ((pid = fork()) == 0)
  70.                 {
  71.                         close(fd);
  72.                         str_echo(cfd);
  73.                         printf("return from str_echo.\n");
  74.                         exit(0);
  75.                 }
  76.                 else
  77.                 {
  78.                         printf("fork pid = %d\n", pid);
  79.                 }

  80.                 close(cfd);
  81.                 printf("farther close cfd.\n");
  82.         }
  83.        
  84.         return 1;
  85. }
复制代码
问题是:
在服务端accept返回来的下面那段代码:
  1. if (cfd < 0 && errno == EINTR)
  2.                 {
  3.                         printf("accept intrrupt by intr,continue.\n");
  4.                         continue;
  5.                 }
  6.                 else
  7.                 {
  8.                         char c = 'A';
  9.                 }
复制代码
如果没有加char c = 'A';这一行,那么在处理完SIGCHLD信号后就段错误了。如果加了这一行就可以顺利运行。事实上着一行随便写个 int a = 1000;也不会段错误。这个怎么解析?不科学啊!这行代码应该不会有执行的机会的(因为正常的时候都是执行cfd < 0 && errno == EINTR 那个条件里面的代码)。但是没有这行代码,就立即段错误。求解!

论坛徽章:
1
2015年亚洲杯之巴林
日期:2015-02-05 20:34:47
2 [报告]
发表于 2012-11-06 21:49 |只看该作者
是不是由于父进程处理完SIGCHLD信号之后,不知道返回到那条执行语句(由于子进程已经结束了?),然后就返回一条随机地址,然后进程去执行那条随机地址的指令就段错误了?但是为什么加一条无关的 char c = 'A',就没有段错误了呢?
有问题的时候的gdb是这样的。(//char c = 'A'; 注释掉)在信号处理函数设置了断点
Breakpoint 1, sig_handle (sig=17) at ser.cpp:7
7                        pid = wait(&status);
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.47.el6.i686 libgcc-4.4.6-3.el6.i686 libstdc++-4.4.6-3.el6.i686
(gdb) n
8                        printf("child %d terminated\n", pid);
(gdb) n
child 1567 terminated/* 这里子进程结束了 */
10        }
(gdb) n
0x0081c8e7 in ?? () from /lib/libc.so.6  //这里应该是accept
(gdb) n
Cannot find bounds of current function//继续执行就段错误。
(gdb)

正常是这样,有char c = 'A';

Breakpoint 1, sig_handle (sig=17) at ser.cpp:7
7                        pid = wait(&status);
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.47.el6.i686 libgcc-4.4.6-3.el6.i686 libstdc++-4.4.6-3.el6.i686
(gdb) n
8                        printf("child %d terminated\n", pid);
(gdb) n
child 570 terminated
10        }
(gdb) n
0x00110424 in __kernel_vsyscall ()
(gdb) n
Single stepping until exit from function __kernel_vsyscall,
which has no line number information.
0x008e8051 in accept () from /lib/libc.so.6
(gdb) n
Single stepping until exit from function accept,
which has no line number information.
main (argc=1, argv=0xbffff2b4) at ser.cpp:70
70                        if (cfd < 0 && errno == EINTR)
(gdb) n
72                                printf("accept intrrupt by intr,continue.\n");
(gdb) n
accept intrrupt by intr,continue.
73                                continue;
(gdb) n
67                while(true)
(gdb) n
69                        int cfd = accept(fd, (struct sockaddr *)&cli, &len);
(gdb)

论坛徽章:
0
3 [报告]
发表于 2012-11-06 23:06 |只看该作者

论坛徽章:
1
2015年亚洲杯之巴林
日期:2015-02-05 20:34:47
4 [报告]
发表于 2012-11-06 23:10 |只看该作者
回复 3# skychgg

应该没有关系吧。
   

论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
5 [报告]
发表于 2012-11-06 23:51 |只看该作者
没看过最新signal相关代码, 说不清楚来龙去脉, 但很显然, 我将 act 初始化为  {0} 则一切正常了;
原本以为是 sa_restorer 的原因, 于是单单将其赋值为 0, 居然还是crash, 于是再实验, 将  sa_flags 赋值为 0, 一切正常了;再将sa_flags 设为 0, sa_restorer 赋值为随机值, 居然还是不崩溃。。。。。

据我所知, sa_restorer 应该指向一个函数指针, 似乎是用来替换默认的堆栈恢复函数的, 不明白怎么随意赋值居然没关系, 而 sa_flags 居然为crash 负责,  貌似它才是赋错值一样无关紧要才对的

论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
6 [报告]
发表于 2012-11-07 00:01 |只看该作者
操, 搞明白了, 果然还是 sa_restorer 搞的鬼,
加上这一句就貌似一切正常了:
act.sa_flags &= ~0x04000000;

摘自最新内核代码:
#define SA_RESTORER     0x04000000

论坛徽章:
1
2015年亚洲杯之巴林
日期:2015-02-05 20:34:47
7
发表于 2012-11-07 00:02
回复 5# zylthinking


感谢回答,但是我看了很多列子(包括网络上的还有unix环境高级编程、unix网络编程),都不用对struct sigaction act;进行初始化,sa_flag = 0也不会段错误,把struct sigaction act 定义为全局变量也不会段错误。在我所说的 那个else 里面加上一句赋值语句也不会段错误(加一句printf也不会段错误),但是没有这一个赋值语句就会段错误。我不知道怎么解析,不知道错在哪里?

   

论坛徽章:
1
2015年亚洲杯之巴林
日期:2015-02-05 20:34:47
8 [报告]
发表于 2012-11-07 00:03 |只看该作者
回复 6# zylthinking

act.sa_flags &= ~0x04000000;

请教一下,加上这句的作用是什么?我原来那样写有什么问题?非常感谢。

   

论坛徽章:
1
2015年亚洲杯之巴林
日期:2015-02-05 20:34:47
9 [报告]
发表于 2012-11-07 00:06 |只看该作者
回复 6# zylthinking

还有怎么解析,
  1. int cfd = accept(fd, (struct sockaddr *)&cli, &len);
  2.                 if (cfd < 0 && errno == EINTR)
  3.                 {
  4.                         printf("accept intrrupt by intr,continue.\n");
  5.                         continue;
  6.                 }
  7.                 else
  8.                 {
  9.                         //char c = 'A';
  10.                 }
复制代码
加上 char c = 'A';就不崩溃了呢?

论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
10 [报告]
发表于 2012-11-07 00:10 |只看该作者
sublx 发表于 2012-11-07 00:06
回复 6# zylthinking

还有怎么解析,加上 char c = 'A';就不崩溃了呢?


你的 sa_flags 本来就是随机值, 加上 c = 'a' 将内存布局改变了, 正好让 sa_flags  & SA_RESTORER == 0 了呗, 不加上, 正好让其 !=0 了呗, 都是概率事件, 碰到了就是碰到了
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP