电子产业一站式赋能平台

PCB联盟网

搜索
查看: 212|回复: 0
收起左侧

嵌入式Linux:进程间通信机制

[复制链接]

1001

主题

1001

帖子

8803

积分

高级会员

Rank: 5Rank: 5

积分
8803
发表于 2024-12-10 08:00:00 | 显示全部楼层 |阅读模式

3rqolratvad64088525943.gif

3rqolratvad64088525943.gif

6 q: k4 _) ]5 Y9 O- d点击上方蓝色字体,关注我们* ^  H! }# g% P7 ~

, M2 \- o2 T8 U1.1、UNIX IPC
% H& r+ S* o3 j; e! S8 y4 K, UUNIX 传统的 IPC 机制包括管道、FIFO 和信号,这些机制最早由 UNIX 系统引入,适用于简单的单机进程间通信。( f+ ~( N- Z  y  ?; o5 |2 t
  • 管道(Pipe)' d& ]# _: [! y, Y0 s
    一种单向、半双工的通信机制,通常用于父子进程间的数据传递。5 [: n$ C& \0 O7 _; X
    父进程可以写入数据,子进程可以读取。
  • FIFO(命名管道); b% D1 ^9 p6 V* z7 K
    类似于管道,但通过文件系统实现,任何进程都可以通过路径访问该管道,实现双向通信。
  • 信号(Signal)
    " i/ H5 a7 y& G/ }信号是一种用于进程间异步通知的机制,可以用于进程之间的简单通信或事件通知,例如 SIGINT(Ctrl+C 发送的中断信号)。
    % c& x  m6 I9 y, t4 w9 o* O

    6 t2 m8 d1 s& J/ e( l1.2、System V IPC* p3 |; w) h* j, V) v$ C
    System V IPC 是 UNIX 的增强版本,主要包括信号量、消息队列和共享内存,适合需要更复杂的进程同步与数据共享的场景。  h5 G0 l- X% d* @
  • 信号量(Semaphore)3 A6 A4 e& J. J7 P( m" Y
    用于进程间的同步,通常用于控制对共享资源的访问。
    ; O" l+ T/ k5 `* H9 h0 o# ~信号量用于防止多个进程同时访问同一资源,避免资源争用问题。
  • 消息队列(Message Queue)  ~7 ~1 t" r' F+ H
    允许进程以消息的形式发送和接收数据。
    & U; O  O+ g( C; N3 G3 \4 ~消息队列是一种先进先出(FIFO)的结构,支持不同类型的消息,使得进程可以基于消息类型进行处理。
  • 共享内存(Shared Memory)0 D2 k8 l4 l' e+ d, ~6 n
    进程之间共享同一块内存区域,允许它们直接读写数据。0 E1 q( f7 _6 Z" g0 M3 b* `* R( ~
    这是最有效的 IPC 方式,因为数据不需要在进程之间复制。
    & O8 V- v" x! `0 ^& H: b

    9 w  J0 D9 g  Y3 v% P. b1.3、POSIX IPC
    2 L. g  S4 k5 l( P- p1 z8 I( B# ?POSIX IPC 是 System V IPC 的改进版本,旨在解决 System V IPC 在灵活性和可移植性上的一些不足。+ R; s1 W" C* ?5 b

    7 u: p! A4 G4 P  h( n1 T  ePOSIX 标准为 UNIX 系统间的兼容性提供了统一的接口,使得程序可以更方便地在不同的 UNIX 系统间移植。! I9 R5 J' U4 a, t0 F
  • POSIX 信号量1 r2 F( P& \4 H: _& T. L
    与 System V 信号量类似,用于进程同步,但提供了更灵活的接口和更强的实时性支持。
  • POSIX 消息队列
    : `7 P2 N; S7 e# r) f改进了 System V 消息队列,允许指定消息的优先级,并提供更简单的接口。
  • POSIX 共享内存" z. |, }; a$ F, q# F6 G4 N
    与 System V 共享内存类似,但具有更好的兼容性和可移植性,接口设计更加现代化。
    " f9 `2 k; t" P3 s* u, t3 i' p
    : J) r& Z! H& Z3 _
    1.4、套接字(Socket)通信
    + [, [# I) A- }+ j0 L套接字是一种既可以用于本地进程间通信,也可以用于网络通信的机制,支持双向数据传输。/ ~8 ?+ a4 E( o" _- O! n& t$ u
    ' s5 P# w: c6 O- J1 C% S
    基于套接字的 IPC 可以实现非常灵活的通信模式,例如客户端-服务器架构,适合在多台计算机之间传递数据。3 r  {% G! z5 C1 `

    / S# q/ Y4 J- @各类 IPC 机制的对比和应用场景:( `6 C5 A4 C! G' `
    1 i2 L& B- W; u7 \8 Y

    bsbbcgpudkr64088526043.png

    bsbbcgpudkr64088526043.png

    1 e, s9 z1 v# |& y8 i5 r
    7 K0 w( b+ f- H8 k1 Q2' p5 X4 v* H. h/ e; ~, ~9 q2 t
    管道(Pipe)
    5 V! p# b6 W+ R& e0 Y管道是一种半双工(单向)的通信方式,通常用于父子进程之间的通信。一个进程可以向管道写入数据,另一个进程从管道读取数据。) @' M8 L9 V; {5 P0 D% m: Z# M

    $ Z- L/ F7 }$ \" eLinux 提供了无名管道和命名管道两种类型。
    # H% L- P/ s! V2 n; X: B" D1 S
  • 无名管道(Anonymous Pipe)
      _  `+ p' R" d( y4 U8 ^只能在具有亲缘关系的进程间使用,比如父进程和子进程。
  • 命名管道(Named Pipe 或 FIFO); J9 d& @' ~% q6 n$ `* O0 i3 _
    通过文件系统中的路径来创建,任意进程都可以访问。
    8 M" x" ]# U) L+ A9 z
    " P" s0 H9 E6 @5 r9 x
    ( v& u5 j6 `* S
    示例) J4 H0 _4 v: _. n

    , {3 z1 z8 a1 r
  • int main() {    int fd[2];    pipe(fd); // 创建无名管道
    2 }7 X; U4 V7 `: B% R2 n    if (fork() == 0) { // 子进程        close(fd[0]); // 关闭读取端        write(fd[1], "Hello, parent!", 15);        close(fd[1]);    } else { // 父进程        char buffer[20];        close(fd[1]); // 关闭写入端        read(fd[0], buffer, sizeof(buffer));        printf("Received: %s% c" E, L( o" e( |3 U, y" G
    ", buffer);        close(fd[0]);    }    return 0;}, s+ Y, G7 L( m: Z
    36 |3 P8 x6 ?3 P/ ]- G: S- w
    消息队列(Message Queue)2 q4 I: _  f" V4 i
    消息队列是一种先进先出的队列,允许进程以消息的形式发送和接收数据。
    / _( L, o, `: N  I
    0 j/ Z* R0 ]* _5 ?8 R& P消息队列可以支持多种类型的消息,通过消息类型实现多种目的的通信。0 ]' T/ K% y2 [/ \
    : R6 S; t# G/ U0 a/ |! ?2 G. |
    示例:进程A可以向队列发送一个带有特定类型的消息,而进程B可以根据消息类型进行处理。/ w4 G: x" I  b% m4 }0 U/ P: r
    : o/ O; I( I9 Y6 d5 J
  • struct msgbuf {    long mtype;    char mtext[100];};
    ( r  R: M; k( ~/ c1 b  _int main() {    key_t key = ftok("msgqueue", 65);    int msgid = msgget(key, 0666 | IPC_CREAT);    struct msgbuf message;
    ! U% J7 H2 M/ }( h" W9 v7 u    message.mtype = 1; // 消息类型    snprintf(message.mtext, sizeof(message.mtext), "Hello Message Queue");    msgsnd(msgid, &message, sizeof(message.mtext), 0);
    4 O. S, w2 B7 K    return 0;}
    & c( N2 S& _! ^# K7 |, d4
    8 Q7 Z+ e9 S/ J共享内存(Shared Memory)5 n# u9 P0 v; h5 O( W2 {$ X, [3 V
    共享内存是最快的 IPC 机制之一,因为进程之间直接访问同一块内存区域,而不需要拷贝数据。
    % F6 p1 I3 x) H" z4 s; S8 M+ U$ U  c$ G& I( D
    通常使用 shmget()、shmat() 和 shmdt() 函数进行共享内存的创建和访问。
    5 t$ @5 q4 n3 |' y
    - R/ [" n( ^2 C( n' v$ s示例$ u) k# I" L8 r* A" l( g' m
    9 K7 G6 M9 f' S& y- G
  • int main() {    key_t key = ftok("shmfile",65);    int shmid = shmget(key, 1024, 0666|IPC_CREAT);    char *str = (char*) shmat(shmid, (void*)0, 0);
    6 N; o+ l. D. N) J: Z2 l/ B    strcpy(str, "Hello Shared Memory");% _. k! K% d/ D0 E/ f4 x
        printf("Data written in memory: %s
    5 b( b$ R7 ^  W", str);    shmdt(str);
    : Z* T& n* {! K3 V7 P    return 0;}( @5 u% U! o( K. t, m# d
    57 C* O# J" v) J) E
    信号量(Semaphore)7 L! O4 r/ |% _8 k3 N
    信号量是一种用于进程同步的机制,通常用于控制多个进程对共享资源的访问。; L$ k- V) r9 g+ }5 p0 b

    : I+ a) ]% y/ x/ I/ ]7 m嵌入式系统中,信号量通常用来避免多个进程同时访问同一资源,防止数据竞争。
    " y# {2 }; Y7 {# U# O0 {
    ' K1 S( _7 ~) _9 o/ y示例:信号量可以通过 semget() 和 semop() 函数来操作,用于锁定或解锁资源。
      t. J2 Y* ^; ^+ d
    / q7 j. S, m8 j  m3 L
  • int main() {    key_t key = ftok("semfile",65);    int semid = semget(key, 1, 0666 | IPC_CREAT);    struct sembuf sem_lock = {0, -1, 0}; // 减1操作    struct sembuf sem_unlock = {0, 1, 0}; // 加1操作1 J- v' w* c8 N, d0 Z( r
        semop(semid, &sem_lock, 1); // 上锁    printf("Critical section) w7 ?1 v$ H3 d9 J! P# Y! u4 [' x
    ");    semop(semid, &sem_unlock, 1); // 解锁0 N7 q7 }5 e2 }: W& V# d* |
        return 0;}1 d5 t; T6 u9 g$ C6 _+ ~
    69 h' w) q- e. J8 t+ T
    套接字(Socket)
      ]9 M4 m' Z! n( u! d9 m$ Q  z3 r套接字不仅支持本地进程间通信,还可以用于网络通信。4 B5 J5 {  }+ ~/ z
    9 l  p: |+ X- A" T, Q" v
    基于套接字的 IPC 支持双向通信,比较灵活,适合嵌入式系统中进程之间需要频繁且复杂的数据交互的情况。
    ; C2 K, s6 A- {2 ?) R" |8 E1 d6 a: u8 m& z) w
    示例/ ^4 k, k+ Z; N9 W

    1 |& n" J4 e. x* F
  • int main() {    int sv[2];    socketpair(AF_UNIX, SOCK_STREAM, 0, sv);; \/ Y6 |' O+ |( `4 T; w
        if (fork() == 0) { // 子进程        close(sv[0]);        write(sv[1], "Hello from child", 16);        close(sv[1]);    } else { // 父进程        char buffer[20];        close(sv[1]);        read(sv[0], buffer, sizeof(buffer));        printf("Received: %s
    ' C# I% D- q- a" Z( [! s", buffer);        close(sv[0]);    }    return 0;}
    " C- N& ~& X4 h" E$ k71 z$ W9 v: \  ^  K3 @9 w% V7 m8 r( }& u
    信号(Signal)
    ! z8 [' A3 T( B8 j7 T0 S; {' k信号是用来通知进程发生某种事件的机制。进程可以捕获、忽略或处理信号,典型的信号包括 SIGINT(中断信号)和 SIGKILL(杀死进程信号)。/ f9 K$ }# h% X6 R* N& _; q6 G0 T
    , g# h& X+ a, g& ^8 Y$ }0 H( Q
    示例:处理 SIGINT 信号(Ctrl+C)。5 v! a  Y. Q2 ]8 S

    ) Y0 s% S2 V/ x( K" k
  • void sigint_handler(int sig) {    printf("Caught signal %d% p' o2 @% z, O$ v* H8 J1 i
    ", sig);}$ g/ D. e' E/ {/ D! s! X& I- p
    int main() {    signal(SIGINT, sigint_handler);    while (1) {        printf("Running...
    / k, ~* q. N* r  e");        sleep(1);    }    return 0;}
    . C- c" f  y1 X( _进程间通信的机制多种多样,选择合适的方式取决于应用场景的需求。, f* \) E: _8 p: p; d

    pxe3tci4tfg64088526143.jpg

    pxe3tci4tfg64088526143.jpg

      n. S" s% ~1 n  R# M( L

    qjqlous0j2064088526243.gif

    qjqlous0j2064088526243.gif

    ! m! z% A. f' G点击阅读原文,更精彩~
  • 回复

    使用道具 举报

    发表回复

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则


    联系客服 关注微信 下载APP 返回顶部 返回列表