Understanding Unix/Linux Programming-用户程序:play_again3

本文探讨了C程序中非阻塞输入的概念及其实现方式,对比了非阻塞与阻塞输入的不同,并通过具体示例代码展示了如何设置非阻塞模式,以及这种方式在实际应用中的利弊。
  1 /* play_again3.c
  2  * purpuse: ask if user wants another play 
  3  * better : instant response without echo
  4               set tty into no-delay mode
  5               read char , return result
  6               reset terminal mode on Internet
  7  * returns: 0 -> yes , 1 -> no 
  8  */
  9  
 10  #include <stdio.h>
 11  #include <stdlib.h>
 12  #include <fcntl.h>
 13  #include <termios.h>
 14  #include <string.h>
 15  
 16  #define ASK "Do you want another play?"
 17  #define TRIES 3
 18  #define SLEEPTIME 2
 19  #define BEEP putchar('\a');
 20 
 21 
 22  int get_response(char *);
 23  int get_ok_char(void);
 24  void set_nodelay_mode(void);
 25  void set_cr_noecho_mode(void);
 26  void tty_mode(int); 
 27  
 28  int main()
 29  {
 30      int response ;
 31      tty_mode(0);   // save tty mode
 32      set_cr_noecho_mode();
 33      set_nodelay_mode();
 34      response = get_response(ASK);
 35      tty_mode(1);   // restore tty mode
 36      return response ;
 37  }
 38  
 39  int get_response(char * qiz)
 40  {
 41      int input ;
 42      int maxtries = TRIES ;
 43      printf("%s(y/n)" , qiz);
 44      fflush(stdout);
 45      while(1)
 46      {
 47         BEEP ;
 48         sleep(SLEEPTIME);
 49         input = tolower(get_ok_char()) ;
 50         if(input == 'y')
 51         {
 52             printf("\n");
 53             return 0 ;
 54         }
 55         if(input == 'n')
 56         {
 57             printf("\n");
 58             return 1 ;
 59         }
 60         if(maxtries -- <= 0 )
 61         {
 62             printf("\n");
 63             return 2 ;
 64         }
 65         BEEP ;
 66      }
 67  }
 68  
 69 int get_ok_char(void)
 70 {
 71     int c ;
 72     while( (c = getchar() ) != EOF && strchr("yYnN" , c ) == NULL )
 73         ;
 74     return c ;
 75 }
 76 
 77 void set_cr_noecho_mode(void)
 78 {
 79     struct  termios ttystate ;
 80     tcgetattr(0 , &ttystate);
 81     ttystate.c_lflag &= ~ICANON ;   // No Buffering
 82     ttystate.c_lflag &= ~ECHO ;
 83     ttystate.c_cc[VMIN] = 1 ;   //Get one char one time 
 84     tcsetattr( 0 , TCSANOW , &ttystate);    
 85 }
 86 
 87 void set_nodelay_mode(void)
 88 {
 89     int termflags ;
 90     termflags = fcntl(0 , F_GETFL);
 91     termflags |= O_NDELAY ;
 92     fcntl(0 , F_SETFL , termflags) ;
 93 }
 94 
 95 void tty_mode(int mode)
 96 {
 97     static struct termios original_mode ;// 设置静态结构体变量
 98     if(mode == 0 )
 99     {
100         tcgetattr( 0 , & original_mode);// 存储原有设置
101     }
102     else
103     {
104         //还原原有设置
105         if( tcsetattr(0 , TCSANOW , & original_mode) == -1 )
106         {
107             perror("Restore tty settings failed!\n");
108         }
109     }
110 }

 这里使用到了非阻塞输入

  怎么解释非阻塞输入与阻塞输入?

  书上解释:

    当调用getchar或者read从文件描述符读取输入时,这些调用通常会等待输入,这叫做阻塞输入(block input)。在play_again的例子中,对于getchar的调用使得程序一直等待用户的输入,知道用户输入一个字符。程序被阻塞,知道能够获得某些字符或是检测到文件的末尾。那么如何关闭输入阻塞呢?

    阻塞不仅仅是终端连接的属性,而是任何一个打开的文件的属性。(也就是说阻塞实际上是文件的属性咯,不论是磁盘文件还是设备文件)。毕竟程序或者进程是与文件通过文件描述符连接的。

    程序可以通过fcntl或者open为文件描述符启动非阻塞输入(nonblock input)。play_again3使用fcntl为文件描述符开启O_NDELAY标志。

    关闭一个文件描述符的阻塞状态并且调用read结果会如何呢?如果能够获得输入,read会获得输入并且返回所获得的字数。如果没有输入字符,read返回0,这就像遇到文件末尾一样,如果有错误,read返回1。

    非阻塞操作内部实现非常简单。每个文件都有一块保存未读取数据的地方。如果文件描述符置位O_NDELAY,并且那块空间是空的,则read调用返回0。

    阅读O_NDELAY相关的Linux源代码,就可以了解实现细节。(准备以后再阅读源码吧

该程序的一些小问题:

  1.  运行在非阻塞模式,程序在调用getchar给用户输入字符之前睡眠2s,就算用户在1s内完成输入,程序也会在2s后得到字符
  2. 在显示提示符之后,对于fflush的调用。如果没有fflush,在调用getchar之前,提示符将不能显示。因为终端驱动程序不仅一行行地缓冲输入,而且还一行行地缓冲输出。驱动程序缓冲输出,直到它接收到一个换行符或者程序试图从终端读取输入。在这个例子中,为了给用户读提示符的时间,需要延迟读入,就必须调用fflush。
    1. 注意:fflush是将(输出)缓冲物理写入的函数。

该程序的一些大问题:

  该程序忽略一切它不想要的字母,只识别合法输入,并在规定时间间隔内无合法输入的情况下自动退出。如果输入Ctrl-C将会如何?不但会中止该程序,也会中止终端程序。为何?因为该程序运行到字符输入时,终端处于非阻塞状态,shell调用获取命令行,但是因为处于非阻塞状态,read立即返回0,程序结束时处于一个错误的状态。

  而在其他一些情况中,像bash和tcsh这些shell,当程序退出或者死亡时,它们会重置终端的属性。

arm-buildroot-linux-gnueabi-gcc: WARNING: unsafe header/library path used in cross-compilation: &#39;-I/usr/local/include&#39; ../lib/tevent/tevent_req.c: In function &#39;_tevent_req_create&#39;: ../lib/tevent/tevent_req.c:78:8: warning: implicit declaration of function &#39;talloc_pooled_object&#39; [-Wimplicit-function-declaration] 78 | req = talloc_pooled_object( | ^~~~~~~~~~~~~~~~~~~~ ../lib/tevent/tevent_req.c:79:12: error: expected expression before &#39;struct&#39; 79 | mem_ctx, struct tevent_req, 2, | ^~~~~~ The following command failed: arm-buildroot-linux-gnueabi-gcc -I../lib/zlib -DMAX_DEBUG_LEVEL=-1 -D__location__=\ -ffunction-sections -fdata-sections -I. -I/data/red-round3/red-round3/Iplatform/openwrt/build_dir/target-arm-openwrt-linux-uclibc-be220v1/samba-3.6.25/source3 -I/data/red-round3/red-round3/Iplatform/openwrt/build_dir/target-arm-openwrt-linux-uclibc-be220v1/samba-3.6.25/source3/../lib/popt -I/data/red-round3/red-round3/Iplatform/openwrt/build_dir/target-arm-openwrt-linux-uclibc-be220v1/samba-3.6.25/source3/../lib/iniparser/src -Iinclude -I./include -I. -I. -I./../lib/replace -I./librpc -I./.. -I./../lib/talloc -I./../lib/tevent -I../lib/tdb/include -DHAVE_CONFIG_H -I/data/red-round3/red-round3/Iplatform/openwrt/staging_dir/target-arm-openwrt-linux-uclibc-be220v1/usr/include -I/data/red-round3/red-round3/Iplatform/openwrt/staging_dir/target-arm-openwrt-linux-uclibc-be220v1/include -I/data/red-round3/red-round3/Iplatform/openwrt/staging_dir/usr-be220v1/include -I/data/red-round3/red-round3/Iplatform/openwrt/../../bcm504L04/toolchain/opt/toolchains/crosstools-arm_softfp-gcc-10.3-linux-4.19-glibc-2.32-binutils-2.36.1/usr/include -Iinclude -I./include -I. -I. -I./../lib/replace -I./librpc -I./.. -I./../lib/popt -I/usr/local/include -I/data/red-round3/red-round3/Iplatform/openwrt/build_dir/target-arm-openwrt-linux-uclibc-be220v1/samba-3.6.25/source3/lib -I.. -D_SAMBA_BUILD_=3 -D_SAMBA_BUILD_=3 -c ../lib/tevent/tevent_req.c -o ../lib/tevent/tevent_req.o Makefile:1675: recipe for target &#39;../lib/tevent/tevent_req.o&#39; failed make[4]: *** [../lib/tevent/tevent_req.o] Error 1 make[4]: Leaving directory &#39;/data/red-round3/red-round3/Iplatform/openwrt/build_dir/target-arm-openwrt-linux-uclibc-be220v1/samba-3.6.25/source3&#39; Makefile:224: recipe for target &#39;/data/red-round3/red-round3/Iplatform/openwrt/build_dir/target-arm-openwrt-linux-uclibc-be220v1/samba-3.6.25/.built&#39; failed make[3]: *** [/data/red-round3/red-round3/Iplatform/openwrt/build_dir/target-arm-openwrt-linux-uclibc-be220v1/samba-3.6.25/.built] Error 2 make[3]: Leaving directory &#39;/data/red-round3/red-round3/Iplatform/packages/opensource/samba&#39; package/Makefile:133: recipe for target &#39;package/feeds/iplatform/samba/compile&#39; failed make[2]: *** [package/feeds/iplatform/samba/compile] Error 2 make[2]: Leaving directory &#39;/data/red-round3/red-round3/Iplatform/openwrt&#39; /data/red-round3/red-round3/Iplatform/openwrt/include/toplevel.mk:184: recipe for target &#39;package/samba/compile&#39; failed make[1]: *** [package/samba/compile] Error 2 make[1]: Leaving directory &#39;/data/red-round3/red-round3/Iplatform/openwrt&#39; Makefile:234: recipe for target &#39;iplatform_package/samba/compile&#39; failed make: *** [iplatform_package/samba/compile] Error 2
最新发布
11-02
<think>Hmm, the user is reporting a cross-compilation failure for Samba on an ARM platform, specifically related to unsafe header paths and missing function declarations. This appears to be a continuation of previous discussions about Samba configuration issues. Looking at the error logs, two main issues stand out: first, the compiler warning about unsafe header path &#39;-I/usr/local/include&#39; which indicates contamination from the host system&#39;s include directories. Second, the fatal error regarding &#39;talloc_pooled_object&#39; function being implicitly declared and then a syntax error with &#39;struct&#39; - classic symptoms of header file mismatch. The context suggests this is part of an OpenWRT build system. From prior interactions, I recall we&#39;ve already addressed similar configuration issues, but now it&#39;s manifesting during actual compilation. The user seems quite technical, likely an embedded systems developer struggling with cross-compilation nuances. What&#39;s particularly interesting is the version conflict angle - &#39;talloc_pooled_object&#39; was introduced in talloc v2.1.0, but the build system appears to be picking up older headers. The path contamination makes this worse by mixing host and target headers. The solution needs to address both the immediate compilation failure and the underlying configuration flaws. I should emphasize: 1) Complete isolation of build environments 2) Forcing internal library usage 3) Temporary patching for backward compatibility 4) Strict path sanitization. The patch approach is safer than upgrading since user is locked to Samba 3.6.25. This user clearly understands build systems but might be missing subtle cross-compilation pitfalls. The questions I&#39;ll pose should help deepen their understanding of build environment isolation and legacy system adaptation techniques. </think>### 交叉编译污染与函数兼容性错误解决方案 #### 关键问题诊断 1. **路径污染警告** `-I/usr/local/include` 导致编译器错误引用宿主机头文件 2. **函数声明冲突** `talloc_pooled_object()` 在交叉编译环境中未定义 3. **语法解析失败** 因头文件污染导致 `struct tevent_req` 解析异常 #### 完整修复方案 ##### 1. 清除路径污染(关键步骤) ```bash # 永久移除污染路径 export CFLAGS=$(echo $CFLAGS | sed &#39;s|-I/usr/local/include||g&#39;) export LDFLAGS=$(echo $LDFLAGS | sed &#39;s|-L/usr/local/lib||g&#39;) # 验证清除效果 echo $CFLAGS | grep -q "/usr/local" || echo "清除成功" ``` ##### 2. 强制使用内部talloc库 修改配置参数: ```diff CONFIGURE_ARGS += \ ... ++ --with-libtalloc=internal \ ++ --with-libtevent=internal \ ``` ##### 3. 应用运行时兼容补丁 ```patch --- a/lib/tevent/tevent_req.c +++ b/lib/tevent/tevent_req.c @@ -75,7 +75,12 @@ struct tevent_req *_tevent_req_create(TALLOC_CTX *mem_ctx, void *async_private, const char *type) { - struct tevent_req *req; + struct tevent_req *req = NULL; + +#if defined(HAVE_TALLOC_POOLED_OBJECT) + req = talloc_pooled_object(mem_ctx, struct tevent_req, 2, 0); +#else + req = talloc(mem_ctx, struct tevent_req); +#endif - req = talloc_pooled_object(mem_ctx, struct tevent_req, 2); if (req == NULL) return NULL; ``` ##### 4. 添加编译宏定义 ```bash # 禁用缺失函数 export CFLAGS="$CFLAGS -DHAVE_TALLOC_POOLED_OBJECT=0" # 添加ARM信号宏 export CFLAGS="$CFLAGS -DTEVENT_NUM_SIGNALS=64" ``` #### 验证流程 ```bash # 清理重建 make distclean # 应用补丁 patch -p1 < tevent_fix.patch # 重新配置 ./configure $CONFIGURE_ARGS # 编译测试 make V=1 ../lib/tevent/tevent_req.o 2>&1 | grep "talloc_pooled_object" ``` > **紧急替代方案**:若仍失败,在配置中禁用tevent模块: > ```bash > CONFIGURE_ARGS+=" --without-libtevent" > ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值