Linux system programming

来源:互联网 发布:京津冀 知乎 编辑:程序博客网 时间:2024/05/16 07:35

第一章:介绍与基本概念
 
1.Linux遵循一切皆文件的哲学,文件通过独一无二的文件描述符来引用,(file  descriptior,简称fd ),文件描述符是一个整数(c的int  类型)。fd由用户空间程序所共享,而用户空间程序可以直接使用fd来访问文件。
 
2.常规文件内含数据字节,而且被组织成一个称为字节流(byte stream)的线性数组(linear  array)。这些字节流可以具有任何价值,而且可以通过任何方式被组织在一起。从系统层面看,除了字节流,linux不会再对文件套上任何结构.
一个文件首次被打开时,文件位置是零,可以手动设置文件的位置,若设置为文件末尾,那么直接写入文件位置末尾之后的位置,则会使中间的字节被补上零,不能将字节写到文件开头之前的位置。
将一个字节写入文件的中间位置,会覆盖之前位于该偏移量的字节。因此,将自己写入文件的中间位置是不可能扩大文件的。



第二章:文件I/O
************************************************
open()系统调用用来将路径名称name所指定的文件映射至一个文件描述符,并与映射成功后返回该文件描述符,文件位置会被设置为零。
int open(const char *name,int flags);
int open(const char *name,int flags,mode_t mode);
除非是在创建新文件,否则mode参数会被忽略掉。



int fd;

fd = open("/home/kidd/madagascar",O_RDONLY);
if(fd == -1)
/* error*/
****************************************************

creat()  即为O_WRONLY | O_CREAT| O_TRUNC参数的open调用


int creat (const char *name,mode_t mode);


int fd;
fd = creat(file,644);
if(fd == -1)
/*   error*/
*****************************************************
#include<unistd.h>


ssize_t read (int fd,void *buf,size_t len);


每次调用会从fd参数所引用的当前文件位置读取len个字节到buf,执行成功后会返回写入buf的字节数目,失败则返回-1并设定errno。文件位置会前进从fd所读取的字节数目,如果fd不具备查找位置的能力(字符设备文件),则每次只会从当前位置读取数据。
unsigned long word;
ssize_t nr;


nr = read(fd,&word,sizeof(unsigned long));
if(nr == -1)
/*error*/


read()返回小于len的非零正数:可供读取的字节数目少于len,系统调用可能收到信号而中断,管道可能坏了(如果fd是管道)等。


read()返回零以指示到达了文件的末端(end of file  简称EOF),阻挡模式下,此调用会受到阻挡(进入休眠状态),直到出现可供读取的字节;非阻挡模式下,调用返回-1,稍后应该在进行此调用。


读取所有字节:
ssize_t ret;


while(len != 0 && (ret = read(fd,buf,len)) != 0){
if(ret == -1){
if(errno == EINTR)
continue;
perror("read");
break;
}
len -= ret;
buf +=ret;
}


非阻挡式读取操作:必须检查EAGAIN,否则就会“严重错误”和“只是缺乏数据”相混淆
char buf[BUFSIZ]
ssize_t nr;
start:
nr = read(fd,buf,BUFSIZ);
if(nr == -1){


if(errno == EINTR)
goto start;
if(errno == EAGAIN)
/* later  */
else
/*  error*/
}
****************************************************************************


write()系统调用:
#include<unistd.h>


ssize_t write(int fd,const void *buf,size_t count);


进行write()调用,就会从buf开始将count个字节写入fd所指文件的当前文件位置。如果fd所指对象不支持查找功能(字符设备),则总是会从(head)写起。
成功返回写入字节,发生错误则返回-1,返回0仅意味着写入了0字节。
基本用法:
const char *buf = "my ship is solid";
ssize_t nr;
nr = write (fd,buf,strlen(buf));
if(nr == -1)
/**error/


检查部分写入的可能性


………………
else if(nr != count)
/*maybe error,but not errno*/


Linux写入为延后写入
*****************************************************************************


fsync()系统调用:
#include<unistd.h>


int fsync(int fd);


调用fsync()可以确保文件描述符fd所映射的文件中所有脏数据会被写回磁盘。fd必须被打开以备写入。此调用会写回数据和元数据,并且等到硬盘回报数据与元数据已经写回磁盘才返回。


