Linux 文件I/0

    經過×××年的工作,經常使用的一些I/O(很重要,因爲Linu一切皆文件)函數,以後用到可以查看下。這段時間不是很忙,僅僅爲了做個筆記整理一下。

    文件I/O和標準I/O庫有的區別:1)文件I/O是系統調用,標準I/O庫是封裝了系統調用的函數庫;2)文件I/O操作的是文件描述符(內核返回,是一個非負整數,其中0,1,2代表標準輸入,標準輸出,標準錯誤),標準I/O操作的是流即就是FILE對象;因此可見標準I/O的移植行更好。說到系統調用,再來說一下,系統調用就是一箇中斷值爲0x80內核實現的一箇中斷。這一篇主要記錄一下對應文件I/O的一些系統調用。

1、打開文件

    int open(const char *pathname, int flag,/*mode_t mode*/)/*成功返回文件描述符*/

    pathname:要打開或創建文件的名字,系統規定了一個最大長度;

    flag:O_RDONLY 只讀打開。O_WRONLY 只寫打開。O_RDWR 讀寫打開。

    O_APPEND 每次寫時追加到文件尾部。

    O_CREAT 若無文件,則創建它。

    O_EXEL 如果同時指定了O_CREAT,文件已經存在,則會出錯。

    O_TRUNC 文件存在,併爲只讀或只寫打開,則將長短截取爲0。

    O_NOCTTY 如果是終端設備,則不將該設備分配作爲此進程的控制終端。

    O_NONBLOCK 將一個FIFO、一個塊特殊文件或一個字符文件設置爲非阻塞模式。

    O_DSYNC、O_RSYNC、O_SYNC三個不是很理解,以後用到再深入理解。

    mode:可又可無,意思就是設置用戶權限,組權限,其他用戶權限 ,例如:777:全權限讀、寫、可執行。

2、creat創建文件

    int creat( const char *pathname, mode_t mode);/*成功返回文件描述符*/

    參數說明通open函數;

    此函數等價於:open( pathname,O_WRONLY| O_CREAT|O_TRUNC,mode);

3、close關閉一個打開文件

    int close(int filedes);

    關閉一個文件還會釋放加在此文件上的記錄鎖;當一個進程終止後,自動關閉文件,所以不用顯示的調用close去關閉此文件。

4、設置當前文件偏移量lseek函數

    off_t lseek(int filedes, off_t offset ,int whence);/*成功返回新的文件偏移量*/

    參數offset和whence有關,解釋如下:

    SEEK_SET:將文件的偏移量設置距文件開始處offset個字節。

    SEEK_CUR:將文件的偏移量設置當前值加上offset,offset可正或負。

    SEEK_END:將文件的偏移量設置爲文件長度加offset,offset可正可負。

    *可以用lseek(fd,0,SEEK_CUR),如果返回值爲-1說明沒有設置偏移量,否則設置了偏移量。

5、從文件中讀數據read函數

    ssize_t read(int filedes,void *buf,size_t nbytes);/*成功返回讀的字節數,如已經到文件結尾返回0,出錯返回-1*/

6、給打開的文件寫數據write

    ssize_t write(int filedes,const void *buf,size_t nbytes);/*成功返回寫的字節數,失敗返回-1*/

    如果open的時候指定了O_APPEND參數,則每次寫之前,將文件偏移量設置到結尾處。在一次成功後,便宜量增加實際寫的字節數。

7、原子操作函數pread和pwrite函數

    其實用open函數中的O_APPEND參數就可以實現原子操作,每次write都是在文件尾部。

    ssize_t pread(int fd,void *buf,size_t count,off_t offset);

    ssize_t pwrite(int fd,const void *buf,size_t count,off_t offset);

    相當於順序調用lseek和read或write

8、複製一個現有的文件描述符dup和dup2

    int dup(int filedes);

    int dup2(int filedes,int filedes2 );

    dup返回當前可用的最小文件描述符的值:dup(1)返回3,3共享1

    dup2如果兩個參數相等返回filedes2,否則,filedes共享filedes2並關閉filedes2。

    舉一個前段時間開發串口進zebra命令行的例子:

int config_console_para(int iConsoleFd);/*modified by zhaoxiaohu*/
/*added by zhaoxiaohu for console to vty*/
struct vty *vty_console_create( char *dev )
{
    int fd;
    int iRet;
    struct vty *vty;
    fd = open( dev, O_RDWR, 0644 );
    if( fd < 0 )
    {
       printf( "error: open console %s error.\r\n", dev );
       return NULL;
    }
    iRet = config_console_para(fd);
    if(0 != iRet)
    {
        printf("console para set error.\r\n");
    }
    vty = vty_get_new();

