Linux系统编程2
umask
umask可用来设定[权限掩码]。[权限掩码]是由3个八进制的数字所组成,将现有的存取权限减掉权限掩码后,即可产生建立文件时预设的权限。
linux创建文件文件默认权限为644,目录默认权限为755,默认权限掩码为022。
open函数
定义
1 2
| int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode);
|
头文件
1 2
| #include <unistd.h> #include <fcntl.h>
|
参数
1、pathname:
相对该文件的绝对路径
2、flags:
flags参数表示打开文件所采用的操作
- O_RDONLY:只读模式
- O_WRONLY:只写模式
- O_RDWR:可读可写
以上三个参数是必须的,下边的选项是选用的,使用时需要与必选参数|
起来,如O_RDONLY|O_CREAT
O_APPEND 表示追加,如果原来文件里面有内容,则这次写入会写在文件的最末尾。
O_CREAT 如果指定文件不存在,则创建这个文件 fd = open("./2.txt",O_RDONLY|O_CREAT,0644);
O_EXCL 表示如果要创建的文件已存在,则出错,同时返回 -1,并且修改 errno 的值。
O_TRUNC 表示截断,如果文件存在,并且以只写、读写方式打开,则将其长度截断为0。
O_NOCTTY 如果路径名指向终端设备,不要把这个设备用作控制终端。
O_NONBLOCK 如果路径名指向 FIFO/块文件/字符文件,则把文件的打开和后继 I/O设置为非阻塞模式(nonblocking mode)
3、mode:
mode参数表示设置文件访问权限的初始值,和用户掩码umask有关,用于创建文件时使用,即第二个参数flags为O_CREAT
时才有用
返回值
正常则返回一个整数,即文件描述符(int型),出现错误返回-1,并且errno
被设置成对应的值
常见错误:
- 打开文件不存在
- 以写方式打开只读文件
- 以只写或读写方式打开目录,只能用只读方式打开目录
read和write函数
1 2 3
| #include <unistd.h> ssize_t read(int fd, void *buf, size_t count);
|
成功:返回读到的字节数
错误,返回-1,设置errno
为对应值
1 2 3 4
| #include <unistd.h> ssize_t write(int fd, const void *buf, size_t count);
|
使用read和write实现cp操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <fcntl.h> #include <stdlib.h>
int main() { char buf[1024]; int n; int fd_rd = open("./1.txt",O_RDONLY); int fd_wr = open("./2.txt",O_WRONLY|O_CREAT|O_TRUNC,0644); if(fd_rd == -1) { perror("open read_txt error"); exit(1); } if(fd_wr == -1) { perror("open write_txt error"); exit(1); } while( (n = read(fd_rd,&buf,sizeof(buf))) != 0) { if(n<0) { perror("read error"); break; } write(fd_wr, buf, n);
} close(fd_rd); close(fd_wr); return 0; }
|
文件描述符
PCB进程控制块,本质是结构体。其中有个指针变量,指向文件描述符表。
文件描述符表是一个数组,其中的下标0、1、2、….1023就是文件描述符,而数组中存的是每个文件的每个文件结构体file的指针。
1 2 3 4 5 6 7 8
| struct file { 文件的偏移量 文件的访问权限 文件的打开标志 文件在内核缓冲区的首地址 .... }
|
其中
0-STDIN_FILENO
1-STDOUT_FILENO
2-STDERR_FILENO
阻塞和非阻塞
读常规文件是不会阻塞的,不管读多少字节,read 一定会在有限的时间内返回。从终端设备或网络读则不一定,如果从终端输入的数据没有换行符,调用 read 读终端设备就会阻塞,如果网络上没有接收到数据包,调用 read 从网络读就会阻塞,至于会阻塞多长时间也是不确定的,如果一直没有数据到达就一直阻塞在那里。同样,写常规文件是不会阻塞的,而向终端设备或网络写则不一定。
产生阻塞的场景。设备文件、读网络文件
/dev/tty —–终端文件 open("/dev/tty,O_RDWR|O_NONBLOCK")
fcntl函数
获取文件状态:F_GETFL int flags = fctl(0,F_GETFL)
设置文件状态:F_SETFL fcntl(STDIN_FILENO,F_SETFL,flags)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| int main() { char buf[10]; int flags,n; if((flags = fcntl(STDIN_FILENO,F_GETFL))==-1) { perror("fcntl error"); exit(1); }
flags |= O_NONBLOCK; int ret; if((ret = fcntl(STDIN_FILENO,F_SETFL,flags))==-1) { perror("fcntl set error"); exit(1); }
tryagain: n = read(STDIN_FILENO,buf,10); if(n<0)
{ if(errno != EAGAIN) { perror("read /dev/tty"); exit(1); } sleep(3); write(STDOUT_FILENO, "try again\n", strlen("try again\n")); goto tryagain; } write(STDOUT_FILENO,buf,n); return 0; }
|
lseek函数
Linux系统中使用系统函数来修改文件偏移量(读写位置)
1 2 3
| #include <sys/types.h> #include <unistd.h> off_t lseek(int fd, off_t offset, int whence);
|
参数whence
SEEK_SET: 从文件头部开始偏移offset个字节。
SEEK_CUR: 从文件当前读写的指针位置开始,增加offset个字节的偏移量。
SEEK_END: 文件偏移量设置为文件的大小加上偏移量字节。
返回值
成功:返回值是较文件起始位置向后的偏移量
失败:返回-1
注意:打开文件后,文件的读和写使用同一个偏移位置
应用场景:
文件打开后,文件的读和写使用同一个偏移位置
使用lseek获取文件大小
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include <fcntl.h> int main(int agrc, char *argv[]) { int fd = open(argv[1],O_RDWR); if(fd==-1) { perror("open error"); exit(1); }
int len = lseek(fd, 0, SEEK_END); printf("length=%d\n",len); return 0; }
|
- 使用lseek拓展文件大小;要想使文件大小真正改变,必须使用IO操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include <fcntl.h> int main(int agrc, char *argv[]) { int fd = open(argv[1],O_RDWR); if(fd==-1) { perror("open error"); exit(1); }
int len = lseek(fd, 100, SEEK_END); printf("length=%d\n",len); write(fd,"\0",1); return 0; }
|
使用act -A Read.txt
查看文件,包括特殊字符
也可以使用truncate
函数直接拓展,truncate(char *path, off_t length)
如果文件以前大于此大小,则多余的数据将丢失。 如果之前的文件较短,则会扩展它,扩展部分读取为空字节(’\0’)。