嵌入式Linux应用编程之I/O进程(上)

【1】i/o
本质就是输入输出函数,也是读写函数

【2】系统调用和库函数
系统调用:
使用函数控制linux内核,linux内核来操作硬件
库函数:
库函数的本质还是系统调用,只不过需要在内存当中开辟一块空间(缓冲区),从而减少系统调用的次数

【3】io分类
文件io:
就是系统调用,例如:open、read、write
移植性比较差
标准io:
就是库函数,例如:printf、scanf
移植性比较好

【4】标准I/O
(1)概念
不仅在UNIX系统,在很多操作系统上都实现了标准I/O库,标准I/O库由ANSI C标准说明。标准I/O库处理很多细节,如缓存分配、以优化长度执行I/O等,这样使用户不必关心如何选择合适的块长度。标准I/O在系统调用函数基础上构造的,它便于用户使用。标准I/O库及其头文件stdio.h为底层I/O系统调用提供了一个通用的接口。标准io本质还是系统调用,只不多在执行系统调用前,在内存当中开辟一块区域(缓冲区)用于减少系统调用的次数。
(2)缓冲区的分类
机制:将要操作的数据先放在缓冲区中,如果刷新缓冲区,就立即执行系统调用, 如果没有缓冲区,数据会一直存储在缓冲区中。
①行缓冲:对于终端操作采用的缓冲区
缓存区大小: 1024字节(1K)
刷新缓存 :程序正常结束、缓存区满、 ’\n’ 、使用fflush函数
②全缓冲:对于文件操作采用的缓冲区
缓存区大小:4096字节(4K)
刷新缓存 :程序正常结束、缓存区满、使用fflush函数
③无缓冲:对于终端操作采用的缓冲区

#include <stdio.h>

int main(int argc, const char *argv[])
{
	//标准io在执行的时候都需要刷新缓冲区,如果不刷新没有办法执行相应操作
	//printf("hello world");

	//如何刷新缓冲区
	//方法1:使用\n
	//printf("hello world\n");
	
	//方法2:当前程序正常结束
	//printf("hello world");
	//return 0;

	//方法3:使用fflush( )函数
	//printf("hello world");
	//fflush(stdout);

	//方法4:当缓冲区满的时候,可以刷新缓冲区
	int i;
	for(i = 1; i <= 300; i++)
	{
		printf("%03d ", i);
	}

	while(1)
		;
	
	return 0;
}

(3)ctags的创建和使用
ctags是在指定目录文件里面建立索引文件,用于查找指定内容(宏定义、重命名、结构体)的具体位置。
① 创建索引文件
在/usr/include目录里面执行sudo ctags -R,会在当前目录下生成tags的索引文件。
测试:vim -t FILE
②将当前索引文件设置为全局
在家目录下的.vimrc文件里面添加set tags+=/usr/include/tags
③ 基本操作指令
vim -t *** 查找对应内容的位置
输入编号可以执行到达执行数据的文件的位置,如果编号较多,可以按
两下esc再输入编号
ctrl+] 追代码
ctrl+t 返回上一级

(4)文件指针(流指针)
FILE结构体:
每一个使用标准io操作的文件都会通过一个结构体来标识当前文件的信息,
这个结构体的类型名为FILE,标准io操作文件是通过当前结构体的指针变量
来操作的,称之为流或者流指针,流就是用来标识和操作文件的。

typedef struct _IO_FILE FILE;

FILE * 流指针的类型
当进程创建或者开启的时候,会先创建三个流指针
stdin 标准输入 从终端输入
stdout 标准输出 从终端输出
stderr 标准出错 从终端出错输出

(5)库函数
①fopen() 打开或者创建文件

	#include <stdio.h>
	FILE *fopen(const char *path, const char *mode);

