Linux环境编程3(系统调用、文件)

一、系统调用(API)

  系统调用就是操作系统提供的一些功能给程序员使用,这些功能已经被封装成C函数的形式,但它们不是标准C的一部分

  一般应用程序运行在用户态(使用的是0 ~ 3G的虚拟内存),系统调用时工作在内核态(使用的是3~4G的虚拟内存)

  常用的标准库函数绝大部分时间运行在用户态,底层偶尔也会调用系统调用进入内核态

  系统调用时运行的就是内核代码,内核代码属性内核的一部分,该代码的外部接口以函数形式定义在共享库中(linux-gate.so ld-linux.so.2)

​ time ./a.out

​ real 0m0.012s 执行总用时

​ user 0m0.000s 用户态执行时间

​ sys 0m0.000s 内核态执行时间

二、一切皆文件

  UNIX/Linux 为操作方便把所有的服务、设备等内容都抽象成了文件的形式,并提供了一套简单而统一的接口,这部分接口称为系统文件IO

  也就是说UNIX/Linux把任何对象都可以当做文件处理,以文件形式访问

文件分类

普通文件				-		包括纯文本文件、二进制文件、
目录文件				d		类似Windows文件夹(必须有执行权限才能进入)
块设备文件			b		保存大块数据的设备,例如硬盘
字符设备文件			c 	    例如键盘、鼠标等等
链接文件				l		类似Windows的快捷方式
管道文件				p
Socket文件		    s		通常用于网络数据链接

三、文件系统的调用

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags);
/*
功能:打开文件
pathname:文件的路径
flags:打开文件的方式
		O_RDONLY		只读
		O_WRONLY		只写
		O_RDWR			读写
		O_APPEND		追加,文件位置指针在末尾
		O_CREAT			文件不存在则创建
		O_EXCL			文件存在则创建失败
		O_TRUNC			如果文件存在则清空
返回值:文件描述符,类似于标准库中的FILE*,代表了一个打开的文件,成功返回非负数,失败返回-1
*/

int open(const char *pathname, int flags, mode_t mode);
/*
功能:创建文件
pathname:文件的路径
flags:打开文件的方式
		只能写O_CREAT
mode:权限掩码
   	    S_IRWXU  00700		拥有者 读写执行权限
		S_IRUSR  00400		拥有者 读权限
		S_IWUSR  00200 		拥有者 写权限
		S_IXUSR  00100 		拥有者 执行权限
		
		S_IRWXG  00070		同组 读写执行权限
		S_IRGRP  00040		同组 读权限
		S_IWGRP  00020		同组 写权限
		S_IXGRP  00010		同组 执行权限
		
		S_IRWXO  00007		其他 读写执行权限
		S_IROTH  00004		其他 读权限
		S_IWOTH  00002		其他 写权限
		S_IXOTH  00001		其他 执行权限

*/

int creat(const char *pathname, mode_t mode);
/*
功能:创建文件
mode:同open的mode
返回值:文件描述符
*/

r			O_RDONLY

r+			O_RDWR

w			O_WRONLY|O_CREAT|O_TRUNC, 0666

w+			O_RDWR|O_CREAT|O_TRUNC, 0666

a			O_WRONLY|O_CREAT|O_APPEND, 0666

a+			O_RDWR|O_CREAT|O_APPEND, 0666
#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);
/*
功能:把内存中的数据写入到文件中
fd:文件描述符,也就是open的返回值
buf:待写入的内存首地址
count:要写入的字节数
返回值:成功写入的字节数
*/

ssize_t read(int fd, void *buf, size_t count);
/*
功能;从文件中读取数据到内存中
fd:文件描述符
buf:数据存储的内存首地址
count:想要读取的字节数
返回值:实际读取到的字节数
*/

int close(int fd);
/*
功能:关闭文件
返回值:成功返回0,失败返回-1
*/

  直接使用标准IO比系统IO写入的速度更快,原因是标准IO有缓冲区机制,在写入数据时并不是直接调用系统IO,而是先把缓冲区填满,然后再调用系统IO把缓冲区中的数据一并写入到文件中,提高读写速度。

  直接使用系统IO会频繁地切换用户态和内核态,更加耗时。

  如果我们也给系统IO增加一个缓存区机制时,它的速度就会比标准IO快

​ 标准IO > 系统IO

​ 系统IO + 缓冲区 > 标准IO

四、随机读写

  每个打开的文件都有一个记录着读写位置的文件位置指针,对文件的读写操作都从该指针指向的位置进行,位置指针会随着读写操作而增加移动。

  一个打开的文件,位置指针指向开头,如果使用O_APPEND,则在文件末尾

  如果想要随意读取文件中的任意位置的数据,可以通过调整位置指针来实现

//标准IO
#include<stdio>

int fseek(FILE *stream, long offset, int whence);
/*
返回值:成功0	失败-1
*/



#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
/*
fd:文件描述符
offset:偏移量
whence:基础位置
		SEEK_SET		文件开头
		SEEK_CUR		当前位置
		SEEK_END		文件末尾
返回值:调整后文件位置指针所在的位置

如果在越过文件末尾的位置写入数据,将在中间形成空洞,空洞会被计算入文件大小中,但是不占用磁盘空间
*/

