文章目录

  • 进程间通信
    • 管道
      • 那管道如何创建呢,背后原理是什么?
    • 消息队列
    • 共享内存
    • 信号量
    • 信号
    • Socket
      • 针对 TCP 协议通信的 socket 编程模型
      • 针对 UDP 协议通信的 socket 编程模型
      • 针对本地进程间通信的 socket 编程模型
    • 总结

进程间通信

管道

如果你学过 Linux 命令,那你肯定很熟悉「 | 」这个竖线。

$ ps auxf | grep mysql

上⾯命令⾏⾥的「 | 」竖线就是⼀个管道,它的功能是将前⼀个命令( ps auxf )的输出,作为后⼀个命令( grep mysql )的输⼊,从这功能描述,可以看出管道传输数据是单向的,如果想相互通信,我们需要创建两个管道才⾏。

同时,我们得知上⾯这种管道是没有名字,所以「 | 」表示的管道称为匿名管道,⽤完了就销毁。

管道还有另外⼀个类型是命名管道,也被叫做 FIFO ,因为数据是先进先出的传输⽅式。

在使⽤命名管道前,先需要通过 mkfifo 命令来创建,并且指定管道名字:

$ mkfifo myPipe

myPipe 就是这个管道的名称,基于 Linux ⼀切皆⽂件的理念,所以管道也是以⽂件的⽅式存在,我们可以⽤ ls 看⼀下,这个⽂件的类型是 p,也就是 pipe(管道) 的意思:

$ ls -l
prw-r--r--. 1 root root 0 Jul 17 02:45 myPipe

接下来,我们往 myPipe 这个管道写⼊数据:

$ echo "hello" > myPipe // 将数据写进管道
// 停住了 ...

你操作了后,你会发现命令执⾏后就停在这了,这是因为管道⾥的内容没有被读取,只有当管道⾥的数据被读完后,命令才可以正常退出。

于是,我们执⾏另外⼀个命令来读取这个管道⾥的数据:

$ cat < myPipe // 读取管道⾥的数据
hello

可以看到,管道⾥的内容被读取出来了,并打印在了终端上,另外⼀⽅⾯,echo 那个命令也正常退出了。

我们可以看出,管道这种通信⽅式效率低,不适合进程间频繁地交换数据。当然,它的好处,⾃然就是简单,同时也我们很容易得知管道⾥的数据已经被另⼀个进程读取了。

那管道如何创建呢,背后原理是什么?

匿名管道的创建,需要通过下⾯这个系统调⽤:

int pipe(int fd[2])

这⾥表示创建⼀个匿名管道,并返回了两个描述符,⼀个是管道的读取端描述符 fd[0] ,另⼀个是管道的写⼊端描述符 fd[1] 。注意,这个匿名管道是特殊的⽂件,只存在于内存,不存于⽂件系统中。


其实,所谓的管道,就是内核⾥⾯的⼀串缓存。从管道的⼀段写⼊的数据,实际上是缓存在内核中的,另⼀端读取,也就是从内核中读取这段数据。另外,管道传输的数据是⽆格式的流且⼤⼩受限。

看到这,你可能会有疑问了,这两个描述符都是在⼀个进程⾥⾯,并没有起到进程间通信的作⽤,怎么样才能使得管道是跨过两个进程的呢?

我们可以使⽤ fork 创建⼦进程,创建的⼦进程会复制⽗进程的⽂件描述符,这样就做到了两个进程各有两个「 fd[0] 与 fd[1] 」,两个进程就可以通过各⾃的 fd 写⼊和读取同⼀个管道⽂件实现跨进程通信了。


管道只能⼀端写⼊,另⼀端读出,所以上⾯这种模式容易造成混乱,因为⽗进程和⼦进程都可以同时写⼊,也都可以读出。那么,为了避免这种情况,通常的做法是:
⽗进程关闭读取的 fd[0],只保留写⼊的 fd[1];
⼦进程关闭写⼊的 fd[1],只保留读取的 fd[0];


