LinuxシステムプログラミングのためのファイルIO
ファイル IO の初日
標準 IO
ファイルを開く: FILE* fopen (ファイル名文字列、オープン モード)
rwa r+ w+ a+
(テキスト ファイル、バイナリ ファイル)
ファイルを閉じる: fclose (FILE* p);
文字を読み取る: int fgetc (FILE* P);
Write文字: fputc(int c,FILE* P)
文字列の読み取り: fgets(char* buf,int size,FILE*)
文字列の書き込み: fputs(char* p,FILE* P);
char arr[4 ] = “ abc” char* p = “abc”; (4 つのメモリ領域の暗記を完了 - Jiang Yang) バイナリ読み取り
: fread (void* buf、各ブロックのサイズ、ブロック数、ファイル ポインタ);
バイナリ書き込み: fwrite (void* buf、各ブロックのサイズ、ブロック数、ファイル ポインタ)
フォーマットされた読み取り: int fscanf(FILE *stream, const char *format, …); scanf フォーマットの書き込みと同様
: int fprintf(FILE *stream, const char *format, …); printf
ファイルのオフセットと同様: fseek、rewind、ftell
(calloc、realloc) malloc -->memset() bzero( )
1. 標準IOの基本概念
1.1c言語の関数(cライブラリ関数)を呼び出して、ある関数(API)を実装する
POSIX:可移植操作系统接口,软件需要在linux下运行,API关联的标准的总称
1.2 システムコール: カーネル内のプログラムインターフェイス、アプリケーションとハードウェアデバイス間の中間層
1> 文件操作系统调用: 打开文件,关闭文件,读写
2> 进程控制系统调用: 创建进程 关闭进程 ps -aux
3> 通信类控制系统调用: 发送消息,接受消息 socket套接字
4> 设备管理系统调用: 打开设备 关闭设备
5> 信息维护类系统嗲用:用户程序和操作系统之间的信息
1.3 ファイルの基本概念
文件:一组相关数据的集合, 文件名:集合的名称
1.4 ファイルの分類 *
普通文件:touch 文件
目录文件: mkdir 目录名
设备文件: 放在dev目录下,和普通文件一样
字符文件: sudo mknod 字符设备名 c 主编号 次编号
块 设 备: sudo mknod 字符设备名 b 主编号 次编号
管道设备: sudo mknod 管道设备名 p 主编号 次编号
网络设备: sudo mknod 网络设备名 s 主编号 次编号
链接文件: 理解成快捷方式
软连接:ln -s 被连接文件名 新文件名 ( 是保存了被连接文件的绝对路径,是另外一个新的文件,访问时用被连接文件的路径替换)
硬链接:ln 被连接文件名 新文件名 (普通文件没区别,指向硬盘的同一块区域)快捷方式
删除对应的被链接文件,软连接会受到影响,而硬链接不会。
1.5 ファイルポインタ FILE*(標準IOとファイルIOの違い)
typedef struct _iobuf{
char* ptr ; //下一个字符的位置
int cnt //剩余字节数
int* base; // 缓冲区的位置
int fd ; // 文件描述符 (文件的标志)
int flag ; //文件的访问模式
} FILE
ファイル記述子: は負ではない整数であり、各プロセスのカーネルによって維持されるオープン ファイルのレコードを指すインデックスです。3
つのデフォルトのオープン ストリーム: stdin (0) 入力ストリーム stdout (1) 出力ストリーム stderr (2) 標準エラーストリーム
1.6 ファイル記述子とファイルポインタの変換*
#include <stdio.h>
函数原型:FILE *fdopen(int fd, const char *mode);
函数作用:把fd的文件描述符转换成文件指针,当文件IO中打开文件,需要用标准IO中的函数时需要使用。
函数参数:fd 文件描述符 mode打开模式
返 回 值:文件指针。
练习:找到标准输入的 文件指针。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE * file = fopen("abc","r");
FILE* p = fdopen(0,"r");
if(p)
{
printf("p = %p\n",p);
}
if(file == NULL)
{
//perror("fdopen");
printf("%s\n",strerror(errno));
exit(0);
}
return 0;
}
函数原型: int fileno(FILE *stream);
函数作用:文件指针转换成文件描述符。
函数参数:stream 是文件指针
返 回 值:文件描述符。
使用: 标准io打开的文件fopen,用read去读取或者write写需要文件描述符,需要进行转换。
2. ファイルIO
2.1 ファイルを開く*
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
函数原型 int open(char *pathname, int flags, mode_t mode);
函数参数:pathname 文件名或者文件路径
flags 打开模式
mode 只有创建时候使用 文件创建权限0666
作 用: 打开文件
返 回 值: 文件描述符,也是文件的打开标志,失败返回-1;
打开模式:
第一组: 只读打开 O_RDONLY
只写方式:O_WRONLY
读写方式:O_RDWR
第二组: O_CREAT 创建文件,如果文件不存在则创建,需要和mode一起配合使用,mode的创建时候的文件权限
O_APPEND 追加的方式写入文件,如果文件已经有内容了,则在文件末尾添加内容,不会覆盖,文件末尾开始读不到内容。
O_TRUNC 覆盖写,每一次写都会把原有内容 清空。
2.2 ファイルを閉じる *
函数原型: int close(int fd);
头文件: #include <unistd.h>
作用: 关闭文件
返回值: 是否成功,如果是0则关闭成功 -1关闭失败
练习:创建danny.log文件 提示3秒后关闭
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int fd = open("danny.log",O_RDWR| O_CREAT,0666);
if(fd == -1)
{
printf("%s\n",strerror(errno));
return 0;
}
sleep(1);
printf("倒计时1妙\n");
sleep(2);
close(fd);
}
2.3 ファイルの読み書き*
#include <unistd.h>
函数原型: ssize_t read(int fd, void *buf, size_t count);
函数参数: fd 文件描述符
buf 存放读取内容的缓存。(数组,malloc申请空间)
count:从文件中读取的字节数
返回值:如果成功返回实际读取的字节数 如果失败返回-1,文件末尾返回0;
函数原型: int write(int fd, const void *buf, long count);
头 文 件: #include <unistd.h>
作 用: 写入文件内
参 数: fd 文件描述符
buf 存放读取内容的缓存。(数组,malloc申请空间)
count:从文件中读取的字节数
返回值:如果成功返回实际写入的字节数
失败返回-1 错误信息错入errno
练习:文件拷贝功能:./cp 文件1 文件2 (实现cp命令)
2.4 ファイルオフセット*
#include <sys/types.h>
#include <unistd.h>
函数原型:off_t lseek(int fd, off_t offset, int whence);
作 用: 移动光标,移动文件偏移位置。
参 数:whence SEEK_CUR 从当前位置为餐卡偏移
SEEK_SET 从文件头开始偏移
SEEK_END 从文件尾开始偏移
offset:偏移多少,正数或者负数,正数往右,负数往
注 意: socket文件--网络设备文件,管道文件没法移动,
2.5 ファイル権限 *
函数原型: int access(const char *pathname, int mode);
头 文 件: #include <unistd.h>
参 数: *pathname,文件名 ,mode 用来判断某一个模式
mode格式如下:
R_OK 判断文件是否有可读权限
W_OK 可写权限
F_OK 文件是否存在
X_OK 判断文件是否可执行
返回值:如果是0则表示成功。失败返回-1 错误信息存入errno中
练习:判断参数中的文件是否存在,如果存在判断有可执行权限
2.6 ファイルの削除*
函数原型:int remove(const char *pathname);
头文件: stdio.h
作用: 删除文件
返回值:如果成功返回0 失败返回-1 errno
如果pathnam是目录,则需要调用rmdir()处理。
如果pathname是文件,则会调用unlink()处理
2.7 fcntl関数*
函数原型: int fcntl(int fd, int cmd, ... /* arg */ );
头 文 件: #include <unistd.h> #include <fcntl.h>
参 数: fd 文件描述符
cmd 命令
F_DUPFD 赋值文件描述符
F_GETFD 获取文件描述符标志
F_SETFD 修改文件描述符
F_GETFL 获取文件的状态标志
F_SETFL 设置文件的状态标志
F_GETLK 获取文件锁
F_SETLK 设置文件锁
F_GETOWN 获取信号处理
F_SETOWN 设置信号处理
第三个:操作命令的参数,比如说设置文件描述符,设置成多少需要用第三个
返回值:如果是错误 返回-1
返回文件描述符 返回文件锁 返回状态标志
第一个重点: 文件状态获取和设置(可读可写)
获取状态标志:通过第二个参数 F_GETFL 来获取到现有的状态标志。例如O_REDONLY ,O_WRONLY, O_RDWR,然后用int变量接受,则得到的属性存入到了int中,
把得到的返回值和某一个属性例如O_REONLY进行按位与操作
增加状态标志: 通过第二个参数F_SETFL来设置,第三个参数是设置的内容,我们可以通过 返回值 |= 你想为期增设的标志,最后调用fcntl函数保存标志。
删除状态标志: 通过第二个参数F_SETFL来设置,第三个参数是设置的内容,我们可以通过 返回值 &=你想为期增设的标志的~来设置,最后调用fcntl函数保存标志。
第二个重点 :设置IO设备非堵塞 F_SETFL
设置: O_NONBLOCK 属性。
第一种设置方式: 在open的时候添加非堵塞属性
第二种设置方式:使用fcntl函数添加非堵塞属性。
第三个重点:设置文件锁 F_SETLK F_GETLK
对文件枷锁后,只允许一个进程读写,其他进程等待读写,直到解锁。
struct flock {
...
short l_type; /* 锁的类型: F_RDLCK, F_WRLCK, F_UNLCK */
short l_whence; /*偏移量的起始位置,枷锁的参考位置 SEEK_SET, SEEK_CUR, SEEK_END */
off_t l_start; /* 正数表示往后偏移后枷锁, */
off_t l_len; /* 枷锁多少个字节 */ 如果字节数为0默认为偏移位置到文件结束。
pid_t l_pid; /* 锁的进程id号
...
};
代码:先定义机构体,给结构体成员赋值,fcntl(fd,F_SETLK,&结构体);
struct flock lock;
lock.type =F_RDLCK;
lock.whence = SEEK_SET;
lock.start = 3;
lock.len = 0;
fcntl(fd,F_SETLK,&lock);
翌日のファイルIO
1つ、
- バッファ: 標準 IO にはバッファがあります。入力データと出力データは最初にキャッシュに入れられます。キャッシュがいっぱいになるか、キャッシュが \n に遭遇すると、キャッシュが更新され、データが画面に更新されます。
#include <stdio.h>
int fflush(FILE *stream);
機能: キャッシュ領域をリフレッシュするには、FILE を使用する必要があります。ファイル IO にはキャッシュ領域がないため、入力キャッシュまたは出力キャッシュをリフレッシュできます。 fflush(stdout) として;
導入: open は作成属性 O_CREAT、0666 を使用します
1. ファイルの作成 【ポイント】
函数原型: int creat(const char *pathname, mode_t mode);
头文件:#include <sys/types.h>
#include <sys/stat.h> #include <fcntl.h>
参数:pathname 文件名/文件路径
mode 创建模式 0666
先从终端 输入一个文件名,如果文件存在则打印存在,如果不存在则创建文件。 ./a.out danny.txt
练习:从终端输入文件名的参数,先判断文件是否存在,如果不存在则创建。
2. fcntl ファイルのロック
对文件枷锁后,只允许一个进程读写,其他进程等待读写,直到解锁。
struct flock {
...
short l_type; /* 锁的类型: F_RDLCK, F_WRLCK, F_UNLCK */
short l_whence; /*偏移量的起始位置,枷锁的参考位置 SEEK_SET, SEEK_CUR, SEEK_END */
off_t l_start; /* 正数表示往后偏移后枷锁, */
off_t l_len; /* 枷锁多少个字节 */ 如果字节数为0默认为偏移位置到文件结束。
pid_t l_pid; /* 锁的进程id号
...
};
代码:先定义机构体,给结构体成员赋值,fcntl(fd,F_SETLK,&结构体);
struct flock lock;
lock.type =F_RDLCK;
lock.whence = SEEK_SET;
lock.start = 3;
lock.len = 0;
fcntl(fd,F_SETLK,&lock);
2. 時刻機能
1. 関数プロトタイプ:time_t time(time_t *); 【ポイント】
参 数: timt_t为 long int类型 的指针 或者NULL
返 回 值:从公元1970年1月1号UTC 0点0分0秒到现在的秒数。
2. 関数プロトタイプ:char *ctime(const time_t *); 【ポイント】
参 数: time_t 是从1970年1月1号0点到现在的时间 time();
返回值: 时间字符串信息,本地时间的日期和事件
作用:获取本地时间,会自动在时间字符串后面加 \r \n自动换行
3. 関数プロトタイプ: struct tm *gmtime(const time_t *);
作用:转换成真实世界所使用的时间和日期表示方式,返回给tm结构体
参数: time_t是从1970年到现在的秒数 time函数的返回值
返回值: tm结构体,保存的是真实世界的时间。
结构体信息如下:
struct tm {【重点】
int tm_sec; /* 秒数 [0-60] (1 leap second) */
int tm_min; /* 分钟数 [0-59] */
int tm_hour; /* 小时 [0-23] */
int tm_mday; /* 一个月中的日期 [1-31] */
int tm_mon; /* 月份[0-11] */
int tm_year; /* 年份 - 1900. */
int tm_wday; /* 周中的天数(星期数). [0-6] */
int tm_yday; /* 一年中的哪一天.[0-365] */
}
4. 関数プロトタイプ: char *asctime(const struct tm *);
参数: tm* 结构体指针,保存的是国际时间的结构体信息
作用: 把国际时间转换成本地时间字符串
返回值:时间字符串
5. 関数プロトタイプ:struct tm *localtime(const time_t *); 【ポイント】
参 数: time_t 是time()函数的返回值
作 用: 把1970年1月1号到现在的秒数转换成本地时间
返 回 值: tm结构体指针
6. 関数プロトタイプ: time_t mktime(struct tm *tm);
参数: tm*的结构体
返回值: time_t 在time函数中的参数中用过
作用: 把tm结构体的时间转换成1970年1月1号到现在的秒数。
3. ファイル属性機能
関数プロトタイプ: int stat(const char *pathname, struct stat *buf); 【ポイント】
头 文 件: #include <sys/types.h> #include <sys/stat.h> #include <unistd.h>
作 用: 获取文件的属性信息
参 数: pathname 文件路径或者文件名 buf 是结构体指针,保存的是文件属性信息
返 回 值: 如果成功返回0 如果是失败返回-1
结构体解析:
struct stat {
dev_t st_dev; /* 文件使用的设备号 */
ino_t st_ino; /* inode 设备的次设备号*/
mode_t st_mode; /* 文件的属性(目录 设备 链接)文件对应的模式 */
nlink_t st_nlink; /* 硬链接数*/
uid_t st_uid; /* 拥有者的用户ID */
gid_t st_gid; /* 组用户id */
dev_t st_rdev; /* 设备id */
off_t st_size; /* 文件的总字节数*/
blksize_t st_blksize; /* 磁盘块大小4k */
blkcnt_t st_blocks; /*占用的磁盘数 */
struct timespec st_atim; /* 最后一次访问的时间 */
struct timespec st_mtim; /* 最后一次修改的时间 */
struct timespec st_ctim; /* 最后一次改变状态的时间 */
}
详解stat结构体中的 st_mode;
总共16位 (short类型)
文件类型属性:(st_mode; 的前4位)
S_ISDIR(m) 目录文件
S_ISCHR(m) 字符设备
S_ISBLK(m) 块设备
S_ISFIFO(m) 管道设备
S_ISLNK(m) s连接设备
S_ISSOCK(m) 网络设备
文件权限( (st_mode; 的后9位))
S_IRWXU 00700 拥有者有可读可写可执行权限 rwx
S_IRUSR 00400 拥有者有可读权限 r
S_IWUSR 00200 拥有者有可写权限 w
S_IXUSR 00100 拥有者有可执行权限 x
S_IRWXG 组的权限 S_IRGRP S_IWGRP S_IXGRP
S_IRWXO (other)其他人权限 S_IROTH S_IWOTH S_IXOTH
3.2 ファイルを削除するには、remove() を呼び出して unlink を呼び出します 【ポイント】
#include <unistd.h>
函数原型:int unlink(const char *pathname);
返回值: 0表示成功 -1表示失败
作用: 删除文件
3.3 ファイル内容の傍受【ポイント】
函数原型: int truncate(const char *path, off_t length);
头文件:#include <unistd.h> #include <sys/types.h>
参 数 : path 文件路径 或者文件名
length 需要截取的长度
返回值: 0表示成功 -1表示失败
函数作用: 从path文件中文件首位置开始截取length个字节。
3.4 ファイル権限の変更
#include <sys/stat.h>
函数原型: int chmod(const char *pathname, mode_t mode);
参 数 : pathname 文件名
mode是文件新的权限 0666
作用: 修改文件权限
返回值: 如果成功返回0 失败返回-1
3.5 権限マスクの変更
#include <sys/types.h>
#include <sys/stat.h>
函数原型: mode_t umask(mode_t mask);
参数: mask 新的权限掩码
返回值: 返回老的权限掩码
4. ディレクトリ操作機能
#include <sys/stat.h>
#include <sys/types.h>
函数原型:int mkdir(const char *pathname, mode_t mode); 【重点】
作用: 创建目录
参数: pathname 文件路径 目录路径 目录名
mode 创建权限 0777
返回值: 成功0 失败-1
#include <unistd.h>
函数原型:int rmdir(const char *pathname); 【重点】
作用: 删除目录 (空目录)
参数: pathname 目录
返回值: 成功0 失败-1
#include <sys/types.h>
#include <dirent.h>
函数原型: DIR *opendir(const char *name); 【重点】
作用: 打开目录 相当于cd 目录名
参数: name 目录名
返回值: dir的结构体指针
#include <sys/types.h>
#include <dirent.h>
函数原型: int closedir(DIR *dirp); 【重点】
参数: dirp 是opendir的返回值
作用: 关闭目录
返回值: 成功0 失败-1
#include <dirent.h>
函数原型: struct dirent *readdir(DIR *dirp); 【重点】
参数: dirp 结构体指针
返回值: 独到的文件或者目录信息 dirent结构体指针,失败返回NULL
作用: 读取目录,按照文件一个一个的读取。
返回值结构体:struct dirent {
ino_t d_ino; /* inode 数目 次设备号 inode节点 */
off_t d_off; /*现在偏移的位置*/
unsigned short d_reclen; /* 目录的长度 */
unsigned char d_type; /* 文件类型 d s l c b */
char d_name[256]; /* 文件名 */
};
struct dirent中d_type中的宏定义如下:
DT_BLK 块设备 DT_CHR 字符设备 DT_DIR 目录 DT_REG 普通文件 DT_LNK 连接
ファイルIO 3日目
1. ディレクトリ機能
#include <sys/types.h>
#include <dirent.h>
関数プロトタイプ:void rewinddir(DIR *dirp);
関数パラメータ:dirp open の戻り値 関数
:ディレクトリの開始位置とオフセットをリセット設定を開始位置に移動します。
関数プロトタイプ void seendir(DIR *dirp, long loc);
関数パラメータ: dirp は open の戻り値
、loc はディレクトリの開始位置からオフセット位置までの長さ (オフセット)
機能: ディレクトリのオフセットを設定します。ディレクトリ
函数原型: long telldir(DIR *dirp);
函数参数: open的返回值 DIR*的结构体指针。
返 回 值: 当前目录的偏移位置
作 用: 查看当前目录的偏移位置
#include <unistd.h>
函数原型: int chdir(const char *path);
函数作用: 跳转到某一个目录中
参数 : 需要跳转的目标目录
2. ライブラリファイルの基本
2.1 ライブラリ ファイルの機能:
对源文件屏蔽 。
尊重开发者的知识产权
使用方便,方便开发。
2.2 ライブラリファイルの概念
本质是上库文件是可执行的二进制文件,可以被操作系统载入内存执行
库是别人写好,成熟的,可以重复使用的某一部分功能。
windows和linux的库文件是不一样的,不兼容。
2.3 ライブラリファイルの分類
ダイナミック ライブラリ: (Windows では dll で終わり、Linux では .so ファイルで終わります)
1>在程序运行的时候加载到目标程序中,编译不检测。
2> 代码小 ,程序升级简单
3>实现资源共享
静的ライブラリ: (Windows では lib ファイル、Linux では .a ファイル)
1>在程序编译阶段连接到目标文件
2>体积大,不利于程序的更新
3>程序运行运行过程中不需要静态库,移植方便。
注意:动态库和静态库是寄生虫,没办法单独运行,单独使用。
3. 静的ライブラリの書き込み
第一步:创建一个源文件和头文件 比如说xx.c xx.h
第二步: 在文件中按照多文件编译写需要的函数代码。
第三步:将库的源文件编译成目标文件 xx.o
gcc -c x.c -o xx.o
第四步: 将目标文件制作成静态库(lib库名.a)
命令:ar crs lib库名.a -o xxx.o
第五步: 编写测试代码 有main函数的代码(调用静态库函数)
第六步: 编译xx.c文件同时链接库文件
gcc -o abc xxx.c -L 目录 -l 库名
第七步:执行可执行程序
注意: gcc -o abc xxx.c -L 目录 -l 库名中 -L后不可以有空格, -l后也不可以有空格, -l后直接加静态库名称不需要加lib的前缀和.a后缀
4. 動的ライブラリの書き込み
第一步:创建一个源文件和头文件 比如说xx.c xx.h
第二步: 在文件中按照多文件编译写需要的函数代码。
第三步:将库的源文件编译成目标文件 xx.o
命令: gcc -fPIC -c xxx.c
fPIC 创建和地址无关的编译程序
第四步:将.o文件编译成动态库(产生了xx.so)
命令:gcc -shared -fPIC -o xxxx.so xxx.o
第五步: 编写测试代码 有main函数的代码(调用动态库函数)
第六步:将编译的xx.c文件编译连接动态库
命令: gcc -o abc xxx.c -L目录 -l库名
第七步: 运行可执行程序。
注意: 在编译完so文件后,需要放入到/lib 或者/usr/lib下
在gcc时 -L不可以省略 并且 -l后如果加空格则需要加动态库的后缀名 如果不加空格 则直接使用名称不用后缀。
运行时动态库必须存在,否则没法找到函数地址,运行出错,但静态库不会,编译完成后删除不会影响代码运行。