精华内容
下载资源
问答
  • Android应用层读写设备节点

    千次阅读 2019-07-13 15:20:33
    Android应用层如何读写设备节点 1、什么是设备节点(设备文件)? Linux中设备节点是通过“mknod”命令来创建的。一个设备节点其实就是一个文件,Linux中称为设备文件。有一点必要说明的是,在Linux中,所有的设备...

    Android应用层如何读写设备节点

    1、什么是设备节点(设备文件)?
    Linux中设备节点是通过“mknod”命令来创建的。一个设备节点其实就是一个文件,Linux中称为设备文件。有一点必要说明的是,在Linux中,所有的设备访问都是通过文件的方式,一般的数据文件程序普通文件,设备节点称为设备文件。所以读写设备节点即读取更改文件数据。

    2、读取设备节点
    例如节点路径为:/sys/wenjian/node

    /**
     * 读取设备节点
     */
    public static String getNodeString(String path) {
        String prop = "waiting";// 默认值
        try {
            BufferedReader reader = new BufferedReader(new FileReader(path));
            prop = reader.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return prop;
    }
    

    调用方法:

    private static final String path = "/sys/auxcheck/auxcheck";
    getNodeString(path );
    

    3、改写节点
    例如节点路径为:/sys/wenjian/node

    /**
     * 改写节点
     */
    public static boolean setNodeString(String path,String value){
        try {
            BufferedWriter bufWriter = null;
            bufWriter = new BufferedWriter(new FileWriter(path));
            bufWriter.write(voltage);  // 写入数据
            bufWriter.close();
            Log.e("fht","改写节点成功!");
        } catch (IOException e) {
            e.printStackTrace();
            Log.e("fht","改写节点失败!");
            return false;
        }
        return true;
    }
    

    调用方法:

    private static final String path = "/sys/auxcheck/auxcheck";
    private string values = "需要写入的数据";
    boolean flag = NaviDebug.setNodeString(path,values);
    

    注意:因为写节点需要高权限,所以可能需要在AndroidMainfest.xml中添加android:sharedUserId=“android.uid.system” 属性,如下:
    在这里插入图片描述

    展开全文
  • 本文主要讲述从实际项目中一个GPIO口控制一个加密芯片上下电的功能,提供动态库给客户,并有Android应用层apk调用.so库文件的例子,希望能为大家入门带来帮助! 以下描述参考摘录了别人的话:...

     本文主要讲述从实际项目中一个GPIO口控制一个加密芯片上下电的功能,提供动态库给客户,并有Android应用层apk调用.so库文件的例子,希望能为大家字符设备驱动以及jni开发入门带来帮助!

    以下描述参考摘录了别人的话:http://koliy.iteye.com/blog/1424304

    android应用层要访问驱动,一般有三种方法。 
    1.应用层 ---> framwork层JNI ---> 驱动c 
    2.应用层 ---> framwork层JNI ---> 硬件抽象层HAL ----> 驱动c 

    3.应用层-->驱动c(读写字符设备)


    三种方法,各有各的好处,第1种,方便与驱动调试实验,只要编译好ko文件和libxxx.so文件,放入系统中编译好就可以立即调试了。 

    第2种JNI方法有些改变和增加了HAl,对驱动有了封装,简单来讲,硬件驱动程序一方面分布在Linux内核中,另一方面分布在用户空间的硬件抽象层中。不要误会android把驱动分成两半了,其实android只是在驱动的调用接口上在增加一层对应接口功能来让framwork层JNI来调用。比如,驱动有read接口,HAl层就是封装read接口,做一个 hal_read接口,里面调用read接口,然后把hal_read接口给JNI调用。 

    明白了吗?这方面有人讲得非常好,有兴趣的可以点击: 
    http://blog.csdn.net/luoshengyang/article/details/6575988 
    好处就是对对厂家来说能把商业秘密隐藏起来,我们做驱动实验的话,操作会极其复杂,不过对理解android整个系统都是极其用用的,因为它从下到上涉及到了android系统的硬件驱动层,硬件抽象层,运行时库和应用程序框架层等等。 

    第3种涉及到Android上层读写设备节点,由于我们现在要讲的GPIO口是一个字符设备驱动,也会创建节点,所以可以通过此方法配置

    这里由于客户需要我们的*.so库目前只讲第1种方法的实现:在此之前,请大家了解下JNI的编程方法,JNI是一种为JAVA和C、C++之间能互相访问所提供的编程接口(自行百度了解)

    下面详细讲解开发过程(android系统大同小异),主要分为三部分:1.GPIO口字符设备驱动的实现,2.jni环境搭建及代码编写,3.应用层调用jni代码的实现

    GPIO口字符设备驱动的实现
    关于GPIO口字符设备驱动我们要做如下步骤:
    1  找到原理图对应的GPIO口并配置它为输出管脚
    gpio口配置(不同平台配置不一样)但目的是一样的 设置GPIO口输出,并默认低电平,我们接的是57管脚
    <&range 56 1 0x1500>
    <&range 57 1 0x1500>   代表57管脚做为gpio口使用,并默认低电平    
    这个io口寄存器地址:0xe46002e4


    但是调试过程中发现
    #define gpio_lp 57 
    gpio_request(gpio_lp,"pos_pwr");
    gpio_set_value(gpio_lp,1);

    GPIO调用request报错,导致GPIO不能用但是换个GPIO口后换个gpio口就不报错了,

    这种原因是由于intel的特殊性:gpio在vmm的地方被调用,在kernel下就不能操作它(一般平台这样操作没问题的)

    后续,想到了另外一种方法

    void __iomem *ldo_mmio_base = ioremap(0xe46002e4, 4);

    iowrite32(0x1700, ldo_mmio_base);   //1700代表设置寄存器(0xe46002e4)为GPIO口,输出  为高

    iowrite32(0x1500, ldo_mmio_base);//1500代表设置寄存器(0xe46002e4)为GPIO口,输出 为底  

    实现了IO口的控制

    1.2  源代码我们放到linux-3.10/drivers/char下面让系统生成设备节点:/dev/mypos
       
     linux-3.10依据自己的kernel名字不同而不同

    linux-3.10/drivers/char/lp6252_switch.c    

    
    
    
    
    
    
    #include <linux/module.h>               /* For module specific items */  
    #include <linux/moduleparam.h>          /* For new moduleparam's */  
    #include <linux/types.h>                /* For standard types (like size_t) */  
    #include <linux/errno.h>                /* For the -ENODEV/... values */  
    #include <linux/kernel.h>               /* For printk/panic/... */  
    #include <linux/fs.h>                   /* For file operations */^M  
    #include <linux/ioport.h>               /* For io-port access */  
    #include <linux/platform_device.h>      /* For platform_driver framework */  
    #include <linux/init.h>                 /* For __init/__exit/... */  
    #include <linux/uaccess.h>              /* For copy_to_user/put_user/... */  
    #include <linux/io.h>                   /* For inb/outb/... */  
    #include <linux/gpio.h>  
    #include <linux/device.h>  
    #include <linux/cdev.h>  
    #include <linux/slab.h>               /*kamlloc */  
    //#include <asm-generic/ioctl.h>  
       
     //ioctl 
    #define CMD_FLAG  'i'  
    #define POS_PWR_ON      _IOR(CMD_FLAG,0x00000001,__u32)    
    #define POS_PWR_OFF     _IOR(CMD_FLAG,0x00000000,__u32) 
    #define gpio_lp			57 
      
    static int  major =0;  
    static struct class *pos_class;  
    struct cdev_pos {  
        struct cdev cdev;  
    };   
    struct cdev_pos *pos_dev;  
      
    static int pos_ioctl(struct file* filp,unsigned int cmd,unsigned long argv)  
    {  
        printk(KERN_INFO "entry kernel.... \n");  
    	printk(KERN_INFO "%d\n", POS_PWR_ON);
    	<span style="color:#ff0000;">void __iomem *ldo_mmio_base = ioremap(0xe46002e4, 4);</span>
      
        switch(cmd)  
        {  
            case POS_PWR_ON:  
            {  
    #if 0
                gpio_set_value(gpio_lp,1);  // 
                printk(KERN_INFO "POS on\n"); 
    #endif
    			iowrite32(0x1700, ldo_mmio_base)
                break;  
            }  
            case POS_PWR_OFF:  
            {  
    #if 0
                gpio_set_value(gpio_lp,0);
                printk(KERN_INFO "POS off \n");
    #endif</span>
    			iowrite32(0x1500, ldo_mmio_base);
                break;  
            }  
            default:  
                return -EINVAL;  
        }  
        return 0;  
    }  
      
      
    //open  
    static int pos_open(struct inode* i_node,struct file* filp)  
    {  
        printk(KERN_INFO "taosong open init.... \n");  
        int err; 
    #if 0	
        err = gpio_request(gpio_lp,"pos_pwr");
        if(err<0)  
        {  
            printk(KERN_INFO "gpio request faile \n");  
            return err;  
        }  
        gpio_direction_output(gpio_lp,1);
     #endif
        return 0;  
    }  
      
    //close  
    static void pos_close(struct inode* i_node,struct file* filp)  
    {  
    printk(KERN_INFO "taosong close init \n");
    #if 0  
        gpio_free(gpio_lp); 
    #endif
        return ;  
    }  
      
    /* file operations */  
    struct file_operations fops={  
        .owner  = THIS_MODULE,  
        .open   = pos_open,  
        .unlocked_ioctl = pos_ioctl, 
        .release= pos_close,  
    };  
      
    static int __init pos_init(void)  
    {  
    printk(KERN_INFO "init .... \n");  
        dev_t dev_no;  
        int result,err;  
        err = alloc_chrdev_region(&dev_no,0,1,"my_pos"); //dynamic request device number  
        if(err<0)  
        {  
            printk(KERN_INFO "ERROR\n");  
            return err;  
        }  
        major = MAJOR(dev_no);  
        pos_dev = kmalloc(sizeof(struct cdev_pos),GFP_KERNEL);  
        if(!pos_dev)  
        {  
            result = -ENOMEM;  
            goto fail_malloc;  
        }  
        memset(pos_dev,0,sizeof(pos_dev));  
          
        cdev_init(&pos_dev->cdev,&fops);   
        pos_dev->cdev.owner = THIS_MODULE;  
        result = cdev_add(&pos_dev->cdev,dev_no,1);   
        if(result <0)  
        {   printk(KERN_INFO "error\n");  
            goto fail_add;  
        }  
        pos_class = class_create(THIS_MODULE,"mypos");  //in sys/class create sysfs file  
        device_create(pos_class,NULL,MKDEV(major,0),NULL,"mypos"); //dynamic create device file  /dev/mypos  
        return 0;  
    fail_add:  
        kfree(pos_dev);  
    fail_malloc:  
        unregister_chrdev_region(dev_no,1);  
        return result;  
      
    }  
      
    static void __exit pos_exit(void)  
    {  
        dev_t dev_no=MKDEV(major,0);  
      
        unregister_chrdev_region(dev_no,1);  
        cdev_del(&pos_dev->cdev);  
        kfree(pos_dev);  
        device_destroy(pos_class,dev_no);  
        class_destroy(pos_class);  
          printk(KERN_INFO "exit........ \n");  
    }  
    module_init(pos_init);  
    module_exit(pos_exit);  
    MODULE_AUTHOR("*@*.com");  
    MODULE_DESCRIPTION("control_pos_power");  
    MODULE_LICENSE("GPL"); 


    要让此代码生效编译进去:

    在相应的makefile改过来

    diff --git a/drivers/char/Makefile b/drivers/char/Makefile
    index e562ed5..98e871f 100644
    --- a/drivers/char/Makefile
    +++ b/drivers/char/Makefile
    @@ -2,6 +2,7 @@
     # Makefile for the kernel character device drivers.
     #
     
    +obj-y                          += lp6252_switch.o
     obj-y                          += mem.o random.o
     obj-$(CONFIG_TTY_PRINTK)       += ttyprintk.o
     obj-y                          += misc.o


    这样加入lp6252_switch.c并修改Makefile后固件生成就会在 /dev/ 下生成节点   /dev/mypos

    要让这个节点让别人可读写,还必须修改节点的系统所有者以及他的权限,这个步骤我们一版在 init.rc中进行

    我的代码是system/core/rootdir/init.rc

    diff --git a/core/rootdir/init.rc b/core/rootdir/init.rc
    index fc5d73f..b3095c0 100644
    --- a/core/rootdir/init.rc
    +++ b/core/rootdir/init.rc
    @@ -308,6 +308,8 @@ on boot
     
         chmod 0777 /dev/ttyS1
         chmod 0777 /sys/devices/l68ie_switch/chip_switch
    +    chown system system /dev/mypos
    +    chmod 0766 /dev/mypos
     
         chown system system /sys/devices/system/cpu/cpufreq/interactive/timer_rate
         chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/timer_rate


    至此一个可用的字符设备节点 /dev/mypos 已经生成并能供给上层可读写的权限



    二.jni环境搭建及代码编写

    2.1 NDK本地开发环境搭建

    这里主要介绍在eclipse上搭建NDK开发环境。

    以前做Android的项目要用到NDK就必须要下载NDK,下载安装Cygwin(模拟Linux环境用的),下载CDT(Eclipse C/C++开发插件),还要配置编译器,环境变量...

    麻烦到不想说了,本人在网上查了一下资料,发现了一个超级快配置NDK的办法。

    Step1:到Android官网下载Android的开发工具ADT(Android Development Tool的缩写),该工具集成了最新的ADT和NDK插件以及Eclipse,还有一个最新版本SDK。解压之后就可以用了,非常爽!

    ADT插件:管理Android SDK和相关的开发工具的    

    NDK插件:用于开发Android NDK的插件,ADT版本在20以上,就能安装NDK插件,另外NDK集成了CDT插件

    也可以在线更新ADT、NDK插件,不过速度超级慢...所以果断在网上下载集成开发工具ADT,下载链接见:http://developer.android.com/sdk/index.html   本地百度云地址链接:http://pan.baidu.com/s/1slcVhxF 密码:ucf9

    Step2:到Android官网下载最新的NDK,注:NDK版本在r7(本文使用android-ndk-r9b)以上之后就集成了Cygwin,而且还是十分精简版。比起下载Cygwin要方便多啦!

    下载链接见:http://developer.android.com/tools/sdk/ndk/index.html  

    android-ndk-r9b 百度云地链接:http://pan.baidu.com/s/1hrPGtKC 密码:od7o

    Step3打开Eclipse,点Window->Preferences->Android->NDK,设置NDK路径,例如我的是E:\qf项目20160603\s600\android-ndk-r9b-windows-x86\android-ndk-r9b



    Step4:新建一个Android工程,在工程上右键点击Android Tools->Add Native Support...,然后给我们的.so文件取个名字,例如:poscontrol 

         

    这时候工程就会多一个jni的文件夹,jni下有Android.mk和poscontrol.cpp(我这里改成.c文件了)文件。Android.mk是NDK工程的Makefile,poscontrol.cpp就是NDK的源文件。


    Step5:右键项目工程点击Run as  就会生成libposcontrol.so


    Step6:完成了,然后运行。运行之前先编译NDK,然后在编译JAVA代码。编译也许会遇到Unable to launch cygpath. Is Cygwin on the path?错误,解决办法如下:

    1.工程右键,点Properties->C/C++ Build的Building Settings中去掉Use default build command,然后输入${NDKROOT}/ndk-build.cmd


    2.在C/C++ Build中点击Environment,点Add...添加环境变量NDKROOT,值为NDK的根目录


    3.再编译,问题就解决啦!


    2.2 jni代码编写

    poscontrol.c
    	#include<stdio.h>
    	#include<stdlib.h>
    	#include<fcntl.h>
    	#include<errno.h>
    	#include<unistd.h>
    	#include<sys/ioctl.h>
    	#include<jni.h>  // 一定要包含此文件
    	#include<string.h>
    	#include<sys/types.h>
    	#include<sys/stat.h>
    	#include <android/log.h>
    	//驱动里的命令码.
    	#define CMD_FLAG 'i'
    	#define LED_ON		_IOR(CMD_FLAG,0x00000001,__u32)
    	#define LED_OFF		_IOR(CMD_FLAG,0x00000000,__u32)
    
    	#define DEVICE_NAME "/dev/mypos"
    	int fd;
    
    
    	static const char *TAG="012";
    	#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO,  TAG, fmt, ##args)
    	#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
    	#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)
    
    	/* * Class:     Linuxc
    	* Method:    openled
    	* Signature: ()I
    	*/
    
      JNIEXPORT void JNICALL Java_com_idean_s600_pay_manage_IntelPosPowerManager_posPowerOn(JNIEnv* env, jclass mc)
    	{
    	  LOGI("POWER ON BY QINFENG");
    	  LOGI("LED_ON:%d   LED_OFF:%d",LED_ON,LED_OFF);
    	  fd=open(DEVICE_NAME,O_RDWR);
    	  if(fd<0)
    		  {
    		        LOGI("don't open dev");
    		    }
    		    else
    		        {
    		    	ioctl(fd,LED_ON,NULL) ;
    		    	LOGI("open success");
    		        }
    
    
    	}
    
    	/* * Class:     Linuxc
    	* Method:    clsoeled
    	* Signature: ()V
    	*/
    	JNIEXPORT void JNICALL Java_com_idean_s600_pay_manage_IntelPosPowerManager_posPowerOff(JNIEnv* env, jclass mc)
    	{
    		LOGI("POWER Off BY QINFENG");
    		ioctl(fd,LED_OFF,NULL) ;
    		close(fd);
    
    	}
    
    关于驱动接口的说明:
    #define DEVICE_NAME "/dev/mypos"
    这个使我们驱动生成的节点,我们后面通过
    fd=open(DEVICE_NAME,O_RDWR);
    ioctl(fd,LED_ON,NULL) ;
    调用节点并通过底层相对应的CMD (LED_ON OR LED_OFF)控制节点的高低电平(加密芯片的上电和下电)
    
    
    
    

    主要提供两个接口给上层调用:
    JNICALL Java_com_idean_s600_pay_manage_IntelPosPowerManager_posPowerOn
    Java_com_idean_s600_pay_manage_IntelPosPowerManager_posPowerOff
    
    
    
    
    根据JNI的规则:
    com_idean_s600_pay_manage_IntelPosPowerManager
         包名:package com.idean.s600.pay.manage;
    类名:IntelPosPowerManager
    而posPowerOn  posPowerOff  为上层要调用的本地接口

    在这里特别说明下,JNI的格式规范要注意的地方: 
    1.函数声明的格式: 
      因JNI会把 '_' 转换成' . ' 所以在类名和函数接口中不要出现' _ ',以免应用层调用不到JNI接口,这方面对初学者来说极其重要,所以用eclipse生成的android类文件,最好改下类名。不了解对实验的热情打击比较重。 
    2.JNI函数分本地方法和静态方法。 
      本地方法: 
            public native int jni();  // 不带static 声明. 
      对应的 JNI 函数中参数的定义有改动: 
            Java_xx_xx_LedControl_jni(JNIEnv*env, jobject obj) 
      静态方法: 
             public static native int jni();  // 带static 声明. 
      对应的 JNI 函数中参数的定义有改动: 
            Java_xx_xx_LedControl_jni(JNIEnv*env, jclass cls)
     
    注意 jobject 和jclass的变动。 

    Android.mk代码如下:
    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_LDLIBS    := -lm -llog 
    LOCAL_MODULE    := poscontrol
    LOCAL_SRC_FILES := poscontrol.c
    include $(BUILD_SHARED_LIBRARY)

    注意LOCAL_MODULE    :poscontrl 代表生成的是libposcontrol.so   apk上层调用的库文件必须一致

    回到 (ndk目录路径)/JNI/(应用工程文件)/  路径下   (个人理解可以本地cmd输入命令编译)
    输入命令 : ../../ndk-build 

    会生成 libs 和obj 2个文件。 libposcontrol.so文件放在 libs /armeabi/ 下 

    至此,JNI编译成功,连接上层关于GPIO的字符驱动节点和上层可调用的SO库文件


    三.应用层调用jni代码的实现(apk编写)

    关于这节主要贴一下源代码:

    IntelPosPowerManager.java

    package com.idean.s600.pay.manage;
    
    
    import android.media.AudioManager;
    import android.os.Bundle;
    import android.app.Activity;
    import android.content.Intent;
    import android.util.Log;
    import android.view.Menu;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    
    public class IntelPosPowerManager extends Activity {
    
    	
        private Button power_on;  
        private Button power_off;
    	private OnClickListener mylistener;  
        
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_intel_pos_power_manager);
    		power_on=  (Button)findViewById(R.id.power_on);
    		power_off= (Button)findViewById(R.id.power_off);
    
    		 //成功点击事件
    		power_on.setOnClickListener(new View.OnClickListener() {
    				@Override
    				public void onClick(View v) {
    					// TODO Auto-generated method stub
    					 Log.d("012", "power_on by android\n");  
    		                posPowerOn();  
    
    						}
    			});
    		power_off.setOnClickListener(new View.OnClickListener() {
    			@Override
    			public void onClick(View v) {
    				// TODO Auto-generated method stub
    				 Log.d("012", "power_off by android\n");  
    	                posPowerOff();  
    
    					}
    		});
    	   
    	    
    	
    	}
     
    	
    	protected void onDestroy(){  
            super.onDestroy();  
        }  
    
    
    		
    
    	@Override
    	public boolean onCreateOptionsMenu(Menu menu) {
    		// Inflate the menu; this adds items to the action bar if it is present.
    		getMenuInflater()
    				.inflate(R.menu.activity_intel_pos_power_manager, menu);
    		return true;
    	}
    	 
    	   static {  
    	        try{  
    	            Log.i("012","try to load poscontrol.so");  
    	            System.loadLibrary("poscontrol");    
    	    //加载本地库,也就是JNI生成的libxxx.so文件,下面再说。  
    	        }catch (UnsatisfiedLinkError ule){  
    	            Log.e("012","WARNING: Could not load poscontrol.so");  
    	        }  
    	    }  
    		/**
    		 * 控制金融芯片上电
    		 */
    		public native static void posPowerOn();
    
    		/**
    		 * 控制金融芯片下电
    		 */
    		public native static void posPowerOff();
    
    }
    
    注意的点:
     System.loadLibrary("poscontrol");  poscontrol名字要和.so库对应起来
    本地调用方法:		 
    控制金融芯片上电
    		public native static void posPowerOn();
    控制金融芯片下电
    		public native static void posPowerOff();

    
    

    附录xml文件布局:

    activity_intel_pos_power_manager.xml

    <?xml version="1.0" encoding="utf-8"?>  
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
        android:orientation="vertical"  
        android:layout_width="fill_parent"  
        android:layout_height="fill_parent"  
        >  
    <TextView    
        android:id="@+id/position"  
        android:layout_centerInParent="true"  
        android:layout_width="wrap_content"   
        android:layout_height="wrap_content"   
        android:textSize="25sp"  
        android:textColor="#ff0000"
        android:text=" power control "  
        />  
    <Button   
        android:id="@+id/power_on"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:textSize="18sp"  
        android:text="power_on"  
        android:layout_toLeftOf="@+id/position"  
        android:layout_centerHorizontal="true"  
        android:layout_alignTop="@+id/position"  
        />  
    <Button   
        android:id="@+id/power_off"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:textSize="18sp"  
        android:text="power_off"  
        android:layout_toRightOf="@+id/position"  
        android:layout_alignTop="@+id/position"  
        />  
    </RelativeLayout>  


    至此,从底层GPIO字符设备驱动 到 JNI 库文件实现,到上层apk调用JNI本地接口功能全部实现


    apk下载地址:http://download.csdn.net/detail/qf0727/9665118


    通过按钮 power_on 和 power_off 就能够控制GPIO口的高低电平了




    android驱动学习---led实验参考:http://koliy.iteye.com/blog/1424304  
    Android NDK开发篇(一):新版NDK环境搭建(免Cygwin,超级快)http://www.2cto.com/kf/201404/292918.html

    展开全文
  • 内核层与应用层通信详解

    千次阅读 2019-07-30 08:41:28
    做驱动开发的肯定会遇到应用层与内核层的通信的问题,首先说内核层与应用层的通信可以大概分为两个方面,第一是应用层向内核层主动传递消息,第二是内核层主动与应用层通信。下面我们将分开来谈两个方面。 我们先来...

    做驱动开发的肯定会遇到应用层与内核层的通信的问题,首先说内核层与应用层的通信可以大概分为两个方面,第一是应用层向内核层主动传递消息,第二是内核层主动与应用层通信。下面我们将分开来谈两个方面。

    我们先来看应用层向内核层传递的方法:

     

     

    BOOL DeviceIoControl ( 
    HANDLE hDevice, // 设备句柄 
    DWORD dwIoControlCode, // IOCTL请求操作代码 
    LPVOID lpInBuffer, // 输入缓冲区地址 
    DWORD nInBufferSize, // 输入缓冲区大小 
    LPVOID lpOutBuffer, // 输出缓冲区地址 
    DWORD nOutBufferSize, // 输出缓冲区大小 
    LPDWORD lpBytesReturned, // 存放返回字节数的指针 
    LPOVERLAPPED lpOverlapped // 用于同步操作的Overlapped结构体指针 
    );

     

     

     

     

     

    DeviceIoControl 这个函数是重点中的重点,几乎所有应用层与内核层的通信与此函数有关,发送控制代码直接到指定的设备驱动程序,使相应的移动设备以执行相应的操作。

    驱动程序可以通过CTL_CODE宏来组合定义一个控制码,并在IRP_MJ_DEVICE_CONTROL的实现中进行控制码的操作。在驱动层irpStack->Parameters.DeviceIoControl.IoControlCode表示了这个控制码。

    说到这里我们不得不提的一个东西就是IRP:

     

    IRP(IO请求包)用于win32和驱动程序通讯,NT内核有一个组件叫做IO管理器。IO管理器负责IRP的分发,驱动程序里创建好设备并且创建好符号链接后,Win32就可以加载驱动了。而要让一个驱动可以处理IRP,必需给驱动添加IRP处理例程。

    添加的方法就是再DriverEntry里面对驱动对象DriverObject操作。该参数是一个指针,指向驱动对象,驱动对象内部有一个MajorFunction数组,该数组的类型是
    NTSTATUS  (*PDRIVER_DISPATCH) (IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp) 。这是一个函数指针,指向每个IRP对于的处理例程。最后就是为所有需要处理的IRP实现对应的例程。

    应用层通过DeviceIoControl 下发指令到内核层,内核层又通过对消息头的判断取出缓冲区的数据进行一系列的操作。

    总得来说来说,有DeviceIoControl的存在,应用层向内核层传递消息就是一件轻松加愉快的事情,我简单的贴一点应用层和内核层的代码,大家可以做参考:

    应用层:

     

    	BOOL bOK = ::DeviceIoControl(hAdapter, IOCTL_PTUSERIO_OPEN_ADAPTER, 
    					pszAdapterName, nBufferLength, NULL, 0, &dwBytesReturn, NULL);
    	// 检查结果
    	if(!bOK)
    	{
    		::CloseHandle(hAdapter);
    		return INVALID_HANDLE_VALUE;
    	}
    	return hAdapter;


     

     

    驱动层:

     

    NTSTATUS DevIoControl(PDEVICE_OBJECT pDeviceObject, PIRP pIrp)
    {
    	// 假设失败
    	NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
    
    	// 取得此IRP(pIrp)的I/O堆栈指针
    	PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
    
    	// 取得I/O控制代码
    	ULONG uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
    	// 取得I/O缓冲区指针和它的长度
    	PVOID pIoBuffer = pIrp->AssociatedIrp.SystemBuffer;
    	ULONG uInSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
    	ULONG uOutSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
    	
    
    	ULONG uTransLen = 0;
    
    	DBGPRINT((" DevIoControl... \n"));
    
    	switch(uIoControlCode)
    	{
    	case IOCTL_GET_SHARE_ADD: 

     

    case IOCTL_PTUSERIO_OPEN_ADAPTER: // 根据控制码响应
    		{

    然后驱动就可以从设定好的缓冲区里取数据进行操作。

     

    第二步咱们来聊聊驱动层如何主动向应用层发消息:

    很多情况下,我们需要驱动主动将消息传递给应用层,例如我们写一个网卡驱动,里面过滤传递的数据包,当数据包经过的时候,我们就需要驱动层将消息主动传递给应用层,这时候简单的DeviceIoControl 已经不能实现,那我们可以用共享事件加共享内存的方式来实现。

    原理:通过Ring3创建事件,并将该事件传递给Ring0,同时Ring3创建监控线程,等待Ring0发起事件,此为应用层驱动层共享事件。同时Ring0在内核分配非分页内存,通过DeviceIoControl 传递给Ring3,此为应用层驱动层共享内存。因在DeviceIoControl 中传送Buffer,涉及到内核数据拷贝,大数据量下使用效率很低,故用共享内存的方法。

    应用层代码:

     

    	
        m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);     // 创建事件
    
    	// 发事件给ring 0
    	
    	if (0 == DeviceIoControl(hFile, SET_EVENT, &m_hEvent, sizeof(HANDLE), NULL, 0, &uRetBytes,NULL))
    	{
    
    		CloseHandle(hFile);
    		CloseHandle(m_hEvent);
    		printf("SET_EVENT failed\n");
    		return FALSE;
    	}
    
    	// 获得ring 0 的共享内存
    	PVOID add = NULL;
    	if (0 == DeviceIoControl(hFile, IOCTL_GET_SHARE_ADD, NULL, 0, &add, sizeof (PVOID), &uRetBytes,NULL))
    	{
    		CloseHandle(hFile);
    		CloseHandle(m_hEvent);
    		return FALSE;
    	}
    	m_pShareMem = (PVOID)add;
    	return TRUE;

     

    	while (1)
    	{
    		WaitForSingleObject(m_hEvent, INFINITE);
    		{
    
    <span style="white-space:pre">			</span>//等待事件发生
    		}
    	}


    内核层:

     

     

         g_pSysAdd = ExAllocatePool(NonPagedPool, 2048*3);
    	g_pMdl = IoAllocateMdl(g_pSysAdd, 2048*3, FALSE, FALSE, NULL);
    	MmBuildMdlForNonPagedPool(g_pMdl);//建立共享内存

     

     

    UserAddr = MmMapLockedPagesSpecifyCache(g_pMdl, UserMode, NonPagedPool, NULL, FALSE, NormalPagePriority);
    *((PVOID*)pIoBuffer) = UserAddr;//将驱动建立的内存地址传递给应用
    
     memset(g_pSysAdd,0,2048*3);
        RtlCopyMemory(g_pSysAdd, test, (DataOffset - 54)*2);//将需要的数据写入内存
        KeSetEvent((PKEVENT)g_pEvent, 0,FALSE);//通知应用层可以读了
    

    接下来我们就可以在应用层读取里面的数据了,地址就是我们通过DeviceIocontrol得到的地址。
     

     

    展开全文
  • 应用层协议——原理

    千次阅读 2018-08-30 11:44:34
    因为网络核心设备(路由器、交换机等,不包括端系统设备)并不在应用层上起作用,只在网络层及下面层次起作用,所以不需要为网络核心设备写对应的应用程序,即开发应用程序的时候只需要考虑适配端系统,不需要考虑...

    应用层协议——原理

      应用层协议的实现,只需要写出能够运行在不同的端系统(服务器、手机、电脑等)和通过网络彼此通信的程序。因为网络核心设备(路由器、交换机等,不包括端系统设备)并不在应用层上起作用,只在网络层及下面层次起作用,所以不需要为网络核心设备写对应的应用程序,即开发应用程序的时候只需要考虑适配端系统,不需要考虑网络核心设备。

    网络应用程序体系结构

      目前主流的网络应用程序体系结构有两种:客户-服务器体系结构(client-server architecture)对等体系结构(P2P)

    • 客户-服务器体系结构(client-server architecture):客户-服务器体系结构中,至少有一个打开的主机,被称为服务器,它服务来自其他许多称为客户的主机的请求。web应用程序就是一个典型的例子,他总是有至少一个web服务器在运行来响应浏览器的请求。客户-服务器体系结构的一个特征就是服务器具有固定且被知晓的IP地址。
    • 对等体系结构(P2P):P2P体系结构对位于数据中心的专用服务器有最小的(或者没有)依赖。应用程序在间断连接的主机对之间使用直接通信,这些主机对被称为对等方。这些对等方并不为服务提供商所有,为用户控制的台式机、笔记本等所有。因为这种对等方通信不必通过专门的服务器,该体系被称为对等方到对等方

    进程通信

    进程的定义

      在操作系统中,进行通信的实际上是进程(process)而不是程序。一个进程可以被认为是运行在端系统中的一个程序。
      两个不同端系统上的进程,通过跨越计算机网络交换报文而相互通信。发送进程生成并向网络中发送报文;接收进程接收这些报文并可能通过报文发送回去进行响应。
      每对通信进程,我们通常将这两个进程之一标识为客户(client),另一个进程标识为服务器(server)。P2P文件共享的某些应用中,一个进程能够既是客户又是服务器。所以我们可以这样定义客户和服务器进程:在给定的一对进程之间的通信回话场景中,发起通信(即在该会话开始时发起与其他进程的联系)的进程被标识为客户,在会话开始时等待联系的进程是服务器

    进程与计算机网络直接的接口

      进程通过一个称为套接字(socket)的软件接口向网络发送报文和从网络接收报文。套接字是同一台主机内应用层与运输层之间的接口,在发送端的应用程序将报文推进套接字,在该套接字的另一侧,运输层协议负责是该报文进入接收进程的套接字。由于该套接字是建立网络应用程序的可编程接口,因此套接字也称为应用程序和网络之间的应用程序编程接口(Application Programming Interface, API)。应用程序开发者可以控制套接字在应用层的一切,但对改套接字的运输层端几乎没有控制权。开发者对运输层的控制仅限于:①选择运输层协议;②也许能设定几个运输层参数,如最大缓存和最大报文段长度等。一旦开发者选择了一个运输层协议,则应用程序就建立在由该协议提供的运输层服务上。

    进程寻址

      在一台主机上运行的进程为了向在另一台主机上运行的进程发送分组,接收进程需要有一个地址。为了标识改接收进程,需要定义两种信息:①主机的地址;②定义在目的主机中的接收进程的标识符。
      在因特网中,主机由其IP地址(IP address)标识。IP地址是一个32比特的量且能够唯一地标识主机。因为一台主机能够运行多个网络应用,发送报文时,发送进程除了要知道目的地的主机地址外,还需要指定运行在接收主机上的接收进程(接收套接字)。目前比较流行的端口有:Web服务器的80端口、SMTP的25端口等。

    运输服务

    可供应用程序使用的运输服务

      网络中运输层的协议不止一种,开发应用时需要根据需求选择相对应的运输层协议。根据对运输层服务的要求,可以将运输层服务大体分为四类:可靠数据传输吞吐量定时安全性

    可靠数据传输

      有时候数据丢失可能会造成灾难性的后果,所以必须做一些工作以确保由应用程序的一端发送的数据正确、完全地交付给该应用程序的另一端。如果一个协议提供了这样的确保数据交付服务,就认为提供了可靠数据传输(reliable data transfer)。当运输协议提供这种服务时,发送进程只要将其数据传递进套接字,就可以完全相信该数据将能无差错地到达接收进程。
      此外,某些进程不能提供可靠数据传输,由发送进程发送的某些数据可能不能够到达接收进程。这种运输层协议一般用于多媒体应用,如音频、视频等。这些应用能够承受一定量的数据丢失,却并不致命。

    吞吐量

      在沿着一条网络路径上的两个进程之间的通信会话场景中,可用吞吐量就是发送进程能够向接收进程交付的比特速率。因为其他会话将共享沿着该网络路径的带宽,并且因为这些会话将会到达和离开,该可用吞吐量将随时间波动。这就要求运输层协议能够以某种特定的速率提供确保的可用吞吐量,及吞吐量服务。使用这种服务,该应用程序能够请求r比特/秒的确保吞吐量,并且该运输协议能够确保可用吞吐量总是至少为r比特/秒。

    定时

      运输层协议能提供定时保证,如发送方注入进套接字中的每个比特到达接收方的套接字不迟于100ms。这种服务队交互式实时应用程序具有很大的吸引力,如网络电话、网络交互游戏等,这些应用为了有效性而要求数据交付有严格的时间限制。

    安全性

      运输协议能够为应用程序提供一种或多种安全性服务。例如,在发送主机中,运输协议能够加密由发送进程传输的所有数据,在接收主机中,运输层协议能够在数据交付给接收进程之前解密这些数据。运输协议还能提供机密性以外的其他安全性服务,包括数据完整性和端点鉴别。

    因特网提供的运输服务

      因特网(更一般的是TCP/IP网络)为应用程序提供两个运输层协议,即UDPTCP。当为因特网创建一个新的应用时,受限要做出的决定是选择UDP还是TCP。每个协议为调用它们的应用程序提供了不同的服务集合。下表为一些应用程序的服务要求。

    应用 数据丢失 带宽 时间敏感
    文件传输 不能丢失 弹性
    电子邮件 不能丢失 弹性
    Web文档 不能丢失 单行(几kbps)
    因特网电话/视频会议 容忍丢失 音频(几kbps~1Mbps)、视频(10kbps~5Mbps) 是,100ms
    存储音频/视频 容忍丢失 同上 是,几秒
    交互式游戏 容忍丢视 几kbps~10kbps 是,100ms
    即时讯息 不能丢失 弹性 是和不是

    TCP服务

      TCP服务模型包括面向连接服务和可靠数据传输服务。当某个应用程序调用TCP作为运输协议时,该应用程序就能获得来自TCP的两种服务。

    • 面向连接的服务:在应用层数据报文开始流动之前,TCP让客户和服务器互相交换运输层控制信息。这个所谓的握手过程提示客户和服务器,使它们为大量分组的到来做好准备。在握手阶段后,一个TCP连接就在两个进程的套接字之间建立了。这条连接是全双工的,即连接双方的进程可以在此连接上同时进行报文的收发。当应用程序结束报文发送时,必须拆除该连接。
    • 可靠的数据传送服务:通信进程能够依靠TCP,无差错、按适当顺序交付所有发送的数据。当应用程序的一端将字节流传进套接字时,它能够依靠TCP将相同的字节流交付给接收方的套接字,而没有字节的丢失和冗余。

        TCP协议还具有拥塞控制机制,这种服务不一定能为通信进程带来直接好处,但能为因特网带来整体好处。当发送方和接收方之间的网络出现拥塞时,TCP的拥塞控制机制会抑制发送进程(客户或服务器)。

    UDP服务

      UDP是一种不提供不必要服务的轻量级运输协议,它仅提供最小服务。UDP是无连接的,因此在两个进程通信前没有握手过程。UDP协议提供一种不可靠数据传送服务,也就是说,当进程将一个报文发送进UDP套接字时,UDP协议并不保证该报文将到达接收进程。不仅如此,达到接收进程的报文也可能是乱序到达的。
      UDP没有包括拥塞控制机制,所以UDP的发送端可以用它选定的任何速率向其下层(网络层)注入数据。

      下表指出了一些流行的因特网应用所使用的运输协议:

    应用 应用层协议 支撑的运输协议
    电子邮件 SMTP [RFC 5321] TCP
    远程终端访问 Telnet [RFC 854] TCP
    Web HTTP [RFC 2616] TCP
    文件传输 FTP [RFC 959] TCP
    流式多媒体 HTTP (如 YouTube) TCP
    因特网电话 SIP [RFC 3261]、RTP [RFC 3550]或专用的(如 Skype) UDP 或 TCP

    因特网运输协议所不提供的服务

      运输层协议服务有可靠数据传输吞吐量定时安全性4个方面的服务。TCP提供了可靠的端到端数据传送,并且TCP在应用层可以很容易地用SSL来加强已提供安全服务。但是,TCP却没有提供吞吐量服务和定时服务,或者说因特网运输协议没有提供这两种服务。

    应用层协议定义

      应用层协议(application-layer protocol)定义了运行在不同端系统上的应用程序进程如何相互传递报文。主要有以下的定义:

    • 交换的报文类型,例如请求报文和响应报文
    • 各种报文类型的语法,如报文中的各个字段及这些字段是如何描述的
    • 字段的语义,即这些字段中包含的信息的含义
    • 一个进程何时以及如何发送报文,对报文进行响应的规则
    展开全文
  • 本文将介绍网络连接建立的过程、收发包流程,以及其中应用层、tcp层、ip层、设备层和驱动层各层发挥的作用。
  • 应用层到驱动层

    千次阅读 2016-12-04 21:51:29
    应用层通过open打开一个设备文件时,在VFS层会建立inode结构体和file结构体,前者是静态的描述设备的一些信息(如:设备号,节点指针,设备类型以及cdev结构体),后者则会描述设备类型的一些动态信息(如:文件...
  • OSI第七层:应用层功能及介绍

    千次阅读 多人点赞 2019-01-16 18:59:55
    OSI应用层功能:应用层提供各种各样的应用层协议,这些协议嵌入在各种我们使用的应用程序中,为用户与网络之间提供一个打交道的接口。 OSI应用层的作用 当我们第一次学习网络,对网络的概念不会很直观,我们使用...
  • 1、OSI的相应层介绍:应用层:为用户应用程序提供服务并支持网络访问。 表示层:将数据转化为与平台无关的格式,并进行加密和压缩处理 会话层:负责管理联网计算机上的应用程序之间的通信,提供一些传输层不具备、...
  • Linux应用层读写i2c从设备(eeprom)

    千次阅读 2018-06-26 11:26:35
    /**********************************... i2c读函数,参数1:从设备地址,参数2:寄存器地址,参数3:读取数据缓冲区,参数4:读取数据大小 *********************************************************************...
  • 应用层前向纠错

    千次阅读 2016-06-02 17:50:56
     应用层,也有应用层的FEC,即对信源进行FEC编码 应用层前向纠错  基于以上这些缺点,使用这几种差错控制技术并不能保证IPTV业务的视频传输质量,但如果在IP网络上采用端到端的应用层可靠性...
  • OSI七层协议在网络传输中扮演的角色及功能:7、应用层——–电脑的各种数据6、表示层 ——– 处理用户信息的表示问题,如编码、数据格式转换和加密解密5、会话层——–会话管理、会话流量控制、寻址、寻址4、传输层...
  • 计算机网络:应用层

    千次阅读 2016-11-21 16:11:47
    应用层介绍应用层为用户提供服务,通信是由逻辑连接提供的,这意味着两个应用层假设存在一个假想的直接连接。标准与非标准由于应用层是唯一向因特网用户提供服务的层次,所以新的应用协议能够轻松地加入因特网。应用...
  • 蓝牙应用层协议介绍

    千次阅读 2019-03-02 15:54:21
    蓝牙应用层协议介绍 本文主要简要介绍如下内容: 蓝牙术语 GAP SDAP SPP GOEP HFP DUN HSP A2DP AVRCP 未完待续。。。 一、蓝牙术语: 1,蓝牙用户接口(UI):蓝牙操作界面 2,蓝牙设备名称:蓝牙...
  • 应用层和传输层的关系

    千次阅读 2013-11-24 19:56:10
    应用层协议代表着服务器上的服务,服务器上的服务如果对客户端提供服务,必须在TCP或UDP端口侦听客户端的请求。 2.3.1 应用层协议和传输层协议的关系 传输层的协议TCP或UDP加上端口就可以标识一个应用层协议,TCP...
  • TCP应用层主要协议

    千次阅读 2015-09-27 21:51:05
    TCP/IP应用层对应了OSI参考模型的上三层(会话层、表示层和应用层),它包括了一些服务。 这些服务是与终端用户相关的认证、数据处理及压缩,应用层还要告诉传输层哪个数据流 是由哪个应用程序发出的。应用层主要...
  • 应用层常见的协议及对应的端口号

    万次阅读 2016-04-10 21:51:36
    在TCP/IP模型中,应用层是最高层。应用层包括所有的高层协议,并且不断有新的协议加入。TCP/IP参考模型中应用层和下层协议之间的关系如下:应用层的许多协议都是基于(C/S client /server)方式的。下面为我们就图片...
  • 应用层与驱动层通信DeviceIoControl

    千次阅读 2017-01-12 18:15:32
    驱动层与应用层通信是通过DeviceIoControl, 首先驱动层要实现: pDriverObject->DriverUnload = MyDriverUnload; pDriverObject->MajorFunction[IRP_MJ_CREATE] = MyCreate; pDriverObject->MajorFunction[IRP_...
  • 多通道协议与应用层网关

    千次阅读 2017-03-20 20:56:46
    多通道协议在整个协议传输过程中会协商建立多个网络连接(通道)传输数据或信令,这样的网络协议有FTP、H.323等常用的网络协议。这些协议在局限在一个网络中工作不会出现问题...因此需要应用层网关,解决跨网连接问题。
  • 应用层(用户模式)I2C驱动支持

    千次阅读 2015-02-08 17:22:19
    应用层(用户模式)I2C驱动支持Linux下I2C设备控制方式分为driver层和应用层两种方式。driver层实现I2C设备控制要求了解I2C机制,调试和调用不方便。Linux还提供了应用层方式(ioctl)控制,这里简单介绍如何配置内核...
  • OpenWRT(八)应用层开发

    万次阅读 2017-01-29 19:05:34
    一、应用层和kernel层很多人都是学习完单片机后才开始学嵌入式Linux的,刚开始学时都不能理解为什么写两个程序才能点亮LED,以前单片机只要写一个程序进去LED就可以控制了啊?这就是操作系统带来的分层思想。其实...
  • 常见的应用层协议

    千次阅读 2018-09-28 17:13:09
    动态主机配置协议,是一个应用层协议,使用UDP协议工作。当我们将客户主机ip地址设置为动态获取方式时,DHCP服务器就会根据DHCP协议给客户端分配IP,使得客户机能够利用这个IP上网。 DHCP操作 1.寻找DHCP Server ...
  • 应用层常见协议及端口号

    千次阅读 2018-03-19 17:27:05
    在TCP/IP模型中,应用层是最高层。应用层包括所有的高层协议,并且不断有新的协议加入。 TCP/IP参考模型中应用层和下层协议之间的关系如下: 应用层的许多协议都是基于(C/S client /server)方式的。 下面为我们...
  • 首先为了理解安卓系统是怎么工作的,就先来看一下android的系统架构,其架构大至可以分为四层:linux内核层、系统运行库层、应用框架层和应用层,那就先说说这几层。 1.linux内核层: Android系统...
  • Android的四个应用层

    千次阅读 2015-11-12 19:01:56
    设备驱动程序(Linux Kernel):该主要包括:驱动、内存管理、进程管理、网络协议等组件。执行c/c++函数库(Libraries)及Android Runtime: Java不能直接访问硬件,要访问硬件,必须使用NDK。NDK是由c/...
  • 层设备与三层设备的区别

    千次阅读 2016-04-02 10:58:49
    1、计算机网络  学过计算机网络的人都知道,网络是分层次的。执行流程与邮局非常类似。例如从省寄信到某个乡村,邮局顺序一次是省邮局、市...而目前有些网络控制设备则工作在应用层,这种设备用于对应用层的报文进行
  • 嵌入式应用层开发要学习什么

    千次阅读 2016-12-20 16:48:22
    如果从应用层的应用开发出发,建议先熟悉API函数,推荐《UNIX环境高级编程》,现在应该是第3版了,如果从应用层的界面开发出发,则建议学习Qt,WinCE或Android等。 但真实的嵌入式开发,熟悉以上还是远远不够的,...
  • 常见应用层协议端口号及简介

    万次阅读 2018-07-04 16:36:12
    常见应用层协议 我们都知道,在TCP/IP模型中,应用层是最高层,应用层包括所有的高层协议,并且不断有新的协议加入。 我们先看一下其常用协议的端口号: 常用服务 协议 端口号 POP3 TCP 110 IMAP ...
  • 驱动层和应用层的明确分工

    千次阅读 2007-09-30 09:36:00
    ATM的外围设备包括读卡器,密码键盘,流水打印机等, 通过串口和ATM的主机交互, 实现主机和外围设备的通讯功能的程序叫"驱动库", 而用户看到的ATM机器的屏幕里的软件是应用层. 应用层调用驱动库来实现ATM的诸多功能....
  • 1. 概述 驱动是没有界面的,那么我们的...为了能与应用层进行通信,我们需要创建一个虚拟的设备,然后创建一个符号与之关联,这些工作做完后,就可以将要与应用层交互的数据放置到设备的I/O堆栈中,供应用层进行...
  • 应用层协议---HTTP协议简单认识

    千次阅读 2019-01-14 11:15:35
    应用层 TCP/IP五层参考模型: 物理:光电信号的传输,以太网协议,集电器 链路:相邻设备之间的数据帧传输,以太网协议,交换机 网络 :地址管理和路由选择 传输 :端与端之间的数据传输,TCP/UDP 应用:应用...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 490,448
精华内容 196,179
关键字:

属于应用层的设备