所以说如果需要双向通信,则应该创建两个管道。
到这⾥,我们仅仅解析了使⽤管道进⾏⽗进程与⼦进程之间的通信,但是在我们 shell ⾥⾯并不是这样的。

在 shell ⾥⾯执⾏ A | B 命令的时候,A 进程和 B 进程都是 shell 创建出来的⼦进程,A 和 B 之间不存在⽗⼦关系,它俩的⽗进程都是 shell。


所以说,在 shell ⾥通过「 | 」匿名管道将多个命令连接在⼀起,实际上也就是创建了多个⼦进程,那么在我们编写 shell 脚本时,能使⽤⼀个管道搞定的事情,就不要多⽤⼀个管道,这样可以减少创建⼦进程的系统开销。

我们可以得知,对于匿名管道,它的通信范围是存在⽗⼦关系的进程。因为管道没有实体,也就是没有管道⽂件,只能通过 fork 来复制⽗进程 fd ⽂件描述符,来达到通信的⽬的。

另外,对于命名管道,它可以在不相关的进程间也能相互通信。因为命令管道,提前创建了⼀个类型为管道的设备⽂件,在进程⾥只要使⽤这个设备⽂件,就可以相互通信。

不管是匿名管道还是命名管道,进程写⼊的数据都是缓存在内核中,另⼀个进程读取数据时候⾃然也是从内核中获取,同时通信数据都遵循先进先出原则,不⽀持 lseek 之类的⽂件定位操作。

消息队列

前⾯说到管道的通信⽅式是效率低的,因此管道不适合进程间频繁地交换数据。

对于这个问题,消息队列的通信模式就可以解决。⽐如,A 进程要给 B 进程发送消息,A 进程把数据放在对应的消息队列后就可以正常返回了,B 进程需要的时候再去读取数据就可以了。同理,B 进程要给 A 进程发送消息也是如此。

再来,消息队列是保存在内核中的消息链表,在发送数据时,会分成⼀个⼀个独⽴的数据单元,也就是消息体(数据块),消息体是⽤户⾃定义的数据类型,消息的发送⽅和接收⽅要约定好消息体的数据类型,所以每个消息体都是固定⼤⼩的存储块,不像管道是⽆格式的字节流数据。如果进程从消息队列中读取了消息体,内核就会把这个消息体删除。

消息队列⽣命周期随内核,如果没有释放消息队列或者没有关闭操作系统,消息队列会⼀直存在,⽽前⾯提到的匿名管道的⽣命周期,是随进程的创建⽽建⽴,随进程的结束⽽销毁。

消息这种模型,两个进程之间的通信就像平时发邮件⼀样,你来⼀封,我回⼀封,可以频繁沟通了。
但邮件的通信⽅式存在不⾜的地⽅有两点,⼀是通信不及时,⼆是附件也有⼤⼩限制,这同样也是消息队列通信不⾜的点。

消息队列不适合⼤数据的传输,因为在内核中每个消息体都有⼀个最⼤⻓度的限制,同时所有队列所包含的全部消息体的总⻓度也是有上限。在 Linux 内核中,会有两个宏定义 MSGMAX 和 MSGMNB ,它们以字节为单位,分别定义了⼀条消息的最⼤⻓度和⼀个队列的最⼤⻓度。

消息队列通信过程中,存在⽤户态与内核态之间的数据拷⻉开销,因为进程写⼊数据到内核中的消息队列时,会发⽣从⽤户态拷⻉数据到内核态的过程,同理另⼀进程读取内核中的消息数据时,会发⽣从内核态拷⻉数据到⽤户态的过程。

共享内存

消息队列的读取和写⼊的过程,都会有发⽣⽤户态与内核态之间的消息拷⻉过程。那共享内存的⽅式,就很好的解决了这⼀问题。

现代操作系统,对于内存管理,采⽤的是虚拟内存技术,也就是每个进程都有⾃⼰独⽴的虚拟内存空间,不同进程的虚拟内存映射到不同的物理内存中。所以,即使进程 A 和 进程 B 的虚拟地址是⼀样的,其实访问的是不同的物理内存地址,对于数据的增删查改互不影响。

