1、linux /dev 目录简介
linux下磁盘块设备都挂接在/dev目录下,这个目录同时包括了所有的外接设备,包括块设备和字符设备,我们可以像访问文件一样很方便的访问这些块设备,linux沿袭了Unix的风格,将所有设备都人称一个文件。
设备文件分为两种:
块设备文件(b)
字符设备文件(c)
设备文件一般存放在/dev目录下
/dev/hd[a-t]: IDE设备,hd代表硬盘的意思,a代表第一个设备,hda1中的“1”代表hda的第一个磁盘分区,依次类推。
/dev/sd[a-z]: SCSI设备
/dev/fd[0-7]: 标准软驱
/dev/md[0-31]: 软raid设备
/dev/loop[0-7]: 本地回环设备
/dev/ram[0-15]: 内存
/dev/null: 无限数据接收设备 ,相当于回收站
/dev/zero: 无限零资源
/dev/tty[0-63]: 虚拟终端
/dev/ttyS[0-3]: 串口
/dev/lp[0-3]: 并口
/dev/console: 控制台
/dev/fb[0-31]: framebuffer
/dev/cdrom => /dev/hdc
/dev/modem => /dev/ttyS[0-9]
/dev/pilot => /dev/ttyS[0-9]
/dev/console: 控制台和/dev/tty[0-63]:虚拟终端的区别与联系
/dev/null 外号叫无底洞,你可以向它输出任何数据,它通吃,并且不会撑着!
/dev/zero 是一个输入设备,你可你用它来初始化文件。
2、几种磁盘I/O方式读写扇区
linux磁盘I/O分为同步与异步两种。在此基础上又可以分为阻塞与非阻塞。异步I/O模式因为其更好的性能受到了广泛的应用。
linux实现异步I/O有两种方式,POSIX AIO和linux kernel自带的libaio。POSIX AIO是由glibc实现的,采用线程或者实时信号来通知IO完成,libaio是linux自带的异步I/O库,采用事件的通知机制,实现了更多的用户API。两者都属于异步非阻塞的方式。
接下来简单介绍一下三种I/O方式实现用户态直接读写磁盘扇区:
(1)同步非阻塞
int main()
{
int fd = open(“/dev/sdb”, O_RDWR); //可读可写的方式打开块设备
char c[5];
if(fd<0){
printf(“open errorn”);
exit(-1);
}
lseek(fd, 0, SEEK_SET); //将文件指针移动到文件头
write(fd, “abcd”, 5); //写入五个字符
lseek(fd, 0, SEEK_SET);
read(fd, c, 5);
printf(“%sn”, c);
close(fd);
return 0;
}
程序中简单的运动open、write和read三个函数就可以对块设备/dev/sdb进行读写,同时还可以使用pread/pwrite和readv/writev等来对块设备进行读写,由于lseek和read/write 调用之间,内核可能会临时挂起进程,所以对同步问题造成了问题,调用pread相当于顺序调用了lseek 和 read,这两个操作相当于一个捆绑的原子操作,它的效果就是每次都从文件头开始读写;readv/writev 在一次函数调用中读、写多个非连续缓冲区,但是这些缓冲区已经用iovec表示好了。减少了系统调用的次数。
(2)异步非阻塞
异步非阻塞有libaio和POSIX AIO两种方式。
a) libaio方式
libaio主要提供了接口函数,来方便用户进行异步IO。具体如下:
/* library wrappers */
extern int io_queue_init(int maxevents, io_context_t *ctxp);
/*extern int io_queue_grow(io_context_t ctx, int new_maxevents);*/
extern int io_queue_release(io_context_t ctx);
/*extern int io_queue_wait(io_context_t ctx, struct timespec *timeout);*/
extern int io_queue_run(io_context_t ctx);
/* Actual syscalls */
extern int io_setup(int maxevents, io_context_t *ctxp);
extern int io_destroy(io_context_t ctx);
extern int io_submit(io_context_t ctx, long nr, struct iocb *ios[]);
extern int io_cancel(io_context_t ctx, struct iocb *iocb, struct io_event *evt);
extern int io_getevents(io_context_t ctx_id, long min_nr, long nr, struct io_event *events, struct timespec *timeout);
libaio的异步机制主要是,将IO请求提交后,会维护一个队列,用来接收已经完成的IO请求,然后利用用户自己定义的回调函数来处理IO结束以后的操作。具体示例如下:
#include
#include
#include #include
#include
#include
int srcfd=-1;
int odsfd=-1;
#define AIO_BLKSIZE 1024 //块大小
#define AIO_MAXIO 64 //异步最大IO个数
//写请求完成之后的回调函数
static void wr_done(io_context_t ctx, struct iocb *iocb, long res, long res2)
{
if(res2 != 0)
{
printf(“aio write errorn”);
}
if(res != iocb->u.c.nbytes)
{
printf( “write missed bytes expect %d got %dn”, iocb->u.c.nbytes, res);
exit(1);
}
free(iocb->u.c.buf);
free(iocb);
}
//读请求完成之后的回调函数
static void rd_done(io_context_t ctx, struct iocb *iocb, long res, long res2)
{
/*library needs accessors to look at iocb*/
int iosize = iocb->u.c.nbytes;
char *buf = (char *)iocb->u.c.buf;
off_t offset = iocb->u.c.offset;
int tmp;
char *wrbuff = NULL;
if(res2 != 0)
{
printf(“aio readn”);
}
if(res != iosize)
{
printf( “read missing bytes expect %d got %d”, iocb->u.c.nbytes, res);
exit(1);
}
/*turn read into write*/
tmp = posix_memalign((void **)&wrbuff, getpagesize(), AIO_BLKSIZE);
if(tmp < 0)
{
printf(“posix_memalign222n”);
exit(1);
}
snprintf(wrbuff, iosize + 1, “%s”, buf);
printf(“wrbuff-len = %d:%sn”, strlen(wrbuff), wrbuff);
printf(“wrbuff_len = %dn”, strlen(wrbuff));
free(buf);
io_prep_pwrite(iocb, odsfd, wrbuff, iosize, offset);
io_set_callback(iocb, wr_done);
if(1!= (res=io_submit(ctx, 1, &iocb)))
printf(“io_submit write errorn”);
printf(“nsubmit %d write requestn”, res);
}
void main(int args,void * argv[])
{
int length = sizeof(“abcdefg”);
char * content = (char * )malloc(length);
io_context_t myctx;
int rc;
char * buff=NULL;
int offset=0;
int num,i,tmp;
if(args < 3)
{
printf(“the number of param is wrongn”);
exit(1);
}
if((srcfd=open(argv[1],O_RDWR)) < 0) //打开设备/dev/sdb1
{
printf(“open srcfile errorn”);
exit(1);
}
printf(“srcfd=%dn”,srcfd);
lseek(srcfd,0,SEEK_SET);
write(srcfd,”abcdefg”,length); //写入字母
lseek(srcfd,0,SEEK_SET);
read(srcfd,content,length); //读出字母正常
printf(“write in the srcfile successful,content is %sn”,content);
if((odsfd=open(argv[2],O_RDWR)) < 0) //打开另一个设备/dev/sdb2
{
close(srcfd);
printf(“open odsfile errorn”);
exit(1);
}
memset(&myctx, 0, sizeof(myctx));
io_queue_init(AIO_MAXIO, &myctx); //初始化一个请求队列
struct iocb *io = (struct iocb*)malloc(sizeof(struct iocb));
int iosize = AIO_BLKSIZE;
tmp = posix_memalign((void **)&buff, getpagesize(), AIO_BLKSIZE);
if(tmp < 0)
{
printf(“posix_memalign errorn”);
exit(1);
}
if(NULL == io)
{
printf( “io out of memeoryn”);
exit(1);
}
io_prep_pread(io, srcfd, buff, iosize, offset);
io_set_callback(io, rd_done); //设置读请求完成之后的回调函数
printf(“START…nn”);
rc = io_submit(myctx, 1, &io); //提交IO请求
if(rc < 0)
printf(“io_submit read errorn”);
printf(“nsubmit %d read requestn”, rc);
struct io_event events[AIO_MAXIO];
io_callback_t cb;
num = io_getevents(myctx, 1, AIO_MAXIO, events, NULL); //获取完成的io事件
printf(“n%d io_request completednn”, num);
for(i=0;i
{
cb = (io_callback_t)events[i].data;
struct iocb *io = events[i].obj;
printf(“events[%d].data = %x, res = %d, res2 = %dn”, i, cb, events[i].res, events[i].res2);
cb(myctx, io, events[i].res, events[i].res2); //调用回调函数处理
}
}
b) POSIX AIO方式实现
同样,POSIX也提供了AIO的API接口
API 函数 说明
aio_read 请求异步读操作
aio_error 检查异步请求的状态
aio_return 获得完成的异步请求的返回状态
aio_write 请求异步写操作
aio_suspend 挂起调用进程,直到一个或多个异步请求已经完成(或失败)
aio_cancel 取消异步 I/O 请求
lio_listio 发起一系列 I/O 操作
#include
#include
#include
#include
#include
#include
#define BUFSIZE 512
int main(){
int fd, ofd ,ret;
struct aiocb my_aiocb;
fd = open(“/dev/sdb”,O_RDONLY);
if (fd < 0)
perror(“open”);
/* Zero out the aiocb structure (recommended) */
bzero( (char *)&my_aiocb, sizeof(struct aiocb) );
/* Allocate a data buffer for the aiocb request */
my_aiocb.aio_buf = malloc(BUFSIZE+1);
if (!my_aiocb.aio_buf)
perror(“malloc”);
/* Initialize the necessary fields in the aiocb */
my_aiocb.aio_fildes = fd;
my_aiocb.aio_nbytes = BUFSIZE;
my_aiocb.aio_offset = 0;
ret = aio_read(&my_aiocb);
while ( aio_error( &my_aiocb ) == EINPROGRESS ) ;
if ((ret = aio_return( &my_aiocb ))>0)
perror(“aio_read”);
else {
/* read failed, consult errno */
perror(“aio_read_error”);
}
return 0;
}