C语言操作hdfs

需求

  • 在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
  • 查看hadoop版本
[supdev@YZ-25-64-219]$ hadoop version
Hadoop 2.7.6
Subversion https://shv@git-wip-us.apache.org/repos/asf/hadoop.git -r 085099c66cf28be31604560c376fa282e69282b8
Compiled by kshvachk on 2018-04-18T01:33Z
Compiled with protoc 2.5.0
From source with checksum 71e2695531cb3360ab74598755d036
This command was run using /usr/local/hadoop-2.7.6/share/hadoop/common/hadoop-common-2.7.6.jar

在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
  • 可以使用下面命令查看所有被加载的动态链接库
ldconfig -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来解决。或者重新编译指定新的路径。

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,919评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,567评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,316评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,294评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,318评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,245评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,120评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,964评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,376评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,592评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,764评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,460评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,070评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,697评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,846评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,819评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,665评论 2 354

推荐阅读更多精彩内容