共享内存的机制,就是拿出⼀块虚拟地址空间来,映射到相同的物理内存中。这样这个进程写⼊的东⻄,另外⼀个进程⻢上就能看到了,都不需要拷⻉来拷⻉去,传来传去,⼤⼤提⾼了进程间通信的速度。

信号量

⽤了共享内存通信⽅式,带来新的问题,那就是如果多个进程同时修改同⼀个共享内存,很有可能就冲突了。例如两个进程都同时写⼀个地址,那先写的那个进程会发现内容被别⼈覆盖了。

为了防⽌多进程竞争共享资源,⽽造成的数据错乱,所以需要保护机制,使得共享的资源,在任意时刻只能被⼀个进程访问。正好,信号量就实现了这⼀保护机制。

信号量其实是⼀个整型的计数器,主要⽤于实现进程间的互斥与同步,⽽不是⽤于缓存进程间通信的数据。

信号量表示资源的数量,控制信号量的⽅式有两种原⼦操作:

⼀个是 P 操作,这个操作会把信号量减去 1,相减后如果信号量 < 0,则表明资源已被占⽤,进程需阻塞等待;相减后如果信号量 >= 0,则表明还有资源可使⽤,进程可正常继续执⾏。

另⼀个是 V 操作,这个操作会把信号量加上 1,相加后如果信号量 <= 0,则表明当前有阻塞中的进程,于是会将该进程唤醒运⾏;相加后如果信号量 > 0,则表明当前没有阻塞中的进程;

P 操作是⽤在进⼊共享资源之前,V 操作是⽤在离开共享资源之后,这两个操作是必须成对出现的。

接下来,举个例⼦,如果要使得两个进程互斥访问共享内存,我们可以初始化信号量为 1 。


具体的过程如下:
进程 A 在访问共享内存前,先执⾏了 P 操作,由于信号量的初始值为 1,故在进程 A 执⾏ P 操作后信号量变为 0,表示共享资源可⽤,于是进程 A 就可以访问共享内存。

若此时,进程 B 也想访问共享内存,执⾏了 P 操作,结果信号量变为了 -1,这就意味着临界资源已被占⽤,因此进程 B 被阻塞。

直到进程 A 访问完共享内存,才会执⾏ V 操作,使得信号量恢复为 0,接着就会唤醒阻塞中的线程B,使得进程 B 可以访问共享内存,最后完成共享内存的访问后,执⾏ V 操作,使信号量恢复到初始值 1。

可以发现,信号初始化为 1 ,就代表着是互斥信号量,它可以保证共享内存在任何时刻只有⼀个进程在访问,这就很好的保护了共享内存。

另外,在多进程⾥,每个进程并不⼀定是顺序执⾏的,它们基本是以各⾃独⽴的、不可预知的速度向前推进,但有时候我们⼜希望多个进程能密切合作,以实现⼀个共同的任务。

例如,进程 A 是负责⽣产数据,⽽进程 B 是负责读取数据,这两个进程是相互合作、相互依赖的,进程 A必须先⽣产了数据,进程 B 才能读取到数据,所以执⾏是有前后顺序的。

那么这时候,就可以⽤信号量来实现多进程同步的⽅式,我们可以初始化信号量为 0 。


具体过程:

如果进程 B ⽐进程 A 先执⾏了,那么执⾏到 P 操作时,由于信号量初始值为 0,故信号量会变为-1,表示进程 A 还没⽣产数据,于是进程 B 就阻塞等待;

接着,当进程 A ⽣产完数据后,执⾏了 V 操作,就会使得信号量变为 0,于是就会唤醒阻塞在 P 操作的进程 B;

最后,进程 B 被唤醒后,意味着进程 A 已经⽣产了数据,于是进程 B 就可以正常读取数据了。

可以发现,信号初始化为 0 ,就代表着是同步信号量,它可以保证进程 A 应在进程 B 之前执⾏。

信号

上⾯说的进程间通信,都是常规状态下的⼯作模式。对于异常情况下的⼯作模式,就需要⽤「信号」的⽅式来通知进程。

