Linux——匿名管道-程序员宅基地

技术标签: Linux操作系统  运维  linux  服务器  

我们之前一直用的是vim来编写代码,现在有了vscode这样强大的编辑器,我们可以把我们的vim放一边了,如果还有小伙伴还没有配置好vscode的远端,可以点击这里:

https://blog.csdn.net/qq_67693066/article/details/136368891

我们今天进入管道的学习:

什么是管道

在计算机领域,管道(Pipeline)是一种将多个命令连接在一起以形成数据流的机制。它允许一个命令的输出成为另一个命令的输入,从而实现命令之间的数据传递和处理。

在 Unix/Linux 系统中,管道通常用竖线符号 | 表示。通过管道,可以将一个命令的输出传递给另一个命令进行处理,从而构建复杂的数据处理流程。

例如,假设我们有两个命令 command1 和 command2,我们可以使用管道将它们连接起来:

command1 | command2

这将会把 command1 的输出作为 command2 的输入,command2 将处理 command1 的输出并生成最终的结果。

管道的优势包括:

简化复杂任务: 管道可以将多个简单的命令组合成一个复杂的任务,使得任务的实现更加简单和高效。
模块化和可重用性: 通过将命令连接在一起,可以更好地组织代码并提高代码的可重用性。每个命令都可以专注于完成一个特定的任务。
减少临时文件: 管道可以避免将数据存储到临时文件中,从而减少了文件 I/O 的开销和磁盘空间的占用。
实时处理: 管道允许命令之间的实时数据传递,这对于需要连续处理数据的任务非常有用,比如日志处理、数据流分析等。

简单来说,管道就是连接多个指令。我们之前也在频繁使用管道:比如我们想统计当前登录到系统的用户数量。
在这里插入图片描述
who指令的结果作为wc -l的输入。

匿名管道的底层原理

我们这里讲的简单一点,现在我们有一个进程,它自身会被以读和写的方式分别打开一次:
在这里插入图片描述
然后这个读和写都会往一个缓冲区输入输出数据:

这个时候父进程创建子进程,子进程发生浅拷贝,指向没有发生变化:
在这里插入图片描述
这里注意一下,管道一般是单向的,所以我们现在想让父进程读,让子进程写
在这里插入图片描述
这样形成了一个单向通道,这个就是一个基本的匿名管道

匿名管道(Anonymous Pipe)是一种用于进程间通信的机制,特别是在 Unix 和类 Unix 系统中。它允许一个进程将输出发送到另一个进程的输入,从而实现进程间的数据传输。
以下是匿名管道的一些关键特点:
单向通信:匿名管道是单向的,只能支持单向数据流。它只能用于单一方向的通信,通常是父进程到子进程或者相反。
创建:匿名管道通过调用系统调用 pipe() 来创建。这个系统调用创建了一个管道,返回两个文件描述符,其中一个用于读取管道,另一个用于写入管道。
父子进程通信:通常,匿名管道用于父子进程之间的通信。在创建子进程后,父进程可以将数据写入管道,而子进程则可以从管道中读取这些数据。
半双工:匿名管道是半双工的,意味着数据只能在一个方向上流动。如果需要双向通信,则需要创建两个管道,或者使用其他的进程间通信机制,比如命名管道或套接字。
进程同步:匿名管道通常用于进程间的同步和协作。一个进程可能会阻塞在读取管道上,直到另一个进程写入数据到管道中为止。
匿名管道在 Unix 系统中被广泛应用,特别是在 shell 编程和进程间通信方面。它提供了一种简单而有效的方式,允许不同进程之间进行数据交换和协作

我也有专门创建管道的函数pipe
在这里插入图片描述
我们可以来试一下:

#include<iostream>
#include<unistd.h>
#include<cassert>
using namespace std;

int main()
{
    
   int pipefd[2] = {
    0};
   int n = pipe(pipefd);
   assert(n == 0);

   cout<<"pipefd[0]"<<"--->"<<pipefd[0]<<"pipefd[1]"<<"--->"<<pipefd[1]<<endl;

    return 0;
}