功能:打开或者创建一个文件,得到一个流指针
参数
path:文件名,可以添加路径,默认不添加为当前路径
mode:文件的访问权限
r 以只读的方式打开文件,定位到文件起始位置
r+ 以读写的方式打开文件,定位到文件起始位置
w 以只写的方式打开文件,如果文件不存在则创建,如果存在则清空,定位到文件起始位置
w+ 以读写的方式打开文件,如果文件不存在则创建,如果存在则清空,定位到文件起始位置
a 以只写的方式打开文件,如果文件不存在则创建,如果存在则追加,定位到文件末尾位置
a+ 以读写的方式打开文件,如果文件不存在则创建,如果存在则追加,定位到文件末尾位置
返回值
成功:流指针
失败:NULL

include <stdio.h>
#include <errno.h>

int main(int argc, const char *argv[])
{
	//使用fopen打开或者创建文件
	FILE *fp;
	if((fp = fopen("file.txt", "r")) == NULL)
	{
		//errno:是一个变量,用于获取函数调用错误后的错误码
		printf("errno = %d\n", errno);

		//使用perror函数打印函数调用失败的错误信息
		perror("fail to fopen");
		return -1;
	}

	//使用fclose关闭流指针
	fclose(fp);
		
	return 0;
}

②perror( ) 输出函数调用失败的错误信息

	#include <stdio.h>
	void perror(const char *s);

功能:输出函数调用失败的错误信息
参数
s:提示字符串
返回值:无
errno 在errno.h定义的全局变量,用于获取函数调用错误后的错误码,可以在errno.h里面找到对用错误码的错误信息
③fclose( ) 关闭一个流指针

	#include <stdio.h>
	int fclose(FILE *fp);

功能:关闭一个流指针
参数
fp:指定的流
返回值
成功:0
失败:EOF
④fprintf( ) 向一个文件写数据

	#include <stdio.h>
	int fprintf(FILE *restrict stream, const char *restrict format, ...);

功能:向一个文件写数据
参数
stream:指定的文件的流指针
format:同printf的参数
返回值
成功:输出的字符的个数
失败:-1

#include <stdio.h>

int main(int argc, const char *argv[])
{
	//向终端输出数据
	fprintf(stdout, "hello world\n");
	char name[] = "zhangsan";
	fprintf(stdout, "My name is %s\n", name);
	
	//向文件写入数据
	FILE *fp;
	if((fp = fopen("file.txt", "w")) == NULL)
	{
		perror("fail to fopen");
		return -1;
	}

	fprintf(fp, "hello world\n");
	char n[] = "zhangsan";
	fprintf(fp, "My name is %s\n", n);

	return 0;
}

⑤freopen( ) 对于一个已有的流进行重定向

	FILE *freopen(const char *restrict pathname, 
		const char *restrict type, FILE* restrict fp)
#include <stdio.h>

int main(int argc, const char *argv[])
{
	printf("nihao beijing\n");
	
	FILE *fp;

	//将标准输出的数据重定向写入一个文件里面
	if((fp = freopen("file.txt", "a", stdout)) == NULL)
	{
		perror("fail to freopen");
		return -1;
	}

	printf("nihao beijing\n");
	
	return 0;
}

⑥字符的读写函数
1 – fgetc( )

		#include <stdio.h>
		int fgetc(FILE *stream);

功能:从一个文件读取一个字符
参数
stream:指定的文件的流指针
返回值
成功:读取到的字符的ascii码值
失败:EOF
如果读取到文件的末尾,会返回EOF

#include <stdio.h>