信号跟信号量虽然名字相似度 66.66%,但两者⽤途完全不⼀样,就好像 Java 和 JavaScript 的区别。

在 Linux 操作系统中, 为了响应各种各样的事件,提供了⼏⼗种信号,分别代表不同的意义。我们可以通过 kill -l 命令,查看所有的信号:

$ kill -l1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR111) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+338) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+843) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+1348) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-1253) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-758) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-263) SIGRTMAX-1 64) SIGRTMAX

运⾏在 shell 终端的进程,我们可以通过键盘输⼊某些组合键的时候,给进程发送信号。例如

Ctrl+C 产⽣ SIGINT 信号,表示终⽌该进程;
Ctrl+Z 产⽣ SIGTSTP 信号,表示停⽌该进程,但还未结束;

如果进程在后台运⾏,可以通过 kill 命令的⽅式给进程发送信号,但前提需要知道运⾏中的进程 PID号,例如:

kill -9 1050 ,表示给 PID 为 1050 的进程发送 SIGKILL 信号,⽤来⽴即结束该进程;

所以,信号事件的来源主要有硬件来源(如键盘 Cltr+C )和软件来源(如 kill 命令)。

信号是进程间通信机制中唯⼀的异步通信机制,因为可以在任何时候发送信号给某⼀进程,⼀旦有信号产⽣,我们就有下⾯这⼏种,⽤户进程对信号的处理⽅式。

1.执⾏默认操作。Linux 对每种信号都规定了默认操作,例如,上⾯列表中的 SIGTERM 信号,就是终⽌进程的意思。
2.捕捉信号。我们可以为信号定义⼀个信号处理函数。当信号发⽣时,我们就执⾏相应的信号处理函数。
3.忽略信号。当我们不希望处理某些信号的时候,就可以忽略该信号,不做任何处理。有两个信号是应⽤进程⽆法捕捉和忽略的,即 SIGKILL 和 SEGSTOP ,它们⽤于在任何时候中断或结束某⼀进程。

Socket

前⾯提到的管道、消息队列、共享内存、信号量和信号都是在同⼀台主机上进⾏进程间通信,那要想跨⽹络与不同主机上的进程之间通信,就需要 Socket 通信了。

实际上,Socket 通信不仅可以跨⽹络与不同主机的进程间通信,还可以在同主机上进程间通信。
我们来看看创建 socket 的系统调⽤:

int socket(int domain, int type, int protocal)

三个参数分别代表:

domain 参数⽤来指定协议族,⽐如 AF_INET ⽤于 IPV4、AF_INET6 ⽤于 IPV6、AF_LOCAL/AF_UNIX ⽤于本机;

type 参数⽤来指定通信特性,⽐如 SOCK_STREAM 表示的是字节流,对应 TCP、SOCK_DGRAM表示的是数据报,对应 UDP、SOCK_RAW 表示的是原始套接字;

protocal 参数原本是⽤来指定通信协议的,但现在基本废弃。因为协议已经通过前⾯两个参数指定完成,protocol ⽬前⼀般写成 0 即可;

根据创建 socket 类型的不同,通信的⽅式也就不同:

实现 TCP 字节流通信: socket 类型是 AF_INET 和 SOCK_STREAM;

实现 UDP 数据报通信:socket 类型是 AF_INET 和 SOCK_DGRAM;

实现本地进程间通信: 「本地字节流 socket 」类型是 AF_LOCAL 和 SOCK_STREAM,「本地数据报 socket 」类型是 AF_LOCAL 和 SOCK_DGRAM。另外,AF_UNIX 和 AF_LOCAL 是等价的,所以AF_UNIX 也属于本地 socket;

接下来,简单说⼀下这三种通信的编程模式。

针对 TCP 协议通信的 socket 编程模型


