电子产业一站式赋能平台

PCB联盟网

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

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

[复制链接]

1001

主题

1001

帖子

8805

积分

高级会员

Rank: 5Rank: 5

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

3rqolratvad64088525943.gif

3rqolratvad64088525943.gif

0 x8 k' E2 b% {6 i点击上方蓝色字体,关注我们2 f  T# N" \3 H% l
' ?2 L* T4 X* m6 \
1.1、UNIX IPC
" f" n; F! u0 L( |$ nUNIX 传统的 IPC 机制包括管道、FIFO 和信号,这些机制最早由 UNIX 系统引入,适用于简单的单机进程间通信。
/ s: |8 x, M' u( J/ {+ J
  • 管道(Pipe)
      M( {% p. [- o7 |# y0 ^一种单向、半双工的通信机制,通常用于父子进程间的数据传递。
    ; n' Q& \; d# B父进程可以写入数据,子进程可以读取。
  • FIFO(命名管道)8 |9 R( y, S& s) n" @! n6 D9 f
    类似于管道,但通过文件系统实现,任何进程都可以通过路径访问该管道,实现双向通信。
  • 信号(Signal)
    7 m) C% Y7 x9 q  ]1 ~" o信号是一种用于进程间异步通知的机制,可以用于进程之间的简单通信或事件通知,例如 SIGINT(Ctrl+C 发送的中断信号)。
      e3 y+ n. v  b' {

    4 a- V$ b1 L1 M1.2、System V IPC+ m6 w( Y8 H8 ~9 |! l
    System V IPC 是 UNIX 的增强版本,主要包括信号量、消息队列和共享内存,适合需要更复杂的进程同步与数据共享的场景。/ v8 @* f8 \# s2 R; u
  • 信号量(Semaphore); q1 v/ V4 q1 f" F, \% L, u1 t0 Z
    用于进程间的同步,通常用于控制对共享资源的访问。
    " r( _& x; K' i6 N) s- \信号量用于防止多个进程同时访问同一资源,避免资源争用问题。
  • 消息队列(Message Queue)
    ; Q) T$ H% G( p1 ^& p允许进程以消息的形式发送和接收数据。
    ! {! b& C4 a- U) b3 ^8 s+ Q  {消息队列是一种先进先出(FIFO)的结构,支持不同类型的消息,使得进程可以基于消息类型进行处理。
  • 共享内存(Shared Memory)& w' x$ f' S7 u/ q' P6 ^; N
    进程之间共享同一块内存区域,允许它们直接读写数据。
    5 _3 G: ?9 E" ]这是最有效的 IPC 方式,因为数据不需要在进程之间复制。
    4 M3 W; y, Y0 T( T7 X

    7 ~# O. a' h+ [$ q- K# o  x$ B, H1.3、POSIX IPC
    1 ]" Y7 m2 T; E$ H; [. o' X: zPOSIX IPC 是 System V IPC 的改进版本,旨在解决 System V IPC 在灵活性和可移植性上的一些不足。
    ! K' _; ?+ w, G" k3 x/ ^8 y; M6 G
    + |! L& [% E! N3 x: Z8 GPOSIX 标准为 UNIX 系统间的兼容性提供了统一的接口,使得程序可以更方便地在不同的 UNIX 系统间移植。
    1 G% W* _9 g* T$ v+ F5 N- G
  • POSIX 信号量
    9 q( B% u% L% o  K, X# F与 System V 信号量类似,用于进程同步,但提供了更灵活的接口和更强的实时性支持。
  • POSIX 消息队列
    ( ]) @! l+ s" C" M: h5 z" q7 l改进了 System V 消息队列,允许指定消息的优先级,并提供更简单的接口。
  • POSIX 共享内存( g+ w6 @) b5 H' U) |6 y$ x. ~
    与 System V 共享内存类似,但具有更好的兼容性和可移植性,接口设计更加现代化。
    % q6 I5 ^% [! I  @: D6 J

    - h+ H8 U: A8 R5 Q" ~1.4、套接字(Socket)通信7 J8 I$ D6 m+ M- H& z% }1 Z
    套接字是一种既可以用于本地进程间通信,也可以用于网络通信的机制,支持双向数据传输。" R$ Y: ]4 q5 w6 D

    ) D) _2 v+ v+ s9 P, H) l3 h! a基于套接字的 IPC 可以实现非常灵活的通信模式,例如客户端-服务器架构,适合在多台计算机之间传递数据。! [& \1 F9 ]$ e9 e6 S6 M
    . ]( n) @. r: R; F$ r6 Z
    各类 IPC 机制的对比和应用场景:
    ( Z/ t3 J! i4 m+ j
    ; A; O8 L2 q( \9 K) Q

    bsbbcgpudkr64088526043.png

    bsbbcgpudkr64088526043.png
      d* Q4 p: `5 [; x& q' T9 C

    ) p1 S7 q; `; s$ f9 p/ O; ^2
    8 {/ i* E" b' U: \& |管道(Pipe)
    / M: o( H% F" b" ]$ f& }$ N& K# f管道是一种半双工(单向)的通信方式,通常用于父子进程之间的通信。一个进程可以向管道写入数据,另一个进程从管道读取数据。
    ) ]' Q  Q. S& J
    . Q4 S4 M$ t& v8 o6 ?Linux 提供了无名管道和命名管道两种类型。
    / x" F: S7 G  y  ~9 b2 S0 ]
  • 无名管道(Anonymous Pipe): u4 ]# B9 j8 s2 ^
    只能在具有亲缘关系的进程间使用,比如父进程和子进程。
  • 命名管道(Named Pipe 或 FIFO)
    " h0 ?1 n2 u; ^) N* N/ Y* B( j( X通过文件系统中的路径来创建,任意进程都可以访问。
    ! v9 X& }, t1 f5 }; F& @& p! D

    3 M( y  u. H' b! w5 `2 M2 v( M

    0 [+ y3 X7 ~; @) i+ M* L: v示例
    3 g) E/ |7 j4 J7 n3 N: v: t' e$ x( d
  • int main() {    int fd[2];    pipe(fd); // 创建无名管道) [) r5 w) e+ r: p# c) u
        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
    # N; K! X/ Y% }' |", buffer);        close(fd[0]);    }    return 0;}- z( \1 F# _! q2 P
    37 o5 u. o& l% i' N% s* p; G% E
    消息队列(Message Queue); v; @1 l& Q1 H5 G, l+ A4 ]% K% y! H
    消息队列是一种先进先出的队列,允许进程以消息的形式发送和接收数据。0 Y  ]5 Y. V0 U9 c6 J, G3 A& C
    # {$ @- d% T( P( n; N( |! |4 M0 [
    消息队列可以支持多种类型的消息,通过消息类型实现多种目的的通信。0 h" j; M( ~  j: I( S& b' t
    + Y6 J9 X7 a' J
    示例:进程A可以向队列发送一个带有特定类型的消息,而进程B可以根据消息类型进行处理。
    5 V# s! [' a$ {. `4 c6 Q. T
    * |! N1 m8 X$ [9 ?# v4 b
  • struct msgbuf {    long mtype;    char mtext[100];};
      r& y* g/ R! _. B8 b7 M" |- Mint main() {    key_t key = ftok("msgqueue", 65);    int msgid = msgget(key, 0666 | IPC_CREAT);    struct msgbuf message;6 a  `% [6 p1 }3 h2 k9 l8 e
        message.mtype = 1; // 消息类型    snprintf(message.mtext, sizeof(message.mtext), "Hello Message Queue");    msgsnd(msgid, &message, sizeof(message.mtext), 0);+ E* ~4 P: h" P% s8 ?4 f1 I  A6 y/ w7 h
        return 0;}
    4 X( J7 M' y0 x; |3 E0 [46 j& T8 ~/ X# H* t) P9 t6 a
    共享内存(Shared Memory)
    2 R" a, Y3 D3 |* K$ j7 c共享内存是最快的 IPC 机制之一,因为进程之间直接访问同一块内存区域,而不需要拷贝数据。
    ) ^# T- W7 X/ M2 t( S# ~
    ! k1 L) f4 H& b- G. l+ A% J; K; R通常使用 shmget()、shmat() 和 shmdt() 函数进行共享内存的创建和访问。
    % ]+ D% J$ E, q( r1 \" [: t" N0 X
      [6 ^2 v/ x8 h示例
    3 `! e& K& c; j: V5 i7 B
    3 r2 p0 d  G/ m' g% I1 n9 i* o: j
  • int main() {    key_t key = ftok("shmfile",65);    int shmid = shmget(key, 1024, 0666|IPC_CREAT);    char *str = (char*) shmat(shmid, (void*)0, 0);
    $ c/ _% n! V0 o$ h    strcpy(str, "Hello Shared Memory");
    : |- e* H2 i0 Q# z2 }& L4 {    printf("Data written in memory: %s
    6 [( @' `! i4 m( C", str);    shmdt(str);
    8 m0 Z4 ~/ A! _, V' A/ i% R, I    return 0;}
    & I* T# m; c6 g. F) B5
    / k* z, K) T5 z" i$ r* a) w信号量(Semaphore)1 Q$ O1 C4 h# I( `% \' |+ R3 a
    信号量是一种用于进程同步的机制,通常用于控制多个进程对共享资源的访问。% ]: e% K$ d2 n3 ?
    & z+ ]6 U- _' I* k* m3 E0 ^+ N
    嵌入式系统中,信号量通常用来避免多个进程同时访问同一资源,防止数据竞争。
    $ z; m: y9 s3 {0 r9 d, a$ A
    5 _3 H; f$ }0 z% x4 \5 c  z示例:信号量可以通过 semget() 和 semop() 函数来操作,用于锁定或解锁资源。% E/ E3 {0 C% G

    1 |/ x" n5 k; R# v; u- h9 v4 Y. B1 `
  • 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操作( ~% j8 c# Z4 R# ~$ }% M
        semop(semid, &sem_lock, 1); // 上锁    printf("Critical section3 l) a0 \5 r, T: s2 k
    ");    semop(semid, &sem_unlock, 1); // 解锁
    8 i6 \& S8 ~0 U# B( M    return 0;}5 G& O3 ?# Y- L& I
    60 U' M5 }/ F* Z# t
    套接字(Socket)3 X, X% r( E% y* [$ [& G
    套接字不仅支持本地进程间通信,还可以用于网络通信。) K' ]( j) L/ u( B! r: O

    * [* U. ^9 h; T% @基于套接字的 IPC 支持双向通信,比较灵活,适合嵌入式系统中进程之间需要频繁且复杂的数据交互的情况。2 v+ x% |6 h  j% E

    . g6 C1 V+ c4 F示例
    $ `1 f) a) Z, O- S" `
    ! C: w+ `9 G) t& O7 S
  • int main() {    int sv[2];    socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
    0 g, v, y' Z; G" {    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
      k" O6 r/ X3 @& q& a- A", buffer);        close(sv[0]);    }    return 0;}7 t4 L# G5 O4 T) [# d0 R
    7
    ) I. D+ ]& b) l信号(Signal)
    3 k9 l+ U  @8 N0 d! M) r9 E* V6 B信号是用来通知进程发生某种事件的机制。进程可以捕获、忽略或处理信号,典型的信号包括 SIGINT(中断信号)和 SIGKILL(杀死进程信号)。
    ; }1 C1 _9 L3 C% D" B$ h" `2 B; E+ u4 p/ A: u( a' E  U
    示例:处理 SIGINT 信号(Ctrl+C)。
    + H8 k5 d7 e* x. Q& H6 N
    9 k( ]* A% ]/ U/ W
  • void sigint_handler(int sig) {    printf("Caught signal %d
    0 ~1 @% u9 {. a5 ~", sig);}& w' T4 S" B' e5 D4 N! w3 g
    int main() {    signal(SIGINT, sigint_handler);    while (1) {        printf("Running...
      Z" \- s( @$ r' g. n, o");        sleep(1);    }    return 0;}
    / x/ s: [8 u/ _6 J$ C7 E进程间通信的机制多种多样,选择合适的方式取决于应用场景的需求。
    * p1 N( Y5 \6 ~" t5 L

    pxe3tci4tfg64088526143.jpg

    pxe3tci4tfg64088526143.jpg
    6 a0 [! y; D# |! T) ^% X4 B' I

    qjqlous0j2064088526243.gif

    qjqlous0j2064088526243.gif

    ! _$ U2 Y& p( ~% }% @9 d点击阅读原文,更精彩~
  • 回复

    使用道具 举报

    发表回复

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

    本版积分规则


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