电子产业一站式赋能平台

PCB联盟网

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

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

[复制链接]

840

主题

840

帖子

6491

积分

高级会员

Rank: 5Rank: 5

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

3rqolratvad64088525943.gif

3rqolratvad64088525943.gif

4 X; z0 v3 ~7 Q, T4 n点击上方蓝色字体,关注我们. @8 O5 `( u4 n% Y, l( k3 L

$ n" [7 N& M+ K8 X, J" M" x$ ~1.1、UNIX IPC
( [) Y/ r' F- u7 c! n% x  YUNIX 传统的 IPC 机制包括管道、FIFO 和信号,这些机制最早由 UNIX 系统引入,适用于简单的单机进程间通信。6 w0 ?1 h& S" ^5 Y* B4 R
  • 管道(Pipe)
    0 Y6 c5 N/ y7 u3 o一种单向、半双工的通信机制,通常用于父子进程间的数据传递。7 f+ l/ T5 t5 z! w! p5 o& O
    父进程可以写入数据,子进程可以读取。
  • FIFO(命名管道)
    7 @' n. ?0 i  H& x! u( ~) ?+ Z; D( ]类似于管道,但通过文件系统实现,任何进程都可以通过路径访问该管道,实现双向通信。
  • 信号(Signal)
    1 i" b1 z& Q3 A8 ?1 R. K信号是一种用于进程间异步通知的机制,可以用于进程之间的简单通信或事件通知,例如 SIGINT(Ctrl+C 发送的中断信号)。* |% k/ h3 u# P/ J

    3 t: ^1 J5 _! Y1.2、System V IPC
    : ?0 |  k9 D1 N+ Z- P5 mSystem V IPC 是 UNIX 的增强版本,主要包括信号量、消息队列和共享内存,适合需要更复杂的进程同步与数据共享的场景。5 u* L5 c, g( {5 D" K
  • 信号量(Semaphore)
    # l. [4 I# x. V用于进程间的同步,通常用于控制对共享资源的访问。) l$ z9 i+ s8 n0 C
    信号量用于防止多个进程同时访问同一资源,避免资源争用问题。
  • 消息队列(Message Queue)
    $ T* B9 }- g2 l! c) v7 o允许进程以消息的形式发送和接收数据。
      V1 s: R+ o! o消息队列是一种先进先出(FIFO)的结构,支持不同类型的消息,使得进程可以基于消息类型进行处理。
  • 共享内存(Shared Memory)
    ' z2 q3 S7 r. W/ q  Y- ~/ f进程之间共享同一块内存区域,允许它们直接读写数据。
    . I) P6 w. Y5 g+ u  f: z# n这是最有效的 IPC 方式,因为数据不需要在进程之间复制。- {6 y8 V; r! K/ t; z! d( W$ }

    " M& U4 v6 P3 L1.3、POSIX IPC
    4 N) D9 I3 S, d* c! ]. L7 f3 jPOSIX IPC 是 System V IPC 的改进版本,旨在解决 System V IPC 在灵活性和可移植性上的一些不足。2 a- ?- R# j1 R1 \2 U( N
    ; g3 u+ w1 q5 ?" L
    POSIX 标准为 UNIX 系统间的兼容性提供了统一的接口,使得程序可以更方便地在不同的 UNIX 系统间移植。: {/ L4 d6 l& M6 _% Q. C
  • POSIX 信号量
    , o- n+ ^8 L$ V' G6 P$ |与 System V 信号量类似,用于进程同步,但提供了更灵活的接口和更强的实时性支持。
  • POSIX 消息队列
    2 i- i" R% G. p/ g+ e改进了 System V 消息队列,允许指定消息的优先级,并提供更简单的接口。
  • POSIX 共享内存
      j  p' w# ?0 u1 m5 p* s与 System V 共享内存类似,但具有更好的兼容性和可移植性,接口设计更加现代化。$ ~6 F; z; H8 Y+ L! E- y0 ]: N

    9 L. ]: U8 d4 L. N1.4、套接字(Socket)通信
    , J7 [% H& z8 P; R3 i2 A  [套接字是一种既可以用于本地进程间通信,也可以用于网络通信的机制,支持双向数据传输。
    * L+ {" X* G2 z( V' g) W6 h
    1 O1 \8 [$ z5 ]+ v. d0 J基于套接字的 IPC 可以实现非常灵活的通信模式,例如客户端-服务器架构,适合在多台计算机之间传递数据。+ o3 Y& w0 c! @5 m- G
    3 k, r, B# P* B3 n0 o, g- F! e' D
    各类 IPC 机制的对比和应用场景:
    $ Z: t* n* d" A* r; t* J
    3 v, t, K9 c. D0 s) m  f6 {# F

    bsbbcgpudkr64088526043.png

    bsbbcgpudkr64088526043.png

    , [1 f, c" u& x. z' n& ?' i! K. M- ]) j$ f' u, A
    23 w9 Z5 i: c' N
    管道(Pipe), S  |6 J9 @" X  i8 h
    管道是一种半双工(单向)的通信方式,通常用于父子进程之间的通信。一个进程可以向管道写入数据,另一个进程从管道读取数据。
    ! Y' c* y8 M" d7 ^" X8 c6 Q' M* h4 S9 E) T& I0 O
    Linux 提供了无名管道和命名管道两种类型。
    . p% J. f; A9 H, h( a  u
  • 无名管道(Anonymous Pipe)  U$ C- t- ?- q5 j# e  [% d1 R
    只能在具有亲缘关系的进程间使用,比如父进程和子进程。
  • 命名管道(Named Pipe 或 FIFO)
    8 w7 d' @5 S. s6 M# i通过文件系统中的路径来创建,任意进程都可以访问。# F3 {. d2 E  u$ g8 T

    & Z2 d) b" V4 c' r/ y
    1 M7 \, r; W8 f
    示例! L( N8 A9 q/ C* R) ?* u" Y8 o; r

    ! d! W5 ]4 _. m9 q3 L
  • int main() {    int fd[2];    pipe(fd); // 创建无名管道
    % I8 ^, H7 c' |0 U9 S$ s+ s    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: %s4 v3 r0 O0 q7 u! d" h
    ", buffer);        close(fd[0]);    }    return 0;}! r4 q! G6 t( E& t9 l# W
    31 ?! G$ x$ n# z$ s
    消息队列(Message Queue)
    . B1 r6 U! @$ L2 @消息队列是一种先进先出的队列,允许进程以消息的形式发送和接收数据。3 D9 S  M. T" Q8 k. q; I
    $ J, `3 @* X7 g5 D
    消息队列可以支持多种类型的消息,通过消息类型实现多种目的的通信。# F- d. i6 _; M) V; p) q2 I) X* g+ _

    0 c/ L( D; j( ^5 R: k4 l3 _示例:进程A可以向队列发送一个带有特定类型的消息,而进程B可以根据消息类型进行处理。
    6 S" _3 O2 i+ p, Q( E+ S$ ~
    7 h+ A0 K$ o" C" y
  • struct msgbuf {    long mtype;    char mtext[100];};
      F* O, f. ?( z: o4 p: s5 hint main() {    key_t key = ftok("msgqueue", 65);    int msgid = msgget(key, 0666 | IPC_CREAT);    struct msgbuf message;
    . r& ]/ J' q% O; k, E( B1 U    message.mtype = 1; // 消息类型    snprintf(message.mtext, sizeof(message.mtext), "Hello Message Queue");    msgsnd(msgid, &message, sizeof(message.mtext), 0);6 ~( L* e& S0 }' T: b! o
        return 0;}3 G, o& E0 ^# e# M' O) P* v
    4& _, U3 V4 h4 M
    共享内存(Shared Memory)" P0 {8 J# b: R9 g* w" C
    共享内存是最快的 IPC 机制之一,因为进程之间直接访问同一块内存区域,而不需要拷贝数据。
    3 c7 ^5 n* a0 S3 @) f. i  h1 [1 [" p/ a  m, |: T
    通常使用 shmget()、shmat() 和 shmdt() 函数进行共享内存的创建和访问。: K  I  k( d. s' v, [  F
    7 I3 y& b! K! Q0 U9 O
    示例4 v* a( f0 R* q' A- t; ?, L# |

    ( v! E* r2 i" Q: d
  • int main() {    key_t key = ftok("shmfile",65);    int shmid = shmget(key, 1024, 0666|IPC_CREAT);    char *str = (char*) shmat(shmid, (void*)0, 0);! f5 f3 |' H' F. E) U
        strcpy(str, "Hello Shared Memory");: l' S! |. n( U# ]/ z' Z
        printf("Data written in memory: %s
      v1 J( u+ i7 j+ l, m/ L) W6 b", str);    shmdt(str);0 T' p: R$ s- Z  B% b
        return 0;}: N2 f/ s$ O4 @/ G4 m
    5
    ) }  h4 E4 F$ f& u! n  y信号量(Semaphore)
    5 b% [7 g, D6 _/ P# Q' l. D信号量是一种用于进程同步的机制,通常用于控制多个进程对共享资源的访问。
    " j$ g# X; C0 M7 p( g: Q. i0 j" W- M8 n
    嵌入式系统中,信号量通常用来避免多个进程同时访问同一资源,防止数据竞争。
    . n. J) I% `2 G, n  i9 X" R" x- L* U# V' Y0 M" z4 `( o0 \4 E; r: ~
    示例:信号量可以通过 semget() 和 semop() 函数来操作,用于锁定或解锁资源。, i9 K% T' W( f' ]+ G! {) M

    $ I2 F" [# _) y) p. i' N0 W$ M
  • 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操作9 U9 M( O) ?$ G' o: A% ^, L
        semop(semid, &sem_lock, 1); // 上锁    printf("Critical section- h0 w6 z% F( \) F" K8 V
    ");    semop(semid, &sem_unlock, 1); // 解锁, f3 M* E4 z9 B
        return 0;}' |# n+ D/ j! K
    6
    1 F/ r) ]8 \/ E' U2 u. ?套接字(Socket)/ J* `3 D0 Q5 d4 R) D4 J# i
    套接字不仅支持本地进程间通信,还可以用于网络通信。# b5 h( S0 l, X

    ; f* b4 {) y9 s- G; i: s基于套接字的 IPC 支持双向通信,比较灵活,适合嵌入式系统中进程之间需要频繁且复杂的数据交互的情况。
    ) [. B$ m  N- t* \  ]; W: z
    + i( V( ^: `) w2 N6 H示例- v8 G( |4 z9 k8 S2 g0 C' i8 B) i( v

    ; _: b. ]& Q0 b4 Q5 Q3 m5 l! t
  • int main() {    int sv[2];    socketpair(AF_UNIX, SOCK_STREAM, 0, sv);7 y4 i8 m0 d+ }1 Z% a
        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
    2 p' e% i% T* n" l. V/ R8 ]1 o9 \", buffer);        close(sv[0]);    }    return 0;}5 `/ q; P3 y3 ^. \# c% K
    7' y  U5 E& V5 k. Q7 M$ `, m6 c
    信号(Signal)
    7 l, @5 o% t' z$ D信号是用来通知进程发生某种事件的机制。进程可以捕获、忽略或处理信号,典型的信号包括 SIGINT(中断信号)和 SIGKILL(杀死进程信号)。9 n% y! b" A& ?* g; `7 X  d
      B* p1 P2 Q6 [7 H; O% G
    示例:处理 SIGINT 信号(Ctrl+C)。
    , I: [+ `* j4 t9 G
    3 v- X; _/ Y$ _6 T6 {/ h1 s
  • void sigint_handler(int sig) {    printf("Caught signal %d+ O% W5 c! L- @& T1 M( o$ u
    ", sig);}
    ( f8 l. C% T' |4 _; {- x  ]int main() {    signal(SIGINT, sigint_handler);    while (1) {        printf("Running...+ t) j6 K+ e% ~8 Z3 [) d' P
    ");        sleep(1);    }    return 0;}$ p  |. u7 w8 [- l2 ~6 H
    进程间通信的机制多种多样,选择合适的方式取决于应用场景的需求。
    9 |; S4 S8 s; y

    pxe3tci4tfg64088526143.jpg

    pxe3tci4tfg64088526143.jpg
    : M) c4 `. T  x+ O

    qjqlous0j2064088526243.gif

    qjqlous0j2064088526243.gif
    # k  A3 H$ H8 }' a$ k
    点击阅读原文,更精彩~
  • 回复

    使用道具 举报

    发表回复

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

    本版积分规则


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