服务端和客户端初始化 socket ,得到⽂件描述符;
服务端调⽤ bind ,将绑定在 IP 地址和端⼝;
服务端调⽤ listen ,进⾏监听;
服务端调⽤ accept ,等待客户端连接;
客户端调⽤ connect ,向服务器端的地址和端⼝发起连接请求;
服务端 accept 返回⽤于传输的 socket 的⽂件描述符;
客户端调⽤ write 写⼊数据;服务端调⽤ read 读取数据;
客户端断开连接时,会调⽤ close ,那么服务端 read 读取数据的时候,就会读取到了 EOF ,待处理完数据后,服务端调⽤ close ,表示连接关闭。

这⾥需要注意的是,服务端调⽤ accept 时,连接成功了会返回⼀个已完成连接的 socket,后续⽤来传输数据。

所以,监听的 socket 和真正⽤来传送数据的 socket,是「两个」 socket,⼀个叫作监听 socket,⼀个叫作已完成连接 socket。

成功连接建⽴之后,双⽅开始通过 read 和 write 函数来读写数据,就像往⼀个⽂件流⾥⾯写东⻄⼀样。

针对 UDP 协议通信的 socket 编程模型


UDP 是没有连接的,所以不需要三次握⼿,也就不需要像 TCP 调⽤ listen 和 connect,但是 UDP 的交互仍然需要 IP 地址和端⼝号,因此也需要 bind。
对于 UDP 来说,不需要要维护连接,那么也就没有所谓的发送⽅和接收⽅,甚⾄都不存在客户端和服务端的概念,只要有⼀个 socket 多台机器就可以任意通信,因此每⼀个 UDP 的 socket 都需要 bind。
另外,每次通信时,调⽤ sendto 和 recvfrom,都要传⼊⽬标主机的 IP 地址和端⼝。

针对本地进程间通信的 socket 编程模型

本地 socket 被⽤于在同⼀台主机上进程间通信的场景:

本地 socket 的编程接⼝和 IPv4 、IPv6 套接字编程接⼝是⼀致的,可以⽀持「字节流」和「数据报」两种协议;
本地 socket 的实现效率⼤⼤⾼于 IPv4 和 IPv6 的字节流、数据报 socket 实现;

对于本地字节流 socket,其 socket 类型是 AF_LOCAL 和 SOCK_STREAM。
对于本地数据报 socket,其 socket 类型是 AF_LOCAL 和 SOCK_DGRAM。

本地字节流 socket 和 本地数据报 socket 在 bind 的时候,不像 TCP 和 UDP 要绑定 IP 地址和端⼝,⽽是绑定⼀个本地⽂件,这也就是它们之间的最⼤区别。

总结

由于每个进程的⽤户空间都是独⽴的,不能相互访问,这时就需要借助内核空间来实现进程间通信,原因很简单,每个进程都是共享⼀个内核空间。

Linux 内核提供了不少进程间通信的⽅式,其中最简单的⽅式就是管道,管道分为「匿名管道」和「命名管道」。

匿名管道顾名思义,它没有名字标识,匿名管道是特殊⽂件只存在于内存,没有存在于⽂件系统中,shell命令中的「 | 」竖线就是匿名管道,通信的数据是⽆格式的流并且⼤⼩受限,通信的⽅式是单向的,数据只能在⼀个⽅向上流动,如果要双向通信,需要创建两个管道,再来匿名管道是只能⽤于存在⽗⼦关系的进程间通信,匿名管道的⽣命周期随着进程创建⽽建⽴,随着进程终⽌⽽消失。

命名管道突破了匿名管道只能在亲缘关系进程间的通信限制,因为使⽤命名管道的前提,需要在⽂件系统创建⼀个类型为 p 的设备⽂件,那么毫⽆关系的进程就可以通过这个设备⽂件进⾏通信。另外,不管是匿名管道还是命名管道,进程写⼊的数据都是缓存在内核中,另⼀个进程读取数据时候⾃然也是从内核中获取,同时通信数据都遵循先进先出原则,不⽀持 lseek 之类的⽂件定位操作。