fdatasync()系统调用:
#include<unistd.h>


int fdatasync(int fd);


此调用和fsync的行为是一样的,但是它只会刷新数据(flush data)。此调用无法保证数据与磁盘是同步的,因此速度可能会更快


两个函数用法一样:
int ret;


ret = fsync(fd);
if(ret == -1)
/*error*/

这两个函数无法保证包含文件的目录项如果有任何变动都能够与磁盘同步化。为了确保目录项的任何变动也能写回磁盘,必须针对目录本身调用fsync()。


sync()系统调用:


可以让所有缓冲区与磁盘同步化


#include<unistd.h>


void sync(void);


此函数不需要参数,也没有返回值,但是能够成功返回。

标准没有要求sync()必须等到所有缓冲区全都被清空到磁盘才返回,只要求该调用将所有缓冲区交付给磁盘,然而linux会等到所有缓冲区都被交付为止,因此调用一次sync就够了,
应用程序应该使用fsync和fdatasync将必要的数据提交给磁盘,在忙碌的系统中,sync()调用可能需要花费几分钟的时间。


ps:


O_SYNC标志
将O_SYNC标志传递给open()表示文件的所有io都需要同步化:

int fd;

fd = open(file,O_WRONLY|O_SYNC);
if(fd == -1){
perror("open");
return -1;
}
读取请求通常需要同步化,否则无法知道缓冲区中提供的数据是否正确。然而,如前所述write()调用通常不需要同步,此调用返回与数据是否提交给磁盘并无关系。
然而使用O_SYNC标志可以建立此关系,确保write()执行io同步化。具体步骤:
每次在write()调用之后和返回之前隐式调用fsync().这样会使写入操作的用户时间和内核时间变差。除非不得以,否则最好不要用同步化io。
通常,应用程序使用fsync和fdatasync来保证写入操作已经将数据写回磁盘,相比较O_SYNC这么做的成本比较低,因为他们被调用的机会比较少。
****************************************************************************
close()系统调用
#include<unistd.h>
int close(int fd);


取消已打开文件描述符fd的映射关系,让进程与相关联文件分离。执行成功会返回零,失败返回-1并且设置errno。最常见的错误就是没有检查close()的返回值,导致错过关键错误。
基本用法:
if(close(fd) == -1)
perror("close");
*****************************************************************************
lseek()系统调用
#include<sys/types.h>
#include<unistd.h>


off_t lseek(int fd ,off_t pos,int origin);


lseek()的行为取决于origin参数:


SEEK_CUR   文件位置被设定为它的当前值加上pos
SEEK_END   文件位置被设定为文件的当前长度加上pos
SEEK_SET   文件的位置被设定为pos,当pos为零时,偏移值会被设定为文件的开头。


例如将文件的位置设定为1825


off_t ret;
ret = lseek(fd,(off_t)1825,SEEK_SET);
if(ret == (off_t)-1)
            /*error*/

将fd的位置设置为文件的末端:
off_t ret;
ret = lseek(fd,0,SEEK_END);
if(ret == (off_t)-1)
/*error*/


查找当前位置:
int pos;
pos = lseek(fd,0,SEEK_CUR);
if(pos == (off_t)-1)
/*error*/
else
 /*pos 是文件当前位置*/

*****************************************************************************************8

针对特定位置的读取和写入


读取:
#define _XOPEN_SOURCE 500
#include <unistd.h>
ssize_t pread(int fd,void *buf,size_t count,off_t pos);
从文件描述符的文件位置pos读取count个字节到buf。


写入:
#define _XOPEN_SOURCE  500
#include <unistd.h>
ssize_t pwrite(int fd,const void *buf,size_t count,off_t pos);
会将count个字节从buf写入文件描述符fd的文件位置pos


和read()或write()的不同:
1.完成工作后不会改变文件指针。
2.可以避免使用lseek()时造成的竞争条件,如果有多个线程共享文件描述符,当第一个线程调用lseek()之后,在它进行读取或者写入操作之前,同一个程序的另一个线程可能会改变文件的位置,使用pread()和pwrite()可避免此类竞争条件。

************************************************************************************






原创粉丝点击