2019-03-09 20:04:07 themagickeyjianan 阅读数 232
  • Android底层技术:Java层系统服务(Android Service)

    为什么我们需要学习撰写系统服务呢? 底层系统服务是Android 框架裡接近Linux/Driver 的部分。为了充分发挥硬件设备的差異化特性,系统服务是让上层Java 应用程序來使用Driver/HW Device 特色的重要管道。于此,先仔细解析Java层系统服务的架构,然后阐述这系统服务的代码开发要点,以及其API设计方法。

    32648 人正在学习 去看看 高煥堂

背景:上大学时,学习操作系统完全不知道这门课到底用来干什么,毕业工作到今天第5年,学习游戏服务器时,才理解到大学操作系统课程如此重要,一些核心概念不理解,无法继续深入学习。下面是一些核心概念:

1)多进程

2)异步io

3)文件、管道、socket

4)tcp协议栈-->抽象为文件句柄-->socket-->一个端口多个客户端连接:

之所以服务器一个端口可以连接多个客户端,就是因为操作系统把一个个连接抽象为文件句柄。

5)redis单线程高并发的原理: 

作为初级理解,认为单线程怎么可能做到高并发,其实redis就是单线程,那是因为redis处理的时候,操作很单一,如果一次操作执行一个算法需要1s,那么神仙也做不到高并发,这样一个1s的耗时过程将会阻塞住主线程,无法高并发;

所以需要把每个请求处理最简单,则自然高并发,因此单线程程序,所以需要把这个1s的耗时操作放到工作队列中处理,这1s的耗时操作不要让它阻塞主线程,像node.js等使用起来对用户来说是单线程,如操作一个数据库,那么必然数据库这操作是放到线程池等处理的,处理完,利用线程或者进程间通信,告诉主线程结果即可。

6)多线程的问题:

比如一个房间的游戏,多线程很难写逻辑。

7)moba服务器:

一个是游戏主线程,打开数据库等操作按道理肯定阻塞主线程,所以使用工作队列处理mysql,有结果再返回。

8)操作系统基本调度原理搞明白就可以了:

说白了,就是把耗时操作从主线程挪开。

9)多线程服务器skynet:

线程的调度和事件通知都是由成本的,设计门槛高时,使用门槛低;

skynet思想就一个:线程池驱动actor,每个房间游戏,一个actor,所以这个房间不会卡主另外一个房间;

最重要的:调度是动态的。

10)多进程利用多核资源原理:

根据cpu核心,启动线程,发挥核心优势;

进程和线程, os会调度cpu核心去执行它们,并不断切换,多个cpu核心一次调度一个进程 或 多个线程,所以多进程可以发挥多核优势;

只不过多线程是系统级别,多进程可以分布式部署多个机器。

11)讲课和做项目:

开始必然糟糕,但是 以后肯定越来越好,每个接口怎么写不是重点,因为一个项目肯定有些地方不尽如人意,项目可控稳定即可,我们应该追求的是:代码问题,我们能迅速找出;

不断学习不断尝试,看书,搞明白基本原理。不要多想很多概念。

12)协程的实现

 

 

 

2012-07-30 22:37:52 linmars24 阅读数 3677
  • Android底层技术:Java层系统服务(Android Service)

    为什么我们需要学习撰写系统服务呢? 底层系统服务是Android 框架裡接近Linux/Driver 的部分。为了充分发挥硬件设备的差異化特性,系统服务是让上层Java 应用程序來使用Driver/HW Device 特色的重要管道。于此,先仔细解析Java层系统服务的架构,然后阐述这系统服务的代码开发要点,以及其API设计方法。

    32648 人正在学习 去看看 高煥堂

       作业用户在一次解决或是一个事务处理过程中要求计算机系统所做的工作的集合,它包括用户程序、所需要的数据集控制命令等。作业是由一系列有序的步骤组成的。在执行一个作业可能会运行多个不同的进程。

          进程:程序在一个数据集上的一次运行过程。是操作系统资源分配的基本单位。

          线程:是进程中的一个实体,是被操作系统独立调度和执行的基本单位。一个进程包含一个或多个线程。

           线程特征

1、线程的执行状态包括运行、就绪和等待。

2、进程中的所有线程共享所属进程内的主存和其他资源。

3、拥有自己的线程控制块和执行栈,寄存器。

           进程和线程的区别

1、进程间是独立的,在内存空间、上下文环境上。而线程是运行在进程空间内的,同一进程所产生的线程共享同一内存空间。

2、进程间是可以并发执行的,线程之间也可以并发执行。但同一进程中的两端代码只有在引入线程的情况下才能并发执行。

3、线程是属于进程的,当进程退出时,该进程所产生的线程都会被强制退出并清除。

4、线程占用的资源要少于进程占用的资源,线程间的切换速度比进程间的切换快的多。

        管程:是定义了一个数据结构和在该数据结构上能为并发进程所执行的一组操作。这些操作能同步进程和改变管程中的数据。它是一种进程同步机制。在结构上类似于面向对象中的类。在功能上和信号量和p,v操作类似。可以更方便的管理系统的临界资源。

         管程特征:

1、模块化:一个管程就是一个可单独编译的实体,结构上和类相仿。

2、抽象数据类型。

3、信息隐蔽。

        管程要素:

1、安全性:管程中的数据变量在管程之外是不可见的,只能有该管程的操作过程存取。

2、互斥性:任一时刻只能有一个调用者进入管程。

3、等待机制:设置等待队列及相应的操作,对资源进行管理。

       管程和上面三个名词,区别大,但是容易混淆。

        管道:是一种进程通信机制。是共享文件模式,它基于文件系统,在两个进程之间,以先进先出的方式实现消息的单向传送。管道是一种特殊文件。


2019-11-03 23:34:23 weixin_43876206 阅读数 106
  • Android底层技术:Java层系统服务(Android Service)

    为什么我们需要学习撰写系统服务呢? 底层系统服务是Android 框架裡接近Linux/Driver 的部分。为了充分发挥硬件设备的差異化特性,系统服务是让上层Java 应用程序來使用Driver/HW Device 特色的重要管道。于此,先仔细解析Java层系统服务的架构,然后阐述这系统服务的代码开发要点,以及其API设计方法。

    32648 人正在学习 去看看 高煥堂

进程间的五种通信方式详解之——管道

进程间的通信方式有以下几种:

  • 管道
  • 消息队列
  • 共享内存
  • 信号量
  • Socke
  • 信号
  • 文件锁

下面就来详细讲解其中一种通信方式——通过管道通信

1、管道

不同的进程可以通过“管道”进行通信,管道具体分为以下两种:

1. 1 匿名管道

1.1.1匿名管道的概念
       所谓“匿名管道”就是在内核中申请一块固定大小的缓冲区,程序拥有写入和读取的权利。
       匿名管道用于进程之间通信,且仅限于本地父子进程之间通信,一般使用fork函数实现父子进程的通信,结构简单,实现容易,类似于一根非水平状态的水管,一端进水另一端出水。

1.1.2 匿名管道的特点:
   ①只提供单向通信,也就是说,两个进程都能访问这个文件,假设进程1往文件内写东西,那么进程2 就只能读取文件的内容。
  ②只能用于具有血缘关系的进程间通信,通常用于父子进程建通信
  ③管道是基于字节流来通信的
  ④依赖于文件系统,它的生命周期随进程的结束结束(随进程)
  ⑤其本身自带同步互斥效果
  
1.1.3 匿名管道的应用:
   匿名管道主要用于本地父进程和子进程之间的通信,在父进程中的话,首先是要创建一个匿名管道,在创建匿名管道成功后,可以获取到对这个匿名管道的读写句柄,然后父进程就可以向这个匿名管道中写入数据和读取数据了,但是如果要实现的是父子进程通信的话,那么还必须在父进程中创建一个子进程,同时,这个子进程必须能够继承和使用父进程的一些公开的句柄,为什么呢?因为在子进程中必须要使用父进程创建的匿名管道的读写句柄,通过这个匿名管道才能实现父子进程的通信,所以必须继承父进程的公开句柄。同时在创建子进程的时候,必须将子进程的标准输入句柄设置为父进程中创建匿名管道时得到的读管道句柄,将子进程的标准输出句柄设置为父进程中创建匿名管道时得到的写管道句柄。然后在子进程就可以读写匿名管道了。