运行:
在这里插入图片描述
这里我们发现pipefd[0]指代的是3,而我们的pipefd[1]指代的是4。其实也很好理解,因为0,1,2被标准输入,标准输出,标准错误占了。所以从3开始。

同时,如果我么查手册会看到这样一段话:
在这里插入图片描述
这段话的主要意思是pipefd[0]是读端,而pipefd[1]是写端。这为我们以后哪个开哪个关提供了依据。

观察匿名管道现象

我们先搭建架子来观察我们匿名管道的现象:

#include<iostream>
#include<unistd.h>
#include<cassert>
#include<sys/types.h>
#include<sys/wait.h>
using namespace std;

int main()
{
    
   //建立管道
   int pipefd[2] = {
    0};
   int n = pipe(pipefd);
   assert(n == 0);

   //创建子进程
   pid_t id = fork();

   //子进程
   if(id < 0)
   {
    
    perror("fork fail");
   }
   
   if(id ==0)
   {
    
       //子进程要做的事
       exit(0);
   }
   
   //父进程要做的事

   //回收子进程
   pid_t rid = waitpid(id,nullptr,0);
   if(rid == id)
   {
    
      cout<<"wait success"<<endl;
   }

   return 0;
}

现在我们想让子进程写,父进程读,我们把相应用不到的管道关闭:

#include<iostream>
#include<unistd.h>
#include<cassert>
#include<sys/types.h>
#include<sys/wait.h>
using namespace std;

int main()
{
    
   //建立管道
   int pipefd[2] = {
    0};
   int n = pipe(pipefd);
   assert(n == 0);

   //创建子进程
   pid_t id = fork();

   //子进程
   if(id < 0)
   {
    
    perror("fork fail");
    return 1;
   }
   
   if(id ==0)
   {
    
       //子进程要做的事
       close(pipefd[0]); //关闭读的通道
       exit(0);
   }
   
   //父进程要做的事
   close(pipefd[1]); //关闭写的通道

   //回收子进程
   pid_t rid = waitpid(id,nullptr,0);
   if(rid == id)
   {
    
      cout<<"wait success"<<endl;
   }

   return 0;
}

我们让子进程写入一些东西,然后让父进程来读,看看行不行:

#include<iostream>
#include<unistd.h>
#include<cassert>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdio.h>
#include<string.h>
using namespace std;
#define MAX 1024

int main()
{
    
   //建立管道
   int pipefd[2] = {
    0};
   int n = pipe(pipefd);
   assert(n == 0);

   //创建子进程
   pid_t id = fork();

   //子进程
   if(id < 0)
   {
    
    perror("fork fail");
    return 1;
   }
   
   if(id ==0)
   {
    
       //子进程要做的事
       close(pipefd[0]); //关闭读的通道

       //向管道写入
       int cnt = 10;

       while(cnt)
       {
    
           //缓冲区
           char message[MAX];

           //向缓冲区里写
           snprintf(message,sizeof(message),"hello father I am child my pid:%d cnt:%d ", getpid(),cnt);

           cnt--;

           //向管道写
           write(pipefd[1],message,strlen(message));

           sleep(1);
       }
       exit(0);
   }
   
   //父进程要做的事
   close(pipefd[1]); //关闭写的通道

   //从管道中读取数据
   char buffer[MAX];

   while(true)
   {
    
      ssize_t n = read(pipefd[0],buffer,sizeof(buffer));
    
      if(n > 0)
      {
    
         cout << getpid() << "," << "chid say :" << buffer << "to me" << endl;
      }
     
   }

   //回收子进程
   pid_t rid = waitpid(id,nullptr,0);
   if(rid == id)
   {
    
      cout<<"wait success"<<endl;
   }

   return 0;
}

在这里插入图片描述
我们看到父进程真的拿到了子进程写的东西,这就是一个最基本的管道的应用。

