《Unix环境高级编程》 总结 (二)

来源:互联网 发布:中国seo排行榜 编辑:程序博客网 时间:2024/05/16 16:57

文件IO (第三章)

1、标准输入、输出、出错:

STDIN_FILENO(0)、STDOUT_FILENO(1)、STDERR_FILENO(2) 定义在<unistd.h>中
文件描述符的范围: 0 —— OPEN_MAX -1

2、open()、openat()


#include <fcntl.h>
int open(const char *path,int oflag, ... /* mode_t mode */);
int openat(int fd,const char *path,int oflag, ... /* mode_t mode */);
成功返回 fd(最小的未用fd),出错返回 -1

#include <unistd.h>
int close(int fd);
成功返回0,出错返回-1

oflag:O_RDONLY / O_WRONLY / RDWR / O_EXEC(只执行打开) / O_SEARCH (不支持)
这5个变量中必须指定且只能指定一个;
O_APPEND 追加
O_CLOEXEC 添加 FD_CLOEXEC 标志
O_CREAT 文件不存在则创建
O_DIRECTORY path不是目录则出错
O_EXCL 如果同时指定了 O_CREAT ,而文件已存在,则出错(可用于测试文件是否
存在,如果不存在,则创建文件)
O_NOCTTY 如果path为终端设备,则不将设备分配作为此进程的控制终端
O_NOFOLLOW path为符号链接则出错
O_NONBLOCK 如path为FIFO/块设备文件/字符设备文件,则以非阻塞文件打开文件
O_SYNC 每次write等待I/O操作完成
O_TRUNC 如果文件存在且可写,则将文件长度截断为0
O_TTY_INIT ?
O_DSYNC 每次write等待I/O操作完成,如果读写操作不影响读取数据,则不等待
O_RSYNC 使read等待直至对文件同一部份挂起的写操作完成
path为绝对路径时,fd被忽略,openat() 同open()
path为相对路径,fd指出path的开始地址
path为相对路径,fd为 AT_FDCWD ,则path以当前目录为起始位置
同一进程中的所有线程共享相同的当前目录
_POSIX_NO_TRUNC 决定文件名过长时是截断还是出错,当有效时,则在整个路径长度超过 PATH_MAX ,或路径中任一文件名超过 NAME_MAX 时出错,errno 为 ENAMETOOLONG

3、create()


#include <fcntl.h>
int create(const char *path, mode_t mode);
成功返回 fd(最小的未用fd),出错返回 -1

等效于:
open(path,O_WRONLY | O_CREAT | O_TRUNC,mode);
create()的不足:只能以只写方式打开文件

4、lseek()


#include <unistd.h>
off_t lseek(int fd,off_t offset,int whence);
成功返回新的文件偏移量,出错返回-1

  • whence 为 SEEK_SET ,offset为距文件开始处的字节;
  • whence 为SEEK_CUR,offset为距当前位置的字节(可正可负);
  • whence 为SEEK_END,offset为距未尾的字节(可正可负)。
获取文件偏移量:
off_t cur;
cur = lseek(fd,0,SEEK_CUR);
如果fd指向管道、FIFO或socket,则lssek返回-1,errno为 ESPIPE 。
因为某些设备可能允许负的偏移量,所以比较lseek的返回值时,不要测试是否小于0,而应测试是否为-1。

5、read()、write()、pread()和pwrite()


#include <unistd.h>
ssize_t read(int fd, void *buf, size_t nbytes);
返回读到的字节数,到末尾返回0,出错返回-1
ssize_t write(int fd, const void *buf, size_t nbytes);
成功返回已写的字节数,出错返回-1
ssize_t pread(int fd, void *buf, size_t nbytes, off_t offset); // 同先lseek后,再read
ssize_t pwrite(int fd, const void *buf, size_t nbytes, off_t offset); // 同先lseek后,再write

6、i 结点

http://www.cnblogs.com/hnrainll/archive/2012/08/25/2237877.html

7、dup()和dup2() (复制fd)


#include <unistd.h>
int dup(int fd);
int dup2(int fd, int fd2);
成功返回新fd,失败返回 -1

dup()的返回值为可用fd的最小值,dup2由fd2指定,如果fd2已打开,则先关闭,如fd等于fd2,则dup2返回fd2,而不关闭。这两个dup函数总是清除新描述字中的 FD_CLOEXEC 。
dup(fd) == fcntl(fd,F_DUPFD,0);
dup2(fd,fd2) == close(fd2); fcntl(fd, FD_DUPFD, fd2);

