(1)用户程序从用户空间向内核空间提交了打开申请。
(2)操作系统会在内核中去检查请求是否合法,如果合法,在内核中申请这个打开文件相关的信息(读写位置,在磁盘中的位置…..全用struct files 来存储)。并且添加到当前进程的PCB 块中的打开文件列表数组中。对应的这个数组下标即文件描述值。
(3)将这个文件描述符返回给用户空间,用户空间接下来对这个文件进行读写就通过这个编号值。
系统默认为每个进程打开了三个文件:
printf/scanf /错标准误输出
文件描述符0(标准输入,键盘 STDIN_FILENO),1(标准输出,显示器STDOUT_FLIENO),2(标准错误输出,显示器STDERR_FILENO)
二 、IO 系统调用函数
建立与断开联系 open/close
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, //打开文件路径
int flags); //打开的方式等flags
int open(const char *pathname, int flags, mode_t mode);
int creat(const char *pathname, mode_t mode);
只读方式 O_READONLY
只写方式 O_WRITEONLY
读写方式O_RDWR
追加方式O_APPEND
/* open/fcntl - O_SYNC is only implemented on blocks devices and on files
located on an ext2 file system */
#define O_ACCMODE 0003
#define O_RDONLY 00
#define O_WRONLY 01
#define O_RDWR 02
#define O_APPEND 02000
#define O_CREAT 0100 /* not fcntl */
#define O_EXCL 0200 /* not fcntl */
#define O_NOCTTY 0400 /* not fcntl */
#define O_TRUNC 01000 /* not fcntl */
#define O_NONBLOCK 04000
#define O_NDELAY O_NONBLOCK
#define O_SYNC 010000
#define FASYNC 020000 /* fcntl, for BSD compatibility */
#define O_DIRECT 040000 /* direct disk access hint - currently ignored */
#define O_LARGEFILE 0100000 //大于2G 的文件
#define O_DIRECTORY 0200000 /* must be a directory */
#define O_NOFOLLOW 0400000 /* don't follow links */
#define O_ATOMICLOOKUP 01000000 /* do atomic file lookup */
int open(const char *pathname, int flags, mode_t mode);
新创建一个文件的真正权限是 mode & ~umask
这个文件打开成功,将返回一个新的文件描述符值,后面针对这个文件的操作就使用这个文件描述
符。
读写文件内容 read/write
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
//从fd 所指向的文件中读取count 字节到buf 为首地址的内存空间中。
ssize_t write(int fd, const void *buf, size_t count);
//往fd 所指向的文件中写入count 字节,这个内容存放在buf 为首地址的内存空间中。
(1) 打开一个文件 并输出文件描述符
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<funtl.h>
int main(int argc,char* argv[])
{
int fd;
fd = open(argv[1],O_RDONLY|O_CREAT,0644);
if(-1 == fd)
{
perror("open");
exit(EXIT_FAILURE);
}
printf("fd:%d\n",fd);
close(fd);
}
(2)把一个文件复制到另一个文件中 只可复制1024大小的文件:
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<funtl.h>
int main(int argc,char* argv[])
{
if(argc != 3)
{
printf("pls input message format as:%s src dst\n",argv[0]);
exit(EXIT_FAILURE);
}
int fd_src, fd_dst;
fd_src = open(argv[1],O_RDONLY,0644);
if(-1 == fd_src)
{
perror("open");
exit(EXIT_FAILURE);
}
fd_dst = open(argv[2],O_WRONLY|O_CREAT,0644);
if(-1 == fd_dst)
{
perror("open det");
exit(EXIT_FAILURE);
}
char buf[1024];
int ret = 0;
memset(buf,'\0',1024);
ret = read(fd_src,buf,1024);
write(fd_dst,buf,ret);
close(fd_src);
close(fd_dst);
printf("fd:%d\n",fd_src);
}
(3) 2的改进修改 可复制任意大小的文件
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<funtl.h>
int main(int argc,char* argv[])
{
if(argc != 3)
{
printf("pls input message format as:%s src dst\n",argv[0]);
exit(EXIT_FAILURE);
}
int fd_src, fd_dst;
fd_src = open(argv[1],O_RDONLY,0644);
if(-1 == fd_src)
{
perror("open");
exit(EXIT_FAILURE);
}
fd_dst = open(argv[2],O_WRONLY|O_CREAT,0644);
if(-1 == fd_dst)
{
perror("open det");
exit(EXIT_FAILURE);
}
char buf[1024];
int ret = 0;
while(1) //每次复制1024 直到复制完成
{
memset(buf,'\0',1024);
ret = read(fd_src,buf,1024);
if(ret == -1)
{
perror("read"); exit(EXIT_FAILURE);
}else if(ret == 0)
{
break;
}else
write(fd_dst,buf,ret);
}
close(fd_src);
close(fd_dst);
printf("fd:%d\n",fd_src);
}
文件位置的修改 lseek
对当前文件的读写位置进行定位。可以在文件中添加空洞。
off_t lseek(int fd, off_t offset, int whence);
SEEK_SET
The offset is set to offset bytes.
SEEK_CUR
The offset is set to its current location plus offset bytes.
SEEK_END
The offset is set to the size of the file plus offset bytes.
也可以用这个函数来实现文件大小的获取。把文件的读写位置设置为文件结束,因为这个函数返回
当前读写位置距离文件头的偏移字节数。
lseek+write=pwrite
lseek+read =pread
ssize_t pread(int fd, void *buf, size_t count, off_t offset);
ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
三、 文件描述符操作
(1)复制功能。用处,第9 章管道时会提到重定向的应用 cat test>test.txt。
复制后,两个文件描述符都指向同一个文件表项,即使用这两个文件描述符中的一个就会影响读写
相关的信息。
dup/dup2/fcntl
int dup(int oldfd); //复制oldfd 这个文件描述符,返回在调用此函数前最小未使用的文件描述符的值。
close(0);
dup(3);
这个代码可以实现输入的重定向。默认从0 读数据,现在0 指向的表项被复制为3,这样从0 读实
际上就是从3 指向的文件读,也就实现的输入的重定向。
int dup2(int oldfd, int newfd);
把oldfd 复制为newfd,如果newfd 已经对应一个打开的文件,则先关闭它。
dup2(3,0);
int fcntl(int fd, int cmd, ... /* arg */ );
cmd:F_DUPFD. 复制文件
fd_dup = fcntl(new_copy_fd, F_DUPFD); 将new_copy_fd 这个文件描述符复制给fd_dup;
(2)锁定功能。并发的环境下,除了使用并发的工具来保护共享文件外,也可以使用文件锁。
flock
int flock(int fd, int operation);
LOCK_SH Place a shared lock. More than one process may hold a shared lock for a given file at a
given time.共享读。
LOCK_EX Place an exclusive lock. Only one process may hold an exclusive lock for a given file at a
given time.排它锁。
LOCK_UN Remove an existing lock held by this process.
LOCK_NB 非阻塞式申请。
强调一下,这里指的是锁定文件描述符。锁定这个文件表项。防止被其它的进程访问,锁整个文
件。
如果要锁定某一部分,可以使用 fcntl 函数。
fcntl(fd,F_SETLKW,struct flock*);
第三个参数如下定义:
struct flock {
...
short l_type; /* Type of lock: F_RDLCK,
F_WRLCK, F_UNLCK */
short l_whence; /* How to interpret l_start: 类似于lseek 函数
SEEK_SET, SEEK_CUR, SEEK_END */
off_t l_start; /* Starting offset for lock */ 起始位置偏移
off_t l_len; /* Number of bytes to lock */ 结束位置偏移
pid_t l_pid; /* PID of process blocking our lock 进程号
(F_GETLK only) */
...
};
(3)属性控制。权限状态,拥有者等。
fcntl:当前的读写状态,当前文件描述符所在的进程等系列信息。参阅书上的5.2.7 内容。
(4)提高效率,同步磁盘。
因此,可以使用 mmap 函数来将某个打开的文件映射到虚拟地址空间,以后操作这个虚拟地址空间
就类似于操作这个文件。
fcntl 锁定文件示例
进程A 进程B
都需要对同一个文件进行写入操作
为了让定入操作是排它的,使用文件锁操作。
本章示例代码:实现大于 2G 的文件的拷贝操作。如果文件大于2G,偏移超过int_32 类型的限制,
为了实现大文件的拷贝,必须使用宏。
(1)在所有的头文件包含加加上
#define _LARGEFILE_SOURCE
#define _FILE_OFFSET_BITS 64
(2)在编译时,加上宏
gcc –D_LARGEFILE64_SOUCR –D_FILE_OFFFSET_BITS=64
下面是一个得到当前cpu利用率的代码:
在Linux中如果要监视一个进程的运行情况,如查看它的CPU使用效率和内存使用情况,就需要从系统的/proc目录的读取一些系统信息
void
get_system_info(info)
struct system_info * info;
{
char buffer[ 4096 + 1 ];
int fd, len;
char * p;
int i;
/* get load averages */
{
fd = open( " loadavg " , O_RDONLY);
len = read(fd, buffer, sizeof (buffer) - 1 );
close(fd);
buffer[len] = ' \0 ' ;
info -> load_avg[ 0 ] = strtod(buffer, & p);
info -> load_avg[ 1 ] = strtod(p, & p);
info -> load_avg[ 2 ] = strtod(p, & p);
p = skip_token(p); /* skip running/tasks */
p = skip_ws(p);
if ( * p)
info -> last_pid = atoi(p);
else
info -> last_pid = - 1 ;
}
/* get the cpu time info */
{
fd = open( " stat " , O_RDONLY);
len = read(fd, buffer, sizeof (buffer) - 1 );
close(fd);
buffer[len] = ' \0 ' ;
p = skip_token(buffer); /* "cpu" */
cp_time[ 0 ] = strtoul(p, & p, 0 );
cp_time[ 1 ] = strtoul(p, & p, 0 );
cp_time[ 2 ] = strtoul(p, & p, 0 );
cp_time[ 3 ] = strtoul(p, & p, 0 );
/* convert cp_time counts to percentages */
percentages( 4 , cpu_states, cp_time, cp_old, cp_diff);
}
/* get system wide memory usage */
{
char * p;
fd = open( " meminfo " , O_RDONLY);
len = read(fd, buffer, sizeof (buffer) - 1 );
close(fd);
buffer[len] = ' \0 ' ;
/* be prepared for extra columns to appear be seeking
to ends of lines */
p = buffer;
p = skip_token(p);
memory_stats[ 0 ] = strtoul(p, & p, 10 ); /* total memory */
p = strchr(p, ' \n ' );
p = skip_token(p);
memory_stats[ 1 ] = strtoul(p, & p, 10 ); /* free memory */
p = strchr(p, ' \n ' );
p = skip_token(p);
memory_stats[ 2 ] = strtoul(p, & p, 10 ); /* buffer memory */
p = strchr(p, ' \n ' );
p = skip_token(p);
memory_stats[ 3 ] = strtoul(p, & p, 10 ); /* cached memory */
for (i = 0 ; i < 8 ;i ++ ) {
p ++ ;
p = strchr(p, ' \n ' );
}
p = skip_token(p);
memory_stats[ 4 ] = strtoul(p, & p, 10 ); /* total swap */
p = strchr(p, ' \n ' );
p = skip_token(p);
memory_stats[ 5 ] = strtoul(p, & p, 10 ); /* free swap */
}
/* set arrays and strings */
info -> cpustates = cpu_states;
info -> memory = memory_stats;
}