读写端的几种情况

写端慢,读端快

我们模拟一下,写端慢,读端快的情况

#include<iostream>
#include<unistd.h>
#include<cassert>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdio.h>
#include<string.h>
using namespace std;
#define MAX 1024

int main()
{
    
   //建立管道
   int pipefd[2] = {
    0};
   int n = pipe(pipefd);
   assert(n == 0);

   //创建子进程
   pid_t id = fork();

   //子进程
   if(id < 0)
   {
    
    perror("fork fail");
    return 1;
   }
   
   if(id ==0)
   {
    
       //子进程要做的事
       close(pipefd[0]); //关闭读的通道

       //向管道写入
       int cnt = 10;

       while(cnt)
       {
    
           //缓冲区
           char message[MAX];

           //向缓冲区里写
           snprintf(message,sizeof(message),"hello father I am child my pid:%d cnt:%d ", getpid(),cnt);

           cnt--;

           //向管道写
           write(pipefd[1],message,strlen(message));

           sleep(100); //模拟写端慢
       }
       exit(0);
   }
   
   //父进程要做的事
   close(pipefd[1]); //关闭写的通道

   //从管道中读取数据
   char buffer[MAX];

   while(true)
   {
    
      ssize_t n = read(pipefd[0],buffer,sizeof(buffer));
    
      if(n > 0)
      {
    
         cout << getpid() << "," << "chid say :" << buffer << "to me" << endl;
      }
     
   }

   //回收子进程
   pid_t rid = waitpid(id,nullptr,0);
   if(rid == id)
   {
    
      cout<<"wait success"<<endl;
   }

   return 0;
}

在这里插入图片描述
我们发现父进程处于一个休眠的状态,很明显,它是在等待我们的子进程进行写入。

这里我们可以得出匿名管道具有同步机制,读端和写端是协同工作的。

写端快,读端慢

我们调换一下,让写端快,读端快:

#include<iostream>
#include<unistd.h>
#include<cassert>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdio.h>
#include<string.h>
using namespace std;
#define MAX 1024

int main()
{
    
   //建立管道
   int pipefd[2] = {
    0};
   int n = pipe(pipefd);
   assert(n == 0);

   //创建子进程
   pid_t id = fork();

   //子进程
   if(id < 0)
   {
    
    perror("fork fail");
    return 1;
   }
   
   if(id ==0)
   {
    
       //子进程要做的事
       close(pipefd[0]); //关闭读的通道

       //向管道写入
       int cnt = 10000;

       while(cnt)
       {
    
           //缓冲区
           char message[MAX];

           //向缓冲区里写
           snprintf(message,sizeof(message),"hello father I am child my pid:%d cnt:%d ", getpid(),cnt);

           cnt--;

           //向管道写
           write(pipefd[1],message,strlen(message));

           cout<<"writing......"<<endl;
       }
       exit(0);
   }
   
   //父进程要做的事
   close(pipefd[1]); //关闭写的通道

   //从管道中读取数据
   char buffer[MAX];

   while(true)
   {
    
      sleep(2); //睡眠2秒
      ssize_t n = read(pipefd[0],buffer,sizeof(buffer));
    
      if(n > 0)
      {
    
         cout << getpid() << "," << "chid say :" << buffer << "to me" << endl;
      }
     
   }

   //回收子进程
   pid_t rid = waitpid(id,nullptr,0);
   if(rid == id)
   {
    
      cout<<"wait success"<<endl;
   }

   return 0;
}


执行:
在这里插入图片描述
过了2秒之后:
在这里插入图片描述
数据一瞬间出来了。

这里我们可以得出匿名管道是面向字节流的,它没有硬性规定我写一条你必须马上读一条,而是以字节流的形式读或写。

管道的大小

我们可以写一段代码来测试我们管道的大小:

#include<iostream>
#include<unistd.h>
#include<cassert>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdio.h>
#include<string.h>
using namespace std;
#define MAX 1024