8、函数sync、fsync和fdatasync

同步内核缓冲区中的数据。

#include <unistd.h>
int fsync(int fd); // 同步数据和属性
int fdatasync(int fd); // 只同步数据
成功返回0,失败返回 -1
void sync(void); // 不等待写完成就返回

有个守护进程updata周期性调用sync来冲洗内核缓冲区,命令sync也会调用sync。
sync不等待写完成就返回,fsync和fdatasync要等待写操作完成后才返回。
fdatasync只影响文件数据部分,fsync还要更新文件属性。

9、函数fcntl

改变已打开文件属性

#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* int arg */);
成功则依赖cmd,出错返回 -1,arg为要设置的值

cmd 取值:
F_DUPFD 复制fd。返回 min(>=arg, 未使用的最小fd),新fd的 FD_CLOEXEC 标志被清除;
F_DUPFD_CLOEXEC 同 F_DUPFD,但要设置 FD_CLOEXEC 标志;
F_GETFD/F_SETFD 得到/设置 FD_CLOEXEC 状态
F_GETFL 返回fd的文件状态标志,如下:
O_RDONLY, O_WRONLY, O_RDWR, O_EXEC, O_SEARCH, O_APPEND, O_NONBLOCK,
O_SYNC (等待写完成 - 数据和属性 - Linux不支持) O_DSYNC (等待写完成 - 仅数据) O_RSYNC (同步读写)
O_RDONLY, O_WRONLY, O_RDWR要与 (val & O_ACCMODE)比较,如下:
int val = fcntl(fd,F_GETFL,0);
switch( val & O_ACCMODE) {
case O_RDONLY:
...
case O_WRONLY:
...
case O_RDWR:
...
}
if(val & O_ADDEND)
...
...

F_SETFL 可设置的值:O_APPEND、O_NONBLOCK、O_SYNC、O_DSYNC、O_RSYNC
F_GETOWN 得到当前接收SIGIO和SIGURG信号的进程ID(正)或进程组ID(负)。
F_SETOWN 设置当前接收SIGIO和SIGURG信号的进程ID或进程组ID,正的arg表示进程ID,负的(|arg|)表示进程组ID

10、函数ioctl

常用于调用驱动接口

#include <unistd.h>
#include <sys/ioctl.h>
int ioctl(int fd, int request, ...);
出错返回-1,成功返回其它值

对于不同的设备文件,可能还需要不同的头文件。

11、/dev/fd

打开文件/dev/fd/n 同 复制描述符n(假设n是打开的),即:
fd = open("/dev/fd/0",mode);  
fd = dup(0);
注:大多数系统,mode是无效的,如n以读打开,再以读写打开"/dev/fd/n",则描述符n仍只能读。
create “/dev/fd/n” 同open加上O_CREATE。

文件和目录 (第四章)

1、函数stat、fstat、lstat和fstatat

获取文件属性相关信息。

stat:指定路径,
fstat:指定文件的fd,
lstat:当文件为符号链接时,返回符号链接的信息,而非其指向的文件的信息
fstat:相关fd的路径。当flag中AT_SYMLINK_NOFOLLOW标志被设置时,如果为符号链接,则返回符号链接本身,否则(默认)返回其指向的文件;如果fd为AT_FDCWD,pathname为相关路径,则
    以当前目前为起始位置;为绝对路径,fd将被忽略。
stat结构体:

其中:timespec结构体至少有下面两项:


2、文件类型

文件类型:
普通文件
目录文件:对目录有读权限,则可读目录的内容,只有内核可直接写目录文件;
块特殊文件:每次以固定长度访问(带缓冲),如磁盘
字符特殊文件:提供不带缓冲的问题,长度可变。设备要么为块特殊文件或者为字符特殊文件。
FIFO
套接字
符号链接

可用stat中的mode_t中来判断,如下:



从stat结构中确定IPC对象类型,如下:

3、设置用户ID和组ID


http://blog.csdn.net/demiaowu/article/details/39370355

4、文件访问权限

文件访问权限位定义在 <sts/stat.h>中,如下:
S_IRUSR,S_IWUSR,S_IXUSR; S_IRGRP,S_IWGRP,S_IXGRP; S_IROTH,S_IWOTH,S_IXOTH
要打开文件,对路径名中的所有目录必须有执行权限;
新建文件/删除文件,对目录要有写和执行权限,对该文件权限无要求;
exec要求对文件有执行权限,且必须为普通文件。

5、新文件和目录的所有权

新文件的UID为进程的EUID,新文件的GID可以为进程的EGID或者它所在目录的GID;

6、access()和faccessat() --- 测试实际 UID和GID 的访问权限


#include <unistd.h>
int access(const char *pathname, int mode);
int faccessat(int fd, const char *pathname, int mode, int flag);
成功返回0,失败返回 -1

mode 取值:
R_OK 测试读权限
W_OK 测试写权限
X_OK 测试执行权限
F_OK 测试文件是否存在
pathname为绝对路径时,忽略fd;fd为 AT_FDCMD而pathname为相对路径时,以当前目录为起始目录;
flag为AT_EACCESS,则检查有效UID和GID而非实际UID和GID。

7、umask() - 为进程设置文件模式创建屏蔽字,并返回之前的值


#include <sys/stat.h>
mode_t umask(mode_t cmask);
返回之前的值。

更改该值并不影响其父进程的屏蔽字

8、chmod()、fchmod()和fchmodat()


#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);
int fchmod(int fd, mode_t mode);
int fchmodat(int fd, const char *pathname, mode_t mode, int flag);
成功返回0,失败返回 -1

flag可设置为 AT_SYSLINK_NOFOLLOW ,从而 fchmodat 不跟随符号链接。
要改变文件权限位,进程EUID必须等于文件的所有者ID,或者该进程为超级用户。
mode取值:


注:该方法只修改i节点中最近一次被更改的时间。

9、粘着位(只有超级用户才能设置)

粘着位,是Unix文件系统权限的一个旗标。最常见的用法在目录上设置粘滞位,如此以来,只有目录内文件的所有者或者root才可以删除或移动该文件。如果不为目录设置粘滞位,任何具有该目录写和执行权限的用户都可以删除和移动其中的文件。实际应用中,粘滞位一般用于/tmp目录,以防止普通用户删除或移动其他用户的文件。
http://blog.csdn.net/virtual_func/article/details/51277051

10、chown()、fchown()、fchownat()和lchown()



11、文件长度

stat结构成员 st_size 表示以字节为单位的文件长度,其只针对普通文件、目录文件和符号链接;
对于目录,长度通常为一个数(16或512)的整数倍;对于符号链接,长度为文件中实际字节数。
st_blksize:块长度
st_blocks:实际块数(Linux默认块长度为 1024)

12、truncate()、ftuuncate() —— 文件截断


#include <unistd.h>
int truncate(const char *pathname,off_t length);
int ftruncate(int fd, off_t length);
成功返回0,失败返回 -1。

如果lenght大于之前长度,文件长度将增加,增加数据读作0。

13、link()、linkat()、unlink()、unlinkat()、remove()


创建目录项:
#include <unistd.h>
int link(const char *existingpath, const char *newpath);
int linkat(int fd, const char *existingpath, int nfd, const char *newpath, int flag);
创建一个指向现有文件的链接,成功返回0,失败返回 -1

若newpath存在,返回出错。且只创建newpath的最后一个分量,路径中的其它部分应当已存在。
fd或nfd中有一个为AT_FDCMD,则基于当前目录计算;有任一路径名为绝对路径,则fd/nfd被忽略。
当fd为符号链接时,flag设置了 AT_SYMLINK_FOLLOW ,则创建符号链接目标的链接;否则创建指向符号链接本身的链接。
一般系统不允许对目录硬链接。

删除目录项:
#include <unistd.h>
int unlink(const char *pathname);
int unlinkat(int fd, const char *pathname,int flag);
成功返回0,失败返回 -1

要删除对文件的链接,必须对目录有写和执行权限;如果对目录设置了粘着位,则对目录要有写权限,且具备下面三个条件之一:
  • 拥有该文件
  • 拥有该目录
  • 具有超级用户权限
系统关闭文件时,先检查打开该文件的进程数,如果为0,再去检查其链接计数,如为0,就删除该文件内容。
当flag设置 AT_REMOVEDIR 时, unlinkat 类似于 rmdir (删除目录)。
unlink常用于确保程序崩溃后,它创建的临时文件也不会遗留下来。
如下pathname为符号链接,则unlink删除该符号链接,没有函数可以通过符号链接来删除它引用的文件。

对于文件,remove()与unlink();对于目录,同 rmdir()。如下:

#include <stdio.h>
int remove(const char *pathname);

14、rename() 和 renameat()

文件重命名,如下:
#include <stdio.h>
int rename(const char *oldname, const char *newname);
int rename(int oldfd, const char *oldname, int newfd, const char *newname);
成功返回0,失败返回 -1

