Linux/Unix系统编程手册读书笔记2

《Linux/Unix系统编程手册》读书笔记 目录

第5章:

主要介绍了文件I/O更深入的一些内容。

原子操作,将一个系统调用所要完成的所有动作作为一个不可中断的操作,一次性执行;这样可以避免竞争状态(两个或多个共享资源的进程或线程的运行结果是一个无法预期的顺序)。

以独占方式创建一个文件:对文件是否存在的检查和创建文件属于同一个原子操作。防止新建文件的时候因为检查文件是否存在和新建文件之间发生中断(而其他进程也在新建相同文件名的文件),导致两个进程都认为自己是文件的创建者。

向文件尾部追加数据:将文件的偏移量的移动与数据的写操作属于同一个原子操作。防止多个进程同时往同一个文件尾部添加数据导致数据混乱。

fcntl(),对一个打开的文件描述符执行一系列的操作。

1 #include <fcntl.h>
2 
3 int fcntl(int fd, int cmd, ...);

fd为文件描述符,cmd是决定具体操作,第三个参数(可选)用来设置为不同的类型。

cmd参数(部分),具体查看man手册:

F_DUPFD 复制文件描述符
F_GETFD 获取文件描述符
F_SET_FD 设置文件描述符
F_GETFL 获取文件访问模式和状态标志
F_SETFL 设置文件访问模式和状态标志

文件描述符与打开文件之间的关系:多个文件描述符可以指向同一个打开文件。他们的关系如下

Linux/Unix系统编程手册读书笔记2

文件描述符表、打开文件表和i-node表。打开文件表的条目成为打开文件句柄(open file handle)。

PS:如果两个不同的文件描述符指向同一个打开文件句柄,这两个文件描述符将共享相同的文件偏移量。(打开文件句柄里包含文件偏移量file offset)。

dup(),复制一个打开的文件描述符oldfd,并返回新的描述符。

dup2(),复制oldfd指定的文件描述符,返回newfd参数指定的描述符。

dup3(),参数与dup2()相同,添加了flags,用于修改系统调用行为。

1 #include <unistd.h>
2 
3 int dup(int oldfd);
4 
5 int dup2(int oldfd, int newfd);
6 
7 int dup3(int oldfd, int newfd, int flags);

成功调用返回新的文件描述符,失败返回-1。

pread()pwrite(),在指定参数所指定的位置进行文件I/O操作,但不改变文件的偏移量。

1 #include <unistd.h>
2 
3 ssize_t pread(int fd, void *buf, size_t count, off_t offset);
4 
5 ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);

fd为文件描述符,buf为缓冲区, count为缓冲区字节数, offset为偏移量。

pread()成功调用返回读取的字节数,失败返回-1

pwrite()成功调用返回写入的字节数,失败返回-1

------------------------省略分散输入和集中输出,截断文件,非阻塞I/O和大文件I/O等一些知识点---------------

练习:

5-1,请使用标准文件I/O系统调用(open()和lseek())和off_t数据类型修改程序清单5-3中的程序。将宏_FILE_OFFSET_BITS的值设置为64进行编译,并测试该程序是否能够成功创建一个大文件。

