一、文件系统调用
fork——父进程打开的文件fork复制到子进程中,子进程也可以访问。
c 语言使用 fopen(),打开一个文件,文件类型为 FILE*
fclose()关闭文件。
Linux通过操作系统的子模块——文件系统来管理文件
通过文件id号来管理文件,每个文件的ID号是唯一的
一、 open
c语言中使用fopen打开文件,fopen是库函数。
Linux 通过open打开文件,是一种系统调用,在内核中实现。
fopen->open(),fopen底层调用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);//路径名称,标志位,文件权限
int fd = open(const char *patnname,int flages);
open的返回值(int)fd 称为文件描述符,通过返回值操作文件。
mode:仅当创建新文件时才使用,用于指定文件的访问权限。
pathname?:待打开/创建文件的路径和名称;
flags:打开标志,用于指定文件的打开/创建模式,这个参数可由以下常量(定义于 fcntl.h)通过逻辑或构成。 ???O_RDONLY ? ? ? 只读模式? ? ?O_WRONLY ? ? ?只写模式? ? ?O_RDWR ? ? ? ? ?读写模式 以上三者是互斥的,即不可以同时使用。
打开/创建文件时,至少得使用上述三个常量中的一个。以下常量是选用的: ? ?O_APPEND ? ? ? ? 每次写操作都写入文件的末尾? ? ?O_CREAT ? ? ? ? ? ?如果指定文件不存在,则创建这个文件? ? ?O_EXCL ? ? ? ? ? ? ? 如果要创建的文件已存在,则返回 -1,并且修改 errno 的值 ? ?O_TRUNC ? ? ? ? ? 如果文件存在,并且以只写/读写方式打开,清空文件全部内容 ,重新写入 ? ?O_NOCTTY ? ? ? ? 如果路径名指向终端设备,不要把这个设备用作控制终端。 ? ?O_NONBLOCK ? 如果路径名指向 FIFO/块文件/字符文件,则把文件的打开和后继 I/O设置为非阻塞模式(nonblocking mode)。 //以下用于同步输入输出 ? ?O_DSYNC ? ? ? ? ?等待物理 I/O 结束后再 write。在不影响读取新写入的数据的前提下,不等待文件属性更新。? ? ?O_RSYNC ? ? ? ? ?read 等待所有写入同一区域的写操作完成后再进行 ? ?O_SYNC ? ? ? ? ? ?等待物理 I/O 结束后再 write,包括更新文件属性的 I/O
当你使用带有O_CREAT标志的open调用来创建文件时,你必须使用有3个参数格式的open调用。第三个参数mode是几个标志按位或后得到的, 这些标志在头文件sys/stat.h中定义,如下所示:
S_IRWXU? ?00700? user (file owner) has read, write, and execute permission S_IRUSR? ? 00400? 读权限,文件属主? ? S_IWUSR? ?00200? 写权限,文件属主 S_IXUSR? ? 00100? 执行权限,文件属主
S_IRWXG? ?00070? group has read, write, and execute permission S_IRGRP? ? 00040? 读权限,文件所属组 S_IWGRP? ?00020? 写权限,文件所属组 S_IXGRP? ? 00010? 执行权限,文件所属组
S_IRWXO ?00007? others have read, write, and execute permission S_IROTH? ?00004? 读权限,其它用户 S_IWOTH? 00002? 写权限,其它用户 S_IXOTH? ?00001? 执行权限,其它用户 ?
返回值:成功则返回文件描述符,否则返回 -1。??返回文件描述符(整型变量0~255)。由open 返回的文件描述符一定是该进程尚未使用的最小描述符。只要有一个权限被禁止则返回-1。 错误代码:(均已E开头,将其去掉就是有关于错误的方面的单词或单词的缩写) ? EEXIST 参数pathname 所指的文件已存在,却使用了O_CREAT和O_EXCL旗标。 ? EACCESS 参数pathname所指的文件不符合所要求测试的权限。 ? EROFS 欲测试写入权限的文件存在于只读文件系统内。
? EFAULT 参数pathname指针超出可存取内存空间。
? EINVAL 参数mode 不正确。 ??ENAMETOOLONG 参数pathname太长。? ENOTDIR 参数pathname不是目录。 ? ENOMEM 核心内存不足。 ? ELOOP 参数pathname有过多符号连接问题。 ? EIO I/O 存取错误。 ?
二、write
包含头文件 #include<unistd.h>
函数原型:ssize_t write(int fd,const void *buf, size_t count);//fd? 文件描述符,要写入数据的文件 , *buf 文件数据内容 ,count计划写入字节数 ,ssize_t实际写入字节数
ssize_t write(int fd, const void *buf, size_t count); 参数: ?? fd:对应打开的文件描述符。 buf:存放数据的空间需要输出的缓冲区 count:计划一次从文件中读多少字节数据最大输出字节计数 ?
返回值:成功返回实际写入的字节数,出错返回-1并设置errno
三、close
关闭文件
int close(int fd);
参数:
fd 要关闭的文件描述符
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<fcntl.h>
5
6 int main()
7 {
8 int fd = open("a.txt",O_WRONLY|O_CREAT,0600);//打开(默认就是当前路径)文件a.txt,O_WRONLY只写,O_CREAT,如果文件不存在,则创建此文件,0600文件权限,自己可读可写,其他没有权限
9 if(fd == -1)
10 {
11 exit(1);//如果失败,则返回1
12 }
13
14 write(fd,"hello",5);//向fd文件中写入hello,计划输入5个字节数据
15
16 close(fd);//关闭文件
17
18 exit(0);
19 }
?
?四、read
读数据
包含头文件 #include <unistd.h>
函数原型: ssize_t read(int fd, void *buf, size_t count);// ssize_t实际读数据字节数,fd 文件描述符 ,buf: 为读出数据的缓冲区; count: 计划读数据数,为每次读取的字节数(是请求读取的字节数,读上来的数据保存在缓冲区buf中,同时文件的当前读写位置向后移)
ssize_t read(int fd, void *buf, size_t count); ? 参数: ? ? ?
fd :对应打开的文件描述符 buf:存放待写入的数据,需要读取的缓冲区 count:计划一次向文件中写入多少数据最大读取字节计数 ? ??? ? ? ?
返回值:成功返回读取的字节数,出错返回-1并设置errno,如果在调read之前已到达文件末尾,则这次read返回0?。?
ssize_t 返回值为0 ,说明:1.文件本身为0;2.读到了文件末尾
?返回值为0,读完文件的唯一标志,判断循环的条件
文件读与写,有一个偏移量count的存在,读写的时候系统会记住位置,会一直往后进行
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<fcntl.h>
5
6 int main()
7 {
8 int fd = open("a.txt",O_RDONLY);//打开文件a.txt,此文件已存在,只读模式
9 if(fd == -1)
10 {
11 exit(1);
12 }
13
14 char buff[128] = {0};
15 int n =read(fd,buff,127);
16 printf("n =%d,buff = %s\n",n,buff);
17
18 close(fd);
19 exit(0);
20 }
~
?
?
?二、文件复制
?两个文件要同时打开,文件A需以只读的方式打开,新文件B需要以写的方式打开。
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<fcntl.h>
5
6 int main()
7 {
8 int fdr = open("passwd",O_RDONLY);//原文件以只读的方式打开
9 int fdw = open("newfile",O_WRONLY);//复制的文件以只写的方式打开,若文件不存在,则可以使用O_CREAT创建
10
11 if(fdr == -1 ||fdw == -1)
12 {
13 exit(1);
14 }
15
16 char buff[128]={0};//保存实际数据
17 int num =0;//保存读到的实际字节数
18 while((num = read(fdr,buff,128)) >0)//read返回值为0,则读完了
19 {
20 write(fdw,buff,num);//写入实际读的数据
21 }
22
23 close(fdw);
24 close(fdr);
25 exit(0);
26 }
?main函数传参形式
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<fcntl.h>
5
6 int main(int argc,char *argv[],char * envp[])
7 {
8 if(argc != 3)
9 {
10 printf("mycp arg err\n");//main函数传参,复制必须两个文件,再加上main可执行文件本身,总共三个参数
11 exit(1);
12 }
13 int fdr = open(argv[1],O_RDONLY);//原文件以只读的方式打开
14 if(fdr == -1)
15 {
16 printf("source erro\n");//原文件打开失败
17 exit(1);
18 }
19 int fdw = open(argv[2],O_WRONLY|O_CREAT,0600);//复制的文件以只写的方式打开
20
21 if(fdw == -1)
22 {
23 printf("newfile erro\n");
24 exit(1);
25 }
26
27 char buff[128]={0};//保存实际数据
28 int num =0;//保存读到的实际字节数
29 while((num = read(fdr,buff,128)) >0)//read返回值为0,则读完了
30 {
31 write(fdw,buff,num);//写入实际读的数据
32 }
33
34 close(fdw);
35 close(fdr);
36 exit(0);
37 }
38
三、fork
在c语言中,使用库函数
| | 类型 | | 标准输入 | 键盘 | FILE* | stdin | 标准输出 | 屏幕 | FILE* | stdout | 标准错误输出 | 屏幕 | FILE* | stderr |
在Linux中,万物皆文件
0 1 2 文件描述符一直存在。
a.txt文件
先open文件再fork?
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<fcntl.h>
5
6 int main()
7 {
8 int fd = open("a.txt",O_RDONLY);//打开文件a.txt
9 if(fd == -1)
10 {
11 exit(1);
12 }
13
14 pid_t pid =fork();//fork()一次
15 if(pid == -1)
16 {
17 exit(1);
18 }
19 if(pid ==0)//子进程
20 {
21 char buff[32] = {0};
22 read(fd,buff,1);//读取一个字节的数据
23 printf("child buff = %s\n",buff);
24 sleep(1);
25 read(fd,buff,1);
26 printf("child buff = %s\n",buff);
27 }
28 else//父进程
29 {
30
31 char buff[32] = {0};
32 read(fd,buff,1);
33 printf("parent buff = %s\n",buff);
34 sleep(1);
35 read(fd,buff,1);
36 printf("parent buff = %s\n",buff);
37 }
38 close(fd);
39 exit(0);
40 }
?先打开文件,再fork(),文件描述符也会被复制给子进程,父子进程通过同一文件描述符访问文件,父子进程共享文件。
先fork再open文件
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<fcntl.h>
5
6 int main()
7 {
8 pid_t pid =fork();
9 if(pid == -1)
10 {
11 exit(1);
12 }
13
14 int fd = open("a.txt",O_RDONLY);
15 if(fd == -1)
16 {
17 exit(1);
18 }
19
20 if(pid ==0)
21 {
22 char buff[32] = {0};
23 read(fd,buff,1);
24 printf("child buff = %s\n",buff);
25 sleep(1);
26 read(fd,buff,1);
27 printf("child buff = %s\n",buff);
28 }
29 else
30 {
31
32 char buff[32] = {0};
33 read(fd,buff,1);
34 printf("parent buff = %s\n",buff);
35 sleep(1);
36 read(fd,buff,1);
37 printf("parent buff = %s\n",buff);
38 }
39 close(fd);
40 exit(0);
41 }
?先fork再打开文件,对于父子进程访问文件来说,父子进程是相互独立的,父子进程会有各自的文件描述符,父子进程通过各自的文件描述符访问文件。
write()输出数据
使用printf()->write()系统调用,在内核中
printf(“hello”)->write(1,"hello",5)
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<fcntl.h>
5
6 int main()
7 {
8 write(1,"hello",5);
9
10
11 exit(0);
12 }
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<fcntl.h>
5
6 int main()
7 {
8 printf("A");//printf是先将数据攒在缓冲区中,然后调用write()输出数据
9 write(1,"B",1);//write()不会影响缓冲区,有数据直接输出
10 fork();//printf没有\n,缓冲区有数据,fork也复制了缓冲区的数据
11
12
13 exit(0);
14 }
?read()键盘读取数据
getchar(),sacnf_s() ->read()
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<fcntl.h>
5
6 int main()
7 {
8 char buff[128]={0};
9 read(0,buff,127);
10 printf("buff = %s\n",buff);
11
12
13
14
15
16
17
18
19
20 exit(0);
21 }
?库函数和系统调用的区别
系统调用在内核中实现,一旦系统调用就会产生中断,进入内核空间,陷入内核(从用户态切换到内核态)。
库函数的实现在库中。
但凡涉及操作系统的硬件资源,但凡需要操作内核中的数据结构,一律需要内核去做,用户没有权限。
|