如果oldname为文件,则newname必须为文件;当newname文件存在时,先删除该文件(删除其目录项),再重命名,且进程对两个目录都要有写权限;
如果oldname为目录,则newname必须为空目录,且newname不能为其子目录;
如果为符号链接,则处理符号链接本身而非其指向的文件;
不能对 . 和 .. 重命名;
如果二者相同,则直接返回成功。

15、符号链接

硬链接直接指向 i 节点;硬链接通常必须在同一文件系统,且只有超级用户可创建指向目录的硬链接,而对于符号链接无限制。
mkdir、mkinfo、mknod、rmdir的参数为符号链接时,出错;
各函数对符号链接的处理方式如下:

例外:当以 O_CREAT 和 O_EXCL 调用open打开符号链接时将出错,errno 为 EEXIST;

符号链接的创建:
#include <unistd.h>
int symlink(const char *actualpath, const char *syspath);
int symlinkat(const char *actualpath, int fd, const char *syspath);
成功返回0,失败返回 -1
actualpath可以不存在,与syspath也可以不在同一文件系统。

打开将读取符号链接本身的内容:
#include <unistd.h>
ssize_t readlink(const char *restrict pathname, char *restrict buf, size_t buflen);
ssize_t readlinkat(int fd, const char *restrict pathname, char *restrict buf, size_t buflen);
成功返回读取的字节数,失败返回 -1
buf中的字符不以 \0 终止,所以要通过buflen来得到长度。

16、文件的时间

对每个文件有3个时间,如下:

注:系统中没有对 i 节点的最后访问时间,所以 access()和stat()不修改任何时间。
各函数对时间的影响如下:

文件访问时间和修改时间的修改如下:
#include <sys/stat.h>
int futimens(int fd, const struct timespec times[2]);
int utimensat(int fd, const char *path, const struct timespec times[2], int flag);
成功返回0,失败 -1。
times[0]为访问时间,times[1]为修改时间,二者采用日历时间,不足秒的用纳秒表示。
若times为 NULL ,则二者都为当前时间;
任一时间的tv_nsec为 UTIME_NOW ,则相应的时间为当前时间(忽略tv_sec);
任一时间的tv_nsec为 UTIME_OMIT ,则相应的时间不变(忽略tv_sec);
函数的执行权限如下:
times为NULL或者任一tv_nsec为UTIME_NOW,则EUID必须等于文件所有者ID,或对文件有写权限,或为root;
times非空,且tv_nsec不是UTIME_NOW,也不是UTIME_OMIT,则EUID必须等于文件所有者ID,或为root;
times非空,且两个tv_nsec都为UTIME_OMIT,不执行权限检查。
flag可设置为 AT_SYMLINK_NOFOLLOW (默认是跟随符号链接)

#include <sys/time.h>
int utimes(const char *pathname, const struct timeval times[2]);
成功返回0,失败 -1。
该函数以秒的微秒表示。
注:不能修改st_ctim的值,因为调用utimes后该值会自动修改。

17、mkdir()、mkdirat() 和 rmdir()

#include <sys/stat.h>
int mkdir(const char *pathname, mode_t mode);
int mkdirat(int fd, const char *pathname, mode_t mode);
成功返回0,失败 -1。
对于目录至少要设置执行权限,以允许访问目录中的文件名。

目录删除,如下:
#include <unistd.h>
int rmdir(const char *pathname);
成功返回0,失败 -1。

对目录有访问权限的用户都可以读目录,只有内核可以写目录。
目录读取相关函数:
#include <dirent.h>
DIR *opendir(const char *pathname);
DIR *fdopendir(int fd); // 从fd中得到 DIR
成功返回指针,失败返回NULL
struct dirent *readdir(DIR *dp); // 返回第一个目录项,当DIR由fdopendir得到时,第一项取决于 fd 的文件偏移量
在目录尾或出错返回NULL
void rewinddir(DIR *dp);
int closedir(DIR *dp);
成功返回0,失败-1
long telldir(DIR *dp);
返回与dp关联的目录中的当前位置。
void seekdir(DIR *dp, long loc);

18、chdir()、fchdir()和getcwd()

更改当前目录,如下:
#include <unistd.h>
int chdir(const char *pathname);
int fchdir(int fd);
成功返回0,失败-1
得到当前目录,如下:
#include <unistd.h>
char *getcwd(char *buf,size_t size);
成功返回buf,失败返回NULL

19、文件访问权限位小结




阅读全文
0 0
原创粉丝点击