int main()
{
    
   //建立管道
   int pipefd[2] = {
    0};
   int n = pipe(pipefd);
   assert(n == 0);

   //创建子进程
   pid_t id = fork();

   //子进程
   if(id < 0)
   {
    
    perror("fork fail");
    return 1;
   }
   
   if(id ==0)
   {
    
       //子进程要做的事
       close(pipefd[0]); //关闭读的通道

       //向管道写入
       int cnt = 0;

       while(1)
       {
    
         //   //缓冲区
         //   char message[MAX];

         //   //向缓冲区里写
         //   snprintf(message,sizeof(message),"hello father I am child my pid:%d cnt:%d ", getpid(),cnt);

         //   cnt--;

         //   //向管道写
         //   write(pipefd[1],message,strlen(message));

         //   cout<<"writing......"<<endl;

         char c = 'a';
         write(pipefd[1], &c, 1);
         cnt++;
         cout << "write ....: " << cnt << endl;
       }
       exit(0);
   }
   
   //父进程要做的事
   close(pipefd[1]); //关闭写的通道

   //从管道中读取数据
   char buffer[MAX];

   while(true)
   {
    
      // sleep(2); //睡眠2秒
      // ssize_t n = read(pipefd[0],buffer,sizeof(buffer));
    
      // if(n > 0)
      // {
    
      //    cout << getpid() << "," << "chid say :" << buffer << "to me" << endl;
      // }
     
   }

   //回收子进程
   pid_t rid = waitpid(id,nullptr,0);
   if(rid == id)
   {
    
      cout<<"wait success"<<endl;
   }

   return 0;
}

在这里插入图片描述
我们发现最后结果是65536,折合下来也就是64kb左右的大小。

我们也可以用指令来查看管道大小:ulimit -a
在这里插入图片描述

我们查看的管道大小为512 * 8 = 4kb,好像比我们看到的小。这个其实不是真正的大小。

写端关闭,读端一直读

我们现在让写段写一段时间后直接关闭,但是读端没有关闭:

#include<iostream>
#include<unistd.h>
#include<cassert>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdio.h>
#include<string.h>
using namespace std;
#define MAX 1024

int main()
{
    
   //建立管道
   int pipefd[2] = {
    0};
   int n = pipe(pipefd);
   assert(n == 0);

   //创建子进程
   pid_t id = fork();

   //子进程
   if(id < 0)
   {
    
    perror("fork fail");
    return 1;
   }
   
   if(id ==0)
   {
    
       //子进程要做的事
       close(pipefd[0]); //关闭读的通道

       //向管道写入
       int cnt = 0;

       while(1)
       {
    
           //缓冲区
           char message[MAX];

           //向缓冲区里写
           snprintf(message,sizeof(message),"hello father I am child my pid:%d cnt:%d ", getpid(),cnt);

           cnt++;

           //向管道写
           write(pipefd[1],message,strlen(message));

          //跳出
          if(cnt > 3) break;

         // char c = 'a';
         // write(pipefd[1], &c, 1);
         // cnt++;
         // cout << "write ....: " << cnt << endl;
       }

       //关闭写端
       close(pipefd[1]);
       exit(0);
   }
   
   //父进程要做的事
   close(pipefd[1]); //关闭写的通道

   //从管道中读取数据
   char buffer[MAX];

   while(true)
   {
    
      //sleep(2); //睡眠2秒
      ssize_t n = read(pipefd[0],buffer,sizeof(buffer));
    
      if(n > 0)
      {
    
         cout << getpid() << "," << "chid say :" << buffer << "to me" << endl;
      }

      cout<<"father return value:"<< n << endl;
     
     sleep(1);
   }

   //回收子进程
   pid_t rid = waitpid(id,nullptr,0);
   if(rid == id)
   {
    
      cout<<"wait success"<<endl;
   }

   return 0;
}

在这里插入图片描述
这样表示:写端关闭,读端一直读取, 读端会读到read返回值为0, 表示读到文件结尾

