Linux系统编程-文件操作
总阅读次
下面主要介绍文件IO操作的4个主要系统调用:open()、read()、write()、close()
Linux程序中可以像文件那样使用磁盘文件、串口、打印机。也就是说Linux任何事物都可以用一个文件来操作。
你只需要很少的函数就可以对文件和设备进行访问和控制。这些函数被称为系统调用。
系统调用由UNIX(和Linux)直接提供,它们本身通向操作系统本身的接口
open
1 | fd=open(pathname,flags,mode) |
函数打开pathname所标识的文件,并返回文件描述符,用以在后续函数中指代打开的文件。
函数原型
1 |
|
open()将返回一文件描述符,用于后续函数调用中指代该文件
若发生错误,则返回-1,并将errno置为相应的错误标志
- flags标志位
标志位 | 用途 |
---|---|
O_RDONLY |
以只读方式打开文件 |
O_WRONLY |
以只写方式打开文件 |
O_RDWR |
以可读写方式打开文件. |
上述三种旗标是互斥的, 也就是不可同时使用, 但可与下列的旗标利用OR(|)运算符组合.
补充
标志位 | 用途 |
---|---|
O_CREAT | 若文件不存在则创建 |
标志O_NOCTTY可以告诉UNIX这个程序不会成为这个端口上的“控制终端”。如果不这样做的话,所有的输入,比如键盘上过来的Ctrl+C中止信号等等,会影响到你的进程。而有些程序比如getty(1M/8)则会在打开登录进程的时候使用这个特性,但是通常情况下,用户程序不会使用这个行为.|
标志位 | 用途 |
---|---|
O_TRUNC | 截断已有文件,使其长度为零 |
O_DIRECT | 无缓冲的输入/输出 |
O_NOCTTY | 不要让pathname*(所指向的终端设备)*称为控制终端。 |
O_NDELAY | 标志则是告诉UNIX,这个程序并不关心DCD信号线的状态——也就是不关心端口另一端是否已经连接。如果不指定这个标志的话,除非DCD信号线上有space电压否则这个程序会一直睡眠。 |
O_EXCL | 如果O_CREAT 也被设置, 此指令会去检查文件是否存在. 文件若不存在则建立该文件, 否则将导致打开文件错误. 此外, 若O_CREAT 与O_EXCL 同时设置, 并且欲打开的文件为符号连接, 则会打开文件失败. |
O_TRUNC | 若文件存在并且以可写的方式打开时, 此旗标会令文件长度清为0, 而原来存于该文件的资料也会消失. |
O_APPEND | 当读写文件时会从文件尾开始移动, 也就是所写入的数据会以附加的方式加入到文件后面. |
O_NONBLOCK | 以不可阻断的方式打开文件, 也就是无论有无数据读取或等待, 都会立即返回进程之中. |
O_SYNC | 以同步的方式打开文件. |
O_NOFOLLOW | 如果参数pathname 所指的文件为一符号连接, 则会令打开文件失败. |
O_DIRECTORY | 如果参数pathname 所指的文件并非为一目录, 则会令打开文件失败。注:此为Linux2. 2 以后特有的旗标, 以避免一些系统安全问题. |
参数mode
当你使用O_CREAT标志的open调用来创建文件,你必须使用3个参数的格式的open调用。
第3个参数mode是几个标志按位或后得到的,这些标志在头文件
sys/stat.h
中定义则有下列数种组合, 只有在建立新文件时才会生效, 此外真正建文件时的权限会受到umask 值所影响, 因此该文件权限应该为 (mode-umaks).
标志位 | 说明 |
---|---|
S_IRWXU 00700 权限 | 代表该文件所有者具有可读、可写及可执行的权限 |
S_IRUSR 或S_IREAD, 00400 权限 | 代表该文件所有者具有可读取的权限 |
S_IWUSR 或S_IWRITE, 00200 权限 | 代表该文件所有者具有可写入的权限 |
S_IXUSR 或S_IEXEC, 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 权限 | 代表其他用户具有可执行的权限 |
返回值:若所有欲核查的权限都通过了检查则返回0 值, 表示成功, 只要有一个权限被禁止则返回-1.
实例程序
1 |
|
错误代码:
|代码 |说明 | |
|:————-:|:——————:|
|EEXIST 参数pathname| 所指的文件已存在, 却使用了O_CREAT 和O_EXCL 旗标.|
|EACCESS 参数pathname |所指的文件不符合所要求测试的权限.|
|EROFS |欲测试写入权限的文件存在于只读文件系统内.|
|EFAULT 参数pathname| 指针超出可存取内存空间.|
|EINVAL 参数mode| 不正确.|
|ENAMETOOLONG 参数 pathname| 太长.|
|ENOTDIR 参数pathname| 不是目录.|
|ENOMEM |核心内存不足.|
|ELOOP 参数pathname |有过多符号连接问题.|
|EIO |I/O 存取错误.|
read
读取文件内容read(),调用从文件描述符fd所指代的打开文件中读取数据
函数原型
1 |
|
系统调用read:从与文件描述符fildes相关联的文件读入nbytes个字节的数据,并把他们放到数据去buff中。它返回实际读入的字节数,这可能会小于请求的字节数。
如果read返回0,就表示未读入任何数据,已达到文件尾部。同样,如果返回的是-1,就表示read调用出现错误。
UNIX和Linux中比较重要的设备文件有3个: /dev/console、/dev/tty和/dev/null
- /dev/console
这个设备代表是系统控制台。错误信息通常会被发送到这个设备
- /dev/tty
如果一个进程有控制终端的话,那么特殊文件/dev/tty就是这个控制终端*(键盘和显示屏,或键盘窗口)的别名(逻辑设备)*
- /dev/null
/dev/null文件是空(null)设备。所有写向这个设备的输出都将被丢弃,而读这个设备会立刻返回一个文件标志.所以在cp命令里可以把它用做复制文件的源文件
目录
目录是用于保存其他文件的节点号和名字的文件。目录文件中的每个数据项都是指向某个文件节点的链接,删除文件名就等于删除与之对应的链接。
文件的节点号可以通过
ls -i
命令查看你可以通过使用ln命令在不同的目录中创建指向同一个文件的链接。
incode编号
文件除了本身包含的内容以外,还包含“管理信息”,它包含文件的创建/修改日期和它的访问权限。这些属性被保存在文件的inode节点中,
它是文件系统中的一个特殊的数据块,它同时还包含文件的长度和文件在磁盘上的存放的位置。系统使用的文件的inode编号,目录结构为文件命名仅仅是为了方便人们使用。
write
原型
1 |
|
系统调用write的作用是把缓冲区buf的前nbytes个字节写入与文件描述符fildes关联的文件中。它返回实际写入的字节数。如果文件描述符有错或者底层的驱动设备对数据块长度比价敏感,返回值可能会小于nbytes
fildes文件描述符*(即fd)*。
标准文件描述符
文件描述符 | 用途 | POSIX名称 | stdio流 |
---|---|---|---|
0 | 标准输入 | STDIN_FILENO | stdin |
1 | 标准输出 | STDOUT_FILENO | stdout |
2 | 标准错误 | STDERR_FILENO | stderr |
在程序中指代这些文件描述符时,可以使用数字(0、1、2)表示,或者采用<unistd.h>所定义的POSIX标准名称。
注意使用POSIX标准命名,一定要加头文件
unistd.h
如果使用数字则不一定要。
实例程序1.
1 | #include<unistd.h> |
实例程序2
1 | //标准输入输出头文件 |
creat
打开文件creat函数
1 | int creat(const char *pathname, mode_t mode);` |
验证&验证
验证结论 执行close(1);后面指令将无法正常执行。
1 表示
标准输出
验证close(文件描述符的功能)
实例程序1.
1 |
|
运行结果
1 | root@ACER:/home/takethat/workspace/linux_program/io/open# gcc open.c -o run_open |
实例程序2.
1 |
|
该程序执行后无任何输出。
程序执行完 fd=close(1);
就不再执行了。
但程序执行close(0); 、 close(2);程序则正常运行
存疑
open返回文件描述符3是什么?
文件描述符不是只有0、1、2吗?
是不是跟函数返回类型定义有关?因为
open函数的定义
1 |
|
write函数定义
1 |
|
实例程序:fd返回3
声明/home/takethat/workspace/linux_program/io/open/file 这个文件是存在的
1 |
|
执行结果
1 | root@ACER:/home/takethat/workspace/linux_program/io/open# gcc fd_open0.c -o run_fd_open0 |
fd返回3
插曲:倘若用在该代码预先用close(0);关闭标准输入,open返回0
fd_open.c
1 |
|
运行结果
1 | fd0 is 0 |
若把 程序fd_open.c
1 | fd0=close(STDIN_FILENO);/*close file descriptor 0*/ |
换成
1 | fd0=close(2);/*close file descriptor 2*/ |
程序运行则是:
1 | fd0 is 0 |
这是因为0被关闭。则0是未被占用的。然后被open使用,所以输出是2
SUSV3固定,如果调用open()成功,必须保证其返回值为进程未使用文件描述符数值最小者。