unix系统编程小结(一)------文件I/O

简介:
   基本看完了《unix/linux编程实践教程》,实现了几个小项目,觉得很不过瘾,书中对很多system call的细节和原理都没讲。在尝试看linux内核分析的书时发现很多困难,所以拿起apue,继续打基础。读着读着,对apue上瘾了。这是题外话,就说这么多。
一.我对文件I/O的理解
       文件I/O最基本的两个函数就是read和write,书中也叫做unbuffered I/O。刚看到这个"unbuffered",我就奇怪,操作系统不是对所有的输入输出都会做缓存吗(delayed write),为什么还会存在unbuffered?但接着我就明白了,这里的ubuffered,是指的是针对与read和write本身来说,他们是没有缓存机制,比如read(fd,temp,100),在读够100个字节后或者遇到文件EOF后就返回,非常单纯。而C库函数中的fread和fwrite,就是利用缓存技术来调用read和write,可以说是buffered I/O。
二.文件描述符
       文件描述符是一个整型,可以理解为一个指向文件的指针(注:整型也可以理解为一个指针,并不是只有void*才可以被叫做指针)。需要注意的就是,一个进程在结束的时候会关闭所有打开的文件描述符,但是0,1,2除外。0,1,2,分别代表标准输入流,标准输出流和标准错误流。这3个文件描述符是由kernel打开和进行管理的,不需要我们来打开和关闭。
三.文件共享机制
       比较重要的就是file table的理解。file table包括了file status flags,the current file offset,a pointer to the v-node table entry for the file.比如对文件temp,调用一次open后,有fd1指向temp,也就有了一个file table。再对temp调用一次open后,有fd2指向temp,再生成一个file table(可以用dup,dup2实现,但又有不同,见下文)。这时,排除其他因素,我们的系统中有一个temp文件,2个文件描述符:fd1,fd2,两个file table(表示同一个文件)。需要注意的是,如果对write操作加上了排他锁,就无法用O_WRONLY同时打开两个文件描述符了。但可以用O_RDONLY同时打开两个文件描述符,fd1,fd2可以同时读,而相应的,fd1指向的file table的current file offset自然可以不同于fd2指向的,这也就可以很容易地理解多个file table的作用了。
四.system call原理与分析
       我喜欢用函数来总结所学的东西,感觉比较实在。
       1.lseek(fd,5,SEEK_SET).修改current offset.返回新的offset.
       2.open(file,O_RDWR).不赘述细节,详见man。先写一个问题,就是O_SYNC,我无法利用代码来感受这个option,如果您能帮忙,小弟我非常感激。值得注意的就是O_APPEND,这个很强大的东西,跟>>重定向的"添加到末尾"是一样的。特别注意,当open(file,O_RDWR | O_APPEND)后,write与read得到的待遇并不相同。每个被打开的文件都有一个current offset,可以用lseek对这个offset进行修改。先贴出代码:
int main()
{
    int fd;
    char buf[100]="abcde";
    //注:temp中只有一行aaaaabbbbbccccc.
    if( (fd=open("temp",O_RDWR|O_APPEND))<0 )
        err_sys("error open!");
    if(lseek(fd,5,SEEK_SET)==-1)
        err_sys("error lseek!");
    if( read(fd,buf,5)<0 ) 
        err_sys("error read!");
    buf[5]='\0';
    printf("%s\n",buf);
    //if(write(fd,buf,5)!=5)
    //    err_sys("write error");
    return 0;
}
         当利用lseek修改current offset为5时,再read,结果如下:
?
bbbbb
  
         这说明read可以不受O_APPEND的影响,可以自由读取文件中任意位置的内容。将上述代码这样修改后:
int main()
{
    int fd;
    char buf[100]="abcde";
    //注:temp中只有一行aaaaabbbbbccccc.
    if( (fd=open("temp",O_RDWR|O_APPEND))<0 )
        err_sys("error open!");
    //if(lseek(fd,5,SEEK_SET)==-1)
    //    err_sys("error lseek!");
    //if( read(fd,buf,5)<0 ) 
    //    err_sys("error read!");
    //buf[5]='\0';
    //printf("%s\n",buf);
    if(write(fd,buf,5)!=5)
        err_sys("write error");
    return 0;
}
         temp文件结果如下:
?
aaaaabbbbbccccc
abcde
  啊,lseek被O_APPEND屏蔽了?哈哈,不是被屏蔽了。原理是这样的,在每次write之前,系统都因为O_APPEND而将current offset设置为文件的末尾,这就说明了上述结果。
       3.fcntl(fd,F_GETFL,0).修改文件属性。
       4.dup(fd1).   dup2(fd1,fd2).都返回复制后的文件描述符。与两次open得到两个文件描述符不同的是,dup2(fd1,fd2)得到的fd2与fd1共享一个file table,而前者是分别拥有一个file table。自然,用read,write在处理fd1,fd2时,就会对两者都起作用了。dup,dup2都是原子操作,而
