嵌入式Linux之 I/O编程

Linux I/O编程

一、文件类型有哪些

文件类型:
Ø 常规文件 r
Ø 目录文件 d
Ø 字符设备文件 c
Ø 块设备文件 b
Ø 管道文件 p
Ø 套接字文件 s
Ø 符号链接文件 l

标准I/O由ANSI C标准定义
主流操作系统上都实现了C库
标准I/O通过缓冲机制减少系统调用,实现更高的效率

二、流的概念

1.标准I/O流

FILE
标准IO用一个结构体类型来存放打开的文件的相关信息
标准I/O的所有操作都是围绕FILE来进行
流(stream)
FILE又被称为流(stream)
文本流/二进制流

2.文本流和二进制流

Windows
二进制流: 换行符 ‘\n’
文本流: 换行符 ‘\r’ ‘\n’
Linux
换行符 ‘\n

3.标准I/O – 流的缓冲类型

全缓冲
当流的缓冲区无数据或无空间时才执行实际I/O操作
行缓冲
当在输入和输出中遇到换行符(‘\n’)时,进行I/O操作
当流和一个终端关联时,典型的行缓冲
无缓冲
数据直接写入文件,流不进行缓冲

例如:.c 文件中 printf("hello word ");语句,当执行时,缓冲区的大小为10242个字节,只有当填满了才会一起输出,,如果加上‘\n’之后,就会直接打印,printf(“hello world \n”);
文本文件每一行的末尾自动带有’\n’

标准I/O –stdin,stdout,stderr

标准I/O预定义3个流,程序运行时自动打开,系统自动定义好的FILE* 类型
在这里插入图片描述

三、标准I/O – 打开流

下列函数可用于打开一个标准I/O流:

FILE *fopen (const char *path, const char *mode);

Ø成功时返回流指针;出错时返回NULL