Linux/Unix系统编程手册读书笔记2
Linux/Unix系统编程手册读书笔记2
 1 /*
 2  * =====================================================================================
 3  *
 4  *       Filename:  large_file.c
 5  *
 6  *    Description:  
 7  *
 8  *        Version:  1.0
 9  *        Created:  2014年03月17日 22时05分50秒
10  *       Revision:  none
11  *       Compiler:  gcc
12  *
13  *         Author:  alan (), alan19920626@gmail.com
14  *   Organization:  
15  *
16  * =====================================================================================
17  */
18 
19 #define _FILE_OFFSET_BITS 64 
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include "tlpi_hdr.h"
23 
24 int main(int argc, char *argv[]){
25     int fd;
26     off_t off;
27     if(argc != 3 || strcmp(argv[1], "--help") == 0)
28         usageErr("%s pathname offset\n", argv[0]);
29 
30     fd = open(argv[1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
31 
32     if(fd == -1)
33         errExit("open");
34 
35     off = atoll(argv[2]);
36     if(lseek(fd, off, SEEK_SET) == -1)
37         errExit("lseek");
38 
39     if(write(fd, "test", 4) == -1)
40         errExit("write");
41     exit(EXIT_SUCCESS);
42     
43 }
View Code

测试结果:

1 lancelot@debian:~/Code/tlpi$ ./large_file largefile 10111222333
2 lancelot@debian:~/Code/tlpi$ ls -l largefile 
3 -rw------- 1 lancelot lancelot 10111222337  4月  9 23:22 largefile

5-2,编写一个程序,使用O_APPEND标志并以写方式打开一个已存在的文件,且将文件偏移量置于起始位置,再写入数据。数据会显示在文件中的哪个位置?为什么?

Linux/Unix系统编程手册读书笔记2
Linux/Unix系统编程手册读书笔记2
 1 /*
 2  * =====================================================================================
 3  *
 4  *       Filename:  5-2.c
 5  *
 6  *    Description:  
 7  *
 8  *        Version:  1.0
 9  *        Created:  2014年03月17日 22时26分51秒
10  *       Revision:  none
11  *       Compiler:  gcc
12  *
13  *         Author:  alan (), alan19920626@gmail.com
14  *   Organization:  
15  * 
16  * =====================================================================================
17  */
18 
19 #include <sys/stat.h>
20 #include <ctype.h>
21 #include <fcntl.h>
22 #include "tlpi_hdr.h"
23 
24 int main(int argc, char *argv[]){
25     int fd;
26     off_t off;
27     ssize_t  numWritten;
28 
29     if(argc != 2 || strcmp(argv[1], "--help") == 0)
30         usageErr("%s file", argv[0]);
31 
32     fd = open(argv[1], O_RDWR | O_APPEND, S_IRUSR | S_IWUSR);
33     if(fd == -1)
34         errExit("open");
35 
36     off = lseek(fd, 0, SEEK_SET);
37     if(off == -1)
38         errExit("lseek");
39 
40     numWritten = write(fd, "Kevin Durant\n", 13);
41     if(numWritten == -1)
42         errExit("write");
43     
44     close(fd);
45     exit(EXIT_SUCCESS);
46 }
View Code

测试结果:

 1 lancelot@debian:~/Code/tlpi$ cat t1
 2 This is the second line.
 3 This is the third line.
 4 This is the append line.
 5 
 6 lancelot@debian:~/Code/tlpi$ ./write_append t1
 7 lancelot@debian:~/Code/tlpi$ cat t1
 8 This is the second line.
 9 This is the third line.
10 This is the append line.
11 
12 Kevin Durant

5-3,本习题的设计目标在于展示为何以O_APPEND标志打开文件来保障操作的原子性是必要的。请编写一程序,可接收多达3个命令行参数:

$ automic_append filename num-bytes [x]

该程序应打开制定的文件,然后以每次调用write()写入一个字节的方式,向文件尾部追加num-bytes个字节。缺省情况下,程序使用O_APPEND标志打开文件,但若存在第三个命令行参数(x),那么打开文件时将不再使用O_APPEND标志,代之以调用write()前调用lseek(fd, 0, SEEK_END)。同时运行该程序的两个实例,不带x参数,将100万字节写入同一个文件:

$ automic_append f1 1000000 & automic_append f1 1000000

重复上述操作,将数据写入另一个文件,但运行时加入x参数

$ automic_append f2 1000000 x & automic_append f2 1000000 x

使用ls -l命令检查文件f1和f2的大小, 并解释两文件大小不同的原因。

Linux/Unix系统编程手册读书笔记2
Linux/Unix系统编程手册读书笔记2
 1 /*
 2  * =====================================================================================
 3  *
 4  *       Filename:  atomic_append.c
 5  *
 6  *    Description:  
 7  *
 8  *        Version:  1.0
 9  *        Created:  2014年03月17日 22时46分49秒
10  *       Revision:  none
11  *       Compiler:  gcc
12  *
13  *         Author:  alan (), alan19920626@gmail.com
14  *   Organization:  
15  *
16  * =====================================================================================
17  */
18 
19 #include <sys/stat.h>
20 #include <fcntl.h>
21 #include "tlpi_hdr.h"
22 
23 int main(int argc, char *argv[]){
24     int i, fd, flags, numBytes;
25     off_t off;
26     ssize_t numWritten;
27 
28 
29     flags = O_RDWR | O_CREAT;
30     if(argc < 3 || strcmp(argv[1], "--help") == 0 )
31         usageErr("%s filename num-bytes [x]");
32 
33     if(argc != 4)
34         flags = flags | O_APPEND;
35 
36     numBytes = getInt(argv[2], 0, "num-bytes");
37 
38     fd = open(argv[1], flags, S_IRUSR | S_IWUSR);
39     if(fd == -1)
40         errExit("open");
41 
42     /*if(argc == 4)
43         if(lseek(fd, 0, SEEK_END) == -1)
44             errExit("lseek");
45 
46     */
47 
48     for(i = 0; i < numBytes; ++i){
49         if(argc > 3 && argv[3] == "x")
50             if(lseek(fd, 0, SEEK_END) == -1)
51                 errExit("lseek");
52 
53         if(write(fd, "A", 1) != 1)
54             fatal("write() failed");
55     }
56         
57     exit(EXIT_SUCCESS);
58 }
View Code

测试结果:

1 lancelot@debian:~/Code/tlpi$ ls -l f1 f2
2 -rw------- 1 lancelot lancelot 2000000  4月  9 23:45 f1
3 -rw------- 1 lancelot lancelot 1000000  4月  9 23:45 f2

 5-4,使用fcntl()和close()来实现dup()和dup2()。务必牢记dup2()需要处理的一种特殊情况,即oldfd与newfd相等。这时,应检查oldfd是否有效,测试fcntl(oldfd, F_GETFL)是否成功就能达到这一目标。若oldfd无效,则dup2()将返回-1,并将errno置为EBADF。

Linux/Unix系统编程手册读书笔记2
Linux/Unix系统编程手册读书笔记2
 1 /*
 2  * =====================================================================================
 3  *
 4  *       Filename:  5-4.c
 5  *
 6  *    Description:  
 7  *
 8  *        Version:  1.0
 9  *        Created:  2014年03月19日 08时48分46秒
10  *       Revision:  none
11  *       Compiler:  gcc
12  *
13  *         Author:  alan (), alan19920626@gmail.com
14  *   Organization:  
15  *
16  * =====================================================================================
17  */
18 
19 #include <sys/stat.h>
20 #include <fcntl.h>
21 #include "tlpi_hdr.h"
22 
23 int Dup(int oldfd);
24 
25 int Dup2(int oldfd, int newfd);
26 
27 int main(){
28     int fd;
29     int newfd;
30     if((fd = open("t1", O_RDONLY)) == -1)
31         errExit("open");
32     
33     //newfd = fcntl(fd, F_DUPFD, fd+1);
34     newfd = Dup(fd);
35     if(newfd == -1)
36         errExit("Dup");
37 
38     printf("old fd is %d, new fd is %d\n", fd, newfd);
39 
40     printf("Input the new fd:");
41     scanf("%d", &newfd);
42 
43     /*if(fcntl(oldfd, F_GETFL) == -1)
44         errExit("fd is ")
45     */
46 
47     newfd = Dup2(fd, newfd);
48     if(newfd == -1)
49         errExit("Dup2");
50     printf("old fd is %d, new fd is %d\n", fd, newfd);
51 
52     exit(EXIT_SUCCESS);
53 }
54 
55 int Dup(int oldfd){
56     int newfd = fcntl(oldfd, F_DUPFD, oldfd+1);
57     return newfd;
58 }
59 
60 int Dup2(int oldfd, int newfd){
61     int fd = newfd;
62     if(fcntl(oldfd, F_GETFL) == -1){
63         errno = EBADF;
64         return -1;
65     }
66     if(oldfd == newfd)
67         return newfd;
68     else{
69         if(fcntl(newfd, F_GETFL) != -1)
70             close(newfd);
71         newfd = fcntl(oldfd, F_DUPFD, fd);
72         if(newfd != fd)
73             newfd = fcntl(newfd, F_SETFD, fd);
74 
75         return newfd;
76     }
77 }
View Code

测试结果:

1 lancelot@debian:~/Code/tlpi$ ./my_dup 
2 old fd is 3, new fd is 4
3 Input the new fd:13
4 old fd is 3, new fd is 13
更多相关文章
  • <Linux/Unix系统编程手册>读书笔记1  (创建于4月3日,最后更新4月7日) <Linux/Unix系统编程手册>读书笔记2  (创建于4月9日,最后更新4月10日) <Linux/Unix系统编程手册>读书笔记3  (创建于4月15日) <Lin ...
  • Linux/Unix系统编程手册读书笔记9文件属性
    <Linux/Unix系统编程手册>读书笔记 目录 在Linux里,万物皆文件.所以文件系统在Linux系统占有重要的地位.本文主要介绍的是文件的属性,只是稍微提及一下文件系统,日后如果有更深入的研究一定会写出来. 下图为磁盘分区与文件系统的关系 文件系统中的文件(目录)在i-node表 ...
  • Linux/Unix系统编程手册读书笔记8文件I/O缓冲
    <Linux/Unix系统编程手册>读书笔记 目录 第13章 这章主要将了关于文件I/O的缓冲. 系统I/O调用(即内核)和C语言标准库I/O函数(即stdio函数)在对磁盘进行操作的时候都会发生缓冲.通过缓冲可以在一定程度上将用户空间与实际的物理设备分离,还可以减少内核访问磁盘的次数. ...
  • Linux/Unix系统编程手册读书笔记7/proc文件的简介和运用
    <Linux/Unix系统编程手册>读书笔记 目录 第11章 这章主要讲了关于Linux和UNIX的系统资源的限制. 关于限制都存在一个最小值,这些最小值为<limits.h>文件中的常量. 通过cat 命令查看: lancelot@debian:~/Code/tlpi$ c ...
  • <Linux/Unix系统编程手册>读书笔记 目录 第9章 这章主要讲了一堆关于进程的ID.实际用户(组)ID.有效用户(组)ID.保存设置用户(组)ID.文件系统用户(组)ID.和辅助组ID. 实际用户ID决定执行者是谁. 有效用户ID决定该进程执行时获取的文件权限.PS:有效用户ID ...
  • Linux/Unix系统编程手册读书笔记5
    <Linux/Unix系统编程手册>读书笔记 目录 第8章 本章讲了用户和组,还有记录用户的密码文件/etc/passwd,shadow密码文件/etc/shadow还有组文件/etc/group. 每个用户都有唯一的用户名和相关的用户标识符(UID).用户可以属于一个或多个组,每个组都 ...
  • Linux/Unix系统编程手册读书笔记4
    <Linux/Unix系统编程手册>读书笔记 目录 第7章: 内存分配 通过增加堆的大小分配内存,通过提升program break位置的高度来分配内存. 基本学过C语言的都用过malloc来分配内存,而malloc都基于brk()和sbrk(). 1 #include <unis ...
  • Linux/Unix系统编程手册读书笔记3
    <Linux/Unix系统编程手册>读书笔记 目录 第6章 这章讲进程.虚拟内存和环境变量等. 进程是一个可执行程序的实例.一个程序可以创建很多进程. 进程是由内核定义的抽象实体,内核为此实体分配执行程序所需的系统资源. 从内核的角度来看,进程是由用户内存空间和内核数据结构组成的.程序的 ...
一周排行
  • eclipse集成python开发环境
      eclipse集成python开发环境,eclipse原本为书写java的ide工具, ...
  • 想了一下以后文章的都从有道笔记链接过来,同时提供pdf的文件http://note.youdao.com/share/?id=22b8265f50042baefe73391f2bab4ebd&type=not ...
  • 根据我个人的理解, 所谓的多进程 只不过是将多个任务放到后台执行而已,很多人都用到过,所以现在讲的主要是控制,而不是实现.先看一个小shell:看执行结果: 很明显是8s======================= ...
  • SCOM2012R2应用程序事务监视无法加载WebRecorder插件
    在配置SCOM 2012 R2的应用程序事务性监视的时候,发生在捕获网页的时候无法加载we ...
  • SQL Server在删除变长列或者减小变长列的长度后,表的大小不会响应自动减小,除非DBA重建索引或者reorganized索引.变长列包括varchar,nvarchar, varchar(max), nvarc ...
  • 一直没有尝试过高级复制技术,闲来无聊.1 配置:主体定义数据库:test.com主体数据库:back.com 高级复制过程:1.处理数据库参数:(两台数据库都要配置)1.1设置global_name为true.(gl ...
  • "快速启动"栏中的"显示桌面"按钮不同于一般的快捷方式.如果不小心删除了"显示桌面"按钮,那么按照一般的方法是不能够恢复的. 要恢复"快速启动&q ...
  • 1.安装相应的软件 (1)安装jdk      下载地址:http://www.Oracle.com/technetwork/java/javase/downloads/index.html (2)安装scala  ...
  • 使用jetty配置 开发web应用        一直以来使用jetty作为我的web开发配置服务器,开始的时候和所有的初学者一样使用tomcat作为开发服务器,可用着用着,感觉tomcat越来越繁琐以及庞大.后来, ...
  •       (1) 重启后永久性生效: 开启:chkconfig iptables on 关闭:chkconfig iptables off (2) 即时生效,重启后失效: 开启:service iptables s ...