C语言操作hdfs

作者: zlcook | 来源:发表于2018-04-26 17:58 被阅读26次

需求

  • 在c++程序中操作hdfs实现数据的读取和写入。

目标

  • 写一个简单的c程序,将一句话写到hdfs上的一个文件中,并查看内容是否写进去了。

环境

思路

  • 安装hadoop
  • 在hdfs上创建一个文件:/liang/hello.txt
  • 编写writepro.c程序,程序中调用hdfs API将“Hello, World!”写到上述文件中。
  • 编译writepro.c成目标文件writepro
  • 运行writepro
  • 将hdfs上的/liang/hello.txt内容拷贝到本地,并使用cat命令查看内容。(完)

安装并运行

  • 解压hadoop-2.7.6.tar.gz到/usr路径下
  • 在etc/profile中配置HADOOP_HOME和 LD_LIBRARY_PATH
JAVA_HOME=/usr/java/jdk1.8.0_151
GCC_HOME=/usr/local/gcc-4.8.5
HADOOP_HOME=/usr/hadoop-2.7.6
PATH=$HADOOP_HOME/bin:$GCC_HOME/bin:$JAVA_HOME/bin:$PATH
export HADOOP_HOME GCC_HOME JAVA_HOME PATH
export LD_LIBRARY_PATH=$JAVA_HOME/jre/lib/amd64/server

在hdfs上建文件

#创建liang目录
hdfs dfs -mkdir /liang
#在本地创建hello.txt文件
touch hello.txt
#将hello.txt发送到/liang目录下
hdfs dfs -put hello.txt /liang
  • 验证文件已创建


    image.png

编写writepro.c程序

//hdfs操作api头文件
#include "hdfs.h"
#include <stdio.h>

int main(int argc, char **argv) {
    //连接hdfs
    hdfsFS fs = hdfsConnect("default", 0);
    //要写的文件路径
    const char* writePath = "/liang/hello.txt";
  //获取写文件对象
    hdfsFile writeFile = hdfsOpenFile(fs, writePath, O_WRONLY |O_CREAT, 0, 0, 0);
    if(!writeFile) {
          fprintf(stderr, "Failed to open %s for writing!\n", writePath);
          exit(-1);
    }
  //要写的内容
    char* buffer = "Hello, World!";
  //开始写内容
    tSize num_written_bytes = hdfsWrite(fs, writeFile, (void*)buffer, strlen(buffer)+1);
  //flush一下
    if (hdfsFlush(fs, writeFile)) {
           fprintf(stderr, "Failed to 'flush' %s\n", writePath);
          exit(-1);
    }
 //关闭连接
    hdfsCloseFile(fs, writeFile);
}

使用gcc编译成writepro

  • 编译成最终执行文件的过程为:源码(.c)---编译-->目标文件(.o)---链接--->执行文件(后缀依据平台或编译器而定)。链接:将程序中使用的库函数与相关路径填入到目标文件中。

  • 要编译成执行文件,就需要指定hdfs.h头文件的位置,以及使用的动态链接库libhdfs.so和动态链接库的位置。动态链接库使用JNI技术实现了c调用java。

  • hdfs.h头文件位置在$HADOOP_HOME/include 下,编译时指定使用libhdfs.so动态链接库,libhdfs.so动态链接库在$HADOOP_HOME/lib/native目录下。

  • 程序运行过程中需要调用动态链接库,而动态链接库在磁盘中,而程序在内存中,速度不一致,所以要先将动态链接库先加载到高速缓存中。

  • 参考:

动态链接库加载到缓存中

  • 知识点:ldconfig与/etc/ld.so.conf
  • ldconfig会将ld.so.conf中配置的目录下的动态链接库加载到高速缓存中
  • 打开ld.so.conf,添加$HADOOP_HOME/lib/native目录


    配置动态链接库
  • 使上面配置其作用
# 下面执行完后不会有任何信息显示
ldconfig
  • 可以使用下面命令查看所有被加载的动态链接库
idconfig -p
显示格式:函数库名称 =》该函数库实际路径

编译成writepro文件

[root@CentOS usr]# gcc writepro.c -I$HADOOP_HOME/include -L$HADOOP_HOME/lib/native -lhdfs -o writepro
writepro.c: 在函数‘main’中:
writepro.c:14:11: 警告:隐式声明与内建函数‘exit’不兼容 [默认启用]
           exit(-1);
           ^
writepro.c:19:71: 警告:隐式声明与内建函数‘strlen’不兼容 [默认启用]
     tSize num_written_bytes = hdfsWrite(fs, writeFile, (void*)buffer, strlen(buffer)+1);
                                                                       ^
