67677新澳门手机版 > 服务器&运维 > Linux 平台相关代码的 C++ 解决方案
Linux 平台相关代码的 C++ 解决方案
2020-03-14 20:23

本文首先提出平台相关代码造成的两个问题,然后针对这两个问题循序渐进依次提出解决方案,在分析了前两个方案弱点的基础上,最后着重介绍一种基于多种设计模式的 Linux 平台相关代码的解决方案,并给出此方案的 C++ 实现。

希望我的一点经验能给大家带来帮助,导致Linux Nginx出错的原因也许还有很多,不过在你遇到错误时,可以先检查一下你程序中的字符串,暂时把他们置为,试试看。没准就是他引起Linux Nginx问题啊。

关于Interfacing with platform specific code的译文

#include <errno.h>

#include <string.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/stat.h>

//! @brief 生成目录

/*!

以指定的权限创建目录.

对于指定要创建的目录,如果其父目录不存在(递归至要生成的目标上报的根目录),那么会创建其对应的父目录

例如,指定要创建的目录: "/foo/bar",如果/foo/目录不存在,那么在创建 bar 目录之前会创建 /foo 目录

对于要生成的目标目录,"./foo/bar" 等价于 "foo/bar"

@param szDirectoryPath 要创建的目录的路径

@param iDirPermission 创建目录时,为所创建的目录所指定的权限

@return 如果创建成功,返回<b>true</b>;如果创建失败,返回<b>false</b>.

*/

static bool CreateDirectory( const char *szDirectoryPath , int iDirPermission = 0744 )

{

if ( NULL == szDirectoryPath )

{

#ifdef DEBUG

fprintf( stderr , "[%s][%d][%s][parameter < szDirectoryPath > for < CreateDirectory > should not be NULL]n" , 

__FILE__ , __LINE__ , __FUNCTION__ );

#endif

return false;

}

const int iPathLength = static_cast< int >( strlen( szDirectoryPath ) );

if ( iPathLength > PATH_MAX )

{

#ifdef DEBUG

fprintf( stderr , "[%s][%d][%s][the path length(%d) exceeds system max path length(%d)]n" , 

__FILE__ , __LINE__ , __FUNCTION__ , iPathLength , PATH_MAX );

#endif

return false;

}

char szPathBuffer[ PATH_MAX ] = { 0 };

memcpy( szPathBuffer , szDirectoryPath , iPathLength );

for ( int i = 0 ; i < iPathLength ; ++i )

{

char &refChar = szPathBuffer[ i ];

//目录分隔符

if ( ( '/' == refChar ) && ( 0 != i ) )

{

refChar = '';

//判断当前目录是否存在

int iStatus = access( szPathBuffer , F_OK );

if ( 0 != iStatus )

{

if ( ( ENOTDIR == errno ) || ( ENOENT == errno ) )

{

//以指定权限创建目录

iStatus = mkdir( szPathBuffer , iDirPermission );

if ( 0 != iStatus )

{

#ifdef DEBUG

fprintf( stderr , "[%s][%d][%s][< mkdir > fail , ErrCode:%d , ErrMsg:%s]n" , 

__FILE__ , __LINE__ , __FUNCTION__ , errno , strerror( errno ) );

#endif

return false;

}

}

else

{

#ifdef DEBUG

fprintf( stderr , "[%s][%d][%s][< access > fail , RetCode: %d , ErrCode:%d , ErrMsg:%s]n" , 

__FILE__ , __LINE__ , __FUNCTION__ , iStatus , errno , strerror( errno ) );

#endif

return false;

}

}

refChar = '/';

}

}

return true;

}

Linux 平台相关代码带来的问题

目前市场上存在着许多不同的 Linux 平台(例如:RedHat, Ubuntu, Suse 等),各大厂商和社区都在针对自己支持的平台进行优化,为使用者带来诸多方便的同时也对软件研发人员在进行编码时带来不少问题:

  1. 由于程序中不可避免的存在平台相关代码(系统调用等),软件研发人员为了保证自己的产品在各个 Linux 平台上运行顺畅,一般都需要在源代码中大量使用预编译参数,这样会大大降低程序的可读性和可维护性。
  2. 接口平台无关性的原则是研发人员必须遵循的准则。但是在处理平台相关代码时如果处理不当,此原则很有可能被破坏,导致不良的编码风格,影响代码的扩展和维护。

本文将针对这两个问题循序渐进依次提出解决方案。