int main(int argc, const char *argv[])
{
	//从终端获取一个字符
#if 0
	int c;
	//c = getchar();
	c = fgetc(stdin);

	printf("[%c] -- %d\n", c, c);
#endif

	if(argc < 2)
	{
		fprintf(stderr, "Usage: %s filename\n", argv[0]);
		return 1;
	}

	//从文件里面读取一个字符
	FILE *fp;
	if((fp = fopen(argv[1], "r")) == NULL)
	{
		perror("fail to fopen");
		return -1;
	}

#if 0
	int c = fgetc(fp);
	printf("[%c] -- %d\n", c, c);
	c = fgetc(fp);
	printf("[%c] -- %d\n", c, c);
	c = fgetc(fp);
	printf("[%c] -- %d\n", c, c);
#endif
	//文件的每一行的结尾处都有一个换行符
	//fgetc可以读取到换行符
	int c;
	while((c = fgetc(fp)) != EOF)
	{
		printf("[%c] -- %d\n", c, c);
	}

	fclose(fp);

	return 0;
}

2 – fputc( )

		#include <stdio.h>
		int fputc(int c, FILE *stream);

功能:向一个文件写入一个字节的数据
参数
c:要写入的字符
stream:指定的文件的流指针
返回值
成功:写入的字符
失败:EOF

#include <stdio.h>

int main(int argc, const char *argv[])
{
	//向终端写入一个字符
#if 0
	putchar('a');

	putchar(122);

	char c = 'W';
	putchar(c);
	putchar(10);
#endif

#if 0
	fputc('T', stdout);
	fputc(100, stdout);
	char a = 'U';
	fputc(a, stdout);
	fputc(10, stdout);
#endif

	//向文件写入一个字符
	FILE *fp;
	if((fp = fopen("file.txt", "r+")) == NULL)
	{
		perror("fail to fopen");
		return -1;
	}

	fputc('h', fp);
	fputc('e', fp);
	fputc('l', fp);
	fputc('l', fp);
	fputc('o', fp);
	fputc('N', fp);
	fputc('O', fp);

	//当文件写完数据后,此时的文件的偏移量是文件的末尾处,所以如果读取数据的话读取不到
#if 0
	int c;
	while((c = fgetc(fp)) != EOF)
	{
		printf("%c - ", c);
	}
	putchar(10);
#endif

	fclose(fp);

	return 0;
}

⑦ 字符串读写函数
1 – fgets( )

		#include <stdio.h>
		char *fgets(char *restrict s, int n, FILE *restrict stream);

功能:从一个文件读取一串字符
参数
s:保存读取的数据
n:读取的字节数
stream:指定的文件的流指针
返回值:
成功:读取的数据
失败:NULL
当读取到文件末尾时,fgets返回NULL

#include <stdio.h>
#include <string.h>

int main(int argc, const char *argv[])
{
	//从终端读取数据
#if 0
	char s[32] = {};
	//gets(s);	
	//fgets从终端读取字符串时,
	//如果读取的内容小于第二个参数值,则会将换行符也读到数组里面
	//如果读取的内容大于第二个参数值,则只会获取到第二个参数-1个字节,最后一个位置补\0
	fgets(s, 6, stdin);
	//取消读取到的换行符
	//s[strlen(s) - 1] = '\0';

	printf("s = %s\n", s);
#endif

	//从文件获取数据
	//如果读取的内容大于fgets的第二个参数,则只能读取第二个参数-1个字节,最后一个位置补\0
	//如果设置的第二个参数要读取的数据查过文件一行内容,则只会读取一行,当读取到行结束符时函数结束
	FILE *fp;
	if((fp = fopen("file.txt", "r")) == NULL)
	{
		perror("fail to fopen");
		return -1;
	}

	char s[32];
	fgets(s, 32, fp);
	printf("s = [%s]\n", s);
	
	fgets(s, 32, fp);
	printf("s = [%s]\n", s);

	fclose(fp);

	return 0;
}

2 – fputs( )

		#include <stdio.h>
		int fputs(const char *restrict s, FILE *restrict stream);

功能:向一个文件写入的数据
参数
s:要写入文件的数据
stream:指定的文件的流指针
返回值:
成功:写入的数据的字符数
失败:EOF

#include <stdio.h>

int main(int argc, const char *argv[])
{
	//向终端写入数据
#if 0
	//puts函数自带换行
	//puts("hello world");
	
	//fputs函数不自带换行符
	fputs("hello world\n", stdout);
#endif

	//向文件写入数据
	FILE *fp;
	if((fp = fopen("file.txt", "w")) == NULL)
	{
		perror("fail to fopen");
		return -1;
	}

	fputs("nihao beijing\n", fp);
	fputs("nihao beijing\n", fp);

	fclose(fp);

	return 0;
}

⑧读写流
1 – fread( )

		#include <stdio.h>
		size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

功能:从一个文件读取数据
参数
ptr:保存读取的内容
size:每次读取的字节数
nmemb:读取的次数
stream:指定的文件的流指针
返回值:
成功:读取的对象数(次数)
失败:EOF

#include <stdio.h>

typedef struct{
	int a;
	char b;
	char c[32];
}mystu;

int main(int argc, const char *argv[])
{
	//从文件获取数据
	FILE *fp;
	if((fp = fopen("file.txt", "r")) == NULL)
	{
		perror("fail to fopen");
		return -1;
	}

#if 0
	char s[32] = {};
	int n = 0;
	n = fread(s, 1, 100, fp);
	printf("s = %s\n", s);
	printf("n = %d\n", n);
#endif

	mystu a;
	fread(&a, 40, 1, fp);
	printf("%d - %c - %s\n", a.a, a.b, a.c);

	return 0;
}

2 – fwrite( )

		#include <stdio.h>
		size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);

功能:向一个文件写数据
参数
ptr:要写的内容
size:每次写入数据的字节数
nmemb:写入的次数
stream:指定的文件的流指针
返回值
成功:写入的对象数(次数)
失败:EOF

#include <stdio.h>

typedef struct{
	int a;
	char b;
	char c[32];
}mystu;

int main(int argc, const char *argv[])
{
	//向文件写数据
	FILE *fp;
	if((fp = fopen("file.txt", "w")) == NULL)
	{
		perror("fail to fopen");
		return -1;
	}

	//char s[] = "hello world";
	//int s[4] = {10, 20, 30, 40};
	mystu s = {100, 'w', "hello world"};
	fwrite(&s, 40, 1, fp);

	return 0;
}

⑨ fseek( ),rewind()
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
功能:设置文件指针的位置
参数:
stream:指定的文件的流指针
offset:偏移量
whence:相对位置
SEEK_SET 文件起始位置
SEEK_CUR 文件指针当前位置
SEEK_END 文件末尾后的位置
返回值:
成功:0
失败:-1

	void rewind(FILE *stream);

功能:将文件指针定位到文件起始位置
参数
stream:指定的文件的流指针
返回值

	rewind(fp) <==> fseek(fp, 0, SEEK_SET)
#include <stdio.h>

int main(int argc, const char *argv[])
{
	FILE *fp;
	//如果文件的访问权限设置为a或者a+,则写对应的文件指针的位置不能改变,只能在文件末尾。但是读没有限制
	if((fp = fopen("file.txt", "a+")) == NULL)
	{
		perror("fail to fopen");
		return -1;
	}

	fputs("0123456789", fp);

	//获取文件的偏移量
	printf("offset = %ld\n", ftell(fp));

	//设置文件指针的位置
	fseek(fp, -6, SEEK_END);
	printf("offset = %ld\n", ftell(fp));

	char s[32] = {};
	while(fgets(s, 32, fp) != NULL)
	{
		printf("s = %s\n", s);
	}
	
	printf("offset = %ld\n", ftell(fp));
	fseek(fp, 2, SEEK_SET);

	fputs("abcd", fp);

	fclose(fp);

	return 0;
}

⑩ ftell( )

	#include <stdio.h>
	long ftell(FILE *stream);

