1. 程式人生 > >linux系統中的IO操作

linux系統中的IO操作

同步IO分為阻塞IO、非阻塞IO、訊號驅動的IO和多路轉接IO。

阻塞IO:

一直阻塞程序直到完成IO操作。

非阻塞IO:

有資料時進行IO操作,沒有資料時立即返回不阻塞程序。

訊號驅動IO:

當有資料到來是傳送訊號給程序執行IO操作,提高CPU的利用率。當設定開啟檔案描述字O_ASYNC標誌是可以用於訊號驅動的IO操作,也可以用過fcntl()改變檔案標籤,當檔案進行IO操作時會產生SIGIO訊號或套接字有帶外資料到來時會產生SIGURG訊號。通過fcntl函式執行F_SETOWN命令可以設定/獲得,接收訊號的程序/程序組ID。

具體例項:

/*
 * main.c
 *
 *  Created on: 2016年10月22日
 *      Author: chy
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/unistd.h>
#include <termios.h>
#include <signal.h>
#include <sys/fcntl.h>
#include <sys/fcntl.h>
#include <stdio.h>
#include <stdlib.h>

FILE *file;

void sigfunc(int sig)
{
	char c,input[128];
	int n;
	static int i = 0;

	if(read(STDIN_FILENO,&c,1) > 0){
		if(c != '\n')
			input[i++] = c;
		else {
			input[i++] = '\0';
			fprintf(file,"NO sig=%d, input line is %s\n",i,input);
			i = 0;
			 if(c == 'q'){
				 fclose(file);
				 _exit(0);
			 }
		}
	}
}

int main(int argc,char *argv)
{
	int falg;
	char buff[256];
	struct termios newseting,old_termios;
	file = fopen("test.txt","w");
	signal(SIGIO,sigfunc);

	tcgetattr(STDIN_FILENO,&old_termios);
	newseting = old_termios;
	newseting.c_iflag &= (~ICANON);
	newseting.c_cc[VTIME] = 0;
	newseting.c_cc[VMIN] = 1;
	tcsetattr(STDIN_FILENO, TCSANOW, &newseting);
	fcntl(STDIN_FILENO, F_SETOWN,getpid());
	falg = fcntl(STDIN_FILENO, F_GETFL,0);
	falg |= O_ASYNC;
	fcntl(STDIN_FILENO,F_SETFL,falg);
	while(1) sleep(1);

	return 0;
}

多路轉接IO:

處理來自多個通道的輸入,通過select函式告訴呼叫它的程序需要等待IO事件的數量。

#include <sys/select.h>
#include <sys/time.h>
int select(int nfds,fd_set *rfds,fd_set *wfds,fd_set *edfs,struct timeval *timeout);
void FD_ZERO(fd_set *fdset); //初始化描述字為空
void FD_CLR(int filedes,fd_set *fdset); //將filedes描述字從fdset描述字集合中清除
int FD_ISSET(int fileds,fd_set *fdset); //判斷filedes是否屬於fdset所指的描述
void FD_SET(int filedes,fd_set *fdset);  //將filedes新增到描述字fset所指的集合中
select中的三個測試描述字集合都為空可以做為定時器切精度高於sleep。

非同步IO:能夠在較短時間內從多個接收通道收集大量的資料,非同步IO操作在IO期間不阻塞發出IO請求的程序,其IO操作由作業系統派生新的執行緒並行的執行,當IO操作結束是傳送訊號給傳送請求IO操作的程序通知其IO操作完成,程序也可以呼叫aio_suspend阻塞程序等待IO操作完成。
#include <aio.h>
int aio_write(struct aiocb *aiocbp);//返回實際寫入的位元組數
int aio_read(struct aiocb *aiocbp); //返回實際讀出的位元組數
int lio_listio(int mode,struct aiocb *restrict const list[restrict],int nent,struct sigevent *restrict notification);
int aio_error(struct aiocb *aiocbp); //正在進行IO操作返回EINPROGRESS
ssize_t aio_return(struct aiocb *aiocbp); //返回讀寫位元組個數
int aio_suspend(const struct aiocb *const list[],int nent,const struct timespec *timeout); //掛起呼叫程序直到IO完成或時間到期
int aio_sysnc(int op,struct aiocb *aiocbp); //把資料同步到物理磁碟

struct aiocb{
     int aio_filds;
     off_t aio_offset;
     volatile void *aio_buf;
     size_t aio_nbytes;
     int aio_reqprio;
     struct sigevent aio_sigevent;
     int aio_lio_opcode;
};

struct sigevent{
     union sigval sigev_value;
     int sigv_signo;
     int sigev_notify;//非同步事件的通知型別 SIGEV_NONE(不通知)、SIGEV_SIGNAL(生成訊號)、SIGEV_THREAD(執行sigev_notify_function指定的函式)
     void (*sigev_notify_function) (union sigval);
     pthread_attr_t *sigev_notify_attrbutes;
};
具體例項:
/*
 * main.c
 *
 *  Created on: 2016年10月20日
 *      Author: chy
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <sys/unistd.h>
#include <aio.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#define buffer_num  2
#define buffer_size 2048
#define ERR(msg,f_num) { \
	if(f_num < 0) { \
		fprintf(stderr,"%s",msg); \
		exit(-1); \
	} \
}

typedef enum{
	buffer_ferr = 1,
	buffer_full,
	buffer_write
}BUFFER;

typedef struct { //緩衝區結構
	BUFFER state;
	int fillpt;
	char buffer[buffer_size];
	struct aiocb aio;
}buffer_t;

static buffer_t buffer[buffer_num];
static sigset_t procmask;
static  int write_num = 0;
static int sig_num;
static volatile off_t seek_ptr;

void sig_func(int signo,siginfo_t *info,void *sig)
{
	int i;
	buffer_t  *temp;

	if(info->si_signo != SIGRTMIN || info->si_code != SI_ASYNCIO)
		return;
	else
		printf("write over\n");
	temp = (buffer_t*)info->si_value.sival_ptr;

	int write_temp_num = 0;
	if(aio_error(&temp->aio) != EINPROGRESS)
		write_temp_num = aio_return(&temp->aio);
	write_num += write_temp_num;
	sig_num++;

	temp->fillpt = 0;
	temp->state = buffer_ferr;
	return;
}

buffer_t *find_empty_buffer()
{
	int i;
	sigset_t newsig;
	sigprocmask(SIG_BLOCK,&procmask,&newsig); //遮蔽非同步寫訊號量

	while(1){
		for(i = 0; i < buffer_num; i++)
			if(buffer[i].state == buffer_ferr)
				break;
		if(i == buffer_num)
			sigsuspend(&procmask); //掛起SIGRTMIN
		else break;
	}
	buffer[i].state = buffer_full;
	buffer[i].fillpt = 0;
	sigprocmask(SIG_SETMASK,&procmask,NULL); //恢復遮蔽非同步寫訊號量
	return (&buffer[i]);
}

void buffer_flush(buffer_t *temp)
{
	temp->aio.aio_offset = seek_ptr; //檔案指標的位置
	seek_ptr += temp->fillpt;
	temp->aio.aio_buf = temp->buffer; //緩衝地地址
	temp->aio.aio_nbytes = temp->fillpt; //要傳輸的位元組數
	temp->aio.aio_reqprio = 0;   //優先移位0
	temp->aio.aio_sigevent.sigev_notify = SIGEV_SIGNAL; //實時訊號型別
	temp->aio.aio_sigevent.sigev_signo = SIGRTMIN; //訊號數
	temp->aio.aio_sigevent.sigev_value.sival_ptr = temp; //攜帶的資訊
	temp->state = buffer_write; //標記為正在寫的狀態

	ERR("write fail\n",aio_write(&temp->aio));

	return;
}

int main(int argc,char *argv[])
{
	int i,file_in,file_out;
	struct sigaction sig;
	buffer_t *buffer_opt;

	if(argc < 3){
		printf("please input three func\n");
		exit(0);
	}

	file_in = open(argv[1],O_RDONLY);
	ERR("open file_in faile\n",file_in);
	file_out = open(argv[2],O_WRONLY | O_CREAT,0777);
	ERR("open file_out faile\n",file_out);

	for(i = 0; i < buffer_num; i++){
		buffer[i].state = buffer_ferr;
		buffer[i].fillpt = 0;
		buffer[i].aio.aio_fildes = file_out; //繫結檔案描述字
	}

	buffer_opt = find_empty_buffer();
	sigemptyset(&procmask);
	sigaddset(&procmask,SIGRTMIN);
	sigemptyset(&sig.sa_mask);
	sig.sa_flags = SA_SIGINFO;
	sig.sa_sigaction = sig_func;

	ERR("sigaction fail\n",sigaction(SIGRTMIN,&sig,NULL));

	int read_num;
	while(1){
		while((read_num = read(file_in,buffer_opt->buffer + buffer_opt->fillpt,buffer_size)) != 0){
			if(read_num > 0){
				buffer_opt->fillpt += read_num;
				if(buffer_opt->fillpt == buffer_size){
					buffer_flush(buffer_opt);
					buffer_opt = find_empty_buffer();
					break;
				}
			}
			if(errno == EINTR)
				break;
			else if(read_num < 0 && errno != EINTR)
				ERR("read faile\n",-1);
		}
	   if(read_num == 0){
			printf("chen\n");
			buffer_flush(buffer_opt);
			break;
		}
	}
	 for(i = 0; i < buffer_num; i++)
		 if(buffer[i].state == buffer_write){
			 struct aiocb *temp[1];
			 temp[0] = &buffer[i].aio;
		     aio_suspend(temp, 1, NULL);
		 }
	 close(file_out);
	 close(file_in);
	 printf("this is over\n");

	 return 1;
}

儲存對映IO:傳統IO進行讀寫檔案要進行多次系統呼叫,並且把檔案載入到自己的地址空間,其效率低,浪費儲存空間。儲存對映IO,系統把檔案的一頁讀取到記憶體,每一個程序把改頁對映到自己的記憶體空間。對映後不再需要read等系統呼叫,可以通過指令直接訪問。儲存對映分為共享對映和私有對映,共享對映每個程序都能夠改變對映頁的內容,當一頁被重新整理時保回會物理磁碟。私有對映,寫檔案將導致複製該頁的一個副本,物理磁碟物件本身不改變。

#include <sys/mman.h>
void *mmap(void *addr,size_t len,int port,int flags,int filedes,off_t off);
int munmap(void *paddr,size_t len); //刪除對映
int msync(void *addr,size_t len,int flags); //寫入到物理磁碟
addr:對映區在記憶體的起始地址。
len:對映的位元組數。
prot:對映區的保護許可權。
flags:對映區的屬性。
filedes: 已開啟檔案的描述字。
off:檔案要對映的其實位元組位置。











相關推薦

Linux系統程式設計---IO操作

1.系統呼叫 系統呼叫效率較低 1.系統呼叫開銷大,要從使用者空間切換到核心空間,然後切換回使用者空間 2.系統呼叫與底層驅動相關,不一定能夠按指定一次寫完。write函式 #include <unistd>​ size_t write(int files,

linux系統IO操作

同步IO分為阻塞IO、非阻塞IO、訊號驅動的IO和多路轉接IO。阻塞IO: 一直阻塞程序直到完成IO操作。 非阻塞IO: 有資料時進行IO操作,沒有資料時立即返回不阻塞程序。 訊號驅動IO: 當有資料到來是傳送訊號給程序執行IO操作,提高CPU的利用率。當設定開啟檔案描述字

Linux系統安裝MySQL數據庫操作手冊

linux mysql數據庫 Linux系統中MySQL數據庫安裝手冊一、安裝概述: 在Linux操作系統中安裝MySQL數據庫是一個我們必須要掌握的一門技術,也決定了你以後找工作的薪資待遇,所以你知道它的厲害了吧!學會安裝只是第一步,你還得學好數據庫的基本操作,以及搭建一個數據庫的主從配置等等

Linux系統MySQL的常用操作命令

char OS var 權限 記錄 port nis whereis null 服務: # chkconfig --list 列出所有系統服務# chkconfig --list | grep on 列出所有啟動的系統服務 # chkconfig -

Linux系統vim的常用操作

######### vim ########## 1.##vim模式## *)命令模式 用vim開啟一個檔案就進入這個模式了(這是vim預設的模式),在這個模式中可以輸入相關命令 *)插入模式 進入vim中按小寫的i鍵進入vim的插入模式 *)退出模式 按Esc鍵按

linux系統設定oracle啟停服務操作步驟

現在很多資料庫伺服器都是安裝的linux系統,比如說,oracle資料庫,安裝完以後,不會隨著linux系統啟動。那麼可以寫一個指令碼,來實現這個功能。 1 配置監聽 安裝完oracle以後,可以配置下監聽服務,這樣方便使用者遠端連線。在下面這兩個路徑下 /opt/orac

linux系統跟蹤高IO等待

原文作者:Jon Buys       跟蹤大型分散式系統的效能問題,從本質上來講是複雜的。應用為什麼慢?瓶頸在哪裡?以我的經驗,最主要的罪魁禍首之一是高IO等待(即high IO wait)。換一個地方用的話來說:每個人都只是在等待[翻譯參考文獻1]。        高IO

linux系統python的模組 || python的檔案操作

一.模組:itchat微信登陸 1.模組:在 Python 中,一個.py檔案就稱之為一個模組(Module)。 •大大提高了程式碼的可維護性; •編寫程式碼不必從零開始。當一個模組編寫完畢,就可以被其 他地方引用; 2.包:如果不同的人編寫的模組名相同怎

Cgroup限制CPU、IO、記憶體以及linux系統的排程策略

一、CPU 1、0核利用 思路: 0核是必須要保護的,否則各種系統命令、喂狗都可能出問題;    &

老男孩教育每日一題-2017年5月12日-磁盤知識點:linux系統LVM配置實現方法?

邏輯卷管理 磁盤 每日一題 1.題目老男孩教育每日一題-2017年5月12日-磁盤知識點:linux系統中LVM配置實現方法?2.參考答案01:將一個或多個物理分區創建為一個PV# pvcreate /dev/sdb{1,2} Physical volume "/dev/sdb1" success

linux系統定時任務

linu tab 一次 腳本 lin edi 路徑 執行 表示 1、查看所有定時任務:命令:“crontab -l” 數字意思解釋如下:從左到右,依次是:分、時、日、月、星期。 2、編輯定時執行計劃:命令:"crontab -e" ,e表示edit修改的意思。 3、修

Linux 系統這樣修復 SambaCry 漏洞

windows 服務器 解決方案 linux 非官方 導讀Samba 很久以來一直是為 linux 系統上的 Windows 客戶端提供共享文件和打印服務的標準。家庭用戶,中型企業和大型公司都在使用它,它作為最佳解決方案在多種操作系統共存的環境中脫穎而出,由於廣泛使用的工具很可能發生這種情

linux 系統的 /bin /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin 目錄的區別

。。 process pre this 用戶 unix table mount sent 先來段英文的: /bin This directory contains executable programs which are needed in single user

Linux 系統安裝Mysql_5.6

上傳 粘貼 啟動mysql grant ont linu chm cli char Linux 系統中安裝Mysql_5.6 安裝包下載地址(http://pan.baidu.com/s/1o8G5q

Linux系統安裝vmTools

cdr cdrom linu 是否 壓縮 進入 span pan tin 以下是,會用到的命令、遇到的問題及解釋: 在虛擬機中安裝centOS系統,因為後續安裝服務或者其他的東西,因此需要安裝vmTools,方便將文件從宿主電腦拖進虛擬機內。(我的是tar壓縮包的vmToo

Linux系統有關/dev/null和/dev/zero文件說明及實踐

linux /dev/null 特殊文件 /dev/zero Linux系統中有關/dev/null和/dev/zero文件說明提示:這個題目完全可以作為一個面試題考考運維的應聘者:面試題:請解釋Linux中/dev/null和/dev/zero兩個文件的作用和區別。在類Unix操作系統中,

運維學習之Linux系統的文件傳輸、歸檔、壓縮

linux不同系統之間的文件傳輸1.文件歸檔1.文件歸檔,就是把多個文件變成一個歸檔文件2.tar c ##創建 f ##指定歸檔文件名稱 t ##顯示歸檔文件中的內容 r ##向歸檔文件中添加文件 --get ##取出單個文件 --delete ##刪除單個文件 x ##取出歸檔文件中的所有內容

Linux系統如何查看日誌信息

日誌文件 系統日誌 楊書凡 日誌文件是用於記錄Linux系統中各種運行消息的文件,不同的日誌文件記載了不同類型的信息,對於診斷和解決系統中的問題很有幫助分析日誌文件 日誌數據主要包括三種類型:內核及系統日誌、用戶日誌、程序日誌 #對於大多數的文本格式的日誌文件,使用tail、more、l

Linux系統修改/etc/profile文件的方法

linux在Linux系統中etc/profile文件一般是不能更改的,想要更改etc/profile文件就要用一些特殊的技巧進行Linux文件修改。本文就來介紹一下Linux系統中修改/etc/profile文件的方法:etc/profile文件是只讀的,直接用vi或gedit打開修改後是無法保存的。要修改

Linux系統svn服務器設置開機啟動

數據庫 檢查 reat version 打開端口 rest grep 建立 標簽 安裝完svn服務器後雖然好用但是因為經常重啟Linux服務器,每次重啟完就要去手動啟動svn服務器,很是麻煩,於是在網上找了一些方法後,自己把svn服務器設置成開機啟動 步驟一:安裝svn服務