同时注意,进程退出,管道自动关闭

写端一直写,读端关闭

#include<iostream>
#include<unistd.h>
#include<cassert>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdio.h>
#include<string.h>
using namespace std;
#define MAX 1024

int main()
{
    
   //建立管道
   int pipefd[2] = {
    0};
   int n = pipe(pipefd);
   assert(n == 0);

   //创建子进程
   pid_t id = fork();

   //子进程
   if(id < 0)
   {
    
    perror("fork fail");
    return 1;
   }
   
   if(id ==0)
   {
    
       //子进程要做的事
       close(pipefd[0]); //关闭读的通道

       //向管道写入
       int cnt = 0;

       while(true)
       {
    
           //缓冲区
           char message[MAX];

           //向缓冲区里写
           snprintf(message,sizeof(message),"hello father I am child my pid:%d cnt:%d ", getpid(),cnt);

           cnt++;

           //向管道写
           write(pipefd[1],message,strlen(message));

           sleep(1);

          //跳出
          //if(cnt > 3) break;

         // char c = 'a';
         // write(pipefd[1], &c, 1);
         // cnt++;
         // cout << "write ....: " << cnt << endl;

         sleep(1);
       }

       //关闭写端
       //close(pipefd[1]);
       exit(0);
   }
   
   //父进程要做的事
   close(pipefd[1]); //关闭写的通道

   //从管道中读取数据
   char buffer[MAX];

   while(true)
   {
    
      //sleep(2); //睡眠2秒
      ssize_t n = read(pipefd[0],buffer,sizeof(buffer));
    
      if(n > 0)
      {
    
         cout << getpid() << "," << "chid say :" << buffer << "to me" << endl;
      }

      cout<<"father return value:"<< n << endl;
     
     sleep(1);

     //直接跳出
     break;
   }

   //关闭读端
   close(pipefd[0]);

   sleep(5);
   int status = 0;
   pid_t rid = waitpid(id, &status, 0);
   if (rid == id)
   {
    
      cout << "wait success, child exit sig: " << (status&0x7F) << endl;
   }

   // //回收子进程
   // pid_t rid = waitpid(id,nullptr,0);
   // if(rid == id)
   // {
    
   //    cout<<"wait success"<<endl;
   // }

   return 0;
}

我们得到一下它的信号:
在这里插入图片描述
我们查一下13号信号:
在这里插入图片描述
13号信号是:SIGPIPE:

SIGPIPE 是在进程向一个已经被关闭的管道(或者其他的类似的通信方式)写入数据时,内核向该进程发送的信号。这个信号的默认行为是终止进程。
常见的场景是,一个进程向另一个进程通过管道发送数据,但接收数据的进程提前退出,导致写入数据的进程尝试往已关闭的管道写入数据。在这种情况下,内核会发送 SIGPIPE 信号给写入数据的进程,通知它目标进程已经退出,不再接收数据。

所以我们才有上述现象。

总结一下管道有4种情况:

管道的4种情况

  1. 正常情况,如果管道没有数据了,读端必须等待,直到有数据为止(写端写入数据了)
  2. 正常情况,如果管道被写满了,写端必须等待,直到有空间为止(读端读走数据)
  3. 写端关闭,读端一直读取, 读端会读到read返回值为0, 表示读到文件结尾
  4. 读端关闭,写端一直写入,OS会直接杀掉写端进程,通过想目标进程发送SIGPIPE(13)信号,终止目标进程

5种特性:

管道的5种特性

  1. 匿名管道,可以允许具有血缘关系的进程之间进行进程间通信,常用与父子,仅限于此
  2. 匿名管道,默认给读写端要提供同步机制 — 了解现象就行
  3. 面向字节流的 — 了解现象就行
  4. 管道的生命周期是随进程的
  5. 管道是单向通信的,半双工通信的一种特殊情况
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_67693066/article/details/136371517

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法