消息队列克服了管道通信的数据是⽆格式的字节流的问题,消息队列实际上是保存在内核的「消息链表」,消息队列的消息体是可以⽤户⾃定义的数据类型,发送数据时,会被分成⼀个⼀个独⽴的消息体,当然接收数据时,也要与发送⽅发送的消息体的数据类型保持⼀致,这样才能保证读取的数据是正确的。

消息队列通信的速度不是最及时的,毕竟每次数据的写⼊和读取都需要经过⽤户态与内核态之间的拷⻉过程。

共享内存可以解决消息队列通信中⽤户态与内核态之间数据拷⻉过程带来的开销,它直接分配⼀个共享空间,每个进程都可以直接访问,就像访问进程⾃⼰的空间⼀样快捷⽅便,不需要陷⼊内核态或者系统调⽤,⼤⼤提⾼了通信的速度,享有最快的进程间通信⽅式之名。但是便捷⾼效的共享内存通信,带来新的问题,多进程竞争同个共享资源会造成数据的错乱。

那么,就需要信号量来保护共享资源,以确保任何时刻只能有⼀个进程访问共享资源,这种⽅式就是互斥访问。信号量不仅可以实现访问的互斥性,还可以实现进程间的同步,信号量其实是⼀个计数器,表示的是资源个数,其值可以通过两个原⼦操作来控制,分别是 P 操作和 V 操作。

与信号量名字很相似的叫信号,它俩名字虽然相似,但功能⼀点⼉都不⼀样。信号是进程间通信机制中唯⼀的异步通信机制,信号可以在应⽤进程和内核之间直接交互,内核也可以利⽤信号来通知⽤户空间的进程发⽣了哪些系统事件,信号事件的来源主要有硬件来源(如键盘 Cltr+C )和软件来源(如 kill 命令),⼀旦有信号发⽣,进程有三种⽅式响应信号 1. 执⾏默认操作、2. 捕捉信号、3. 忽略信号。有两个信号是应⽤进程⽆法捕捉和忽略的,即 SIGKILL 和 SEGSTOP ,这是为了⽅便我们能在任何时候结束或停⽌某个进程。

前⾯说到的通信机制,都是⼯作于同⼀台主机,如果要与不同主机的进程间通信,那么就需要 Socket 通信了。Socket 实际上不仅⽤于不同的主机进程间通信,还可以⽤于本地主机进程间通信,可根据创建Socket 的类型不同,分为三种常⻅的通信⽅式,⼀个是基于 TCP 协议的通信⽅式,⼀个是基于 UDP 协议的通信⽅式,⼀个是本地进程间通信⽅式。

以上,就是进程间通信的主要机制了。你可能会问了,那线程通信间的⽅式呢?

同个进程下的线程之间都是共享进程的资源,只要是共享变量都可以做到线程间通信,⽐如全局变量,所以对于线程间关注的不是通信⽅式,⽽是关注多线程竞争共享资源的问题,信号量也同样可以在线程间实现互斥与同步:互斥的⽅式,可保证任意时刻只有⼀个线程访问共享资源;同步的⽅式,可保证线程 A 应在线程 B 之前执⾏;

学自小林coding,侵删