1.1.4 匿名管道代码举例:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>

#define BUFFER_SIZE 25
#define READ_END	0
#define WRITE_END	1

int main(void)
{
	char write_msg[BUFFER_SIZE] = "Greetings";
	char read_msg[BUFFER_SIZE];
	pid_t pid;
	int fd[2];

	/* create the pipe */
	if (pipe(fd) == -1) {
		fprintf(stderr,"Pipe failed");
		return 1;
	}

	/* now fork a child process */
	pid = fork();

	if (pid < 0) {
		fprintf(stderr, "Fork failed");
		return 1;
	}

	if (pid > 0) {  /* parent process */
		/* close the unused end of the pipe */
		close(fd[READ_END]);

		/* write to the pipe */
		write(fd[WRITE_END], write_msg, strlen(write_msg)+1); 

		/* close the write end of the pipe */
		close(fd[WRITE_END]);
	}
	else { /* child process */
		/* close the unused end of the pipe */
		close(fd[WRITE_END]);

		/* read from the pipe */
		read(fd[READ_END], read_msg, BUFFER_SIZE);
		printf("child read %s\n",read_msg);

		/* close the write end of the pipe */
		close(fd[READ_END]);
	}

	return 0;
}
1.2 命名管道

1.2.1 命名管道概念:
       所谓“命名管道”就是在内核中申请一块固定大小的缓冲区,程序拥有写入和读取的权利,没有血缘关系的进程也可以进程间通信。
       命名管道可在同一台计算机的不同进程之间或在跨越一个网络的不同计算机的不同进程之间,支持可靠的、单向或双向的数据通信。
       命名管道(NamedPipe)是服务器进程和一个或多个客户进程之间通信的单向或双向管道。不同于匿名管道的是:命名管道可以在不相关的进程之间和不同计算机之间使用,服务器建立命名管道时给它指定一个名字,任何进程都可以通过该名字打开管道的另一端,根据给定的权限和服务器进程通信。

1.2.2 命名管道的特点:

  1. 命名管道可以在无关的进程之间交换数据,进行通信。(这一点与匿名管道不同)
  2. 命名管道有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。

1.2.3 命名管道的作用:
       在计算机编程里,命名管道是一种从一个进程到另一个进程用内核对象来进行信息传输。和一般的管道不同,命名管道可以被不同进程以不同的方式方法调用(可以跨权限、跨语言、跨平台)。只要程序知道命名管道的名字,发送到命名管道里的信息可以被一切拥有指定授权的程序读取,但对不具有制定授权的。命名管道是一种FIFO(先进先出,First-In First-Out)对象。

1.2.4 命名管道的代码举例:
pip_write.c

以下代码是通过命名管道进行写操作。

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>

#define FIFO_NAME "test_fifo"
#define BUFFER_SIZE (512)

#define TESTF_IN "test.dat"
#define OP_LEN (100)

FILE* fp_in;

int main(char argc, char* argv[])
{
    int pipe_fd;
    int res;
    int open_mode = O_WRONLY;
    int bytes_sent = 0;
    int bytes_read = 0;
    char buffer[BUFFER_SIZE + 1];

    if (access(FIFO_NAME, F_OK) == -1)
    {
        res = mkfifo(FIFO_NAME, 0777);
        if (res != 0)
        {
            fprintf(stderr, "Could not create fifo %s\n", FIFO_NAME);
            exit(EXIT_FAILURE);
        }
    }

    printf("Process %d opening FIFO O_WRONLY\n", getpid());
    pipe_fd = open(FIFO_NAME, open_mode);
    printf("Process %d result %d\n", getpid(), pipe_fd);


    if (pipe_fd != -1)
    {

        if ((fp_in = fopen(TESTF_IN, "r")) < 0)
        {
         fprintf(stderr, "Open input file failed:%s\n", TESTF_IN);
            exit(EXIT_FAILURE);
        }

        while ((bytes_read = fread(buffer, sizeof(char), OP_LEN, fp_in)))
        {
            printf("PRODUCE: %d, %s", bytes_read, buffer);

            res = write(pipe_fd, buffer, bytes_read);
            if (res == -1)
            {
                fprintf(stderr, "Write error on pipe\n");
                exit(EXIT_FAILURE);
            }    
            bytes_sent += res;        

            memset(buffer, 0, BUFFER_SIZE);
            if (feof(fp_in) != 0)
            {
                printf("read over\n");
                break;
            }
        }

        (void)close(pipe_fd);
        fclose(fp_in);
    }
    else
    {
        exit(EXIT_FAILURE);
    }

    return 0;
}

pip_read.c

以下代码是通过命名管道进行读操作。

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>


#define FIFO_NAME "test_fifo"
#define BUFFER_SIZE (512)
#define OP_LEN (100)

int main(char argc, char* argv[])
{
    int pipe_fd;
    int res;
    int open_mode = O_RDONLY;
    char buffer[BUFFER_SIZE + 1];
    int bytes_read = 0;

    memset(buffer, '\0', sizeof(buffer));

    printf("Process %d opening FIFO O_RDONLY\n", getpid());
    pipe_fd = open(FIFO_NAME, open_mode);
    printf("Process %d result %d\n", getpid(), pipe_fd);

    if (pipe_fd != -1)
    {
        do
        {
            res = read(pipe_fd, buffer, OP_LEN);
            printf("CONSUME: %d, %s\n", res, buffer);
            bytes_read += res;

            memset(buffer, 0, BUFFER_SIZE);

            if (res < OP_LEN)
            {
                printf("read over\n");
                break;
            }
        } while (res > 0);
        (void)close(pipe_fd);
    }
    else
    {
        exit(EXIT_FAILURE);
    }

    printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);
    exit(EXIT_SUCCESS);
    return 0;
}

以上是进程间的五种通信方式详解之——管道方式的详解
对于另外几种进程间的通信方式会后续更新,敬请期待… …

在这里插入图片描述

【Java Web】相关技术文章:
【Java Web总结】Java Web项目中 的.classpath、.mymetadata、.project文件作用
【Java Web问题解决】Tomcat报错javax.servlet.ServletException: Error instantiating servlet class.报错404
【比较】什么是“服务器端跳转”“客户端跳转”,二者有什么区别?
【总结】表单提交的get和post有什么不同?
【Java Web问题解决】Tomcat报错:java.lang.ClassCastException: cannot be cast to javax.servlet.Filter解决办法
【Java Web问题解决】Filter过滤器初始化方法init()执行了两次原因及解决方法
【总结】Java Web 中的4种属性范围(page、request、session、application)
【Java Web问题解决】Tomcat报错:java.sql.SQLException: No suitable driver found for jdbc:mysql://
【Java Web问题解决】Tomcat启动时控制台出现中文乱码的问题解决方法
【示例项目】java实现通过身份证号码判断籍贯所在地区
【总结】HTTP协议中的状态码(200、403、404、500等)
【Java Web问题解决】提交表单后显示乱码原因及解决办法
【Java Web总结】JSP页面的生命周期详解
【Java Web总结】JSP页面实现类详解
【Java Web 问题解决】Tomcat启动失败 报错:Server Tomcat v9.0 Server at localhost failed to start.
【Java Web问题解决】连接数据库出错:java.sql.SQLException: No suitable driver found for jdbc:mysql://localhost:3306/
【Java Web问题解决】使用过滤器Filter解决提交表单后显示乱码问题
【Java Web问题解决】过滤器Filter进行编码过滤后页面空白、显示不了原因及解决办法

【Linux 操作系统】相关技术文章:
【Linux问题解决】Ubuntu Linux 安装gcc4.9 g++4.9报错“没有可供安装的候选者”解决办法
【Linux教程】Ubuntu Linux 更换源教程
【Linux教程】如何实现在Ubuntu Linux和windows之间复制粘贴、拖拽复制文件?
【Linux问题解决】操作系统用C语言多线程编程 对‘pthread_create’未定义的引用 报错解决办法
【Linux教程】Linux中用C语言多线程编程之pthread_join()函数
【Linux操作系统、C语言】在Linux中用C语言进行OpenMP并行程序设计之常见指令、库函数和指令总结
【VMware 虚拟机问题解决】VMware Workstation pr无法在Windows上运行的解决方案
【Linux 问题解决】Ubuntu执行apt-get命令报错:无法获得锁 /var/lib/dpkg/lock…解决方案