#!/bin/bash  # v.0.0.1  # create by jackbillow at 2007.10.15  # nginx - This shell script takes care of starting and stopping nginx.  # chkconfig: - 60 50  # description: nginx [engine x] is light http web/proxy server  # that answers incoming ftp service requests.  # processname: nginx  # config: /usr/local/nginx/conf/nginx.conf  nginx_path="/usr/local/nginx" nginx_pid="/var/run/nginx/nginx.pid" # Source function library.  . /etc/rc.d/init.d/functions  # Source networking configuration.  . /etc/sysconfig/network  # Check that networking is up.  [ ${NETWORKING} = "no" ] && exit 0  [ -x $nginx_path/sbin/nginx ] || exit 0  RETVAL=0 prog="nginx" start() {  # Start daemons.  if [ -e $nginx_pid -a ! -z $nginx_pid ];then  echo "nginx already running...."  exit 1  fi  if [ -e $nginx_path/conf/nginx.conf ];then  echo -n $"Starting $prog: "  $nginx_path/sbin/nginx -c $nginx_path/conf/nginx.conf &  RETVAL=$?  [ $RETVAL -eq 0 ] && {  touch /var/lock/subsys/$prog  success $"$prog"  }  echo  else  RETVAL=1 fi  return $RETVAL  }  # Stop daemons.  stop() {  echo -n $"Stopping $prog: "  killproc -d 10 $nigx_path/sbin/nginx  RETVAL=$?  echo  [ $RETVAL = 0 ] && rm -f $nginx_pid /var/lock/subsys/$prog  }  # See how we were called.  case "$1" in  start)  start  ;;  stop)  stop  ;;  reconfigure)  stop  start  ;;  status)  status $prog  RETVAL=$?  ;;  *)  echo $"Usage: $0 {start|stop|reconfigure|status}"  exit 1  esac  exit $RETVAL 

这是一个关于这个问题的论坛讨论,还包括iOS的具体内容。

通过设置预编译选项来处理平台相关代码

通过为每个平台设置相关的预编译宏能够解决 Linux 平台相关代码的问题,实际情况下,很多软件开发人员也乐于单独使用这种方法来解决问题。

假设现有一动态库 Results.so,SomeFunction() 是该库的一个导出函数,该库同时为 Rhel,Suse,Ubuntu 等三个平台的 Linux 上层程序服务。(后文例子均基于此例并予以扩展。)

以上就是对Linux Nginx的详细介绍,希望大家有所收获。

有时,有必要访问平台特定的API,例如添加由诸如Swarm的框架提供的排行榜功能或者广告服务集成。

清单 1. 设置预编译选项示例代码如下:
// Procedure.cpp 
 void SomeFunction() 
 { 
    //Common code for all linux 
    ...... 
    ...... 
 #ifdef RHEL 
    SpecialCaseForRHEL(); 
 #endif 

 #ifdef SUSE 
    SpecialCaseForSUSE(); 
 #endif 

 #ifdef UBUNTU 
    SpecialCaseForUBUNTU(); 
 #endif 

    //Common code for all linux 
    ...... 
    ...... 

 #ifdef RHEL 
    SpecialCase2ForRHEL(); 
 #endif 

 #ifdef SUSE 
    SpecialCase2ForSUSE(); 
 #endif 

 #ifdef UBUNTU 
    SpecialCase2ForUBUNTU(); 
 #endif 

    //Common code for all linux 
 ...... 
 ...... 
 }

开发人员可以通过设置 makefile 宏参数或者直接设置 gcc 参数来控制实际编译内容。

例如:

gcc -D RHEL Procedure.cpp -o Result.so -lstdc++   // Use RHEL marco

新澳门手机版mg游戏,SpecialCaseForRHEL(),SpecialCaseForSUSE(),SpecialCaseForUBUNTU() 分别在该库 (Results.so) 的其他文件中予以实现。

新澳门手机版mg游戏 1

以下示例假定我们仅在Android上实现非常简单的排行榜API。 对于其他目标平台,我们只需要记录调用或提供模拟返回值。

图 1. 清单 1 代码的结构图

新澳门手机版mg游戏 2

Android API如下所示:

带来的问题

  1. SomeFunction() 函数代码冗余,格式混乱。本例仅涉及三个预编译选项,但实际情况中由于 Linux 版本众多并且可能涉及操作系统位数的问题,增加对新系统的支持会导致预编译选项不断增多,造成 SomeFunction() 函数结构十分混乱。
  2. 新增其他平台相关接口(例如:增加 SpecialCase3ForRHEL(),SpecialCase3ForSUSE(),SpecialCase3ForUBUNTU),会成倍增加代码中预编译宏的数量。
  3. 破坏了接口平台无关性的原则。SpecialCaseForRHEL(),SpecialCaseForSUSE(),SpecialCaseForUBUNTU() 只是同一功能各个平台的不同实现,属于封装内容,不应该分开暴露给调用者。

可见,简单利用预编译宏来解决平台相关代码产生的问题不是一个好的方法,并没有解决本文开始提出的两个问题。后文将通过三个方案依次解决这些问题。

/** Let's assume this is the API provided by Swarm **/
public class LeaderboardServiceApi {
   public void submitScore(String user, int score) { ... }
}

解决方案 1:根据接口平台无关性原则进行优化

实质上,SpecialCaseForRHEL(),SpecialCaseForSUSE(),SpecialCaseForUBUNTU() 只是同一功能在不同平台上的实现,SpecialCase2ForRHEL(),SpecialCase2ForSUSE(),SpecialCase2ForUBUNTU() 亦如此。对于调用者,应该遵循接口平台无关性的原则,使用统一的接口进行调用,这样才能简化代码,使代码易于维护。

第一步是以接口的形式创建API的抽象。该接口被放入核心项目中: