一.零碎调用

1.1 零碎调用概述

零碎调用,操作系统提供给用户程序调用的一组API接口,是应用程序同零碎之间数据交互的桥梁。

Linux 的运行空间分为内核空间与用户空间,它们各自运行在不同的级别中,逻辑上互相隔离。
用户过程在通常状况下不容许拜访内核数据,也无奈应用内核函数,它们只能在用户空间操作用户数据,调用用户空间函数。
但存在很多状况,用户过程须要取得零碎服务,那就要利用零碎调用了。

1.2 用户态和内核态

CPU能够在不同的特权级别下运行,相应的操作系统也有不同的运行级别,那就是用户态和内核态。
内核态下的运行过程能够毫无限度的拜访各种资源,而在用户态下的用户过程的各种操作则都有着限度,例如用户态下的用户过程不能随便拜访内存。

属于内核的零碎调用肯定是运行在内核态下,他是操作系统内核的一部分的,以软件中断的形式从用户态切换到内核态。

1.3 零碎调用和库函数

Linux 下对文件操作有两种形式:零碎调用(system call)和库函数调用(Library functions)。
库函数调用(Library functions)则分为须要零碎调用和不须要零碎调用两种。

零碎调用是须要耗费工夫的,程序中频繁的应用零碎调用会升高程序的运行效率,所以库函数拜访文件的时候依据须要,会设置不同类型的缓冲区,从而缩小了间接调用 IO 零碎调用的次数,进步了拜访效率。

1.4 虚拟地址空间

每个过程都会调配虚拟地址空间,在32位机器上,该地址空间为4G 。

二.文件

2.1 概述

Linux中,所有皆文件。文件为操作系统服务和设施提供了一个简略而统一的接口。这意味着程序齐全能够像应用文件那样应用磁盘文件、串行口、打印机和其余设施。

文件通常由两局部组成: 内容 + 属性,即治理信息,包含文件的创立批改日期和拜访权限等。属性均保留在 inode 节点中。
inode - "索引节点",贮存文件的元信息,比方文件的创建者、文件的创立日期、文件的长度和文件在磁盘上寄存的地位等等。每个inode都有一个号码,操作系统用inode号码来辨认不同的文件。

目录是用于保留其余文件的节点号和名字的文件,每个数据项为指向文件节点的链接。

2.2 文件描述符

咱们能够零碎调用中 I/O 的函数(I:input,输出;O:output,输入),对文件进行相应的操作( open()、close()、write() 、read() 等)。

关上现存文件或新建文件时,零碎(内核)会返回一个文件描述符,文件描述符用来指定已关上的文件。这个文件描述符相当于这个已关上文件的标号,文件描述符是非负整数,是文件的标识,操作这个文件描述符相当于操作这个描述符所指定的文件。

程序运行起来后(每个过程)都有一张文件描述符的表,规范输出、规范输入、规范谬误输出设备文件被关上,对应的文件描述符 0、1、2 记录在表中。程序运行起来后这三个文件描述符是默认关上的。

1)0: 规范输出   STDIN_FILENO2)1: 规范输入   STDOUT_FILENO3)2: 规范谬误   STDERR_FILENO

三.罕用文件IO操作

3.1 open、close函数

open函数:创立一个新的文件描述符(文件或设施)。

#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);性能:    关上文件,如果文件不存在则能够抉择创立。参数:    pathname:文件的门路及文件名    flags:关上文件的行为标记,必选项 O_RDONLY, O_WRONLY, O_RDWR    mode:这个参数,只有在文件不存在时无效,指新建文件时指定文件的权限返回值:    胜利:胜利返回关上的文件描述符    失败:-1

flags必选项:

取值阐明
O_RDONLY以只读的形式关上
O_WRONLY以只写的形式关上
O_RDWR以可读、可写的形式关上

可选项,须要和必选项联合应用

取值阐明
O_CREAT文件不存在则创立文件,应用此选项时需应用mode阐明文件的权限
O_EXCL如果同时指定了O_CREAT,且文件曾经存在,则出错
O_TRUNC如果文件存在,则清空文件内容
O_APPEND写文件时,数据增加到文件开端
O_NONBLOCK对于设施文件, 以O_NONBLOCK形式关上能够做非阻塞I/O

close函数

#include <unistd.h>int close(int fd);性能:    敞开已关上的文件参数:    fd : 文件描述符,open()的返回值返回值:    胜利:0    失败: -1, 并设置errno

示例代码

#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>int main(){    // open 函数和close函数示例    int fd = open("helloworld.log",O_RDWR | O_CREAT,0644);    if(-1 == fd){        perror("open error");        return -1;    }    printf("文件描述符 【%d】\n",fd);    close(fd);    return 0;}

3.2 write

#include <unistd.h>ssize_t write(int fd, const void *buf, size_t count);性能:    把指定数目的数据写到文件(fd)参数:    fd :  文件描述符    buf : 数据首地址    count : 写入数据的长度(字节)返回值:    胜利:理论写入数据的字节个数    失败: - 1

示例代码

#include <stdio.h>#include <sys/types.h>#include <string.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>int main(){    // write 函数和read函数    int fd = open("new.log",O_RDWR | O_CREAT,0644);    if(-1 == fd){        perror("open error");        return -1;    }    // 写入数据    char *str = "helloworld";    write(fd,str,strlen(str));    close(fd);    return 0;}

3.3 read

#include <unistd.h>ssize_t read(int fd, void *buf, size_t count);性能:    把指定数目的数据读到内存(缓冲区)参数:    fd : 文件描述符    buf : 内存首地址    count : 读取的字节个数返回值:    胜利:理论读取到的字节个数    失败: - 1

示例代码

#include <stdio.h>#include <sys/types.h>#include <string.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>int main(){    // read函数    int fd = -1;    int ret = -1;    char buf[128];    fd = open("new.log",O_RDWR | O_CREAT,0644);    if(-1 == fd){        perror("open error");        return -1;    }    memset(buf,0,128);    ret = read(fd,buf,128); // 最多读取128个字节保留到buf中    if(-1 == ret){        perror("read error");        return -1;    }    printf("读取到内容[%s]\n",buf);    close(fd);    return 0;}

3.4 lseek函数

#include <sys/types.h>#include <unistd.h>off_t lseek(int fd, off_t offset, int whence);性能:    扭转文件的偏移量参数:    fd:文件描述符    offset:依据whence来挪动的位移数(偏移量),能够是负数,也能够正数,如果负数,则绝对于whence往右挪动,如果是正数,则绝对于whence往左挪动。如果向前挪动的字节数超过了文件结尾则出错返回,如果向后挪动的字节数超过了文件开端,再次写入时将增大文件尺寸。    whence:其取值如下:        SEEK_SET:从文件结尾挪动offset个字节        SEEK_CUR:从以后地位挪动offset个字节        SEEK_END:从文件开端挪动offset个字节返回值:    若lseek胜利执行, 则返回新的偏移量    如果失败, 返回-1

代码示例

#include <stdio.h>#include <sys/types.h>#include <string.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>int main(){    // read函数    int fd = -1;    int ret = -1;    char buf[128];    fd = open("new.log",O_RDWR | O_CREAT,0644);    if(-1 == fd){        perror("open error");        return -1;    }    memset(buf,0,128);    ret = read(fd,buf,128); // 最多读取128个字节保留到buf中    if(-1 == ret){        perror("read error");        return -1;    }    printf("读取到内容[%s]\n",buf);    // 此时文件曾经到开端,挪动文件指针到结尾再次读取    lseek(fd,0,SEEK_SET);        memset(buf,0,128);    ret = read(fd,buf,128); // 最多读取128个字节保留到buf中    if(-1 == ret){        perror("read error");        return -1;    }    printf("读取到内容[%s]\n",buf);    close(fd);    return 0;}

3.5 perror和error

errno是一个全局变量, 当零碎调用后若出错会将errno进行设置,罕用错误代码的取值和含意如下:l   EPERM:     操作不容许l   ENOENT:   文件或目录不存在。l   EINTR:     零碎调用被中断。l   EAGAIN:    重试,下次有可能胜利!l   EBADF:     文件描述符生效或自身有效l   EIO:        I/O谬误。l   EBUSY:     设施或资源忙。l   EEXIST:     文件存在。l   EINVL:      有效参数。l   EMFILE:     关上的文件过多。l   ENODEV:    设施不存在。l   EISDIR:      是一个目录。l  ENOTDIR:     不是一个目录。

两个无效函数可报告呈现的谬误: strerror 和 perror。
perror 函数也把errno 变量中报告的以后谬误映射成一个字符串,并把它输入到规范谬误输入流。
strerror函数把谬误代号映射成一个字符串,该字符串对产生的谬误类型进行阐明。

3.6 阻塞和非阻塞

阻塞和非阻塞是文件的属性,譬如
read函数在读取一般文件时,是非阻塞的,
read函数在读取设施文件时,是阻塞的,
read函数在读取管道、套接字时,也是阻塞的

一般文件不用说,下面代码能够验证,
读取设施文件,示例如下

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <unistd.h>#include <sys/stat.h>#include <fcntl.h>int main(int argc, char *argv[]){    //读规范输出    char buf[1024];    memset(buf, 0, sizeof(buf));    int n = read(STDIN_FILENO, buf, sizeof(buf));    printf("n==[%d], buf==[%s]\n", n, buf);    return 0;}

四.文件操作汇总

4.1 fstat、stat和lstat零碎调用

fstat 作用:获取文件的状态信息,该信息将会写入一个buf中,buf的地址会以参数的模式传递给fstat。
stat 和 lstat 均通过文件名查问状态信息,当文件名是符号链接时,lstat返回的时符号链接自身的信息,而stat 返回的时改链接指向的文件的信息。

#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>int stat(const char *path, struct stat *buf);int fstat(int fd, struct stat *buf);int lstat(const char *path, struct stat *buf);// struct stat构造阐明struct stat {    dev_t           st_dev;     //文件的设施编号    ino_t           st_ino;     //节点    mode_t          st_mode;            //文件的类型和存取的权限    nlink_t         st_nlink;       //连到该文件的硬连贯数目,刚建设的文件值为1    uid_t           st_uid;     //用户ID    gid_t           st_gid;     //组ID    dev_t           st_rdev;        //(设施类型)若此文件为设施文件,则为其设施编号    off_t           st_size;        //文件字节数(文件大小)    blksize_t       st_blksize;     //块大小(文件系统的I/O 缓冲区大小)    blkcnt_t        st_blocks;      //块数    time_t          st_atime;       //最初一次拜访工夫    time_t          st_mtime;       //最初一次批改工夫    time_t          st_ctime;       //最初一次扭转工夫(指属性)};//st_mode 标记有一系列相干的宏,定义见 sys/stat.h 中

stat示例代码

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <unistd.h>#include <sys/stat.h>#include <fcntl.h>int main(int argc, char *argv[]){    //int stat(const char *pathname, struct stat *buf);    //获取文件属性    struct stat sb;    stat(argv[1], &sb);    //获取文件类型    if ((sb.st_mode & S_IFMT) == S_IFREG)      {        printf("一般文件\n");    }        else if((sb.st_mode & S_IFMT) ==S_IFDIR)    {        printf("目录文件\n");    }    else if((sb.st_mode & S_IFMT) ==S_IFLNK)    {        printf("连贯文件\n");    }        if (S_ISREG(sb.st_mode))     {         printf("一般文件\n");    }    else if(S_ISDIR(sb.st_mode))    {        printf("目录文件\n");    }    else if(S_ISLNK(sb.st_mode))    {        printf("连贯文件\n");    }    //判断文件权限    if(sb.st_mode & S_IROTH)    {        printf("---R----");    }    if(sb.st_mode & S_IWOTH)    {        printf("---W----");    }        if(sb.st_mode & S_IXOTH)    {        printf("---X----");    }    printf("\n");    return 0;}

通过man 2 stat 能够找到代码示例。

4.2 access 函数

#include <unistd.h>int access(const char *pathname, int mode);性能:测试指定文件是否具备某种属性参数:    pathname:文件名    mode:文件权限,4种权限        R_OK:   是否有读权限        W_OK:   是否有写权限        X_OK:   是否有执行权限        F_OK:   测试文件是否存在返回值:    0:  有某种权限,或者文件存在    -1:没有,或文件不存在

示例

#include <stdio.h>#include <unistd.h>int main(int argc, char **argv){    // 查看参数个数    if (2 != argc)    {        printf("usage: ./a.out filename\n");         return 1;    }    //判断文件是否存在    if (access(argv[1], F_OK) == 0)    {        printf("文件存在...\n");     }    else    {        printf("文件不存在....\n");     }    //判断文件是否有读的权限    if (access(argv[1], R_OK) == 0)    {        printf("能够读\n");     }    else    {        printf("不能够读\n");     }    return 0;}

4.3 chmod函数

#include <sys/stat.h>int chmod(const char *pathname, mode_t mode);性能:批改文件权限参数:    filename:文件名    mode:权限(8进制数)返回值:    胜利:0    失败:-1

4.4 chown 函数

#include <unistd.h>int chown(const char *pathname, uid_t owner, gid_t group);性能:批改文件所有者和所属组参数:    pathname:文件或目录名    owner:文件所有者id,通过查看 /etc/passwd 失去所有者id    group:文件所属组id,通过查看 /etc/group 失去用户组id返回值:    胜利:0    失败:-1

4.5 truncate

#include <unistd.h>#include <sys/types.h>int truncate(const char *path, off_t length);性能:批改文件大小参数:    path:文件文件名字    length:指定的文件大小        a)比原来小, 删掉后边的局部        b)比原来大, 向后拓展返回值:    胜利:0    失败:-1

4.6 link函数

#include <unistd.h>int link(const char *oldpath, const char *newpath);性能:创立一个硬链接参数:    oldpath:源文件名字    newpath:硬链接名字返回值:    胜利:0    失败:-1

4.7 symlink

#include <unistd.h>int symlink(const char *target, const char *linkpath);性能:创立一个软链接参数:    target:源文件名字    linkpath:软链接名字返回值:    胜利:0    失败:-1

4.8 readlink函数

#include <unistd.h>ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);性能:读软连贯对应的文件名,不是读内容(该函数只能读软链接文件)参数:    pathname:软连贯名    buf:寄存软件对应的文件名    bufsiz :缓冲区大小(第二个参数寄存的最大字节数)返回值:    胜利:>0,读到buf中的字符个数    失败:-1

4.9 unlink函数

#include <unistd.h>int unlink(const char *pathname);性能:删除一个文件(软硬链接文件)参数:    pathname:删除的文件名字返回值:    胜利:0    失败:-1

4.10 rename函数

#include <stdio.h>int rename(const char *oldpath, const char *newpath);性能:把oldpath的文件名改为newpath参数:oldpath:旧文件名newpath:新文件名返回值:胜利:0失败:-1

五.文件描述符操作

5.1 dup 和 dup2

dup

#include <unistd.h>int dup(int oldfd);性能:    通过 oldfd 复制出一个新的文件描述符,新的文件描述符是调用过程文件描述符表中最小可用的文件描述符,最终 oldfd 和新的文件描述符都指向同一个文件。参数:    oldfd : 须要复制的文件描述符 oldfd返回值:        胜利:新文件描述符        失败: -1

示例

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <unistd.h>#include <sys/stat.h>#include <fcntl.h>int main(){    int fd = -1;    int newfd = -1;    // 关上一个文件    fd = open("new.log", O_RDWR | O_CREAT, 0644);    if(-1 == fd){        perror("open");        return 1;    }    // 复制文件描述符    newfd = dup(fd);    if(-1 == newfd){        perror("dup");        return 1;    }    printf("fd:%d newfd:%d\n",fd,newfd);    // 敞开文件    close(fd);    close(newfd);    return 0;}

dup2

#include <unistd.h>int dup2(int oldfd, int newfd);性能:    通过 oldfd 复制出一个新的文件描述符 newfd,如果胜利,newfd 和函数返回值是同一个返回值,最终 oldfd 和新的文件描述符 newfd 都指向同一个文件。参数:    oldfd : 须要复制的文件描述符    newfd : 新的文件描述符,这个描述符能够人为指定一个非法数字(0 - 1023),如果指定的数字曾经被占用(和某个文件有关联),此函数会主动敞开 close() 断开这个数字和某个文件的关联,再来应用这个非法数字。返回值:    胜利:返回 newfd    失败:返回 -1

示例

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <unistd.h>#include <sys/stat.h>#include <fcntl.h>int main(){    int fd = -1;    int newfd = -1;    // 关上一个文件    fd = open("new.log", O_RDWR | O_CREAT, 0644);    if(-1 == fd){        perror("open");        return 1;    }    // 复制文件描述符    newfd = 10;    newfd = dup2(fd,newfd);    if(-1 == newfd){        perror("dup");        return 1;    }    printf("fd:%d newfd:%d\n",fd,newfd);    // 敞开文件    close(fd);    close(newfd);    return 0;}

5.2 fcnlt函数

#include <unistd.h>#include <fcntl.h>int fcntl(int fd, int cmd, ... /* arg */);性能:扭转已关上的文件性质,fcntl针对描述符提供管制。参数:    fd:操作的文件描述符    cmd:操作形式    arg:针对cmd的值,fcntl可能承受第三个参数int arg。返回值:    胜利:返回某个其余值    失败:-1

fcntl代码示例: 复制一个现有的描述符(cmd=F_DUPFD)

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <unistd.h>#include <sys/stat.h>#include <fcntl.h>int main(){    int fd = -1;    int newfd = -1;    // 关上一个文件    fd = open("new.log", O_RDWR | O_CREAT, 0644);    if(-1 == fd){        perror("open");        return 1;    }    // 复制文件描述符    newfd = fcntl(fd,F_DUPFD,10);        printf("fd:%d newfd:%d\n",fd,newfd);    // 敞开文件    close(fd);    close(newfd);    return 0;}

示例 取得/设置文件状态标记(cmd=F_GETFL或F_SETFL)

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <unistd.h>#include <sys/stat.h>#include <fcntl.h>int main(){    int fd = -1;    int ret = -1;    // 关上一个文件    fd = open("new.log", O_RDWR | O_CREAT, 0644);    if(-1 == fd){        perror("open");        return 1;    }    // 获取文件状态标记    ret = fcntl(fd,F_GETFL);    if(-1 == ret){        perror("fcntl");        return 1;    }    if(ret & O_NONBLOCK){        printf("文件为非阻塞\n");    }    else{        printf("文件为阻塞");    }    // 设置文件状态标记    // 设置为非阻塞    ret |= O_NONBLOCK;    ret = fcntl(fd,F_SETFL,ret);        if (-1 == ret)    {        perror("fcntl");         return 1;    }    // 获取文件状态标记    ret = fcntl(fd,F_GETFL);    if(-1 == ret){        perror("fcntl");        return 1;    }    if(ret & O_NONBLOCK){        printf("文件为非阻塞\n");    }    else{        printf("文件为阻塞");    }    // 敞开文件    close(fd);    return 0;}

六 目录操作汇总

6.1 getcwd

#include <unistd.h>char *getcwd(char *buf, size_t size);性能:获取以后过程的工作目录参数:    buf : 缓冲区,存储以后的工作目录    size : 缓冲区大小返回值:    胜利:buf中保留以后过程工作目录地位    失败:NULL

6.2 chdir函数

#include <unistd.h>int chdir(const char *path);性能:批改以后过程(应用程序)的门路参数:    path:切换的门路返回值:    胜利:0    失败:-1

6.3 opendir函数

#include <sys/types.h>#include <dirent.h>DIR *opendir(const char *name);性能:关上一个目录参数:    name:目录名返回值:    胜利:返回指向该目录构造体指针    失败:NULL

6.4 closedir函数

#include <sys/types.h>#include <dirent.h>int closedir(DIR *dirp);性能:敞开目录参数:    dirp:opendir返回的指针返回值:    胜利:0    失败:-1

6.5 readdir函数

#include <dirent.h>struct dirent *readdir(DIR *dirp);性能:读取目录参数:    dirp:opendir的返回值返回值:    胜利:目录构造体指针    失败:NULL//构造体阐明struct dirent{    ino_t d_ino;                  // 此目录进入点的inode    off_t d_off;                    // 目录文件结尾至此目录进入点的位移    signed short int d_reclen;      // d_name 的长度, 不蕴含NULL 字符    unsigned char d_type;           // d_type 所指的文件类型     char d_name[256];               // 文件名};

d_type取值阐明

取值含意
DT_BLK块设施
DT_CHR字符设施
DT_DIR目录
DT_LNK软链接
DT_FIFO管道
DT_REG一般文件
DT_SOCK套接字
DT_UNKNOWN未知