功能:获取当前文件指针的位置
参数
stream:指定的文件的流指针
返回值
成功:获取的文件指针的位置
失败:-1
【5】文件IO
(1)概念
文件io本质是系统调用,用户通过应用层的函数调用linux内核的函数来操作硬件,文件io通过文件描述符来标识和操作文件。

(2)文件描述符
对于内核而言,所有打开文件都由文件描述符引用。文件描述符是一个非负整数。当打开一个现存文件或创建一个新文件时,内核向进程返回一个文件描述符。
当读、写一个文件时,用open或creat返回的文件描述符标识该文件,将其作为参数传送给read或write。文件描述符用于标识和操作文件。当进程创建或者开启的时候,系统会自动创建三个文件描述符
0 标准输入 STDIN_FILENO
1 标准输出 STDOUT_FILENO
2 标准出错 STDERR_FILENO
(3)系统调用函数
① open()

	#include <sys/types.h>
	#include <sys/stat.h>
	#include <fcntl.h>

	int open(const char *pathname, int flags);
	int open(const char *pathname, int flags, mode_t mode);

功能:打开或者创建一个文件,得到一个文件描述符
参数
pathname:文件名
flags:文件标志位
O_RDONLY 只读
O_WRONLY 只写
O_RDWR 可读可写
O_APPEND 以追加的方式打开文件
O_CREAT 当文件不存在则创建
O_EXCL 一般与O_CREAT一起用,表示如果文件存在则open函数执行失败
O_TRUNC 如果文件存在则清空
mode:如果使用O_CREAT,则必须使用第三个参数
一般使用八进制数设置文件权限,例如0664
返回值:
成功:文件描述符
失败:-1
标准io文件权限 ----------------------------文件io文件权限
r -------------------------------------------------O_RDONLY
r+ -----------------------------------------------O_RDWR
w ------------------------------------------------O_WRONLY | O_CREAT | O_TRUNC, 0664
w+ ----------------------------------------------O_RDWR | O_CREAT | O_TRUNC, 0664
a ------------------------------------------------O_WRONLY | O_CREAT | O_APPEND, 0664
a+ ----------------------------------------------O_RDWR | O_CREAT | O_APPEND, 0664

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
	int fd;

	//使用open函数打开或者创建文件
	//if((fd = open("file.txt", O_RDONLY)) < 0)
	//if((fd = open("file.txt", O_RDONLY | O_CREAT, 0664)) < 0)
	//if((fd = open("file.txt", O_RDONLY | O_CREAT | O_TRUNC, 0664)) < 0)
	if((fd = open("file.txt", O_RDONLY | O_CREAT | O_APPEND, 0664)) < 0)
	{
		perror("fail to open");
		return -1;
	}

	//关闭文件描述符
	close(fd);
	
	return 0;
}

② close( )

	#include <unistd.h>
	int close(int fd);

功能:关闭一个文件描述符
参数
fd:指定的文件描述符
返回值
成功:0
失败:-1

③ read( )

	#include <unistd.h>
	ssize_t read(int fd, void *buf, size_t count);

功能:从一个文件里面读取数据
参数
fd:指定的文件描述符
buf:保存读取的数据
count:想要读取的字节数
返回值
成功:实际读取的字节数
失败:-1

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, const char *argv[])
{
	//从终端读取数据
#if 0
	char s[32] = {};
	//如果输入的数据小于read的第三个参数,则会将换行符也保存在第二个参数里面
	read(0, s, 32);
	printf("s = %s\n", s);
#endif

	//从文件读取数据
	int fd;
	if((fd = open("file.txt", O_RDONLY)) < 0)
	{
		perror("fail to open");
		return -1;
	}

	char buf[32] = {};
	ssize_t bytes;
	//read从文件里面读取数据时,不会按照行来读取,根据第三个参数读取数据,也会将每一行的结束符读取到
	//read函数从文件里面读取数据,第三个参数表示每次想读取的字节数,而返回值表示实际读取的字节数
	bytes = read(fd, buf, 32);
	
	printf("buf = %s\n", buf);
	printf("bytes = %d\n", bytes);

	return 0;
}