大话操作系统(11)进程间通信相关推荐

  1. 红旗Linux可以兼容,红旗 Linux 桌面操作系统11来了:支持国产自主CPU,全新UI风格设计,兼容面广...

    原标题:红旗 Linux 桌面操作系统11来了:支持国产自主CPU,全新UI风格设计,兼容面广 红旗Linux桌面操作系统11将于1月10日开放预览版的下载,新版本具有良好的硬件兼容,支持多款国产自主 ...

  2. 红旗 Linux 桌面操作系统 11 来了

    点击上方"民工哥技术之路",选择"设为星标" 回复"1024"获取独家整理的学习资料! 红旗Linux桌面操作系统11将于1月10日开放预览 ...

  3. 红旗linux 桌面10 下载,已开放下载:红旗 Linux 桌面操作系统11 社区预览版,附链接...

    原标题:已开放下载:红旗 Linux 桌面操作系统11 社区预览版,附链接 正如之前的预告,1月10日,红旗Linux桌面操作系统11预览版的下载已经公开提供,感兴趣的用户可以免费安装体验. 红旗Li ...

  4. 红旗 Linux 桌面操作系统11来了:支持国产自主CPU,全新UI风格设计,兼容面广...

    链接:https://reurl.cc/g8ke9X 红旗Linux桌面操作系统11将于1月10日开放预览版的下载,新版本具有良好的硬件兼容,支持多款国产自主CPU品牌,同时还具有丰富的外设支持及海量 ...

  5. Android操作系统11种传感器

    Android操作系统11种传感器介绍 在Android2.3 gingerbread系统中,google提供了11种传感器供应用层使用.      #define SENSOR_TYPE_ACCEL ...

  6. [转帖]Android操作系统11种传感器介绍

    Android操作系统11种传感器介绍 在Android2.3 gingerbread系统中,google提供了11种传感器供应用层使用. #define SENSOR_TYPE_ACCELEROME ...

  7. MTK Android操作系统11种传感器介绍

    Android操作系统11种传感器介绍 1 加速度传感器 2 磁力传感器 3 方向传感器 4 陀螺仪传感器 5 光线感应传感器 6 压力传感器 7 温度传感器 8 接近传感器 9 重力传感器 10 线 ...

  8. 【操作系统】进程间通信的五种方式

    引言 1.进程对白:管道.记名管道.套接字 1.管道 2.虫洞:套接字 3.信号 4.信号旗语:信号量 5.进程拥抱:共享内存 引言 进程作为人类的发明,自然免不了脱离人类的习性,也有通信需求.如果进 ...

  9. 【操作系统】进程间通信

    多个进程可以共享系统中的各种资源,但其中许多资源一次只能为一个进程使用,我们把一次仅允许一个进程使用的资源成为临界资源.许多物理设备都属于临界资源,如打印机等. 对临界资源的访问,必须互斥的进行在,在 ...

最新文章

  1. java方法的调用怎么跳出_JAVA 的一个方法调用另一个方法时,怎么把那个方法里的数据调用出来...
  2. PEAK6 2020校招OA
  3. ORACLE 回收站管理
  4. infoseccrypto_java下载_關於php接ICBC的支付接口的解決方案
  5. 自定义checkbox样式
  6. excel线性拟合的斜率_协方差分析:方差分析与线性回归的统一
  7. 追求极致速度,极简多模态预训练模型ViLT,推理速度比UNITER快60倍!(ICML2021)...
  8. 偶搜集到的源码列表如下,跟大家分享分享。
  9. EventBus全面讲解和案例
  10. php装逼的面试问题,面试读心术,教你如何优雅的在面试官前装逼
  11. 完全总结bash中的条件判断test [ [[ 使用
  12. 2022 各国程序员薪资大揭秘!
  13. 黑苹果 中文四叶草下载
  14. 【js学习笔记】去除省、市、区、特别行政区、自治区
  15. BJOI2019 勘破神机 新解
  16. python中vstack作用_numpy中的vstack和hstack
  17. html不同域名显示不同内容,前端基础面试题(HTML+CSS部分)
  18. php大商创 安装,新零售电商系统:大商创X安装教程【宝塔环境】
  19. Kafka之分区副本与ISR
  20. USB驱动分析(一)

热门文章

  1. Django中的Model(字段) - 第五轻柔的code - 博客园
  2. 为什么从此电脑访问不了ftp_蓝奏云网盘为什么访问不了?(附资源)
  3. inc si指令的作用_亲水作用色谱(HILIC)(二):可选的固定相有哪些?
  4. 自动驾驶研究生就业如何,自动驾驶的研究方向
  5. 画意三峡---先睹为快
  6. 用python的tkinter做游戏(八)—— 实现图片在tkinter中自适应大小(自动匹配窗口)
  7. 中国石油大学远程教育《大学英语(四)》第二阶段在线作业
  8. mysql b树_为什么 MongoDB 索引选择B树,而 Mysql 选择B+树(精干总结)
  9. python画太极图
  10. 三个月时间,如何成就自己成为一名数据分析师