面试题——蓝

1. 可重入是什么概念?

可重入代码(Reentry code)也叫纯代码(Pure code)是一种允许多个进程同时访问的代码。为了使各进程所执行的代码完全相同,故不允许任何进程对其进行修改。程序在运行过程中可以被打断,并由开始处再次执行,并且在合理的范围内(多次重入,而不造成堆栈溢出等其他问题),程序可以在被打断处继续执行,且执行结果不受影响。

(不应该有全局变量或静态变量,因为这些变量会保存某一个进程的修改。可重入代码中的变量应该都是局部变量,每次重新调用时变量重新被赋值,从而保证,每个进程对它的访问都产生同样的结果。)

void test()
{
  int i;
  i=2;
  printf("%d\n",i );
  i++;
  printf("%d\n",i);
}
结果:每次都是:2 \n 3
void test()
{
  static int i=2;
  printf("%d\n",i );
  i++;
  prinft("%d\n",i);
}
结果:第一次:2 \n 3;第二次:3 \n 4
 

2. 如何确保代码可重入?

若一个程序或子程序可以安全的被并行执行,则称其为可重入(reentrant或re-entrant)的;即,当该子程序正在运行时,可以再次进入并执行它。
若一个函数是可重入的,则该函数:
不能含有静态(全局)非常量数据。 不能返回静态(全局)非常量数据的地址。 只能处理由调用者提供的数据。 不能依赖于单实例模式资源的锁。 不能调用不可重入的函数。 多 '用户/对象/进程优先级' 以及多进程一般会使得对可重入代码的控制变得复杂。同时,IO代码通常不是可重入的,因为他们依赖于像磁盘这样共享的、单独的资源。
 
3. 进程间通讯方式?线程间通讯方式?
进程间:管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
    有名管道 (namedpipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
    信号量(semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
    消息队列( messagequeue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
    信号 (sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
    共享内存(shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
    套接字(socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
线程间通信可以通过下列三种方法:  1)使用全局变量实现线程间通信  2)使用消息实现线程间通信  3)使用CEvent类实现线程间通信
MFC下常用方法:
1.全局变量  注:定义全局变量时最好使用volatile来定义,以防编译器对此变量进行优化。
2.Message消息机制,常用的Message通信的接口主要有两个:PostMessage为线程向主窗口发送消息。而PostThreadMessage是任意两个线程之间的通信接口。
3.CEvent对象,是MFC中的一个对象,可以通过对CEvent的触发状态进行改变,从而实现线程间的通信和同步。
线程间同步机制:锁机制:包括互斥锁、条件变量、读写锁(或者说 互斥mutex、信号量Semaphore、临界区Critical section)
    互斥锁提供了以排他方式防止数据结构被并发修改的方法。
    读写锁允许多个线程同时读共享数据,而对写操作是互斥的。
    条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。
    信号量机制(Semaphore):包括无名线程信号量和命名线程信号量
    信号机制(Signal):类似进程间的信号处理
  线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制。
 
4. volatile关键词含义?————直接存取原始内存地址
volatile是一个类型修饰符(type specifier)。它是被设计用来修饰被不同线程访问和修改的变量。如果没有volatile,基本上会导致这样的结果:要么无法编写多线程程序,要么编译器失去大量优化的机会,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。
volatile的作用: 作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值.就是防止编译器对代码进行优化
常用于:1)并行设备的硬件寄存器(如:状态寄存器)
    2)一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) 
    3)多线程应用中被几个任务共享的变量
  1).一个参数既可以是const还可以是volatile吗?是的。只读的状态寄存器。是volatile因为它可能被意想不到地改变,是const因为程序不应该试图去修改它。
  2). 一个指针可以是volatile 吗?是的。如:当一个中断服务子程序修改一个指向一个buffer的指针时。
  3). 下面的函数被用来计算某个整数的平方,它能实现预期设计目标吗?  intsquare(volatileint*ptr)  {  return*ptr**ptr;  }
  *ptr的值可能在两次取值语句之间发生改变,因此a和b可能是不同的。结果,这段代码可能返回的不是你所期望的平方值!
 
5. 关键字static作用?与关键字extern的区别?

  1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
  2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
  3) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。

  (本地化数据和代码范围的好处和重要性)

  extern和static完全不同,static会定义一个变量,extern不能定义一个变量,extern表示的是“已经存在一个变量,但是不在当前的编译单元内,需要在其他编译单元中寻找。
  文件a.c
  static int i;   //只在a文件中用
  int j;            //在工程里用
  static void init(){}  //只在a文件中用
  void callme()          //在工程中用
  {static int sum;}

  上面的全局 i 变量和 init() 函数只能用在a.c文件中,全局变量sum的作用域只在callme里。

  变量j和函数callme()的全局限扩充到整个工程文件。

  所以可以在下面的b.c中用extern关键字调用。extern告诉编译器这个变量或者函数在其他文件里已经被定义了。

  文件b.c
  extern int j;                //调用a文件里的
  extern void callme();   //调用a文件里的
  int main(){}

    extern的另外用法是当C和C++混合编程时如果c++调用的是c源文件定义的函数或者变量,那么要加extern来告诉编译器用c方式命名函数:

    文件A.cpp调用a.c里面的变量i和函数callme()
    extern "C"  //在c++文件里调用c文件中的变量
    {  int j;  void callme();  }
    int main()
    {  callme();  }
 
6. 查看当前目录下是否存在一个叫abc的文件
  VB——if dir("c:\a.txt")<>"" then  存在 endif
  php——<?php   $filename = '/path/to/foo.txt'; if (file_exists($filename))
      { echo "The file $filename exists"; }
      else { echo "The file $filename does not exist"; } ?> 
  Linux下  ( if access(const char* pathname, F_OK)==0)  存在 (F_OK文件是否存在  R_OK读权限  W_OK写权限  X_OK执行权限
       ( if opendir(const char* pathname)==0)  存在(不存在 NULL)
      find -name "$(date +%F)*" -exec mv {} ./history \;发现当前目录下有用当天日期为文件名的文件,即做MV
  C++  #include <iostream>  #include <fstream>  using namespace std;  #define FILENAME "stat.dat"
        int main(){     fstream _file;     _file.open(FILENAME,ios::in);     
          if(!_file)     {  cout<<FILENAME<<"没有被创建";  }
             else  {  cout<<FILENAME<<"已经存在";}  return 0;  } 
  C语言  #include  <io.h>  #include  <stdio.h>  #include  <stdlib.h>
        void main( void ) { if( (_access( "ACCESS.C", 0 )) != -1 ) { printf( "File ACCESS.C exists " );    
                   if(   (_access( "ACCESS.C", 2 )) != -1 )      printf( "File ACCESS.C has write permission " );   }  }
 
7. 嵌入式系统常对寄存器或变量进行位操作,给定一个整形变量value,设置value的bit 3,清除value的bit 3,以上操作保持其它位不变
  value |=0x01<<3  ;  value &=-(0x01<<3)
 
8. 嵌入式系统访问某特定内存位置,32位机要求设置一绝对地址为0x000067a9的整形变量值为0xaa66,代码完成
  若这个地址是代码段(方法)位置,且已知函数为void型,无参,如何调用?
  1) int* p;  p=0x000067a9;  *p=0xaa66  2)  void (*p)();声明  p=0x000067a9;定义  *p();调用
 
 
posted on 2014-11-03 21:14  1129496211  阅读(145)  评论(0编辑  收藏  举报