- 浏览: 421793 次
- 性别:
- 来自: 北京
-
最新评论
-
springdata_spring:
apache lucene开源框架demo使用实例教程源代码下 ...
有关Lucene的问题(6):Lucene的事务性 -
jaychang:
必须要感谢作者的分享,对理解Lucene的工作原理帮助很大
Lucene学习总结之一:全文检索的基本原理 -
yin_kaihua:
...
Lucene学习总结之三:Lucene的索引文件格式 (1) -
djh122:
...
Lucene 原理与代码分析完整版 -
wayne0830:
多谢楼主分享!
Lucene 原理与代码分析完整版
1、同运行环境交互
1.1、命令行
-
当一个程序从shell启动的时候,其参数列表包括程序名称及所有的命令行参数
% ls -s / 其参数列表包含三项: 第一项是程序名称ls,第二项和第三项分别是两个命令行参数,-s和/ |
-
main函数可以通过argc和argv两个参数来访问命令行参数列表:argc是命令行参数的个数,argv是命令行参数字符串指针所组成的数组
#include <stdio.h> int main (int argc, char* argv[]) { printf (“The name of this program is ‘%s’.\n”, argv[0]); printf (“This program was invoked with %d arguments.\n”, argc - 1); /* Were any command-line arguments specified? */ if (argc > 1) { /* Yes, print them. */ int i; printf (“The arguments are:\n”); for (i = 1; i < argc; ++i) printf ("%s\n", argv[i]); } return 0; } |
-
命令行参数分两类:选项和参数
-
选项有两种格式:
-
短格式:包括一个连字符和一个字母。短格式选项有利于用户的尽快输入。
-
长格式:包括两个连字符,及一个有小写字母,大写字母及连字符组成的选项名称。长格式选项更易记易读。
-
短格式 |
长格式 |
目的 |
-h |
--help |
显示用法概要 |
-o filename |
--output filename |
指定输出文件名 |
-v |
--verbose |
打印详细信息 |
-
函数getopt_long可以解析短格式和长格式的选项
-
Include <getopt.h>.
-
提供两个数据结构
-
第一个数据结构一是一个字符串,包含所有的短格式选项,每个选项一个字母。需要跟一个参数的选项后面有一个冒号。char* const short_options = “ho:v”;
-
第二个数据结构是一个option结构体组成的数组。数组中的每一项对应一个长选项,包括四个域:
-
第一个域是长选项的名称
-
第二个域表示此选项是否需要跟一个参数
-
第三个域是NULL
-
第四个域是一个字符,是此长选项对应的短选项
-
数组的最后一项为四个域都是0的项
-
-
const struct option long_options[] = { { “help”, 0, NULL, ‘h’ }, { “output”, 1, NULL, ‘o’ }, { “verbose”, 0, NULL, ‘v’ }, { NULL, 0, NULL, 0 } }; |
-
getopt_long函数的参数依次为argc, argv, 短选项字符串,长选项数组
-
每次getopt_long被调用时,其解析一个选项,返回相应的短选项。当没有选项的时候返回-1
-
应该在循环中调用getopt_long,然后用switch根据返回的短选项进行相应的处理
-
当getopt_long遇到一个非法的选项时,其打印出错误信息,并返回'?'
-
当一个选项需要跟一个参数的时候,有一个全局变量optarg指向此参数字符串
-
当getopt_long完成解析所有的选项后,有一个全局变量optind包含第一个非选项参数在argv中的偏移量
#include <getopt.h> #include <stdio.h> #include <stdlib.h> /* The name of this program. */ const char* program_name; /* Prints usage information for this program to STREAM (typically stdout or stderr), and exit the program with EXIT_CODE. Does not return. */ void print_usage (FILE* stream, int exit_code) { fprintf (stream, “Usage: %s options [ inputfile ... ]\n”, program_name); fprintf (stream, “ -h --help Display this usage information.\n” “ -o --output filename Write output to file.\n” “ -v --verbose Print verbose messages.\n”); exit (exit_code); } /* Main program entry point. ARGC contains number of argument list elements; ARGV is an array of pointers to them. */ int main (int argc, char* argv[]) { int next_option; /* A string listing valid short options letters. */ const char* const short_options = “ho:v”; /* An array describing valid long options. */ const struct option long_options[] = { { “help”, 0, NULL, ‘h’ }, { “output”, 1, NULL, ‘o’ }, { “verbose”, 0, NULL, ‘v’ }, { NULL, 0, NULL, 0 } /* Required at end of array. */ }; /* The name of the file to receive program output, or NULL for standard output. */ const char* output_filename = NULL; /* Whether to display verbose messages. */ int verbose = 0; /* Remember the name of the program, to incorporate in messages. The name is stored in argv[0]. */ program_name = argv[0]; do { next_option = getopt_long (argc, argv, short_options, long_options, NULL); switch (next_option) { case ‘h’: /* -h or --help */ /* User has requested usage information. Print it to standard output, and exit with exit code zero (normal termination). */ print_usage (stdout, 0); case ‘o’: /* -o or --output */ /* This option takes an argument, the name of the output file. */ output_filename = optarg; break; case ‘v’: /* -v or --verbose */ verbose = 1; break; case ‘?’: /* The user specified an invalid option. */ /* Print usage information to standard error, and exit with exit code one (indicating abnormal termination). */ print_usage (stderr, 1); case -1: /* Done with options. */ break; default: /* Something else: unexpected. */ abort (); } } while (next_option != -1); /* Done with options. OPTIND points to first nonoption argument. For demonstration purposes, print them if the verbose option was specified. */ if (verbose) { int i; for (i = optind; i < argc; ++i) printf (“Argument: %s\n”, argv[i]); } /* The main program goes here. */ return 0; } |
1.2、标准输入输出
-
C标准库提供三个流:
-
标准输入流:stdin
-
标准输出流:stdout
-
标准错误流:stderr
-
-
三个流出了用C标准库访问外,还可以直接通过Unix API直接对文件描述符进行操作
-
stdin的文件描述符为0,stdout为1,stderr为2
-
标准输出流和标准错误流都可以重定向到一个文件或者一个管道(pipe)
% program > output_file.txt 2>&1 % program 2>&1 | filter 2<&1表示文件描述符2也即stderr将合并到文件描述符1也即stdout中。 欲重定向stdout和stderr到一个文件,2>&1必须跟在一个文件后面。 欲重定向stdout和stderr到一个管道,2>&1必须在一个管道前面。 |
-
stdout有缓冲,此缓存可以通过fflush(stdout)显式的写出。
-
stderr没有缓冲。
1.3、程序退出码
-
当一个程序结束的时候,其在退出码中返回程序的退出状态。
-
退出码是一个整形数。
-
退出码为0表示程序成功退出,退出码非零表示程序因错误而退出。
-
最近运行的程序的退出码可以通过一个特殊的shell变量$?得到。
% ls / bin coda etc lib misc nfs proc sbin usr boot dev home lost+found mnt opt root tmp var % echo $? 0 % ls bogusfile ls: bogusfile: No such file or directory % echo $? 1 |
1.4、环境变量
-
环境变量是一组变量及其值的组合。
-
常用的环境变量:
-
USER保存用户名
-
HOME保存主目录的路径
-
PATH保存一个以冒号分隔的一系列目录的列表。Linux将从这些目录中寻找运行的命令。
-
-
printenv命令可以打印所有的环境变量。
-
在程序中,可以用getenv函数得到环境变量的值。
-
可以用函数setenv和unsetenv来设置和清除环境变量。
-
可以通过一个特殊的全局变量environ来得到所有的环境变量。
#include <stdio.h> /* The ENVIRON variable contains the environment. */ extern char** environ; int main () { char** var; for (var = environ; *var != NULL; ++var) printf (“%s\n”, *var); return 0; } |
-
当一个程序运行起来的时候,其继承了启动程序的环境变量。
-
环境变量多用于给程序传递配置信息。
1.5、使用临时文件
-
临时文件存储在/tmp文件夹中。
-
不同的进程应该使用不同的临时文件。
-
没有授权的用户不能通过改变或者替换临时文件来改变程序运行的行为。
-
临时文件名应该以不可预测的方式生成。
-
mkstemp函数
-
根据一个文件名模板创建一个独一无二的文件名。
-
其创建的文件应被设置了除当前用户外其他用户无法访问的权限。
-
其以可读可写的方式打开临时文件。
-
被mkstemp的临时文件不会被自动删除。
-
如果一个临时文件仅仅在一个程序内部使用,而不会被其他程序使用,则应该在mkstemp生成临时文件后,对临时文件进行unlink,从而程序退出时临时文件会被删除。
-
#include <stdlib.h> #include <unistd.h> /* A handle for a temporary file created with write_temp_file. In this implementation, it’s just a file descriptor. */ typedef int temp_file_handle; /* Writes LENGTH bytes from BUFFER into a temporary file. The temporary file is immediately unlinked. Returns a handle to the temporary file. */ temp_file_handle write_temp_file (char* buffer, size_t length) { /* Create the filename and file. The XXXXXX will be replaced with characters that make the filename unique. */ char temp_filename[] = “/tmp/temp_file.XXXXXX”; int fd = mkstemp (temp_filename); /* Unlink the file immediately, so that it will be removed when the file descriptor is closed. */ unlink (temp_filename); /* Write the number of bytes to the file first. */ write (fd, &length, sizeof (length)); /* Now write the data itself. */ write (fd, buffer, length); /* Use the file descriptor as the handle for the temporary file. */ return fd; } /* Reads the contents of a temporary file TEMP_FILE created with write_temp_file. The return value is a newly allocated buffer of those contents, which the caller must deallocate with free. LENGTH is set to the size of the contents, in bytes. The temporary file is removed. */ char* read_temp_file (temp_file_handle temp_file, size_t* length) { char* buffer; /* The TEMP_FILE handle is a file descriptor to the temporary file. */ int fd = temp_file; /* Rewind to the beginning of the file. */ lseek (fd, 0, SEEK_SET); /* Read the size of the data in the temporary file. */ read (fd, length, sizeof (*length)); /* Allocate a buffer and read the data. */ buffer = (char*) malloc (*length); read (fd, buffer, *length); /* Close the file descriptor, which will cause the temporary file to go away. */ close (fd); return buffer; } |
-
如果不需要把临时文件传给另一个程序,则可以用tmpfile函数
-
其创建和打开一个临时文件
-
返回一个指向文件的指针
-
临时文件已经被unlink了,当文件指针被关闭的时候(fclose)或者程序结束的时候,临时文件会自动删除。
-
2、编写及使用库
2.1、静态链接库(Archives)
-
静态链接库仅仅将一系列对象文件(.obj)归档为一个文件。
-
当链接器遇到一个静态链接库的时候,其将需要的对象文件提取出来,并将它链接到程序文件中。
-
静态链接库可用ar命令创建。
% ar cr libtest.a test1.o test2.o |
-
当链接静态链接库的时候,静态链接库的指定顺序是很重要的。
-
当链接器依次解析命令行中指定的静态链接库时,其首先在已经解析过的对象文件中寻找所有被引用了,但是没有定义的标识符,然后在静态链接库中将包含这些标识符的对象文件抽取出来,链接到程序文件中。
2.2、动态链接库(Shared Libraries)
-
当一个动态链接库被链接到一个程序文件中的时候,最后的程序文件并不包括动态链接库中的代码,而是仅仅包括对动态链接库的引用。
-
动态链接库可被多个程序共享。
-
动态链接库不仅仅是一组对象文件的简单归档,而是多个对象文件的重新组合。
-
当一个程序链接一个动态链接库的时候,其或者包括动态链接库的所有代码,或者不包括动态链接库的任何代码,而不像静态链接库一样包括程序需要的那一部分。
-
当创建一个动态链接库德时候,需要用-fPIC参数编译对象文件。
% gcc -c -fPIC test1.c |
-
可以将多个对象文件合并成一个动态链接库。
% gcc -shared -fPIC -o libtest.so test1.o test2.o |
-
链接一个动态链接库和链接一个静态链接库一样。
% gcc -o app app.o -L. –ltest |
-
由于链接的时候没有指定.so还是.a,链接器优先选择动态链接库,除非显式指定选择静态链接库。
% gcc -static -o app app.o -L. –ltest |
-
ldd命令显示链接到程序的所有动态链接库。
-
当链接一个动态链接库到一个程序的时候,链接器并不保存动态链接库的全路径,而仅仅是动态链接库的名称。
-
当程序运行的时候,系统首先寻找动态链接库,然后加载它。
-
默认情况下,系统在/lib和/usr/lib文件夹下寻找动态链接库。
-
当链接动态链接库到程序的时候,如果指定-Wl,-rpath参数,则系统会在/usr/local/lib下寻找动态链接库。
-
设定LD_LIBRARY_PATH环境变量,程序运行时会在此环境变量指定的文件夹下寻找动态链接库。
Create test1.cvoid f1() { printf("Test 1 fucntion 1"); } gcc -c -fPIC test1.c -rw-rw-r-- 1 liuchao liuchao 1124 Jul 31 19:51 test1.o Create test2.cvoid f2() { printf("Test 2 function 2"); } gcc -c -fPIC test2.c -rw-rw-r-- 1 liuchao liuchao 1124 Jul 31 19:52 test2.o Create libstatictest.aar cr libstatictest.a test1.o test2.o -rw-rw-r-- 1 liuchao liuchao 2508 Jul 31 19:53 libstatictest.a Create libdynamictest.sogcc -shared -fPIC -o libdynamictest.so test1.o test2.o -rwxrwxr-x 1 liuchao liuchao 4573 Jul 31 19:54 libdynamictest.so Create app.cint main(int argc, char* argv[]) { f2(); } gcc -c app.c -rw-rw-r-- 1 liuchao liuchao 772 Jul 31 19:55 app.o Create staticappgcc -o staticapp app.o -L. –lstatictest -rwxrwxr-x 1 liuchao liuchao 4861 Jul 31 19:56 staticapp Create dynamicappgcc -o dynamicapp app.o -L. -ldynamictest Run staticapp./staticapp Test 2 function 2 Run dynamicapp./dynamicapp ./dynamicapp: error while loading shared libraries: libdynamictest.so: cannot open shared object file: No such file or directory Ldd staticappldd staticapp linux-gate.so.1 => (0x0060d000) libc.so.6 => /lib/libc.so.6 (0x007a3000) /lib/ld-linux.so.2 (0x00786000) Ldd dynamicappldd dynamicapp linux-gate.so.1 => (0x00861000) libdynamictest.so => not found libc.so.6 => /lib/libc.so.6 (0x00111000) /lib/ld-linux.so.2 (0x00786000) export LD_LIBRARY_PATH=.export LD_LIBRARY_PATH=. echo $LD_LIBRARY_PATH . ./dynamicapp Test 2 function 2 |
2.3、库依赖
-
一个库会经常依赖另一个库:例如libtiff库包含读写TIFF图片文件的函数,其依赖libjpeg库和libz库。
-
创建使用libtiff库的程序:
#include <stdio.h> #include <tiffio.h> int main (int argc, char** argv) { TIFF* tiff; tiff = TIFFOpen (argv[1], “r”); TIFFClose (tiff); return 0; } |
-
编译程序,链接libtiff
% gcc -o tifftest tifftest.c –ltiff |
-
链接器选择动态链接库libtiff。由于libtiff依赖libjpeg和libz,所以他们也被链接进来。
% ldd tifftest libtiff.so.3 => /usr/lib/libtiff.so.3 (0x4001d000) libc.so.6 => /lib/libc.so.6 (0x40060000) libjpeg.so.62 => /usr/lib/libjpeg.so.62 (0x40155000) libz.so.1 => /usr/lib/libz.so.1 (0x40174000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) |
-
如果是静态链接库,其依赖的库不会被链接进来。
% gcc -static -o tifftest tifftest.c -ltiff /usr/bin/../lib/libtiff.a(tif_jpeg.o): In function ‘TIFFjpeg_error_exit’: tif_jpeg.o(.text+0x2a): undefined reference to ‘jpeg_abort’ /usr/bin/../lib/libtiff.a(tif_jpeg.o): In function ‘TIFFjpeg_create_compress’: tif_jpeg.o(.text+0x8d): undefined reference to ‘jpeg_std_error’ tif_jpeg.o(.text+0xcf): undefined reference to ‘jpeg_CreateCompress’ ... |
-
如果欲链接静态链接库及其依赖的静态链接库,必须显式指定。
% gcc -static -o tifftest tifftest.c -ltiff -ljpeg –lz |
-
如果两个静态链接库是相互依赖的,则应该在命令行上指定两次。
% gcc -static -o app app.o -lfoo -lbar –lfoo 例如程序app中的函数void a()在libfoo.a的A.o中实现,A.o中的函数void b()在libbar.a的B.o中实现,B.o中的函数void c()在libfoo.a的C.o中实现。则链接器的工作过程如下:
|
2.4、动态加载和卸载库
-
调用dlopen函数可以加载动态链接库libtest.so
dlopen (“libtest.so”, RTLD_LAZY) |
-
要使用动态加载函数,要include<dlfcn.h>,并且链接的时候要指定-ldl参数来链接libdl库。
-
dlopen的返回值是一个void *的指针,作为访问动态链接库的句柄。
-
可以讲句柄传给函数dlsym来得到加载的动态链接库中的某个函数的地址。
void* handle = dlopen (“libtest.so”, RTLD_LAZY); void (*test)() = dlsym (handle, “my_function”); (*test)(); dlclose (handle); |
-
dlopen和dlsym失败的时候返回NULL,可以用函数dlerror得到可描述失败原因的信息。
-
dlclose函数卸载动态链接库。
发表评论
-
linux Kill多个进程
2010-05-16 00:15 5682经常需要Kill多个进程,这些进程包含共同的关键字,可以用一条 ... -
高级Linux程序设计第五章:进程间通信
2010-04-29 00:22 3083五种进程间通信的方式: 共享内存(shared ... -
高级Linux程序设计第四章:线程
2010-04-27 00:09 2035要想使用POSIX标准线 ... -
高级Linux程序设计第三章:进程
2010-04-25 21:48 2098高级Linux程序设计第一章:入门 http:// ... -
高级Linux程序设计第一章:入门
2010-04-22 00:31 18701、用GCC编译 1.1、创建源文件 (main.c ...
相关推荐
##### 第2章:编写优秀的GNU/Linux软件 - **代码质量**:重点强调了如何编写高质量、可维护的Linux软件,包括编码规范、代码结构设计等内容。 - **测试方法**:介绍了几种常见的软件测试方法和技术,确保程序在发布...
通过阅读《Linux高级程序设计》第二版,读者不仅可以掌握Linux程序设计的基本原理,还能获得实际操作的经验,从而在开发过程中游刃有余。虽然这里只提到了第九章,但通常这些主题会在全书中得到深入讨论,每章节都会...
《嵌入式操作系统实验相关学习 第二章Linux程序设计基础—C环境》是关于Linux系统下C语言编程的一份教程,旨在帮助读者掌握在Linux环境中进行C程序开发的基础知识和技巧。以下是章节主要内容的详细说明: 1. **...
首先,我们来看第二章“LINUX下C语言开发工具”。这一章主要介绍了在Linux环境下进行C语言编程所必备的开发工具,如GCC编译器、GDB调试器以及头文件和库的使用。理解这些工具的使用方法,是进行高效程序开发的基础。...
#### 第二章 Linux系统入门 **知识点解析:** 1. **Linux系统有哪些运行级别?其含义是什么?** - Linux系统通常有7个运行级别,分别为0到6。 - **0:** 关闭系统。 - **1:** 单用户模式,仅允许root用户登录...
"嵌入式Linux应用程序开发第9章多线程编程" 本章主要讲解了 Linux 中的多线程编程,包括 Linux 线程的基本概念、创建及使用、属性设置、同步与互斥问题的处理等内容。通过学习本章,读者将掌握 Linux 中线程的基本...
第二章:Linux内核与设备驱动 了解Linux内核是嵌入式开发的基础。本章将介绍内核的工作原理、如何编译定制内核以及设备驱动程序的作用。设备驱动是连接硬件和操作系统的关键,我们将学习如何编写简单的字符设备驱动...
第二章:什么是 shell 本章节主要介绍了 shell 的基本概念和类型,包括 Bourne shell、C shell 和 Korn shell 等。 第三章:文件系统中跳转 本章节主要讲述了 Linux 文件系统的基本结构和跳转命令,包括 cd、pwd ...
### Linux内核设计与实现——第三版知识点概览 #### 一、书籍基本信息 - **书名**:《Linux内核开发》(第三版) - **作者**: Robert Love - **出版社**: Pearson Education, Inc. - **出版年份**: 2010年 - **ISBN*...
第二章:字符设备驱动程序 2.1 字符设备介绍 字符设备是指数据以字节流形式传输的设备,如键盘、串口和终端。与块设备(如硬盘)不同,字符设备通常不支持随机存取,而是按顺序读写。 2.2 字符设备驱动框架 字符...
2. 第二章:嵌入式Linux系统基础 - 学习编写Makefile,理解和使用Linux多线程库,以及进程应用程序设计。通过实验,学生将掌握基本的Linux C程序开发和多线程编程。 3. 第三章:嵌入式Linux内核 - 实验涉及...
#### 第二章:嵌入式系统与Linux的特点 - **嵌入式系统定义**:一种专用的计算机系统,主要用于执行特定的任务或功能。 - **嵌入式系统的特性**: - **应用中心**:围绕特定的应用场景进行设计。 - **计算机技术...
在Linux系统中,窗口程序设计是一项关键技能,特别是在开发跨平台的应用时,如使用QT4框架。QT4是一个强大的开源库,支持C++语言,提供了丰富的功能和组件,用于构建图形用户界面(GUI)应用程序。以下是对标题和...
《嵌入式Linux应用程序开发标准教程(第2版)》主要分为3个部分,包括Linux基础、搭建嵌入式Linux环境和嵌入式Linux的应用开发。Linux基础部分从Linux基础、基本操作命令讲起,为Linux初学者能快速入门提供了保证。...
**第二部分:字符设备** - **第4章:字符设备驱动程序** - 详细介绍如何设计和实现字符设备驱动程序,包括数据传输、错误处理等关键问题。 - **第5章:异步串行接口** - 探讨如何为串行通信设备编写驱动程序,并...
《嵌入式Linux应用程序开发标准教程》第二版全集涵盖了嵌入式系统开发中的关键知识点,包括基础理论、编程技术和实战应用。以下是根据压缩包文件名解析出的主要内容: 1. **第一章:嵌入式系统概述** 嵌入式系统是...