【Python】相关技术文章:
【总结】Python与C语言、Java等语言基本语法的不同点
【总结】分析Python中的循环技巧
【总结】Python语言是编译型语言还是解释型语言?(Python程序执行过程)
【总结】Python2 和 Python3 的区别
利用Python一层循环打印 * 型三角形
【总结】Python与C语言、Java等语言基本语法的不同点
【总结】你知道吗?——元组其实是可变的序列!
【Python爬虫教程】Python爬虫基本流程及相关技术支持
【Python问题解决】PyCharm中debug报错:using cython not found. pydev debugger: process 13108 is connecting原因及解决
【Python总结】闭包及其应用

【IntelliJ IDEA教程】相关技术文章:
【Intellij IDEA教程】怎么自动清除无效的import导入包、清除无效的import导入包的快捷键
【IntelliJ IDEA教程】在IntelliJ IDEA启动项目 Warning:java: 源值1.5已过时, 将在未来所有发行版中删除 解决办法
【IntelliJ IDEA教程】提示信息Unmapped Spring configuration files found.Please configure Spring facet. 解决办法
【IntelliJ IDEA教程】怎么取消IntelliJ IDEA对单词拼写的检查

【Jupyter Notebook教程】相关技术文章:
【Python教程】Jupyter Notebook把一段很长的代码分成多行的解决办法】

2018-04-17 00:07:37 bian_cheng_ru_men 阅读数 96
  • Android底层技术:Java层系统服务(Android Service)

    为什么我们需要学习撰写系统服务呢? 底层系统服务是Android 框架裡接近Linux/Driver 的部分。为了充分发挥硬件设备的差異化特性,系统服务是让上层Java 应用程序來使用Driver/HW Device 特色的重要管道。于此,先仔细解析Java层系统服务的架构,然后阐述这系统服务的代码开发要点,以及其API设计方法。

    32648 人正在学习 去看看 高煥堂

进程间通信的目的:

1.数据传递

2.资源共享

3.通知事件

进程间通信的发展历史:

1.管道

2.SystemV

3.POSIX(可移植操作系统接口)

管道:

1.匿名管道pipe

2.命名管道

SysV IPC:

1.消息队列

2.共享内存

3.信号量

POSIX IPC:

1.消息队列

2.共享内存

3.信号量

4.互斥量

5.条件变量

6.读写锁

Socket :(进程间通信)

进程间共享的几种方式:


1.左边两个进程共享存留于文件系统中某个文件上的某些信息,为了访问这些信息,每个进程都得穿越内核如(read、write、lseek等)。当一个文件有待更新时,某种形式的同步是需要的,这样既可以保护多个写入者,防止相互串扰,也可以保护一个或多个读者,防止写入者的干扰。

2.中间两个进程共享驻留于内核中的某些信息。管道是这种共享类型的典型例子。现在访问共享信息的每次操作都涉及向内核的一个系统调用。

3.右边两个进程有一个双方都能访问的共享内存区。每个进程一旦设置号该共享内存区,就能根本不涉及内核而访问其中的数据,共享该内存区的进程需要某种形式的同步

IPC对象的持续性:

1.随进程持续性的 IPC   :一直存在到打开着该对象的最后一个进程关闭该对象为止。如管道和FIFO;

2.随内核持续性的IPC  :一直存在到得喝重新自举或显式的删除该对象为止。如SystemV 消息队列和共享内存区。 Posix消息队列和共享内存区。

3.随文件系统持续性的IPC :一直存在到显式的删除该对象为止。即使内存重新自举了,该对象还是保持其值。Posix消息队列和共享内存区。

进程同步:(司机-售票员)两个或多个进程需要相互配合才能完成一项任务

