fork——父进程打开的文件fork复制到子进程中,子进程也可以访问。
c 语言使用 fopen(),打开一个文件,文件类型为 FILE*
fclose()关闭文件。
Linux通过操作系统的子模块——文件系统来管理文件
通过文件id号来管理文件,每个文件的ID号是唯一的
一、 openc语言中使用fopen打开文件,fopen是库函数。
Linux 通过open打开文件,是一种系统调用,在内核中实现。
fopen->open(),fopen底层调用open()
包含头文件#include
#include
#include
函数原型:
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 存取错误。
包含头文件 #include
函数原型: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 #include2 #include 3 #include 4 #include 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
函数原型: 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 #include2 #include 3 #include 4 #include 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 = %sn",n,buff); 17 18 close(fd); 19 exit(0); 20 } ~
二、文件复制
两个文件要同时打开,文件A需以只读的方式打开,新文件B需要以写的方式打开。
1 #include2 #include 3 #include 4 #include 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三、fork2 #include 3 #include 4 #include 5 6 int main(int argc,char *argv[],char * envp[]) 7 { 8 if(argc != 3) 9 { 10 printf("mycp arg errn");//main函数传参,复制必须两个文件,再加上main可执行文件本身,总共三个参数 11 exit(1); 12 } 13 int fdr = open(argv[1],O_RDONLY);//原文件以只读的方式打开 14 if(fdr == -1) 15 { 16 printf("source erron");//原文件打开失败 17 exit(1); 18 } 19 int fdw = open(argv[2],O_WRONLY|O_CREAT,0600);//复制的文件以只写的方式打开 20 21 if(fdw == -1) 22 { 23 printf("newfile erron"); 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
在c语言中,使用库函数
| 类型 | |||
| 标准输入 | 键盘 | FILE* | stdin |
| 标准输出 | 屏幕 | FILE* | stdout |
| 标准错误输出 | 屏幕 | FILE* | stderr |
在Linux中,万物皆文件
0 1 2 文件描述符一直存在。
a.txt文件
先open文件再fork1 #include2 #include 3 #include 4 #include 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 = %sn",buff); 24 sleep(1); 25 read(fd,buff,1); 26 printf("child buff = %sn",buff); 27 } 28 else//父进程 29 { 30 31 char buff[32] = {0}; 32 read(fd,buff,1); 33 printf("parent buff = %sn",buff); 34 sleep(1); 35 read(fd,buff,1); 36 printf("parent buff = %sn",buff); 37 } 38 close(fd); 39 exit(0); 40 }
先打开文件,再fork(),文件描述符也会被复制给子进程,父子进程通过同一文件描述符访问文件,父子进程共享文件。
先fork再open文件1 #include2 #include 3 #include 4 #include 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 = %sn",buff); 25 sleep(1); 26 read(fd,buff,1); 27 printf("child buff = %sn",buff); 28 } 29 else 30 { 31 32 char buff[32] = {0}; 33 read(fd,buff,1); 34 printf("parent buff = %sn",buff); 35 sleep(1); 36 read(fd,buff,1); 37 printf("parent buff = %sn",buff); 38 } 39 close(fd); 40 exit(0); 41 }
先fork再打开文件,对于父子进程访问文件来说,父子进程是相互独立的,父子进程会有各自的文件描述符,父子进程通过各自的文件描述符访问文件。
write()输出数据使用printf()->write()系统调用,在内核中
printf(“hello”)->write(1,"hello",5)
1 #include2 #include 3 #include 4 #include 5 6 int main() 7 { 8 write(1,"hello",5); 9 10 11 exit(0); 12 }
1 #includeread()键盘读取数据2 #include 3 #include 4 #include 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 }
getchar(),sacnf_s() ->read()
1 #include库函数和系统调用的区别2 #include 3 #include 4 #include 5 6 int main() 7 { 8 char buff[128]={0}; 9 read(0,buff,127); 10 printf("buff = %sn",buff); 11 12 13 14 15 16 17 18 19 20 exit(0); 21 }
系统调用在内核中实现,一旦系统调用就会产生中断,进入内核空间,陷入内核(从用户态切换到内核态)。
库函数的实现在库中。
但凡涉及操作系统的硬件资源,但凡需要操作内核中的数据结构,一律需要内核去做,用户没有权限。



