1.1 C标准函数与系统函数的区别
有一定编程基础的小伙伴应该都接触过文件编程吧,file. 在C语言里面是包一个<file.h>的头
每一个文件都有一个缓冲区,C和系统函数的区别也不想说太多,系统函数可以实现不同进程共享一个缓冲区,而C函数不行。
1.2 PCB的概念
PCB(process control block),进程控制块。 Linux的进程控制块为一个由结构task_struct所定义的数据结构,task_struct存/include/ linux/sched.h 中,其中包括管理进程所需的各种信息。 在创建一个新进程时,系统在内存中申请一个空的task_struct区,即空闲PCB块,并填入所需信息。
1.3 open/close
首先了解一下文件描述符,和文件描述符表。 注意:以下内容记住基于进程,所以文件描述符和符表都存在PCB里面了。 文件描述符表:纪录文件描述符使用情况的表。 文件标书符:在一个进程创建时吗,默认自动打开三个文件,即生成了三个文件描述符: STDINFILENO —>0 STDOUT_FILENO —>1 STDERR_FILENO —>2 标准输入输出流和标准错误流
之后再开辟新文件就会生成新的文件描述符,默认使用空闲的最小的文件描述符。 这里就可以将输入输出重定向:关闭输入输出流,而后重新打开文件,就可以将输入输出重定向到新开文件中。
好,我们来看怎么打开文件z
代码语言:javascript复制#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);
//一般用fd接收返回值
//返回值 :成功返回重新分配的文件描述符,出错则返回-1并设置errno
参数释义: pathname:要打开或创建的文件名,既可以是绝对目录,也可以是相对目录。 flags:打开模式: O_RDONLY 以只读形式打开 O_WRONLY 以只写形式打开 O_RDWR 可读可写形式打开 O_APPEND 表示追加,从文件末尾添加内容,而不覆盖原有内容 O_CREAT 若文件不存在则创建,仅此处会用到第三个参数,赋予文件权限 O_EXCL 和 O_CREAT 共用,如果文件已存在则出错返回 O_TRUNC 这个咱也没用过,如果文件已存在,并且有可写模式打开,则将其长度截断为0字节 O_NONBLOCK 对于设备文件,做非阻塞I/O.
第三个参数指定权限,以八进制数表示。
再看一下如何关闭文件
代码语言:javascript复制#include <unistd.h>
int close(int fd);
//返回值:成功返回0,失败返回-1并设置errno
参数释义: fd为要关闭的文件描述符。 在进程结束时,系统自动调用close关闭所有文件。
1.4 read/write
read函数从打开的文件中读取数据 write函数向打开的文件中写入数据
代码语言:javascript复制#include <unistd.h>
ssize_t read(int fd,void *buf,size_t count);
ssize_t write(int fd,void *buf,size_t count);
//返回值:成功返回读取/写入的字节数,失败返回-1并设置errno。
参数释义: fd:文件描述符 buf:缓存,一般用char数组 count:要读取/写入的字节数
ssize_t:表示有符号的size_t。
有些情况下,count可能不会那么刚好。 从终端设读,通常以行为单位,读到换行符就返回了 从网络读后面socket部分会再说
1.5 阻塞和非阻塞
读常规文件是不会阻塞的 从终端设备或网络读取就不一定了 如果终端输入的数据没有换行符,调用read的终端设备就会阻塞 如果网络上没有收到数据包,调用read从网络读就会阻塞 至于阻塞多久那就不确定了 如果一直没有数据到就一直阻塞在那里
解决阻塞的一个办法叫轮询
1.6 lseek
每个打开的文件都会纪录当前读写的位置,不过那个O_APPEND比较特殊点。 也可以通过lseek来人为操控文件指针偏移位置。
上代码:
代码语言:javascript复制#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd,off_t offset,int whence);
//这里允许偏移超过文件末尾,中间空出来的位置都是0.
参数释义:fd文件描述符 offset:偏移量 whence:偏移的起始位置
whence: SEEK_SET:从文件开始处计算 SEEK_CUR:从当前文件偏移处计算 SEEK_END:从文件结束处计算
若lseek成功执行,返回一个新的偏移量。
注意:偏移之后写入一个空值,不然会偏移不成功。
1.7 fcntl
可以用fcntl对一个已打开的文件进行修改属性,而不必重新open一个文件 不过这个我是没试过了
不过文件锁需要用到这个
Linux中文件记录锁可以对文件某一区域进行文件记录锁的控制。它是通过fcntl函数来实现的。
代码语言:javascript复制#include <unistd.h>
#include <fcntl.h>
int fcntl (int fd,int cmd,struct flck *lock);
//功能说明:管理文件记录锁的操作
//返回值:调用成功返回0,失败返回-1
参数释义: fd:文件描述符; cmd:功能符号; (F_SETLK用来设置或释放锁; F_GETLK用来获得锁信息;) lock:存储锁信息的结构体指针;
struct flock { short l_type; /* 锁的类型 / short l_whence; / 偏移量的起始位置: / off_t l_start; / 从l_whence的偏移量 / off_t l_len; / 从l_start开始的字节数 / pid_t l_pid; / 锁所属进程ID(一般不用) */ } l_type有F_RDLCK读锁、F_WRLCK写锁及F_UNLCK空锁。 l_whence有SEEK_SET、SEEK_CUR和SEEK_END。 l_len为0时表示从起点开始直至最大可能位置为止。
因为这个我也不太会,但是我想会,那就上代码
代码语言:javascript复制#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(){
int fd;
struct flock lock; //声明锁变量
if((fd = open("example",O_CREAT | O_TRUNC | O_RDWR, S_IRWXU)) == -1)
{
printf("open file errorn");
return -1;
}
memset(&lock,0,sizeof(struct flock));//清空锁变量
//设置锁变量
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
//拿到锁则对文件执行后续操作
if(fcntl(fd,F_GETLK,&lock) == 0)
{
//判断该锁是否空锁
if(lock.l_type != F_UNLCK)
{
printf("lock can not by set in fdn");
}
else
{
lock.l_type = F_WRLCK;//要上锁时才给出锁的类型
if(fcntl(fd,F_SETLK,&lock) == 0)
printf("set write lock success!n");
else
printf("set write lock fail!n");
getchar();
lock.l_type = F_UNLCK;//在释放锁之前将锁置空
fcntl(fd,F_SETLK,&lock);//释放锁
}
}
close(fd);//关闭文件描述符
return 0;
}
1.8 ioctl
ioctl用于向设备发送控制和配置命令,有些命令也需要读写一些数据,但是这些数据是不能用write/read来进行读写的,如串口线啊之类的。
代码语言:javascript复制#include <sys/ioctl.h>
int ioctl(int d,int request,······);
//d是某个设备的文件描述符,request 是ioctl的命令。可变参数取决于request。
//成功返回一个值,也是取决于request,失败返回-1并设置而errno。