writepro.c:23:11: 警告:隐式声明与内建函数‘exit’不兼容 [默认启用]
           exit(-1);
  • -I指定"hdfs.h"头文件位置、-l指定使用的动态链接库、-L指定到哪里去寻找使用的动态链接库。
    • -lhdfs是-l和hdfs两个部分组成,hdfs指明的是libhdfs.so这个函数库,其中lib和扩展名(.a或so)不用写。
  • 编译会有警告。

运行writepro

参考文档:CLASSPATH

  • libhdfs.so动态链接库实现了c调用hdfs java程序,即其依赖于java,所以hadoop的jar包和相关配置文件也就需要加载到内存中。为此在运行前需要配置CLASSPAT环境变量,这样在程序运行过程中就可以根据CLASSPATH指定的路径去加载jar和相关配置到内存,以提供c通过JNI调用。

  • 配置临时classpath

#hadoop classpath --glob命令会生成classpath所需内容
[root@CentOS /]# export CLASSPATH=`hadoop classpath --glob`
  • 运行程序
[root@CentOS /]# ./usr/writepro
18/04/26 23:59:47 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
  • 执行会有一个警告。

查看内容是否写入

  • 将hdfs上hello.txt内容读取到helloresult.txt中
[root@CentOS /]# hadoop fs -get /liang/hello.txt helloresult.txt
[root@CentOS /]# cat helloresult.txt
Hello, World!

总结

  • 不知道对不对


    c-hdfs调用关系

libjvm.so动态链接库找不到问题

1.编译时报 libjvm.so动态链接库找不到

[root@CentOS usr]# gcc writepro.c -I$HADOOP_HOME/include -L$HADOOP_HOME/lib/native -lhdfs -o writepro

/usr/bin/ld: warning: libjvm.so, needed by /usr/hadoop-2.7.6/lib/native/libhdfs.so, not found (try using -rpath or -rpath-link)
/usr/hadoop-2.7.6/lib/native/libhdfs.so: undefined reference to `JNI_CreateJavaVM@SUNWprivate_1.1'
/usr/hadoop-2.7.6/lib/native/libhdfs.so: undefined reference to `JNI_GetCreatedJavaVMs@SUNWprivate_1.1'
collect2: 错误:ld 返回 1

2.问题分析

  • libjvm.so位置:/usr/java/jdk1.8.0_151/jre/lib/amd64/server目录下

  • writepro依赖libhdfs.so,而libhdfs.so依赖libjvm.so,在编译时需要指定lib的路径,在执行时也需要指定寻找路径。

  • /usr/hadoop-2.7.6/lib/native/libhdfs.so依赖libjvm.so,动态链接库,而libjvm.so找不到。在编译时通过-Wl,rpath、LD_LIBRARY_PATH、ld.so.conf等配置libjvm.so路径。

解决方法

  • 通过LD_LIBRARY_PATH环境变量指定libjvm寻找路径(上面编译就是采用这一种)
# 指定编译和执行寻找libjvm路径
export LD_LIBRARY_PATH=$JAVA_HOME/jre/lib/amd64/server
gcc writepro.c -I$HADOOP_HOME/include -L$HADOOP_HOME/lib/native -lhdfs -o writepro
  • 或者编译时通过-Wl,rpath指定寻找路径
#指定编译和执行寻找libjvm路径
gcc writepro.c -I$HADOOP_HOME/include -L$HADOOP_HOME/lib/native -lhdfs -Wl,-rpath=${JAVA_HOME}/jre/lib/amd64/server -o writepro
  • 或者在ld.so.conf中配置运行时寻找路径
