zoukankan      html  css  js  c++  java
  • Linux 0.11中write实现

    看了一下Linux 0.11版本号write的实现,首先它在标准头文件unistd.h中有定义

    int write(int fildes, const char * buf, off_t count);

    接下来看write.c

    /*
     *  linux/lib/write.c
     *
     *  (C) 1991  Linus Torvalds
     */
    
    #define __LIBRARY__
    #include <unistd.h>
    
    //定义write的实现
    _syscall3(int,write,int,fd,const char *,buf,off_t,count)


    这里说明一下为什么要有#define __LIBRARY__。

    由于在unistd.h有

    #ifdef __LIBRARY__
    
    /*中间省略*/
    
    #define __NR_write	4
    
    /*中间省略*/
    
    //有3个參数的系统调用宏函数
    #define _syscall3(type,name,atype,a,btype,b,ctype,c) 
    type name(atype a,btype b,ctype c) 
    { 
    long __res; 
    __asm__ volatile ("int $0x80" 
    	: "=a" (__res) 
    	: "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)),"d" ((long)(c))); 
    if (__res>=0) 
    	return (type) __res; 
    errno=-__res; 
    return -1; 
    }
    
    #endif /* __LIBRARY__ */


    能够发现,若在#include <unistd.h>之前没有#define __LIBRARY__,则以下的_syscall3(int,write,int,fd,const char *,buf,off_t,count)是找不到的。
    这样的设计非常优美,不须要系统调用的文件通过不包括#define __LIBRARY__,便能省去unistd.h中一些没用的定义。


    这样write便拥有了实现,我们发现当中有__NR_##name,在write中它为__NR_##write。它在上面有定义#define __NR_write4。4代表什么。有以下的定义

    fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,
    sys_write, 	... };


    也就是所write会调用sys_write,sys_write在read_write.c文件里

    int sys_write(unsigned int fd,char * buf,int count) //将用户进程要写的内容写入到内核的文件页面缓冲中
    {
    	struct file * file;
    	struct m_inode * inode;
    	
    	//异常错误处理
    	if (fd>=NR_OPEN || count <0 || !(file=current->filp[fd]))
    		return -EINVAL;
    	if (!count)
    		return 0;
    	//取文件相应的i节点,若是管道文件,而且是写管道文件模式。则进行写管道操作
    	inode=file->f_inode;
    	if (inode->i_pipe)
    		return (file->f_mode&2)?

    write_pipe(inode,buf,count):-EIO; //假设是字符型文件。则进行写字符设备 if (S_ISCHR(inode->i_mode)) return rw_char(WRITE,inode->i_zone[0],buf,count,&file->f_pos); //假设是块设备文件,则进行块设备写操作 if (S_ISBLK(inode->i_mode)) return block_write(inode->i_zone[0],&file->f_pos,buf,count); //假设是常规文件,则进行写文件 if (S_ISREG(inode->i_mode)) return file_write(inode,file,buf,count); printk("(Write)inode->i_mode=%06o ",inode->i_mode); return -EINVAL; }



    这里看一看常规文件写操作file_write,在file_dev.c中

    //依据i节点和文件结构信息。将用户数据写入指定设备
    int file_write(struct m_inode * inode, struct file * filp, char * buf, int count)
    {
    	off_t pos;
    	int block,c;
    	struct buffer_head * bh;
    	char * p;
    	int i=0;
    
    /*
     * ok, append may not work when many processes are writing at the same time
     * but so what. That way leads to madness anyway.
     */
     //假设是要向文件后加入数据。则将文件读写指针移到文件尾部。否则就将在文件读写指针出写入
    	if (filp->f_flags & O_APPEND)
    		pos = inode->i_size;
    	else
    		pos = filp->f_pos;
    	//若已经写入字节数i小于须要写入的字节数count,则循环
    	while (i<count) {
    	//创建数据块号pos/BLOCK_SIZE在设备上相应的逻辑块
    		if (!(block = create_block(inode,pos/BLOCK_SIZE)))
    			break;
    		if (!(bh=bread(inode->i_dev,block)))
    			break;
    		//求出文件读写指针在数据块中的偏移值c,将p指向读出数据块缓冲区中開始读取的位置。置该缓冲区已改动标志
    		c = pos % BLOCK_SIZE;
    		p = c + bh->b_data;
    		bh->b_dirt = 1;
    		//从開始读写位置到块末共可写入c = BLOCK_SIZE-c个字节,若c大于剩余还需写入的字节数count-i,则
    		//此次仅仅需再写入c = count-i
    		c = BLOCK_SIZE-c;
    		if (c > count-i) c = count-i;
    		//文件读写指针前移此次需写入的字节数。假设当前文件读写指针位置值超过了文件的大小,则改动i节点中文件
    		//大小字段,并置i节点已改动标志
    		pos += c;
    		if (pos > inode->i_size) {
    			inode->i_size = pos;
    			inode->i_dirt = 1;
    		}
    		//已写入字节计数累加此次写入的字节数c。从用户缓冲区buf中复制c个字节到快速缓冲区中p指向開始的位置处。
    		//然后释放该缓冲区
    		i += c;
    		while (c-->0)
    			*(p++) = get_fs_byte(buf++);
    		brelse(bh);
    	}
    	//更改文件改动时间为当前时间
    	inode->i_mtime = CURRENT_TIME;
    	//假设此次操作不是在文件尾部加入,则把文件读写指针调整到当前写位置。并更改i节点改动时间为当前时间
    	if (!(filp->f_flags & O_APPEND)) {
    		filp->f_pos = pos;
    		inode->i_ctime = CURRENT_TIME;
    	}
    	return (i?

    i:-1); }



    总结:


    我们在用户层能够对磁盘、串口和文件通用wirte,但在系统调用层便进行了区分。

  • 相关阅读:
    MVP模式
    开源代码SlidingMenu的使用
    常用命令(Linux、Android、adb)
    一文搞清楚Minor GC、Major GC 、Full GC 之间的关系
    阿里最新38道Java面试题解析(MyBatis+消息队列+Redis)
    从5个方面让你真正了解Java内存模型
    深入理解JVM:元空间大小详细解析
    面试必问:JVM类加载机制详细解析
    5个点彻底搞清楚SpringBoot注解
    8种创建Java线程的方式,你知道几个?
  • 原文地址:https://www.cnblogs.com/yfceshi/p/6946474.html
Copyright © 2011-2022 走看看