-
IO模型: 一種通信的模型
IO, input output 其實就是 ###通信####爲了解決#通信#過程中,出現的問題. -
IO模型: 網絡編程
-
阻塞
一個運行的進程 去訪問資源,發現資源不滿足,於是進程 ## 主動睡眠## 等待資源滿足,被喚醒.當資源(一般伴隨中斷)到來的時候, (中斷)去喚醒等待的進程.##睡眠###,不消耗
- 定義並初始化一個##等待隊列###
- 運行的進程發現資源不滿足, 主動將自己掛載到等待隊列上去. 然後讓內核重新調度.
- 某個執行單元將數據送達, 然後喚醒等待隊列上的進程.
a = {b=3;c=4; b+c};
- 非阻塞
進程去訪問資源,發現資源不滿足, 進程立馬去做其他任務, 通過輪訓的方式,不斷地檢查資源.內核如何支持 非阻塞呢???
struct file {
unsigned
int f_flags; //有32個bit, 每一個bit表示一種功能. 可讀 可寫 可執行 權限 ....其中,有一個bit 表示改文件 當前是 阻塞(0)還是非阻塞(1).一個文件默認是阻塞.
};
- 異步通知
linux系統中,異步通知使用 #信號#來實現.當內核 有數據之後, 主動向用戶程序發送一個信號, app得到信號之後,再去內核訪問數據.
故事1:老王燒開水。
出場人物:老王,水壺兩把(普通水壺,簡稱水壺;會響的水壺,簡稱響水壺)。老王想了想,有好幾種等待方式
1.老王用水壺煮水,並且站在那裏,不管水開沒開,每隔一定時間看看水開了沒。 -同步阻塞
2.老王還是用水壺煮水,不再傻傻的站在那裏看水開,跑去寢室上網,但是還是會每隔一段時間過來看看水開了沒有,水沒有開就走人。 -同步非阻塞
3.老王這次使用高大上的響水壺來煮水,站在那裏,但是不會再每隔一段時間去看水開,而是等水開了,水壺會自動的通知他。 -異步阻塞
4.老王還是使用響水壺煮水,跑到客廳上網去,等着響水壺自己把水煮熟了以後通知他。 -異步非阻塞
故事2::在網上買了個東西,然後就等東西送到快遞去取了。就有這4種情況:
你立刻傻站在快遞門口等東西來
你繼續做你要做的事,不會因爲會有包裹影響正常生活該做的
東西郵到給你發短信
東西郵到沒人吭聲通知你
阻塞(因爲會有包裹,你就專門等包裹來,包裹不到你就在快遞門口罷工!絕食!)
非阻塞(不會因爲有包裹你就影響你正常生活和該做的工作)
異步(人家很敬業的通知你)
同步(暴走大世界裏黑ems郵件堆成山卻不通知人取就是這種情況啦)
然後排列組合,又有了這四種情況:
1.同步阻塞:包裹來了不通知你,你就傻站在快遞門口等包裹來了
2.同步非阻塞(輪詢):你該幹啥幹啥,雖然人家不通知你,但你時不時的跑去快遞問問我包裹到了沒.至於你是每天中午問呢還是每隔一小時就跑去問一次,那就看你有多能墨跡了
3.異步阻塞:雖然人家會短信通知你,但你偏要在快遞門口傻站着等包裹到.有時間,就是這麼任性!但我估計程序猿應該沒誰會寫出這麼任性的代碼吧?
4.異步非阻塞(回調):其實第一次聽說要在快遞外面等郵件我是拒絕的,因爲,你不能讓我擱下所有工作,我就馬上去快遞外面等快遞……等了一陣時間,手機,DUANGDUANGDUANG~~我的快遞到快遞裏了
- 非阻塞IO中可以使用自旋鎖、原子變量
- 阻塞IO中可以使用互斥體
簡單實例
- chdev.c
/*****************************************************************
* Copyright (C) 2019 Sangfor Ltd. All rights reserved.
*
* 文件名稱:chdev.c
* 創 建 者:yinfei-hu
* 創建日期:2019年03月07日 星期4 20時22分05秒
* 功能描述:操作LED GPIO
*
*****************************************************************/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/sched.h>
#include "comm.h"
/*
############# io 模型 #####################
阻塞
1. 定義並初始化 一個等待隊列
wait_queue_head_t waitqhead;
void init_waitqueue_head(wait_queue_head_t *q)
2. 運行的進程 發現資源不滿足, 主動將自己 掛載到 等待隊列上去.
然後讓內核重新調度.
進程主動調用該 宏, 該進程 在 宏內部 阻塞/睡眠
不在返回. 當被喚醒的時候,該函數/進程 返回.
wait_event_interruptible(wait_queue_head_t wq,
int condition)
3. 某個執行單元 將數據送達, 然後喚醒等待隊列上的 進程.
void wake_up_interruptible(wait_queue_head_t *wq)
非阻塞
實現邏輯
如果用戶要求 文件時非阻塞的, 並且當前沒有數據,則 立馬返回-EAGAIN.
異步通知
1.定義 struct fasync_struct *fapp;
2.實現 fops.fasync 方法
3.發信號
kill_fasync(&pt_gstruct->fapp,SIGIO,POLL_IN)
*/
#define GPX2CON 0x11000c40
#define GPX2DAT 0x11000c44
struct global_struct {
struct class *cls;
int major,minor;
/* 字符設備驅動對象 */
struct cdev chdevobj; //inode->i_cdev
void *config;
void *data;
wait_queue_head_t waitqhead; //定義一個等待隊列
int data_flags; //數據有和無的標誌, 0-無 1-有
char buf[128];
struct fasync_struct *fapp; //異步通知的實現
};
/* 全局變量 */
struct global_struct gstruct;
int led_init(void)
{
int val;
gstruct.config = ioremap(GPX2CON,4);
gstruct.data = ioremap(GPX2DAT,4);
val = readl(gstruct.config);
val = val & (~0xF<<28);
val = val | 1<<28;
writel(val ,gstruct.config);
//關燈
val = readl(gstruct.data);
val = val & ~(1<<7);
writel(val,gstruct.data);
return 0;
}
int led_ctrl(int on )
{
int val;
if(on){
val = readl(gstruct.data);
val = val| (1<<7);
writel(val,gstruct.data);
}else {
//關燈
val = readl(gstruct.data);
val = val & ~(1<<7);
writel(val,gstruct.data);
}
return 0;
}
int led_deinit(void)
{
int val;
//關燈
val = readl(gstruct.data);
val = val & ~(1<<7);
writel(val,gstruct.data);
return 0;
}
/*
open, 一般執行對硬件進行初始化的動作
*/
int chdev_open(struct inode *inode, struct file *file)
{
struct global_struct *pt_gstruct ;
printk("%s->%d\n",__FUNCTION__,__LINE__);
/*inode->i_cdev 記錄了 該文件節點所對應的 字符設備對象的地址
一個大結構體變量gstruct, 如果已知其中一個 成員的地址&chdevobj,
如何求取大結構體變量 gstruct 的地址呢..... 成員地址減去 成員前面的大小
bigstruct *pt_gstruct =
container_of(little_member_addr,大結構體類型,小成員的變量名)
*/
pt_gstruct = container_of(inode->i_cdev ,struct global_struct,chdevobj);
// pt_gstruct ====== &gstruct
file->private_data = pt_gstruct;
led_init();
/*成功, 返回0
失敗, 返回一個負數, 記錄 數值不能亂寫,要根據 內核的錯誤宏定義
*/
return 0;
}
/*
close, 一般執行對硬件進行 去初始化的動作
*/
int chdev_close (struct inode *inode, struct file *file)
{
struct global_struct *pt_gstruct = file->private_data;
printk("%s->%d major%d minor%d\n",__FUNCTION__,__LINE__,pt_gstruct->major,pt_gstruct->minor);
led_deinit();
/*成功, 返回0
失敗, 返回一個負數, 記錄 數值不能亂寫,要根據 內核的錯誤宏定義
*/
return 0;
}
ssize_t chdev_write(struct file *file, const char __user *usr, size_t size, loff_t *loff)
{
int ret;
struct global_struct *pt_gstruct = file->private_data;
printk("%s->%d major%d minor%d\n",__FUNCTION__,__LINE__,
pt_gstruct->major,pt_gstruct->minor);
/* *usr來自用戶空間, 可能是null,或者 非法地址
如果不加判斷,內核直接使用, 會造成內核 異常
我們一般使用 安全版本的 memcpy ,copy_from_user
返回 實際尚未拷貝的字節數, 你要求拷貝100,實際拷貝89,返回11
*/
ret = copy_from_user(pt_gstruct->buf,usr,size);
if(ret ){
printk("%s->%d copy_from_user err\n",__func__,__LINE__);
return -16;
}
printk("%s->%d get buf:%s\n",__func__,__LINE__,pt_gstruct->buf);
pt_gstruct->data_flags = 1;
wake_up_interruptible(&pt_gstruct->waitqhead);
//異步通知,發送信號
kill_fasync(&pt_gstruct->fapp,SIGIO,POLL_IN);
/*成功, 返回實際拷貝的大小
失敗, 返回一個負數, 記錄 數值不能亂寫,要根據 內核的錯誤宏定義
*/
return size ;
}
ssize_t ch_read(struct file *file, char __user *usr, size_t size, loff_t *loff)
{
int ret;
struct global_struct *pt_gstruct = file->private_data;
printk("%s->%d major%d minor%d\n",__FUNCTION__,__LINE__,pt_gstruct->major,pt_gstruct->minor);
/*
非阻塞, 實現邏輯
如果用戶要求 文件時非阻塞的, 並且當前沒有數據,則 立馬返回.
*/
if(file->f_flags & O_NONBLOCK){
if(pt_gstruct->data_flags == 0){
return -EAGAIN;
}
}
/*注意, 如果 沒有數據,則當前進程 阻塞在該函數內部
不在返回 . 當數據到來,被喚醒,該函數 返回
該函數,再帶 判斷, 當有數據的時候,該函數 不會睡眠, 沒有數據則會睡眠
*/
wait_event_interruptible(pt_gstruct->waitqhead,pt_gstruct->data_flags);
/* usr 來自用戶空間, 有可能 是個非法地址
需要使用 安全版本 memcpy
*/
ret = copy_to_user(usr,pt_gstruct->buf,size);
if(ret ){
printk("%s->%d copy_to_user err\n",__func__,__LINE__);
return -16;
}
memset(pt_gstruct->buf,0,sizeof(pt_gstruct->buf));
pt_gstruct->data_flags = 0; //取走之後,清空數據標誌位
/*成功, 返回實際拷貝的大小
失敗, 返回一個負數, 記錄 數值不能亂寫,要根據 內核的錯誤宏定義
*/
return size;
}
long chdev_ioctl(struct file *file, unsigned int cmd, unsigned long args)
{
int ret;
struct tv_switch swtich;
struct tv_chnl chnl;
struct tv_vol vol;
struct tv_stat stat;
struct led_ctrl ctl;
struct global_struct *pt_gstruct = file->private_data;
printk("%s->%d major%d minor%d\n",__FUNCTION__,__LINE__,pt_gstruct->major,pt_gstruct->minor);
switch(cmd){
case TV_CMD_SWITCH:
ret = copy_from_user(&swtich,(void *)args,sizeof(swtich) );
printk("%s->%d TV_CMD_SWITCH get on%d\n",__func__,__LINE__,swtich.on);
break;
case TV_CMD_CHNL:
ret = copy_from_user(&chnl,(void *)args,sizeof(chnl));
printk("%s->%d TV_CMD_CHNL get up%d num%d\n",__func__,__LINE__,chnl.up,chnl.num);
break;
case TV_CMD_VOL:
ret = copy_from_user(&vol,(void *)args,sizeof(vol));
printk("%s->%d TV_CMD_VOL get up%d step%d\n",__func__,__LINE__,vol.up,vol.step);
break;
case TV_CMD_STAT:
stat.chnl = 129;
stat.vol = 56;
stat.light = 57;
ret = copy_to_user((void *)args,&stat,sizeof(stat));
break;
case LED_CMD_SW:
ret = copy_from_user(&ctl,(void *)args,sizeof(ctl));
led_ctrl(ctl.on);
break;
}
return 0;
}
int chdev_fasync(int fd, struct file *file, int on)
{
struct global_struct *pt_gstruct = file->private_data;
return fasync_helper(fd,file ,on,&pt_gstruct->fapp);
}
struct file_operations chdev_ops = {
.open = chdev_open,
.release = chdev_close,
.write = chdev_write,
.read = ch_read,
.unlocked_ioctl = chdev_ioctl,
.fasync = chdev_fasync,
};
int module_fun_init(void)
{
int ret;
dev_t devno;
/* 等待 隊列初始化*/
init_waitqueue_head(&gstruct.waitqhead);
memset(gstruct.buf,0,sizeof(gstruct.buf));
gstruct.data_flags = 0;
/* 1.向內核申請設備號*/
ret = alloc_chrdev_region(&devno,0,1,"chdev test");
if(ret <0){
printk("alloc_chrdev_region err %s->%d\n",__FUNCTION__,__LINE__);
return -12;
}
gstruct.major = MAJOR(devno); //major = devno >> 20;
gstruct.minor = MINOR(devno); //devno <<12>>12;
devno = MKDEV(gstruct.major,gstruct.minor); //major<<20 | minor;
printk("%s->%d get major%d minor%d\n",__FUNCTION__,__LINE__,gstruct.major,gstruct.minor);
/*創建並初始化 字符設備驅動 對象*/
cdev_init(&gstruct.chdevobj,&chdev_ops);
ret = cdev_add(&gstruct.chdevobj,devno,1);
if(ret <0){
printk("cdev_add err %s->%d\n",__FUNCTION__,__LINE__);
return -14;
}
/*第三步, 1.創建一個類*/
gstruct.cls = class_create(THIS_MODULE,"chdev class");
if(!gstruct.cls){
printk("class_create err %s->%d\n",__FUNCTION__,__LINE__);
return -16;
}
/*2.創建一個節點/文件 ,在類下
printf(const char * fmt,...); printf("%s %d %c %lf ",........);
在 /dev/目錄下,會創建 /dev/chdev0 文件節點
*/
device_create(gstruct.cls,NULL,devno,NULL,"chdev%d",0);
return 0;
}
void module_fun_exit(void)
{
//釋放 init裏面的動作,注意 和init是完全的逆序
//釋放文件節點
device_destroy(gstruct.cls,MKDEV(gstruct.major,gstruct.minor));
//釋放類
class_destroy( gstruct.cls);
//刪除 對象
cdev_del(&gstruct.chdevobj);
//釋放設備號
unregister_chrdev_region(MKDEV(gstruct.major,gstruct.minor),1);
printk("%s->%d\n",__FUNCTION__,__LINE__);
}
/* 內核模塊入口 module_init 裏面的參數,指向誰,誰就是模塊入口函數*/
module_init(module_fun_init);
/* 內核模塊出口 module_exit 裏面的參數,指向誰,誰就是模塊出口函數*/
module_exit(module_fun_exit);
/*模塊作者信息*/
MODULE_AUTHOR("YinFei.Hu <[email protected]>");
/*模塊信息*/
MODULE_DESCRIPTION("Kernel module driver");
/*告訴內核願意遵守gpl協議*/
MODULE_LICENSE("GPL");
- appwrite.c
/*****************************************************************
* Copyright (C) 2019 Sangfor Ltd. All rights reserved.
*
* 文件名稱:appwrite.c
* 創 建 者:yinfei-hu
* 創建日期:2019年03月07日 星期4 20時22分05秒
* 功能描述:通過之後設備的write控制led
*
*****************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include "comm.h"
int main(void)
{
int ret;
int fd;
char buf[128];
fd = open("/dev/chdev0",O_RDWR);
if(fd <0){
perror("open err");
return -1;
}
strcpy(buf,"usr msg comes from write app");
ret = write(fd, buf,sizeof(buf));
if(ret <0){
perror("write err");
return -2;
}
sleep(1);
close(fd);
return 0;
}
- appread-signal.c
/*****************************************************************
* Copyright (C) 2019 Sangfor Ltd. All rights reserved.
*
* 文件名稱:appread-signal.c
* 創 建 者:yinfei-hu
* 創建日期:2019年03月07日 星期4 20時22分05秒
* 功能描述:異步通知讀
*
*****************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include "comm.h"
int fd;
void sigio_fun(int signo)
{
int ret;
char buf[128];
memset(buf,0,sizeof(buf));
ret = read(fd,buf,sizeof(buf));
if(ret <0){
perror("read err");
return ;
}
printf("int signal fun ,usr get :%s\n",buf);
}
int main(void)
{
int ret;
int val;
fd = open("/dev/chdev0",O_RDWR);
if(fd <0){
perror("open err");
return -1;
}
/* 註冊信號 */
signal(SIGIO, sigio_fun);
/* 設置 fd的 文件屬主, 告訴文件,你的主人進程是哪一個
當文件有了信號之後,轉發給 屬主進程
*/
fcntl(fd,F_SETOWN,getpid());
/*讓文件支持 異步通知模式
file裏面 f_flags, 其中某一個bit表示該文件支持 異步模式
*/
val = fcntl(fd,F_GETFL,0);
val |= FASYNC;
fcntl(fd,F_SETFL,val);
while(1){
sleep(1);
void do_something(void); //延時
}
close(fd);
return 0;
}