#-ljvm -L${JAVA_HOME}/jre/lib/amd64/server让其编译通过
gcc writepro.c -I$HADOOP_HOME/include -L$HADOOP_HOME/lib/native -lhdfs -ljvm -L${JAVA_HOME}/jre/lib/amd64/server -o writepro
#在ld.so.conf中添加libjvm路径让其其执行通过,配置完要执行ldconfig
[root@CentOS usr]# vim /etc/ld.so.conf
include ld.so.conf.d/*.conf
/usr/hadoop-2.7.6/lib/native
/usr/java/jdk1.8.0_151/jre/lib/amd64/server

查看writepro所依赖的动态函数库

  • 上述三种方案编译后的writepro都可以正常执行,并且不冲突。下面查看writepro和libhdfs.so所依赖的动态链接库。
# 库=》对应路径(地址)
[root@CentOS usr]# ldd writepro
    linux-vdso.so.1 =>  (0x00007ffe329d6000)
    libhdfs.so.0.0.0 => /usr/hadoop-2.7.6/lib/native/libhdfs.so.0.0.0 (0x00007f32dbfdb000)
    libc.so.6 => /lib64/libc.so.6 (0x000000310f800000)
    libjvm.so => /usr/java/jdk1.8.0_151/jre/lib/amd64/server/libjvm.so (0x00007f32dafe3000)
    libdl.so.2 => /lib64/libdl.so.2 (0x0000003110000000)
    libpthread.so.0 => /lib64/libpthread.so.0 (0x000000310fc00000)
    /lib64/ld-linux-x86-64.so.2 (0x000000310f400000)
    libm.so.6 => /lib64/libm.so.6 (0x0000003110800000)

root@CentOS usr]# ldd /usr/hadoop-2.7.6/lib/native/libhdfs.so
    linux-vdso.so.1 =>  (0x00007fff8b31c000)
    libjvm.so => /usr/java/jdk1.8.0_151/jre/lib/amd64/server/libjvm.so (0x00007fb42669f000)
    libdl.so.2 => /lib64/libdl.so.2 (0x00007fb42649a000)
    libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fb42627d000)
    libc.so.6 => /lib64/libc.so.6 (0x00007fb425ee9000)
    /lib64/ld-linux-x86-64.so.2 (0x000000310f400000)
    libm.so.6 => /lib64/libm.so.6 (0x00007fb425c64000)

执行writepro出错

  • writepro编译完成后,对于通过LD_LIBRARY_PATH和ld.so.conf方案解决libjvm问题的情况,如果将LD_LIBRARY_PATH和ld.so.conf对应的libjvm内容去掉,再次执行writepro时就会出现如下错误:
[root@CentOS usr]# ./writepro 
./writepro: error while loading shared libraries: libjvm.so: cannot open shared object file: No such file or directory
  • 查询ldd writepro也会出现not found问题
[root@CentOS usr]# ldd writepro
    linux-vdso.so.1 =>  (0x00007ffdf21c6000)
    libhdfs.so.0.0.0 => /usr/hadoop-2.7.6/lib/native/libhdfs.so.0.0.0 (0x00007f50526b9000)
    libjvm.so => not found
    libc.so.6 => /lib64/libc.so.6 (0x000000310f800000)
    libjvm.so => not found
    libdl.so.2 => /lib64/libdl.so.2 (0x0000003110000000)
    libpthread.so.0 => /lib64/libpthread.so.0 (0x000000310fc00000)
    /lib64/ld-linux-x86-64.so.2 (0x000000310f400000)
  • 解决办法:在LD_LIBRARY_PATH和ld.so.conf任何一个地方配置libjvm.so路径就可以,无需重新编译。

  • 对于通过-Wl,rpath方式,如果libjvm.so路径变了,可以配置LD_LIBRARY_PATH和ld.so.conf来解决。或者重新编译指定新的路径。

参考

相关文章

  • C语言操作hdfs

    需求 在c++程序中操作hdfs实现数据的读取和写入。 目标 写一个简单的c程序,将一句话写到hdfs上的一个文件...

  • c++_动态对象创建

    一.c语言 c语言使用malloc 来创建堆对象 二.c++ new操作符 delete操作符 如果new []...

  • C语言文件输入和输出操作的学习心得(四)

    概述 C语言文件输入和输出操作的学习心得(一)C语言文件输入和输出操作的学习心得(二)C语言文件输入和输出操作的学...

  • [Swift]结构体指针操作

    C语言的指针操作 在c语言中申明一个变量并通过指针修改该变量的值 a value is 2 c语言操作结构体指针操...

  • Hadoop HDFS文件操作API

    使用JAVA操作HDFS: 使用Shell操作HDFS: Usage: hadoop fs [generic op...

  • 指针(转)

    C语言中的精华是什么,答曰指针,这也是C语言中唯一的难点。C是对底层操作非常方便的语言,而底层操作中用到最多的就是...

  • hadoop笔记(四)HDFS的shell和api

    前面进行了hdfs原理的学习,下面进行hdfs的shell操作和api操作。1、hdfs命令hadoop的shel...

  • 我的书目

    基础篇: 语言(c, scheme): c:命令式语言的代表, 高级语言,最接近底层的高级语言。操作系统的实现语言...

  • “学习,是一种升级”文集目录

    一、C语言 C语言学习:链表的概念和其简单操作 C语言学习:关于数据的几种排序算法 C语言项目:学生信息管理系统 ...

  • 通过API访问HDFS

    通过API操作HDFS 今天的主要内容 HDFS获取文件系统 HDFS文件上传 HDFS文件下载 HDFS目录创建...

网友评论

    本文标题:C语言操作hdfs

    本文链接:https://www.haomeiwen.com/subject/bglmlftx.html