标准I/O – fopen – mode参数
在这里插入图片描述
提示:* 当给定”b”参数时,表示以二进制方式打开文件,但Linux下忽略该参数 在这里插入图片描述
`提示:表格中说文件必须存在的 r 、r+,如果文件不存在则会报错;

fopen 打开标准文件示例程序

#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *fp;
if ((fp = fopen(“test.txt”,“r+)) == NULL) {
printf(“fopen error\n”);
return -1;
}……
return 0;
}

fp指针返回NULL则打开错误,打印错误还有其他几种格式

标准I/O – fopen – 新建文件权限

1、fopen() 创建的文件访问权限是0666(rw-rw-rw-)
2、Linux系统中umask设定会影响文件的访问权限,其规则
为(0666 & ~umask)
3、用户可以通过umask函数修改相关设定

标准I/O – 处理错误信息

extern int errno;
void perror(const char *s);
char *strerror(int errno);

1、errno 存放错误号
2、perror先输出字符串s,再输出错误号对应的错误信息
3、strerror根据错误号返回对应的错误信息

错误信息处理 – 示例1

#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *fp;
if ((fp = fopen(“test.txt”
,
“r+)) == NULL) {
perror(“fopen”);
return -1;
}
fopen: No such file or directory

错误信息处理 – 示例2

#include <stdio.h>
#include <string.h>
#include <errno.h>
int main(int argc, char *argv[])
{
FILE *fp;
if ((fp = fopen(“test.txt”
,
“r+)) == NULL) {
printf(“fopen: %s\n”
, strerror(errno));
return -1;
}
fopen: No such file or directory

四、标准I/O – 关闭流

int fclose(FILE *stream)

1、fclose()调用成功返回0,失败返回EOF,并设置errno
2、流关闭时自动刷新缓冲中的数据并释放缓冲区
3、当一个程序正常终止时,所有打开的流都会被关闭。
4、流一旦关闭后就不能执行任何操作
提示:程序中打开一个文件必须做关闭文件处理

五、标准I/O – 读写流

流支持不同的读写方式:
1、读写一个字符: fgetc()和fputc() 一次读/写一个字符
2、读写一行: fgets()和fputs() 一次读/写一行
3、读写若干个对象:fread()和fwrite() 每次读/写若干个对象,而 每个对象具有相同的长度

标准I/O – 按字符输入

下列函数用来输入一个字符:

#include <stdio.h>
int fgetc(FILE *stream);
int getc(FILE *stream);
int getchar(void);//从键盘中获取字符

1、成功时返回读取的字符;若到文件末尾或出错时返回EOF(返回值为-1)
2、getchar()等同于fgetc(stdin)

标准I/O – fgetc – 示例

int ch;
ch = fgetc(stdin);
printf(%c\n”, ch);
FILE *fp;
int ch, count = 0;
if ((fp = fopen(argv[1],“r”)) == NULL)
{
  perror(“fopen”); return -1;
}
while ((ch = fgetc(fp)) != EOF)
{
  count++;
}
printf(“total %d bytes\n”, count);

标准I/O – 按字符输出

下列函数用来输出一个字符:

#include <stdio.h>
int fputc(int c, FILE *stream);
int putc(int c, FILE *stream);
int putchar(int c);//将字符打印在屏幕上

1、成功时返回写入的字符;出错时返回EOF
2、putchar(c )等同于fputc(c, stdout)

标准I/O – fputc – 示例

fputc(‘a’, stdout);
putchar(‘\n’);
FILE *fp;
int ch;
if ((fp = fopen(argv[1],“w”)) == NULL) 
{
    perror(“fopen”); return -1;
}
for(ch =‘a’; ch <=‘z’; ch++)
{
    fputc(ch, fp);
}

标准I/O – 按行输入

下列函数用来输入一行:

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

Ø 成功时返回s,到文件末尾或出错时返回NULL
Ø gets不推荐使用,容易造成缓冲区溢出
Ø 遇到’\n’或已输入size-1个字符时返回,总是包含’\0’

标准I/O – fgets – 示例

#define N 6
char buf[N];
fgets(buf, N, stdin);
printf(%s”, buf);

标准I/O – 按行输出

下列函数用来输出字符串:

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

Ø 成功时返回输出的字符个数;出错时返回EOF
Ø puts将缓冲区s中的字符串输出到stdout,并追加’\n’
Ø fputs将缓冲区s中的字符串输出到stream

标准I/O – fputs – 示例

puts(“hello world”);
FILE *fp;
char buf[] =“hello world”;
if ((fp = fopen(argv[1],“a”)) == NULL) {
perror(“fopen”);
return -1;
}
fputs(buf, fp);

Ø 注意:输出的字符串中可以包含’\n’,也可以不包含

标准I/O – 按对象读写

下列函数用来从流中读写若干个对象:

#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t n, FILE *fp);
size_t fwrite(const void *ptr, size_t size, size_t n, FILE *fp);

Ø 成功返回读写的对象个数;出错时返回EOF
Ø 既可以读写文本文件,也可以读写数据文件
Ø 效率高

标准I/O – fread/fwrite – 示例

int s[10];
if (fread(s, sizeof(int), 10, fp) < 0) {
perror(“fread”);
return -1;
}
struct student {
int no;
char name[8];
float score;
} s[] = {{ 1,“zhang”, 97}, {2,“wang”, 95}};
fwrite(s, sizeof(struct student), 2, fp);

六、标准I/O – 刷新流

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

Ø成功时返回0;出错时返回EOF
Ø将流缓冲区中的数据写入实际的文件
ØLinux下只能刷新输出缓冲区

定位流 – ftell/fseek/rewind

#include <stdio.h>
long ftell(FILE *stream);
long fseek(FILE *stream, long offset, int whence);
void rewind(FILE *stream);

Ø ftell() 成功时返回流的当前读写位置,出错时返回EOF
Ø fseek()定位一个流,成功时返回0,出错时返回EOF
Ø whence参数:SEEK_SET/SEEK_CUR/SEEK_END
Ø offset参数:偏移量,可正可负
Ø rewind()将流定位到文件开始位置
Ø 读写流时,当前读写位置自动后移

示例一(在文件末尾追加字符’t’)

FILE *fp = fopen(“test.txt”,“r+);
fseek(fp, 0, SEEK_END);
fputc(‘t’, fp)

示例二(获取文件长度)

FILE *fp;
if ((fp = fopen(“test.txt”,“r+)) == NULL) {
perror(“fopen”);
return -1;
}
fseek(fp, 0, SEEK_END);
printf(“length is %d\n”, ftell(fp))

标准I/O – 判断流是否出错和结束

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

Øferror()返回1表示流出错;否则返回0
Øfeof()返回1表示文件已到末尾;否则返回0

标准I/O – 格式化输出

#include <stdio.h>
int printf(const char *fmt,);
int fprintf(FILE *stream, const char *fmt,);
int sprintf(char *s, const char *fmt,

Ø成功时返回输出的字符个数;出错时返回EOF
Ø使用起来很方便,强烈推荐!

标准I/O – 格式化输出 – 示例

以指定格式 “年-月-日” 分别写入文件和缓冲区

int year, month, date;
FILE *fp;
char buf[64];
year = 2014; month = 10; date = 26;
fp = fopen(“test.txt”,“a+);
fprintf(fp,%d-%d-%d\n”, year, month, date);
sprintf(buf,%d-%d-%d\n”, year, month, date);

七、 文件I/O – 介绍

什么是文件I/O?
Ø posix(可移植操作系统接口)定义的一组函数
Ø 不提供缓冲机制,每次读写操作都引起系统调用
Ø 核心概念是文件描述符
Ø 访问各种类型文件
Ø Linux下, 标准IO基于文件IO实现

文件I/O – 文件描述符

每个打开的文件都对应一个文件描述符
Ø 文件描述符是一个非负整数。Linux为程序中每个打开的
文件分配一个文件描述符
Ø 文件描述符从0开始分配,依次递增。
Ø 文件IO操作通过文件描述符来完成
文件描述符概念是0-1023的数字,表示文件
注意:文件IO不提供缓冲机制
在这里插入图片描述
在这里插入图片描述

标注IO和文件IO读写操作对比
在这里插入图片描述

文件I/O – open

open函数用来创建或打开一个文件:

#include <fcntl.h>
int open(const char *path, int oflag,);

Ø 成功时返回文件描述符;出错时返回EOF
Ø 打开文件时使用两个参数
Ø 创建文件时第三个参数指定新文件的权限
Ø 只能打开设备文件,不能创建设备文件
在这里插入图片描述

文件I/O – open – 示例1

以只写方式打开文件1.txt。如果文件不存在则创建,如果
文件存在则清空:

int fd;
if ((fd = open(1.txt”, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) {
perror(“open”);
return -1;
}……

文件I/O – open – 示例2

以读写方式打开文件1.txt。如果文件不存在则创建,如果
文件存在则报错:

int fd;
if ((fd = open(1.txt”, O_RDWR|O_CREAT|O_EXCL, 0666)) < 0) {
	if (errno == EEXIST) {
	perror(“exist error”);
	} else {
	perror(“other error”);
	}
}

文件I/O – close

close函数用来关闭一个打开的文件:

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

Ø 成功时返回0;出错时返回EOF
Ø 程序结束时自动关闭所有打开的文件
Ø 文件关闭后,文件描述符不再代表文件

文件I/O – read

read函数用来从文件中读取数据:

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

Ø 成功时返回实际读取的字节数;出错时返回EOF
Ø 读到文件末尾时返回0
Ø buf是接收数据的缓冲区
Ø count不应超过buf大小

文件I/O – read – 示例

从指定的文件(文本文件)中读取内容并统计大小

int main(int argc, char *argv[]) {
{
int fd, n, total = 0;
char buf[64];
if (argc < 2) {
printf(“Usage : %s <file>\n”
, argv[0]); return -1;
}
if ((fd = open(argv[1], O_RDONLY)) < 0) {
perror(“open”); return -1;
}
while ((n = read(fd, buf, 64)) > 0) {
total += n;
}……

文件I/O – write

文件I/O – write
write函数用来向文件写入数据:

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

Ø 成功时返回实际写入的字节数;出错时返回EOF
Ø buf是发送数据的缓冲区
Ø count不应超过buf大小

文件I/O – write – 示例

将键盘输入的内容写入文件,直到输入quit

int fd;
char buf[20];
if ((fd = open(argv[1], O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) {
perror(“open”); return -1;
}
while (fgets(buf, 20, stdin) != NULL) {
if (strcmp(buf,
“quit\n”) == 0) break;
write(fd, buf, strlen(buf));
}……

文件IO – lseek

lseek函数用来定位文件:

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

Ø 成功时返回当前的文件读写位置;出错时返回EOF
Ø 参数offset和参数whence同fseek完全一样

访问目录 – opendir

opendir函数用来打开一个目录文件:

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

Ø DIR是用来描述一个打开的目录文件的结构体类型
Ø 成功时返回目录流指针;出错时返回NULL

访问目录 – readdir

readdir函数用来读取目录流中的内容:

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

Ø struct dirent是用来描述目录流中一个目录项的结构体类型
Ø 包含成员char d_name[256] 参考帮助文档
Ø 成功时返回目录流dirp中下一个目录项;
Ø 出错或到末尾时时返回NULL

访问目录 – closedir

closedir函数用来关闭一个目录文件:

#include <dirent.h>
int closedir(DIR *dirp);

Ø 成功时返回0;出错时返回EOF

访问目录 – 示例代码

打印指定的目录下所有文件名称

int main(int argc, char *argv[]) {
{
DIR *dirp;
struct dirent *dp;
if (argc < 2) {
printf(“Usage : %s <directory>\n”
, argv[0]); return -1;
}
if ((dirp = opendir(argv[1])) == NULL) {
perror(“opendir”); return -1;
}
while ((dp = readdir(dirp)) != NULL) {
printf(%s\n”
, dp->d_name);
}
closedir(dirp);

修改文件访问权限 – chmod/fchmod

chmod/fchmod函数用来修改文件的访问权限:

#include <sys/stat.h>
int chmod(const char *path, mode_t mode);
int fchmod(int fd, mode_t mode);

Ø 成功时返回0;出错时返回EOF
Ø root和文件所有者能修改文件的访问权限
示例: chmod(“test.txt”, 0666);

获取文件属性 – stat/lstat/fstat

stat/lstat/fstat函数用来获取文件属性:

#include <sys/stat.h>
int stat(const char *path, struct stat *buf);
int lstat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);

Ø 成功时返回0;出错时返回EOF
Ø 如果path是符号链接stat获取的是目标文件的属性;而
lstat获取的是链接文件的属性

文件属性 – struct stat

struct stat是存放文件属性的结构体类型:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

八、库的概念

库是一个二进制文件,包含的代码可被程序调用
Ø 标准C库、数学库、线程库…… Ø 库有源码,可下载后编译;也可以直接安装二进制包
Ø /lib /usr/lib
库是事先编译好的,可以复用的代码。
Ø 在OS上运行的程序基本上都要使用库。使用库可以提高
开发效率。
Ø Windows和Linux下库文件的格式不兼容
Ø Linux下包含静态库和共享

静态库创建(1)

确定库中函数的功能、接口
Ø 编写库源码hello.c
#include <stdio.h>
void hello(void) {
printf(“hello world\n”);
return;
}
Ø 编译生成目标文件
$ gcc -c hello.c -Wall

静态库创建(2)
创建静态库 hello
$ ar crs libhello.a hello.o
Ø 查看库中符号信息
$nm libhello.a

hello.o:
0000000 T hello
U puts

链接静态库

编写应用程序test.c

#include <stdio.h>
void hello(void);
int main() {
hello();
return 0;
}

Ø 编译test.c 并链接静态库libhello.a
$ gcc -o test test.c -L. -lhello
$ ./test
hello world

共享库特点

编译(链接)时仅记录用到哪个共享库中的哪个符号,不
复制共享库中相关代码
• 程序不包含库中代码,尺寸小
• 多个程序可共享同一个库
• 程序运行时需要加载库
• 库升级方便,无需重新编译程序
• 使用更加广泛

共享库创建(1)

确定库中函数的功能、接口
Ø 编写库源码hello.c bye.c
#include <stdio.h>
void hello(void) {
printf(“hello world\n”);
return;
}
Ø 编译生成目标文件
$ gcc -c -fPIC hello.c bye.c -Wall

共享库创建(2)

创建共享库 common
$ gcc -shared -o libcommon.so.1 hello.o bye.o
Ø 为共享库文件创建链接文件
$ ln -s libcommon.so.1 libcommon.so
Ø 符号链接文件命名规则
lib<库名>.so

链接共享库

Ø 编写应用程序test.c
#include <stdio.h>
#include “common.h”
int main() {
hello();
bye();
return 0;
}
Ø 编译test.c 并链接共享库libcommon.so
$ gcc -o test test.c -L. -lcommo

加载共享库

执行程序
$ ./test
./test: error while loading shared libraries: libcommon.so
cannot open shared object file : No such file or directory
Ø 添加共享库的加载路径
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
$ ./test
hello world
bye!

如何找到共享库

为了让系统能找到要加载的共享库,有三种方法 :
Ø 把库拷贝到/usr/lib和/lib目录下
Ø 在LD_LIBRARY_PATH环境变量中添加库所在路径
Ø 添加/etc/ld.so.conf.d/*.conf文件,执行ldconfig刷新

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大东北哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值