嵌入式Linux网络编程·笔记3·系统调用篇(二)
七、fcntl(操作文件的特性)
1、头文件:#include <unistd.h> #include <fcntl.h>
2、函数原型:int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
3、函数形参:fd:文件描述词;
cmd:操作命令;
arg:供命令使用的参数;
lock:同arg。
4、函数返回值:与命令有关。如果出错,所有命令都返回-1,如果成功则返回某个其他。
下列三个命令有特定返回值:
F_DUPFD返回新的文件描述符;
F_GETFD返回相应标志;
F_GETFL以及F_GETOWN返回一个正的进程ID或负的进程组ID。
5、功能描述:(file control)根据文件描述词来操作文件的特性。
具体功能及其对应cmd值(5种):
(1)复制一个现有的描述符(cmd=F_DUPFD)。
(2)获得/设置文件描述符标志(cmd=F_GETFD或F_SETFD)。
(3)获得/设置文件状态标志(cmd=F_GETFL或F_SETFL)。
(4)获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN)。
(5)获得/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW)。
6、特殊文件描述符:1个程序会打开3个文件:标准输入0,标准输入1,标准出错2。
7、阻塞与非阻塞
(1)阻塞:指调用结果返回前,当前线程会被挂起。调用线程只有在得到结果后才会返回。
(2)非阻塞:指在不能立刻得到结果前,该调用不会阻塞当前线程。
(3)阻塞和非阻塞,描述的是一种状态,同步与非同步描述的是行为方式
(4)读文件因为没有数据而阻塞会进入休眠状态,休眠时不占用cpu,节省cpu资源。
(5)举例(来源知乎):
你打电话问书店老板有没有某本书,若是阻塞式调用,你会一直把自己“挂起”,直到得到这本书有没有的结果,若是非阻塞式调用,你不管老板有没有告诉你,你自己先一边去玩了, 当然你也要偶尔过几分钟check一下老板有没有返回结果。在这里阻塞与非阻塞与是否同步异步无关。跟老板通过什么方式回答你结果无关。
8、如何将文件设置为非阻塞?(设备文件)
(1)open打开设备文件时,选O_NONBLOCK(有名管道(默认阻塞),可指定O_NONBLOCK)。
(O_NONBLOCK读取不到数据时会回传-1,并且设置errno为EAGAIN)
代码:fd = read(fd,O_RDONLY|O_NONBLOCK);
(2)选择fcntl设置非阻塞的情况:
① 文件已打开却无所需的指定文件状态标志,且不改open参数时,则使用fcntl补设。
② 只有一个文件描述符无路径名时,无法在open指定,则使用fcntl来重设或者补设。
(STDIN_FILENO:接收键盘的输入;STDOUT_FILENO:向屏幕输出)
9、使用fcntl将文件设置为非阻塞(例:将0(键盘输入)设置为O_NONBLOCK)
(1)方式1 -- 重设
fcntl(0, F_SETFL, O_RDONLY | O_NONBLOCK);
(2)方式2 -- 补设
flag = fcntl(0, F_GETFL); //获取原有文件状态标志(设原有为O_RDONLY)
flag = flag | O_NONBLOCK; //通过|(或)操作,在已有的标志上增设O_NONBLOCK
fcntl (0, F_SETFL, flag); //将修改后的“文件状态标志”设置(此时为O_RDONLY|O_NONBLOCK)
八、mmap(存储映射)
1、头文件:#include <sys/mman.h>
2、函数原型:void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
3、函数形参:addr:指定文件应被映射到的进程空间的起始地址;
length:共享内存映射的有效文件大小(字节数),须<=文件的实际大小;
prot:指定共享内存的访问权限(读写属性),不能与文件的打开模式冲突;
flags:指定映射对象的共享属性,映射选项和映射页是否可以共享;
fd:用于创建共享内存映射区的文件描述符,一般由open()返回;
offset:偏移量,表示被映射对象内容的起点,一般设为0,表从头开始。
注:(1)addr一般被指定一个空指针(NULL),表示让系统自动分配。
(2)如果MAP_ANONYMOUS被设定,fd值应为-1,表示进行匿名映射。
(3)prot可选值域:PROT_READ(可读);
PROT_WRITE(可写);
PROT_EXEC(可执行);
PROT_NONE(不可访问,此值不常用)。
(4)flags两个值:MAP_SHARED(权限为共享)、 MAP_PRIVATE(权限为私有)。
4、函数返回值: 成功执行时,返回最后文件映射区的首地址(指针)。失败时,mmap()返回MAP_FAILED[其值为(void *)-1],错误存于errno。
5、功能描述:创建共享内存映射。
(memory map)地址的映射, 实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对应关系,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写这页面到对应的文件磁盘上,即完成了对文件的操作而不必再调用read,write等系统调用函数。相反,内核空间对这段区域的修改也直接反映用户空间,使得进程间通过映射同一普通文件实现共享内存。(创建新的vm_area_struct结构,并将其与文件物理磁盘地址相连)
6、注意事项:(*重要)
(1)用于创建映射区的文件大小为0,实际指定非0大小创建映射区,出“总线错误”。
(2)用于创建映射区的文件大小为0,实际指定为0大小创建映射区,出“无效参数”。
(3)用于创建映射区的文件读写属性为只读,映射区为读写,出“无效参数”
(4)创建映射区,需要read权限,当访问权限指定为“共享”,mmap的读写权限应<=文件的open权限,只写不行。
(5)文件描述符fd,在mmap创建映射区完成即可关闭,后续访问文件用地址访问。
(6)offset必须为4096整数倍(MMU映射最小单位为4k -- 一页)。
(7)对申请的内存,不能越界访问。
(8)mmap用于释放的地址必须为mmap申请返回的地址。
(9)映射区访问权限为“私有”,对内存所作修改,只对内存有效,不会反应到物理磁盘上。
(10)映射区访问权限为“私有”,只需要open文件时有读权限,用于创建映射区即可。
7、优点:系统调用-保护内核。
8、缺点:开销比较大。
9、保险调用方式:
fd = open(“”,O_RDWR);
……
mmap(NULL,有效文件大小,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
九、munmap(释放映射)
1、头文件:#include <sys/mman.h>
2、函数原型:int munmap(void *addr, size_t length);
3、函数形参:addr:指定文件应被映射到的进程空间的起始地址;
length:共享内存映射的有效文件大小(字节数)。
4、函数返回值:成功返回0,失败返回-1,错误存于erron。
十、dup与dup2(重定向)
1、头文件:#include <unistd.h>
2、函数原型:int dup(int oldfd);
int dup2(int oldfd, int newfd);
3、函数形参:oldfd要被复制的文件描述符;
newfd在dup2函数中指定的新文件描述符。
4、函数返回值:
dup:成功返回最小的尚未被使用过的文件描述符,失败返回-1,错误存于erron。
dup2:成功返回新的文件描述符,失败返回-1,错误存于erron。
5、作用:
(1)dup:用来复制参数oldfd所指的文件描述符,成功时返回的新文件描述符和参数oldfd指向同一个文件,这两个描述符共享同一个数据结构,共享所有的锁定,读写指针和各项全现或标志位。
(2)dup2:用来复制参数oldfd所指的文件描述符,并将oldfd拷贝到参数newfd后一起返回。若参数newfd为一个打开的文件描述符,则newfd所指的文件会先被关闭,若newfd等于oldfd,则返回newfd,而不关闭newfd所指的文件。dup2所复制的文件描述符与原来的文件描述符共享各种文件状态。共享所有的锁定,读写指针和各项全现或标志位。
6、newfd和oldfd具有的共同点:
(1)相同的打开文件(管道);
(2)相同的文件指针,即两个文件共享一个文件指针;
(3)相同的访问模式。读取、写入;
(4)相同的文件状态标识。
十一、stat与fstat(获取文件信息)
1、头文件:#include <sys/types.h> #include <sys/stat.h> #include <unistd.h>
2、函数原型:int stat(const char *pathname, struct stat *statbuf);
int fstat(int fd, struct stat *statbuf);
3、函数形参:pathname:创建文件名(路径);
struct stat: 该类型的结构体(statbuf为指向结构体的指针);
fd:(fstat中)已打开文件的文件描述符
4、函数返回值:(均为)成功返回0,失败返回-1,错误存于erron中。
5、作用:
(1)将参数pathname所指向的文件状态复制到参数statbuf所指向的结构(struct stat)中。
(2)stat:通过文件名获取文件信息,无需打开文件。
(3)fstat:需在文件打开时,才可获取文件信息。
6、结合mmap函数使用:stat提供信息(文件的大小),mmap实施映射(共享内存)。
十二、readdir(打开目录)
1、头文件:#include<dirent.h> #include<sys/types.h>
2、定义函数:struct dirent *readdir(DIR *dir);
3、函数形参:struct dirent:该类型的结构体(readdir为指向结构体的指针);
dir:目录名(字符串);
4、函数返回值:成功返回参数dir目录流的下个目录进入点,失败(有错误发生或读取到目录文件尾)则返回NULL。
5、例子:读取/etc/rc.d 目录文件结构,然后显示该目录下的文件