五、系统IO读写文本文件

  系统IO是没有fprintf/fscanf函数的,因此不能直接读写文本文件

  写文本文件:

    对象 sprintf 转换成字符串,然后在通过write写入文件

    read字符串形式读取,然后再使用sscanf解析转换成对应的数据使用

六、文件描述符

  1、非负整数,代表一个打开的文件

  2、通过系统调用(open\creat)返回,也可以被内核空间引用

  3、它代表了一个内核对象(类似于FILE对象),因为内核不能暴露该对象的地址,因此通过它来表示

  4、内核中有一张表记录了所有打开的文件对象,文件描述符就是访问这张表的下标,相当于访问文件对象的凭证

​ 内核中有三个默认打开的文件描述符

​ 0 标准输入 stdin

​ 1 标准输出 stdout

​ 2 标准错误 stderr

文件描述符的复制

#include <unistd.h>

int dup(int oldfd);
/*
功能:复制一个已经打开的文件描述符
返回值:返回一个没有用过的最小的文件描述符
*/

int dup2(int oldfd, int newfd);
/*
功能:复制成一个指定的文件描述符
newfd:想要复制成的文件描述符,如果newfd已经打开,会先关闭,再复制
返回值:返回newfd的值
*/

//注意:复制成功后,相当于两个不同的文件描述符对应同一个打开的文件

七、文件同步

  1、在写入数据时内存与磁盘之间也有一个缓冲区,这种机制降低了磁盘的读写次数,提高了读写的效率

  2、这种机制带来的后果就是磁盘中的数据与实际写入的数据不匹配,系统提供了三个函数可以让缓冲区中的数据立即写入到磁盘上

#include <unistd.h>

void sync(void);
/*
功能:把缓冲区中的数据立即同步到磁盘上
注意:并不会等待数据全部同步完,而是把缓冲区的数据加入写入队列后,立即返回
*/

int fsync(int fd);
/*
功能:把指定文件的内容从缓冲区同步到磁盘上
注意:会等到全部写入到磁盘后才会返回
返回值:0成功 ,-1失败
*/

int fdatasync(int fd);
/*
功能:把指定文件的内容从缓冲区同步到磁盘上,只同步文件的内容不同步属性
注意:会等到全部写入到磁盘后才返回
*/

八、文件属性

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int stat(const char *path, struct stat *buf);
/*
功能:根据文件的路径获取文件的属性
buf:存储文件属性的结构体指针,是一个输出型参数
*/
int fstat(int fd, struct stat *buf);
/*
功能:根据文件描述符获取文件的属性
*/
int lstat(const char *path, struct stat *buf);
/*
功能:获取软链接文件的文件属性
*/

struct stat {
   dev_t     st_dev;     // 设备IO
   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; // IO块字节数
   blkcnt_t  st_blocks;  // 占用512字节块数
   time_t    st_atime;   // 最后访问时间
   time_t    st_mtime;   // 最后内容修改时间
   time_t    st_ctime;   // 最后状态修改时间
};

/*
&操作
在POSIX中,有对于st_mode 有以下宏可以判断文件属于哪个类型:
    S_ISREG(m)  检查是否是普通文件
    S_ISDIR(m)  检查是否是目录文件
    S_ISCHR(m)  检查是否是字符设备文件
    S_ISBLK(m)  检查是否是块设备文件
    S_ISFIFO(m) 检查是否是管道文件
    S_ISLNK(m)  检查是否是软链接文件
    S_ISSOCK(m) 检查是否是Socket文件

使用st_mode 与下面掩码进行处理,能够获取文件的类型和权限:

    S_IFMT     0170000   获取文件类型的掩码
    S_IFSOCK   0140000   socket文件
    S_IFLNK    0120000   软链接文件
    S_IFREG    0100000   普通文件
    S_IFBLK    0060000   块设备文件
    S_IFDIR    0040000   目录文件
    S_IFCHR    0020000   字符设备文件
    S_IFIFO    0010000   管道文件
    S_ISUID    0004000   设置用户ID
    S_ISGID    0002000   设置组ID
    S_ISVTX    0001000   sticky bit (see below)
    
    S_IRWXU    00700     用户权限掩码
    S_IRUSR    00400     用户读权限
    S_IWUSR    00200     用户写权限
    S_IXUSR    00100     用户执行权限
    S_IRWXG    00070     组权限掩码
    S_IRGRP    00040     		读
    S_IWGRP    00020     		写
    S_IXGRP    00010     		执行
    S_IRWXO    00007     其他用户掩码
    S_IROTH    00004     		读
    S_IWOTH    00002     		写
    S_IXOTH    00001     		执行
*/

九、文件权限

#include <unistd.h>

int access(const char *pathname, int mode);
/*
功能:测试当前用户对文件有哪些权限
pathname:文件路径
mode:想要测试的权限
		F_OK		文件是否存在
		R_OK		写权限
		W_OK		读权限
		X_OK		执行权限
返回值:存在则返回0,不存在返回-1
注意:如果文件不存在测试RWX权限也会返回-1
*/