④ write( )

	#include <unistd.h>
	ssize_t write(int fd, const void *buf, size_t count);

功能:向一个文件写数据
参数
fd:指定的文件描述符
buf:要写入的数据
count:写入数据的长度``
返回值
成功:写入的字节数
失败:-1

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main(int argc, const char *argv[])
{
	//向终端写数据
#if 0
	char s[12] = "hello world";
	ssize_t bytes;
	bytes = write(1, s, sizeof(s));
	
	printf("bytes = %d\n", bytes);
#endif

	//向文件写数据
	int fd;
	if((fd = open("file.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664)) < 0)
	{
		perror("fail to open");
		return -1;
	}

	char s[32] = "hello world\nnihao beijing";
	write(fd, s, strlen(s));

	return 0;
}

⑤ lseek( )

	#include <sys/types.h>
	#include <unistd.h>
	off_t lseek(int fd, off_t offset, int whence);

功能:设置和获取文件的偏移量
参数
fd:指定的文件描述符
offset:偏移量
whence:相对位置
SEEK_SET 文件起始位置
SEEK_CUR 文件当前位置
SEEK_END 文件末尾位置
返回值
成功:当前文件的位置
失败:-1

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main(int argc, const char *argv[])
{
	//向文件写数据
	int fd;
	if((fd = open("file.txt", O_RDWR | O_CREAT | O_TRUNC, 0664)) < 0)
	{
		perror("fail to open");
		return -1;
	}

	char s[32] = "0123456789";
	write(fd, s, strlen(s));

	//获取当前文件的偏移量
	printf("offset = %ld\n", lseek(fd, 0, SEEK_CUR));

	//设置文件的偏移量
	lseek(fd, 5, SEEK_SET);
	printf("offset = %ld\n", lseek(fd, 0, SEEK_CUR));

	char buf[32] = {};
	read(fd, buf, 32);
	printf("buf = %s\n", buf);

	lseek(fd, 3, SEEK_SET);
	printf("offset = %ld\n", lseek(fd, 0, SEEK_CUR));

	write(fd, "abcd", 4);

	return 0;
}

【6】获取文件信息和目录的操作
(1)使用stat( )获取文件信息

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int stat(const char *path, struct stat *buf);

功能:获取文件的信息
参数
path:文件名,可以添加路径,默认不添加为当前路径
buf:文件信息结构体
struct stat {
dev_t st_dev; /* ID of device containing file / 文件所在设备的ID
ino_t st_ino; /
inode number / inode节点号
mode_t st_mode; /
protection / 模式(文件类型、访问权限)
nlink_t st_nlink; /
number of hard links / 链向此文件的连接数(硬连接)
uid_t st_uid; /
user ID of owner / 用户id
gid_t st_gid; /
group ID of owner / 组id
dev_t st_rdev; /
device ID (if special file) / 设备号,针对设备文件
off_t st_size; /
total size, in bytes / 文件大小,字节为单位
blksize_t st_blksize; /
blocksize for file system I/O / 系统块的大小
blkcnt_t st_blocks; /
number of 512B blocks allocated / 文件所占块数
time_t st_atime; /
time of last access / 最近存取时间
time_t st_mtime; /
time of last modification / 最近修改时间
time_t st_ctime; /
time of last status change */ 文件创建的时间
};
返回值
成功:0
失败:-1

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
	//使用stat函数获取文件的信息
	struct stat s;
	if(stat(argv[1], &s) == -1)
	{
		perror("fail to stat");
		return -1;
	}

	printf("dev_num = %d\n", (int)s.st_dev);
	printf("inode_num = %d\n", (int)s.st_ino);
	printf("hard_link_num = %d\n", s.st_nlink); //获取当前文件硬链接文件的个数(包括文件本身)
	printf("uid = %d\n", s.st_uid);
	printf("gid = %d\n", s.st_gid);
	printf("file_size = %ld\n", s.st_size);
	
	return 0;
}

(2)目录的操作
①opendir( )

	#include <sys/types.h>
	#include <dirent.h>
	DIR *opendir(const char *name);

功能:打开一个目录文件,返回一个目录流指针
参数
name:目录文件的名字
返回值
成功:目录流指针
失败:NULL

② readdir( )

	#include <dirent.h>
	struct dirent *readdir(DIR *dirp);

功能:读取目录的信息
参数
dirp:目录流指针,opendir的返回值
返回值
成功:保存目录信息的结构体

			struct dirent {
				ino_t          d_ino;       /* inode number */
				off_t          d_off;       /* offset to the next dirent */
				unsigned short d_reclen;    /* length of this record */
				unsigned char  d_type;      /* type of file; not supported
                                          by all file system types */
				char           d_name[256]; /* filename */
			};

失败:NULL
如果读取完目录的信息,也返回NULL

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>

int main(int argc, const char *argv[])
{
	DIR *dirp;
	struct dirent *dir_ent;

	//打开目录文件
	if((dirp = opendir(argv[1])) == NULL)
	{
		perror("fail to opendir");
		return -1;
	}

	//获取目录中的信息
	//readdir调用一次只获取一个文件的信息
	while((dir_ent = readdir(dirp)) != NULL)
	{
		printf("%d -- %s\n", (int)dir_ent->d_ino, dir_ent->d_name);
	}

	return 0;
}

【7】静态库和动态库
(1)库的概念
本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。

(2)库的分类
静态库和共享库(动态库)

静态库在程序编译时会被连接到目标代码中,程序运行时将
不再需要该静态库,因此体积较大。

动态库在程序编译时并不会被连接到目标代码中,而是在程
序运行是才被载入,因此在程序运行时还需要动态库存在,
因此代码体积较小。

共享库的好处是,不同的应用程序如果调用相同的库,那么在
内存里只需要有一份该共享库的实例。

(3)静态库(static library)的创建
静态库对函数库的链接是放在编译时期(compile time)完成的。
程序在运行时与函数库再无瓜葛,移植方便
浪费空间和资源,因为所有相关的对象文件(object file)与
牵涉到的函数库(library)被链接合成一个可执行文件(executable file)
1)生成制作库文件所需的功能函数的可重定向文件
gcc -c fun.c -o fun.o
2)生成静态库文件
ar crs libfun.a fun.o
lib 表示库文件,一般不能省略
fun 表示库的名字
.a 表示静态库
3)静态库生效
gcc main.c -o main -L. –lfun
-L 指定库的路径
-l 指定库的名字

(4)共享库(动态库)( dynamic library )
动态库把对一些库函数的链接载入推迟到程序运行的时期(runtime)。
可以实现进程之间的资源共享。
将一些程序升级变得简单。
甚至可以真正做到链接载入完全由程序员在程序代码中控制。
1)生成制作库文件所需的功能函数的可重定向文件
gcc -fPIC -c fun.c -o fun.o
-fPIC 生成与位置无关的代码(生成的动态库文件在内存上
可以自动寻找一块合适的内存区域)
2)生成动态库文件
① gcc -shared fun.o -o libfun.so
.so 表示动态库文件
②gcc main.c -o main -L. -lfun
为了让执行程序顺利找到动态库,有三种方法 :
① 把库拷贝到/usr/lib或者/lib目录下。
② 在LD_LIBRARY_PATH环境变量中加上库所在路径。
③ 添加/etc/ld.so.conf.d/*.conf文件,把库所在的路径加到文件末尾,
并执行ldconfig刷新。这样,加入的目录下的所有库文件都可见

猜你喜欢

转载自blog.csdn.net/ShawnWang1994/article/details/84035108