    if( vty == NULL ) return NULL;
    vty->fd = fd;
    vty->type = VTY_CONSOLE;
    strcpy( vty->address, "Console" );
    vty->node = LOGIN_NODE;
    vty->fail = 0;
    vty_clear_buf( vty );
    vty->status = VTY_NORMAL;
    vty_hello( vty );
    vty_prompt( vty );
    buffer_flush_all( vty->obuf, vty->fd );
    /* Add read/write thead */
    vty_event( VTY_WRITE, vty );
    vty_event( VTY_READ, vty );
    return vty;
}
/*b-console config added by zhaoxiaohu,2018-12-19*/
/*配置串口參數*/
int config_console_para(int iConsoleFd)
{
    struct termios tConsolePara;

    if (iConsoleFd < 3 )
    {
        printf("fd is error.\r\n");
        return -1;
    }
    
    if(tcgetattr(iConsoleFd,&tConsolePara) != 0)
    {
        printf("get console para error.\r\n");
        return -1;
    }
    tConsolePara.c_lflag &= ~ (ICANON | ECHO | ECHOE | ISIG);
    tConsolePara.c_cc[VERASE] = 1;
    if(tcsetattr(iConsoleFd,TCSANOW,&tConsolePara) != 0)
    {
        printf("config console para error.\r\n");
        return -1;
    }
    if( cfsetispeed(&tConsolePara,B115200) != 0 )/*設置爲115200Bps*/
    {
        printf("config console para error.\r\n");
        return -1;
    }
    if( cfsetospeed(&tConsolePara,B115200) != 0 )/*設置爲115200Bps*/
    {
        printf("config console para error.\r\n");
        return -1;
    }
    return 0;
}
/*e-console config added by zhaoxiaohu,2018-12-19*/

/*b-added by zhaoxiaohu for console to connect vty shell*/
void console_connect_vty(void )
{
    struct vty *vty;
    int iConsoleFd, iRet;
    vty = vty_console_create("/dev/console");   
    g_pVty = vty;
    g_iConsoleFd = vty->fd;
    iConsoleFd = vty->fd;
    dup2(iConsoleFd,0);/*close 0,1,2,共享到串口*/
    dup2(iConsoleFd,1);/**/
    dup2(iConsoleFd,2);/**/
    setvbuf(stdout,NULL,_IONBF,0);/*set stdout no buffer,printf to console .added by zhaoxiaohu-2019.4.10*/
    struct timeval time_now;
    struct timeval *time_wait;
    while(1)
    {
        #if 1
        time_now.tv_sec = vty->v_timeout;
        time_now.tv_usec = 0;

        if( time_now.tv_sec > 0 )
            time_wait = &time_now;
        else
            time_wait = NULL;
        
        iRet = select( iConsoleFd + 1, &vty->read_set, &vty->write_set, NULL, time_wait );

        if( iRet <= 0 )
        {
            /* error: close vty */
            if( iRet < 0 ) break;

            /* rc == 0, timeout ! console timeout */
            if( vty->type == VTY_CONSOLE )
            {
                vty_timeout( vty );
                continue;
            }
            else
            {
                vty_timeout( vty );
                break;
            }
        }
        #endif
        if( FD_ISSET( iConsoleFd, &vty->read_set ) )
            vty_read( vty );

        if( FD_ISSET( iConsoleFd, &vty->write_set ) )
            vty_flush( vty );

        /* console can't close */
        if( vty->type == VTY_CONSOLE ) continue;

        if( vty->status == VTY_CLOSE ) break;
    }
    return;
}
/*e-added by zhaoxiaohu for console to connect vty shell*/


9、改變打開文件的性質函數fcntl

    int fcntl(int filedes,int cmd,/*int arg*/,/*flock記錄鎖*/)

    函數有五種功能通過設置cmd:

    1)複製一個現存的描述符(F_DUPFD);