#include <sys/stat.h>

int chmod(const char *path, mode_t mode);
/*
功能:根据文件路径修改文件权限
path:文件路径
mode:由三位的八进制数组成的权限码
		0644		普通文件
		0755		
返回值:成功返回0,失败返回-1
*/
  
int fchmod(int fd, mode_t mode);
/*
功能:根据文件描述符修改文件权限

*/
/*
权限屏蔽码:
		如果我们不让新创建的文件具有某些权限,可以通过设置权限屏蔽码进行过滤
		通过命令 umask 查看当前终端的权限屏蔽码
    通过命令 umask 0xxx 可以修改当前终端的权限屏蔽码
    注意:权限屏蔽码对于chmod/fchmod函数和命令无效
*/
#include <sys/types.h>
#include <sys/stat.h>

mode_t umask(mode_t mask);
/*
功能:设置当前进程的权限屏蔽码
mask:想要设置的权限屏蔽码
返回值:返回旧的权限屏蔽码
注意:该函数的设置只对当前进程有效,进程结束后就失效了
*/

十、修改文件大小

#include <unistd.h>
#include <sys/types.h>

int truncate(const char *path, off_t length);
/*
功能:根据文件路径修改文件的大小
length:想要修改成的字节数大小
*/

int ftruncate(int fd, off_t length);
/*
功能:根据文件描述符修改文件大小
length:想要成的字节数大小
*/

十一、删除和重命名

#include <stdio.h>

int remove(const char *pathname);
/*
功能:标准库中的删除文件函数,底层调用unlink
pathname:待删除的文件路径
*/

#include <unistd.h>

int unlink(const char *pathname);
/*
功能:删除文件
*/

int rename(const char *oldpath, const char *newpath);
/*
功能:重命名文件
*/

十二、链接文件

​ Linux的文件系统

  inode 信息块:默认128b 记录了文件的权限、大小、所有者、修改时间等信息,以及对应的block的位置信息。

  block 块:默认4k 记录了文件名和真正的数据信息

  每个文件必须拥有唯一一个inode以及若干个block,读取文件时,需要借助目录的block中记录的文件名找到该文件的inode号,通过inode读取到该文件的block

什么软硬链接文件?

  硬链接:硬链接文件没有自己的inode和block,只是在不同目录下复制了一份源文件的inode信息,可以通过inode访问到源文件的同一份block

  软链接:软链接文件会建立自己的新的inode和block,而软链接文件的block中存储的是源文件的文件名和inode号

区别

  1、如果修改硬链接文件内容,源文件以及其他的硬链接文件也会被修改,而软链接不会

  2、当删除源文件,只是删除了源文件的inode,硬链接文件不受影响,但是软链接文件无法访问源文件

  3、当硬链接数被删除为0时,文件才算是被真正的删除了

  4、软链接可以链接目录,硬链接不可以

  5、软链接可以跨文件系统,硬链接不行

#include <unistd.h>

int link(const char *oldpath, const char *newpath);
/*
功能:创建硬链接文件
*/

int symlink(const char *oldpath, const char *newpath);
/*
功能:创建软链接文件
*/

ssize_t readlink(const char *path, char *buf, size_t bufsiz);
/*
功能:读取软链接文件数据,而非链接目标数据,读取的其实是链接的路径
buf:是一个输出型参数,获取链接路径
bufsize:获取的字节数
*/

十三、目录操作

#include <sys/stat.h>
#include <sys/types.h>
int mkdir(const char *pathname, mode_t mode);
/*
功能:创建目录
mode:目录的权限,注意必须要有执行权限,否则无法进入
*/


#include <unistd.h>
int rmdir(const char *pathname);
/*
功能:删除空目录
*/

int chdir(const char *path);
/*
功能:进入某个目录,相当于cd命令
*/

char *getcwd(char *buf, size_t size);
/*
功能:获取当前工作目录,相当于pwd命令
*/


#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
/*
功能:打开目录文件,返回一个目录流
name:选择要打开的目录

*/


#include <dirent.h>
struct dirent *readdir(DIR *dirp);
/*
功能:从目录流中读取一条记录
struct dirent {
   ino_t          d_ino;       //	i节点号
   off_t          d_off;       //	下一条条目的偏移量
   unsigned short d_reclen;    //	当前条目的长度
   unsigned char  d_type;      //	文件类型
       DT_BLK      块设备文件
       DT_CHR      字符设备文件
       DT_DIR      目录文件
       DT_FIFO     管道文件
       DT_LNK      链接文件
       DT_REG      普通文件
       DT_SOCK     socket文件
       DT_UNKNOWN  未知类型
   char           d_name[256]; //	文件名
};

*/

void seekdir(DIR *dirp, long loc);
/*
功能:设置目录流的位置指针,用于随机读取
*/

long telldir(DIR *dirp);
/*
功能:设置当前目录流的位置指针位置
*/

#include <sys/types.h>
#include <dirent.h>
void rewinddir(DIR *dirp);
/*
功能:设置目录流的位置指针到开头
*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值