linux系统编程之文件与io(一)
经过了漫长的学习,C语言相关的的基础知识算是告一段落了,这也是尝试用写博客的形式来学习c语言,回过头来看,虽说可能写的内容有些比较简单,但是个人感觉是有史起来学习最踏实的一次,因为里面的每个实验都是自己亲自验证过的,我机智不算聪明,所以也没必要去校仿那些“大脑非常聪明”的理解能力很强的“高人”,也许我之前学的那些基础知识在别人来说可能也就一周就完全通了,甚至比你还要理解得透,那我只能“羡慕”,“羡慕”过后,还得去寻找属于自己的学习方法,不管什么形式,只要是最适全自己的就是“好”的,我还是会这种形式,一步一步去向我的目标迈进的,加油!
头一热,又发表了些许感言,算是给自己沉默的学习上的一种精神上的鼓励吧,今天开始,开始利用已学过的c来学习linux系统编程相关的东东,算是继续对c进行深入吧,言归正传:
关于什么是linux系统编程,这里就不介绍了,相关的理论可以找相关资料进行查阅,对于程序员来说,只有代码是"最亲切"的,所以正式开始系统编程之前,先学一个东东:
错误处理:
![](http://images0.cnblogs.com/blog/324374/201312/23204531-d542e1350f874f24bf4bfd1254971633.png)
![](http://images0.cnblogs.com/blog/324374/201312/23204625-882ac6ca046d43f887954a0dd8cbefc3.png)
下面以具体代码来说明下:
![](http://images0.cnblogs.com/blog/324374/201312/23210548-f974323492434ecc8c73313efb3cef05.png)
编译运行:
![](http://images0.cnblogs.com/blog/324374/201312/23210905-90524cb53c364c95a601e29c37a3e0a8.png)
可见perror的出错信息的格式是固定的,不是太灵活,下面再看下更灵活打印错误的方式:
![](http://images0.cnblogs.com/blog/324374/201312/23211723-6f24a05ae565403dbfea9109f91d44af.png)
编译运行:
![](http://images0.cnblogs.com/blog/324374/201312/23211847-790f1b8087024475add2a20d5a9260c9.png)
通过这个程序,我们可以看到,用fprintf(关于它的使用,随后会进行详述)可以自定义错误输出格式,会比perror更加灵活。
说明:linux中有很多错误代码,如下:
![](http://images0.cnblogs.com/blog/324374/201312/23212320-5a6146ca1ce04d5d8dd60ab90a091d06.png)
都可以通过strerror来将错误码转换成错误文本,上面程序已经看到了,下面再来试验一下另外一个错误码:
![](http://images0.cnblogs.com/blog/324374/201312/23212809-5f0674074b314170ba65d2cfb20b29bd.png)
好了,下面正式进入文件与io:
什么是I/0:
对于I/0,我想学过编译的都不会陌生,这里就不多说了,下面简单总结下:
![](http://images0.cnblogs.com/blog/324374/201312/23213706-8f848dd5ccf24b4b907311f4dc1490b3.png)
而对于linux来说,有两种类型的I/O:
![](http://images0.cnblogs.com/blog/324374/201312/23214020-a75b0b08795f448d85d19a538a9ca699.png)
文件描述符【这是新概念,需好好理解】:
我们知道对于ANSI C库来讲,平常操纵文件是FILE文件指针来描述的, 如:
FILE* fp;
![](http://images0.cnblogs.com/blog/324374/201312/23214711-f7e6215bd5784f8a8c06851c03e62bbb.png)
说明:
实际上系统调用与ANSI C是一一对应的,只是描述不一样
系统调用:文件描述符,int类型 | ANSI C:文件指针,FILE*类型 |
STDIN_FILENO | stdin |
STDOUT_FILENO | stdout |
STDERR_FILENO | stderror |
文件描述符与文件指针相互转换:
对于系统调用中的文件描述符与ANSI C标准库中的文件指针可以通过以下函数进行转换:
![](http://images0.cnblogs.com/blog/324374/201312/23215900-4a21d7b9eb5747bb9bef1315b8fe9aed.png)
![](http://images0.cnblogs.com/blog/324374/201312/23220548-451179ddde8c4c7a8e39dc085bb80fcb.png)
编译运行:
![](http://images0.cnblogs.com/blog/324374/201312/23220700-37bd61152a344d168a6740f172f36393.png)
至于文件描述符转换成文件指针,这时就不演示了,比较简单。
文件系统调用:
open系统调用:
![](http://images0.cnblogs.com/blog/324374/201312/23222023-4ff7706c941e44bc84a735410fb84473.png)
用man查看下:
![](http://images0.cnblogs.com/blog/324374/201312/23223405-6b125c52b3bf45329f69c799fd199c61.png)
![](http://images0.cnblogs.com/blog/324374/201312/23223919-8353dc37dde44b3fa6e5c1a81b5b6962.png)
运行:
![](http://images0.cnblogs.com/blog/324374/201312/23224012-78b60c8f68fe42e9b72b75a39885e21a.png)
对于其错误输出,还可以perror来输出,篇章开头已经介绍过了,如下:
![](http://images0.cnblogs.com/blog/324374/201312/23224321-23b08f0dc727426db1f98d4b25752ab6.png)
运行:
![](http://images0.cnblogs.com/blog/324374/201312/23224559-674cf7f4da0e4217bff11067b0406125.png)
下面要对代码进行封装了,对于程序出错都需要输出错误信息,并且让程序退出,所以,可以将上面这种错误输出的方式定义为宏,方便其之后调用,如下:
![](http://images0.cnblogs.com/blog/324374/201312/23225148-dff77ea4ff34470dbac25e0bf9106219.png)
运行,看效果:
![](http://images0.cnblogs.com/blog/324374/201312/23225251-1508af66eb284649adae189a09f36742.png)
但是,上面的这种宏定义还不够专业,下面这种方式是linux内核中会经常用到的,体现了专业,呵呵:
![](http://images0.cnblogs.com/blog/324374/201312/23225701-80788b5813784f0fbedfb31f7b767f4a.png)
从上面这种打开方式来看,如果文件不存在时,是不会去创建的,那有没有当文件不存时主动去创建呢,其中open的第二个参数就是用来设定打开方式的:
![](http://images0.cnblogs.com/blog/324374/201312/24212047-5ddf5d5a2ae941ee85e57bbcbde27325.png)
这次编译运行:
![](http://images0.cnblogs.com/blog/324374/201312/24212221-594de0da051240dcb55c4a2dfac20548.png)
关于打开方式,有下列几种:
![](http://images0.cnblogs.com/blog/324374/201312/24212321-c6f467380e9f42cebab70563d5f2b8eb.png)
下面还有另外一种打开文件的函数:
![](http://images0.cnblogs.com/blog/324374/201312/24211124-0e0ccbf34a034082b19aa2ff622b5823.png)
对于这种打开方式,多了一个权限的参数,下面来看一下具体使用:
![](http://images0.cnblogs.com/blog/324374/201312/24213816-a927d206677b4a8fa521cf5c28083991.png)
编译运行:
![](http://images0.cnblogs.com/blog/324374/201312/24213959-62cc08af630e4079bb97f3b0e8b08d17.png)
这里得对文件的权限进行说明一下,以便于理解这个权限参数:
![](http://images0.cnblogs.com/blog/324374/201312/24214216-01b36dacd28844f2b5cc3a1a637c5837.png)
理解了文件权限相关的知识,回到上面的程序来,我们在创建程序时,给定的文件权限是0666,也就是rw-rw-rw- ,但是从实际创建的文件来看,它的权限是:
![](http://images0.cnblogs.com/blog/324374/201312/24214722-e47cd4549bb348a88995fa9331de82fe.png)
这是为什么呢?这时需要解释一个新概念:umask
![](http://images0.cnblogs.com/blog/324374/201312/24214953-985a7a4a356847f6b82dd80a35453e51.png)
查看一下当前系统的umask是多少:
![](http://images0.cnblogs.com/blog/324374/201312/24215736-3d1743352080491ca1d8d8cb023d9d2b.png)
0666-0022=0644,就刚好是我们所看到的结果,但是注意,并非是真正意义上的相减运算,如果我们把权限改一下结果就不是这种公式推导出来的结果了:
![](http://images0.cnblogs.com/blog/324374/201312/26210731-a84d2b48c46149e59113a54a6fa6c08e.png)
先来按上面的公式来推算下创建文件的权限:0655-0022=0633,编译运行,见证事实啦:
![](http://images0.cnblogs.com/blog/324374/201312/26211229-308ec1791fdc400b9ddf5fad8e694213.png)
可见,上面算权限的公式并非只是算术与umask相减,用简单的图来说明下umask的真正意义:
![](http://images0.cnblogs.com/blog/324374/201312/26213008-1c6f4fbd23544fd4a9b6b5a28f5704b6.png)
其实对于umask可以在程序中手动更改,我们可以将它设置为0,这样我们指定什么权限,最终文件创建就是什么权限了,如下:
![](http://images0.cnblogs.com/blog/324374/201312/26213632-5beddc8122fd4ea3823c776463a97643.png)
编译运行:
![](http://images0.cnblogs.com/blog/324374/201312/26213923-472b48d936be4bb39dfd216e1fdd0728.png)
![](http://images0.cnblogs.com/blog/324374/201312/26214326-0492cd9f2521482cac9fd818d41185e2.png)
目前我们以O_CREATE方式创建的文件,不管我们运行多少次,文件是否已经存在了,也是每次创建成功:
![](http://images0.cnblogs.com/blog/324374/201312/26214902-4566277bb4d945ba9885b7605153cb8e.png)
这时,如果想要不重复创建文件,则加上O_EXCL打开方式,再看下运行结果:
![](http://images0.cnblogs.com/blog/324374/201312/26215412-e1e917a366e748b5ad3e15fc9e1d66ef.png)
运行:
![](http://images0.cnblogs.com/blog/324374/201312/26215800-6a1f0fdabebf449fa168033c90b31eea.png)
关于文件打开失败,有很多信息,这些全可以在open的帮助man里面找到,下面就对其中的某些错误进行一些解释:
![](http://images0.cnblogs.com/blog/324374/201312/26223350-02d7c9c7f4f845419f462f300a3ea9a3.png)
那一个进程打开文件的最大数怎么查看呢?
![](http://images0.cnblogs.com/blog/324374/201312/26223150-2d85f7f0fc614c29a90245be95aa4007.png)
一个系统打开文件的最大数又怎么查看呢?
![](http://images0.cnblogs.com/blog/324374/201312/26223601-f65a2d2a5e504b28a6b4dab8bbf9e0f5.png)
另外,对于文件访问权限的指定除了直接输入数字之外,还能利用宏的方式创建,有如下权限组合:
![](http://images0.cnblogs.com/blog/324374/201312/26220919-202809c973764c23b13ecf3db9825df0.png)
实际上,查看open函数的帮助就有介绍:
![](http://images0.cnblogs.com/blog/324374/201312/26221219-ca4c76c37a004ef480da312a0242acd1.png)
下面以这种宏的方式来指定文件权限:
![](http://images0.cnblogs.com/blog/324374/201312/26221639-455489b6777d40809b897231c0ed0531.png)
这时来查看下是否成功创建这种权限的文件:
![](http://images0.cnblogs.com/blog/324374/201312/26221822-54dbabfd14174e5290a01e547b378d52.png)
close系统调用:
![](http://images0.cnblogs.com/blog/324374/201312/26222039-c3b8b8e92f384b0eb5f6204533401313.png)
这个比较简单:
![](http://images0.cnblogs.com/blog/324374/201312/26222210-fa7388288f7d4bc6acdf965e97dc596f.png)
creat系统调用【很少用到,可完全被open代替】:
![](http://images0.cnblogs.com/blog/324374/201312/26222244-6d48843f46284b84a671fc8f0a9d724d.png)
![](http://images0.cnblogs.com/blog/324374/201312/26222320-9297b817e0224030ab57df52111f71ef.png)
所以说,这个函数很少会用到,直接用open就可以,了解一下。
好了,今天先学到这,下节见!