dup(filed);/*等效於*/
fcntl(filed,F_DUPFD,0);
dup2(filed,filed2); /*等效於*/
close(filed2);
fcntl(filed,F_DUPFD,filed2);
/*dup2 和close+fcntl區別是:前者是原子操作,後者不是*/

    2)獲取或設置文件描述符標記(F_GETFD或F_SETFD);

    3) 獲取或設置文件描述符標誌(F_GETFL或F_SETFL),可以改變的幾個標誌:O_APPEND、O_NONBLOCK、O_SYNC、O_DSYNC、O_RSYNC、O_FSYNC、O_ASYNC;

    4) 獲取或設置文件異步I/O所有權(F_GETOWN或F_SETOWN),獲取或設置接收SIGIO、SIGURG信號的進程ID或進程組ID;

    5)獲取或設置記錄鎖(F_GETLK、F_SETLK或F_SETLKW),通過結構體flock。

10、I/0操作的雜貨鋪ioctl函數

    不能用上面函數操作的文件都可以用ioctl操作

    int ioctl(int filedes,int request ,/*void *arg*/)

    功能很多,大致分爲:1)套接口2)文件操作3)接口操作4)arp高速緩存操作5)路由表操作6)流系統。

    舉一個前段時間工作中用開發靜態路由,查詢arp緩存表的例子:

/*b-added by zhaoxiaohu to serch ip at arp cache*/
/*定義結構體*/
typedef struct tArpTable{
       struct tArpTable *pNext;
       unsigned char data[0];
}*ptVlanIpDev;

ptVlanIpDev g_ptVlanIpDevTable = NULL;
/*顯示交換芯片添加的虛接口,vlan ip*/
void vlanIpDevTableDisplayAll()
{
    ptVlanIpDev pTemp = g_ptVlanIpDevTable;
    while(pTemp)
    {
        printf("%s\r\n",pTemp->data);
        pTemp = pTemp->pNext;
    }
        
}
/*清除虛接口表*/
void arpTableClear()
{
    ptVlanIpDev pTemp = NULL;
    while(g_ptVlanIpDevTable)
    {
        pTemp = g_ptVlanIpDevTable;
        g_ptVlanIpDevTable = pTemp->pNext;
        free(pTemp);
        pTemp = NULL;  
    }
}
/*獲取虛接口*/
int get_vlan_ip_dev()
{
       unsigned char ucBuf[256] = {0};
       FILE *fp;
       fp = popen("find /sys/class/net/ -name sw.*", "r");
       if( NULL == fp )
       {
           return -1;
       }
       arpTableClear();
       while( fgets( ucBuf, sizeof(ucBuf), fp ) )
       {
            
            ptVlanIpDev pTemp = NULL;
            if( NULL == (pTemp = (ptVlanIpDev)calloc(1,sizeof(struct tArpTable) + sizeof(ucBuf) )) )
                continue;
            memcpy( pTemp->data,ucBuf,strlen(ucBuf) );
            pTemp->pNext = g_ptVlanIpDevTable;
            g_ptVlanIpDevTable = pTemp;
            memset( ucBuf,0,sizeof(ucBuf) );
       }
       pclose(fp);
       return OK;
}
int ipnet_arp_for_cache(  )
{
    
    int sfd,ret;
    unsigned char *ucMac;
    unsigned char ucIpAddrStr[32] = {0};
    struct arpreq arp_req;
    struct sockaddr_in *sin;
    get_vlan_ip_dev();
    ptVlanIpDev pTemp = g_ptVlanIpDevTable;
    ip2Str( g_ArpFindIpAddr, ucIpAddrStr );/*要查詢的ip*/
    while(pTemp) /*遍歷所有的虛接口*/
    {
        sin = ( struct sockaddr_in * )&( arp_req.arp_pa );
        memset( &arp_req, 0, sizeof(arp_req) );
        sin->sin_family = AF_INET;
        inet_pton( AF_INET, ucIpAddrStr, &(sin->sin_addr) );
        
        strncpy( arp_req.arp_dev, pTemp->data+15,strlen(pTemp->data+15)-1 );
        sfd = socket( AF_INET, SOCK_DGRAM, 0 );

        ret = ioctl( sfd, SIOCGARP, &arp_req );
        if (ret < 0) {
            goto nextNode;
        }
        if ( arp_req.arp_flags & ATF_COM ) /*找到ip對應的mac地址*/
        {
            
            ucMac = (unsigned char *)arp_req.arp_ha.sa_data;
    //        printf("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
      //              ucMac[0], ucMac[1], ucMac[2], ucMac[3], ucMac[4], ucMac[5]);
            memcpy( gArpFindMac, ucMac, 6 );
        } 
        else 
        {
   //         printf("MAC: Not in the ARP cache.\n");
        }
        nextNode:
        pTemp = pTemp->pNext;
    }
    
    return OK;
}


/*e-added by zhaoxiaohu to serch ip at arp cache*/



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章