进程互斥:(卖票,提款等)由于各个进程都要访问共享资源,而且这些资源需要排他使用,因此各个进程间需要竞争使用这些资源,这种关系就叫进程互斥

司机售票员问题:

司机:

while(1)
{
    启动车辆;

    正常行驶;

    到站停车;
}
售票员:
while(1)
{
    关门;

    卖票;

    开门;
}

卖票:

售票机:

while(p > 0)                  //p为还有的票数
{
   p--;
}
信号量机制:(计数器)

PV操作提出者:迪杰斯特拉(Dijkstra)

信号量:

互斥:P操作和V操作在同一个进程中

同步:P操作和V操作在不同的进程中

信号量的结构体模型:

struct semaphore{
        int value;                               //资源数目
        struct task_struct *ptr;                 //等待该信号量,处于等待状态
};
信号量值的含义:

S > 0:表示S有这么多的资源可以使用

S = 0:表示没有资源可以使用,也没有进程在该信号量上等待资源

S < 0:表示有|S|个进程在等待该资源

P原语:
	p(s)
	{
		s.value--;			
		{
			将该进程只为等待状态
			将该进程的task_struct插入到相应的等待队列
		}
	}
V原语:
	v(s)
	{
		s.value++;
		if(s.value <= 0)
		{
			将等待队列上的进程唤醒(一般为队头进程)
			将其改为就绪状态
			放入就绪队列
		}
	}

PV原语表示司机售票员问题:


售票机:Sem(1)

while(1)

{

    P(Sem);

    if(p > 0)    p--;

    V(Sem);

}


死锁:是指两个或两个以上的进程(或线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

死锁发生的四个条件:
1、互斥条件:进程对资源的访问是排他性的,如果一个线程对占用了某资源,那么其他线程必须处于等待状态,直到资源被释放。
2、请求和保持条件:进程T1至少已经保持了一个资源R1占用,但又提出对另一个资源R2请求,而此时,资源R2被其他进程T2占用,于是该进程T1也必须等待,但又对自己保持的资源R1不释放。
3、不剥夺条件:进程已获得的资源,在未使用完之前,不能被其他进程剥夺,只能在使用完以后由自己释放。

4、环路等待条件:在死锁发生时,必然存在一个“进程-资源环形链”,即:{p0,p1,p2,...pn},进程p0(或线程)等待p1占用的资源,p1等待p2占用的资源,pn等待p0占用的资源。(最直观的理解是,p0等待p1占用的资源,而p1而在等待p0占用的资源,于是两个进程就相互等待)

预防死锁:

1.破坏请求保持条件。

1> 一次性分配给进程它在运行期间所需要的全部资源。缺点:造成资源的浪费

2> 允许一个进程只获得运行初期所需要的资源后,便开始运行,进程运行过程中再逐步释放已分配给自己的,并且已用毕的全部资源,然后请求新的所需要的资源。

2.破坏不可抢占条件。

当一个进程保持了某些不可被强占资源的进程,提出新的资源请求而不能被满足时,它必须释放已经保持得所有资源,待以后需要时再重新申请。也就是说已占有的资源被暂时的释放,或者说被抢占

3.破坏循环等待条件

规定每个进程必须按序请求资源

一般的死锁避免算法:(银行家算法)

银行家算法:

1.当顾客的资金需求小于银行家现有的资金总量,接纳该客户

2.顾客可以分期贷款,但贷款总额不能超过最大需求量

3.当银行家现有的资金不能满足顾客的需求,可以推迟支付,但总能在有限的时间内让顾客得到贷款

4.当顾客使用完全部贷款,一定能够在有限的时间内归还所有资金

哲学家就餐问题:典型的死锁问题

while(1)
{
	思考;
	if(饿)
	{
		拿左筷子;
		拿右筷子;

		吃;
		放左筷子;
		放右筷子;
	}
}

1. 为了克服死锁风险,可以再添加5根筷子,或者教会哲学家仅用一根筷子吃饭。

2. 另一种方法是添加一个服务员,他只允许4位哲学家同时进入餐厅,由于最多有四位哲学家就坐,则至少有一位哲学家拿到可两根筷子.


2018-04-06 17:21:58 m0_37925202 阅读数 1545
  • Android底层技术:Java层系统服务(Android Service)

    为什么我们需要学习撰写系统服务呢? 底层系统服务是Android 框架裡接近Linux/Driver 的部分。为了充分发挥硬件设备的差異化特性,系统服务是让上层Java 应用程序來使用Driver/HW Device 特色的重要管道。于此,先仔细解析Java层系统服务的架构,然后阐述这系统服务的代码开发要点,以及其API设计方法。

    32648 人正在学习 去看看 高煥堂
管道概念

1、管道是操作提供的一块操作系统内存;
2、管道是Unix中最古老的进程间通信方式;
3、我们把一个进程连接到另一进程的数据流称为管道。

管道通信原理

父进程和子进程之间,或者两个兄弟进程之间,可以通过系统调用建立起一个单向的通信管道。但是这种管道只能由父进程开建立,对于子进程来说是静态的,与生俱来的。管道两端的进程各自都将该管道视作一个文件。一个进程写,另一个进程读。并且,通过管道传递的内容遵循“先入先出”(FIFO)的原则。每个管道都是单向的,需要双向通信时就要建立两个管道。

管道是半双工通信,如果要实现全双工通信,就需要创建两个管道。

系统调用pipe()函数实现

管道机制的主体是系统调用pipe(),但是由pipe()所建立的管道的两端都在同一进程中,所以必须在fork的配合下,才能在父子进程之间或者两个子进程之间建立起进程间的通信管道。由于管道两端都是以(已打开)文件的形式出现在相关的进程中,在具体实现上也是作为匿名文件来实现的。所以pipe()的代码与文件系统密切相关。

  • 从键盘读取数据,写入管道,读取管道,写到屏幕
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>

int main()
{
  int fds[2];
  char buf[100];
  int len;

  if(pipe(fds) == -1)
  perror("make pipe");
  exit(-1);

//read from stdin
while(fgets(buf,100,stdin))
{
  len = strlen(buf);
   //read into pipe
   if(write(fds[1],buf,len)!=len)
     perror("write to pipe");
     break;
 }

memset(buf,0x00,sizeof(buf));

//read from pipe
if((len = read(fds[0],buf,100)) == -1)
{
  perrpr("read from pipe");
  break;
}

//write to stdout
if(write(1,buf,len) != len))
{
  perror("write to stdout");
  break;
}
}
注意:

使用管道有一些限制:

两个进程通过一个管道只能实现单向通信,比如上面的例子,父进程写子进程读,如果

有时候也需要子进程写父进程读,就必须另开一个管道。请读者思考,如果只开一个管道,

但是父进程不关闭读端,子进程也不关闭写端,双方都有读端和写端,为什么不能实现双向

通信?

管道的读写端通过打开的文件描述符来传递,因此要通信的两个进程必须从它们的公共

祖先那里继承管道文件描述符。上面的例子是父进程把文件描述符传给子进程之后父子进程

之间通信,也可以父进程fork两次,把文件描述符传给两个子进程,然后两个子进程之间通

信,总之需要通过fork传递文件描述符使两个进程都能访问同一管道,它们才能通信。

使用管道需要注意以下4种特殊情况(假设都是阻塞I/O操作,没有设置O_NONBLOCK标

志):

1.如果所有指向管道写端的文件描述符都关闭了(管道写端的引用计数等于0),而仍

然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就

像读到文件末尾一样。

2.如果有指向管道写端的文件描述符没关闭(管道写端的引用计数大于0),而持有管

道写端的进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数

据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。

3.如果所有指向管道读端的文件描述符都关闭了(管道读端的引用计数等于0),这时

有进程向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终止。

在第33章信号会讲到怎样使SIGPIPE信号不终止进程。

4.如果有指向管道读端的文件描述符没关闭(管道读端的引用计数大于0),而持有管

道读端的进程也没有从管道中读数据,这时有进程向管道写端写数据,那么在管道被写满时

再次write会阻塞,直到管道中有空位置了才写入数据并返回。

管道的这四种特殊情况具有普遍意义。

管道

阅读数 94

FIFO 管道

阅读数 7

没有更多推荐了,返回首页