close(fd2);
fcntl(fd1,F_DUPFD,fd2);
        可以实现dup2的功能,但会破坏其原子性。
五.总结
        基础,原理,这都是需要掌握的。之前我有些激进了,想尽快去读linux内核,现在看来,还有很长的路要走。
六.我的一些笔记(有些摘自apue)
        1.off_t 是一个long int
        2.read,write这两个system call在使用时会改变当前的offset
        3.read,write starts at the file's current offset.
        4. size_t 是为了方便系统之间的移植而定义的,是unsigned int.    ssize_t是signed size_t
        5.the lseek fnction modifies only the current file offset in the file table entry . No I/O takes place.
        6.Any operation that requires more that one function call cannot be atomic,as there is always the possibility that the kernel can temporarily suspend the process between the two function calls.
        7.creat(filename,FILE_MODE).如果filename文件已经存在,将会删除filename的所有内容
   8.delayed write:When we write data to a file ,the data is normally copied by the kernel into one of its buffers and queued for writing to disk at some later time.
   9.cat不直接接受标准输入。 比如 more file1 | cat file2 的结果只会输出file2,而这样more file1 | cat - file2就会输出file1和file2了。注:-可以用/dev/fd/0替代,表示连结标准输入.
   10.用read,write系统调用时,用户没有对输入输出做缓存,但是内核是会对所有输入输出做缓存的。可以这样说,在这个进程的user time没有进行缓存,但是system time在进行缓存
   11.O_SYNC会一直起作用.而fsync和fdatasync是一次性的操作。
   12.每次open系统调用后,都会生成一个新的file table entry.
        13.注意write,read,printf等函数的'\0'问题
   14.2>&1 是将2重定向到1,也就是将stderr重定向到stdout,就是让stderr也指向stdout。 再抽象一些,2>&1,就是将文件描述符2重定向到文件描述符1所指向的文件,使得二者指向同一个文件。
   参考资料:apue(unix环境高级编程)
        如果您觉得我的文章对您有帮助,请您赞一下,非常感谢!


本文转自NeilHappy 51CTO博客,原文链接:http://blog.51cto.com/neilhappy/1078667,如需转载请自行联系原作者
相关文章
|
存储 Shell Linux
【Shell 命令集合 网络通讯 】Linux 显示Unix-to-Unix Copy (UUCP) 系统的状态信息 uustat命令 使用指南
【Shell 命令集合 网络通讯 】Linux 显示Unix-to-Unix Copy (UUCP) 系统的状态信息 uustat命令 使用指南
126 0
|
5月前
|
Unix Shell API
组合思维:Unix 哲学到底给现代编程带来哪些重要启示?
Unix哲学提供了一套简洁而强大的设计理念,这些理念在现代编程中依然具有重要的指导意义。通过模块化设计、组合工具、避免过早优化以及注重可复用性和可扩展性,开发者可以构建出更高效、更健壮的软件系统。希望本文能够帮助读者深入理解Unix哲学,并在实际开发中应用这些宝贵的设计原则。
93 25
|
9月前
|
算法 Unix 数据安全/隐私保护
Python编程--UNIX口令破解机
Python编程--UNIX口令破解机
84 1
|
11月前
|
开发框架 Unix Linux
LangChain 构建问题之在Unix/Linux系统上设置OpenAI API密钥如何解决
LangChain 构建问题之在Unix/Linux系统上设置OpenAI API密钥如何解决
146 0
Unix环境高级编程(第三版)中apue.h头文件及其依赖安装教程
Unix环境高级编程(第三版)中apue.h头文件及其依赖安装教程
297 0
|
Oracle 关系型数据库 Unix
SAP系统拷贝 UNIX + Oracle
SAP系统拷贝 UNIX + Oracle
93 1
|
网络协议 Unix 应用服务中间件
|
Unix Shell Linux
在Unix/Linux操作系统中,Shell脚本广泛用于自动化任务
在Unix/Linux操作系统中,Shell脚本广泛用于自动化任务
140 2
|
5月前
|
安全 Unix Linux
Unix:Linux的“祖师爷”
Unix的诞生 Unix操作系统诞生于1969年,由肯·汤普逊(Kenneth Lane Thompson)和丹尼斯·里奇(Dennis MacAlistair Ritchie)在AT&T的贝尔实验室开发。其初衷是为了在闲置的PDP-7计算机上开发一个简单的操作系统,以便进行编程和游戏。最初的Unix是用汇编语言编写的,但随后为了更高效的开发和更好的可移植性,里奇和汤普逊用C语言重写了Unix的大部分代码,这奠定了Unix的基础,并促进了C语言的广泛应用。
96 2