• Linux系统编程之进程间通信(IPC)


    一.进程间通信概述

    进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。

    IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC

    二.管道

    1.匿名管道

    (1)特点

    1.半双工,具有固定的写端和读端

    2.只能用与具有亲缘关系的进程之间的通信(父子进程和兄弟进程之间)

    3.可以看成一种特殊的文件,不属于其他任何文件系统,只存在于内存中

    4.管道中的数据读走就没了

    (2)API

    int pipe(int pipefd[2])

    返回值:成功返回0,失败返回-1 

    fd[0]:读端,为读而打开 

    fd[1]:写端,为写而打开

    关闭管道:关闭读端和写端      

    使用write函数进行写入,使用read函数进行读出,使用close函数关闭

    1. #include <stdio.h>
    2. #include <unistd.h>
    3. #include <string.h>
    4. #include <stdlib.h>
    5. int main()
    6. {
    7. int fd[2];
    8. int pid;
    9. char buf[128];
    10. // int pipe(int pipefd[2]);
    11. if(pipe(fd) == -1){
    12. printf("creat pipe failed\n");
    13. }
    14. pid = fork();
    15. if(pid<0){
    16. printf("creat child failed\n");
    17. }
    18. else if(pid > 0){
    19. sleep(3);
    20. printf("this is father\n");
    21. close(fd[0]);
    22. write(fd[1],"hello from father",strlen("hello form father"));
    23. wait();
    24. }else{
    25. printf("this is child\n");
    26. close(fd[1]);
    27. read(fd[0],buf,128);
    28. printf("read from father: %s\n",buf);
    29. exit(0);
    30. }
    31. return 0;
    32. }

     2.有名管道

    (1)特点

    1.可以在无关的进程之间交换数据

    2.有路径名,以一种特别设备文件形式存在于文件系统中,可以用一般的文件I/O函数操作

    (2)API

    int mkfifo(const char *pathname, mode_t mode);

    返回值:成功返回0,失败返回-1

    write.c

    1. #include <sys/types.h>
    2. #include <sys/stat.h>
    3. #include <stdio.h>
    4. #include<errno.h>
    5. #include <fcntl.h>
    6. #include <string.h>
    7. // int mkfifo(const char *pathname, mode_t mode);
    8. int main()
    9. {
    10. int cnt = 0;
    11. char *str = "message from fifo";
    12. if( (mkfifo("./file",IPC_CREAT|0600) == -1) && errno!=EEXIST){
    13. printf("mkfifo failuer\n");
    14. perror("why");
    15. }
    16. int fd = open("./file",O_WRONLY);
    17. printf("write open success\n");
    18. while(1){
    19. write(fd, str, strlen(str));
    20. sleep(1);
    21. if(cnt == 5){
    22. break;
    23. }
    24. }
    25. close(fd);
    26. return 0;
    27. }

    read.c

    1. #include <sys/types.h>
    2. #include <sys/stat.h>
    3. #include <stdio.h>
    4. #include<errno.h>
    5. #include <fcntl.h>
    6. // int mkfifo(const char *pathname, mode_t mode);
    7. int main()
    8. {
    9. char buf[30] = {0};
    10. int nread = 0;
    11. if( (mkfifo("./file",0600) == -1) && errno!=EEXIST){
    12. printf("mkfifo failuer\n");
    13. perror("why");
    14. }
    15. int fd = open("./file",O_RDONLY);
    16. printf("open success\n");
    17. while(1){
    18. nread = read(fd,buf,30);
    19. printf("read %d byte from fifo,context:%s\n",nread,buf);
    20. }
    21. close(fd);
    22. return 0;
    23. }

    三.消息队列

    1.特点

    1.消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先权

    2.消息队列对立与发送与接收进程,进程终止时,消息队列及其内容并不会被删除

    3.消息队列可以实现消息的随即查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取

    2.API

    1.创建或打开消息队列

    int msgget(key_t key, int msgflg);
    返回值:成功返回队列ID,失败返回-1

    2.添加消息

     int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

    返回值:成功返回0,失败返回-1

    3.读取消息

     ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
    返回值:成功返回消息数据的长度,失败返回-1

    4.控制消息队列

    int msgctl(int msqid, int cmd, struct msqid_ds *buf);

    返回值:成功返回0,失败返回-1

    msgget创建一个新队列:

    1.如果没有与键值key对应的消息队列,并且flag中包含了IPC_CREAT标志位

    2.key参数为IPC_PRIVATE,创建一个私有消息队列

    msgrcv在读取时的type参数

    type含义
    ==0返回消息队列中的第一个消息
    >0返回队列中消息类型为type的第一个消息
    <0返回队列中消息类型值小于或等于type绝对值的消息,如果有多个,则取类型值最小的消息

    msgSend.c

    1. #include <stdio.h>
    2. #include <sys/types.h>
    3. #include <sys/ipc.h>
    4. #include <sys/msg.h>
    5. #include <string.h>
    6. // int msgget(key_t key, int msgflg);
    7. // int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    8. // ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
    9. // int msgflg);
    10. struct msgbuf {
    11. long mtype; /* message type, must be > 0 */
    12. char mtext[256]; /* message data */
    13. };
    14. int main()
    15. {
    16. //1.huoqu
    17. struct msgbuf sendBuf = {888,"this is message from quen"};
    18. struct msgbuf readBuf;
    19. memset(&readBuf,0,sizeof(struct msgbuf));
    20. key_t key;
    21. key = ftok(".",'m');
    22. printf("key=%x\n",key);
    23. int msgId = msgget(key, IPC_CREAT|0777);
    24. if(msgId == -1 ){
    25. printf("get que failuer\n");
    26. }
    27. msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);
    28. printf("send over\n");
    29. msgrcv(msgId, &readBuf,sizeof(readBuf.mtext),988,0);
    30. printf("reaturn from get:%s\n",readBuf.mtext);
    31. msgctl(msgId,IPC_RMID,NULL);
    32. return 0;
    33. }

    msgGet.c

    1. #include <stdio.h>
    2. #include <sys/types.h>
    3. #include <sys/ipc.h>
    4. #include <sys/msg.h>
    5. #include <string.h>
    6. // int msgget(key_t key, int msgflg);
    7. // int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    8. // ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
    9. // int msgflg);
    10. struct msgbuf {
    11. long mtype; /* message type, must be > 0 */
    12. char mtext[256]; /* message data */
    13. };
    14. int main()
    15. {
    16. //1.huoqu
    17. struct msgbuf readBuf;
    18. key_t key;
    19. key = ftok(".",'m');
    20. printf("key=%x\n",key);
    21. int msgId = msgget(key, IPC_CREAT|0777);
    22. if(msgId == -1 ){
    23. printf("get que failuer\n");
    24. }
    25. memset(&readBuf,0,sizeof(struct msgbuf));
    26. msgrcv(msgId, &readBuf,sizeof(readBuf.mtext),888,0);
    27. printf("read from que:%s\n",readBuf.mtext);
    28. struct msgbuf sendBuf = {988,"thank you for reach"};
    29. msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);
    30. msgctl(msgId,IPC_RMID,NULL);
    31. return 0;
    32. }

    四.共享内存

    1.注意事项

    共享内存大小以M对齐

    2.API

    1.创建或获取一个共享内存

    int shmget(key_t key, size_t size, int shmflg);

    返回值:成功返回共享内存ID,失败返回-1

    2.连接共享内存到当前进程的地址空间

    void *shmat(int shmid, const void *shmaddr, int shmflg);

    返回值:成功返回指向共享内存的指针,失败返回-1

    shmflg = 0即为可读可写

    shmaddr = 0即让内存分配共享空间

    3.断开与共享内存的连接

    int shmdt(const void *shmaddr);

    返回值:成功返回0,失败返回-1

    4.控制共享内存的相关信息

    int shmctl(int shmid, int cmd, struct shmid_ds *buf);

    返回值:成功返回0,失败返回-1

    ipcs -m:查看系统中有哪些共享内存

    ipcrm -m 共享内存ID :删除共享内存

    shmw.c

    1. #include <sys/ipc.h>
    2. #include <sys/shm.h>
    3. #include <stdlib.h>
    4. #include <stdio.h>
    5. #include <string.h>
    6. //int shmget(key_t key, size_t size, int shmflg);
    7. int main()
    8. {
    9. int shmid;
    10. char *shmaddr;
    11. key_t key;
    12. key = ftok(".",1);
    13. shmid = shmget(key,1024*4,IPC_CREAT|0666);
    14. if(shmid == -1){
    15. printf("shmget noOk\n");
    16. exit(-1);
    17. }
    18. shmaddr = shmat(shmid,0,0);
    19. printf("shmat ok\n");
    20. strcpy(shmaddr,"chenlichen");
    21. sleep(5);
    22. shmdt(shmaddr);
    23. shmctl(shmid, IPC_RMID, 0);
    24. printf("quit\n");
    25. return 0;
    26. }

    shmr.c

    1. #include <sys/ipc.h>
    2. #include <sys/shm.h>
    3. #include <stdlib.h>
    4. #include <stdio.h>
    5. #include <string.h>
    6. //int shmget(key_t key, size_t size, int shmflg);
    7. int main()
    8. {
    9. int shmid;
    10. char *shmaddr;
    11. key_t key;
    12. key = ftok(".",1);
    13. shmid = shmget(key,1024*4,0);
    14. if(shmid == -1){
    15. printf("shmget noOk\n");
    16. exit(-1);
    17. }
    18. shmaddr = shmat(shmid,0,0);
    19. printf("shmat ok\n");
    20. printf("data: %s\n:",shmaddr);
    21. shmdt(shmaddr);
    22. printf("quit\n");
    23. return 0;
    24. }

    五.信号

    1.概述

    kill -l:查看系统中所有信号

    信号处理:忽略、捕捉或默认动作

    SIGKILL、SIGSTOP不可忽略

    2.API

    1.信号处理函数的注册

    sighandler_t signal(int signum, sighandler_t handler);

    int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

    忽略信号:SIG_IGN

    kill -信号id 进程号:给指定进程发送指定信号

     struct sigaction {
                   void     (*sa_handler)(int);
                   void     (*sa_sigaction)(int, siginfo_t *, void *);        //最后一个参数非空为有数据,为空就是没数据
                   sigset_t   sa_mask;        //默认阻塞,处理一个信号
    时不处理其他信号               

                    int        sa_flags;        //SA_SIGINFO:收数据
               };

    2.信号发送函数

    int kill(pid_t pid, int sig);

     int sigqueue(pid_t pid, int sig, const union sigval value);

    union sigval {
                   int   sival_int;
                   void *sival_ptr;
               };
    signalDemo1.c

    1. #include
    2. #include
    3. // typedef void (*sighandler_t)(int);
    4. // sighandler_t signal(int signum, sighandler_t handler);
    5. void handler(int signum)
    6. {
    7. printf("get signum=%d\n",signum);
    8. switch(signum){
    9. case 2:
    10. printf("SIGINT\n");
    11. break;
    12. case 9:
    13. printf("SIGKILL\n");
    14. break;
    15. case 10:
    16. printf("SIGUSR1\n");
    17. break;
    18. }
    19. printf("never quit\n");
    20. }
    21. int main()
    22. {
    23. signal(SIGINT,SIG_IGN);
    24. signal(SIGKILL,SIG_IGN);
    25. signal(SIGUSR1,handler);
    26. while(1);
    27. return 0;
    28. }

    signalDemo1CON.c

    1. #include
    2. #include
    3. #include
    4. int main(int argc ,char **argv)
    5. {
    6. int signum;
    7. int pid;
    8. char cmd[128]={0};
    9. signum = atoi(argv[1]);
    10. pid = atoi(argv[2]);
    11. printf("num=%d,pid=%d\n",signum,pid);
    12. // kill(pid, signum);
    13. sprintf(cmd,"kill -%d %d",signum,pid);
    14. system(cmd);
    15. printf("send signal ok");
    16. return 0;
    17. }

    NiceSignal.c

    1. #include <signal.h>
    2. #include <stdio.h>
    3. // int sigaction(int signum, const struct sigaction *act,
    4. // struct sigaction *oldact);
    5. void handler(int signum , siginfo_t *info, void *context)
    6. {
    7. printf("get signum %d\n",signum);
    8. if(context != NULL){
    9. printf("from:%d\n",info->si_pid);
    10. // printf("get data=%d\n",info->si_int);
    11. // printf("get data=%d\n",info->si_value.sival_int);
    12. // printf("get data=%s\n",context);
    13. printf("get data=%d\n",*(int *)(info->si_value.sival_ptr));
    14. }
    15. }
    16. int main()
    17. {
    18. struct sigaction act;
    19. printf("pid = %d\n",getpid());
    20. act.sa_sigaction = handler;
    21. act.sa_flags = SA_SIGINFO; //be able to get message
    22. sigaction(SIGUSR1,&act,NULL);
    23. while(1);
    24. return 0;
    25. }

    sendSignal.c

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. // int sigqueue(pid_t pid, int sig, const union sigval value)
    7. int main(int argc, char **argv)
    8. {
    9. int signum;
    10. int pid;
    11. union sigval value;
    12. signum = atoi(argv[1]);
    13. pid = atoi(argv[2]);
    14. value.sival_int = 100;
    15. sigqueue(pid,signum,value);
    16. printf("%d,done\n",getpid());
    17. return 0;
    18. }

    六.信号量

    信号量是一个计数器,用于实现进程间的互斥与同步,而不是用于存储进程间通信数据

    1.特点

    1.信号量用于进程间同步,若要在进程间传递数据需结合共享内存

    2.信号量基于操作系统的PV操作,程序对信号量的操作都是原子操作

    3.每次对信号量的PV操作不仅限于对信号量值加1或减1,而且可以加减任意正整数

    4.支持信号量组

    2.API

    1.创建或获取一个信号量组

    int semget(key_t key, int nsems, int semflg);

    返回值:返回成功返回信号量集ID,失败返回-1

    2.对信号量组进行操作

    int semop(int semid, struct sembuf *sops, size_t nsops);

    返回值:成功返回0,失败返回-1

    3.控制信号量的相关信息

    int semctl(int semid, int semnum, int cmd, ...);

    返回值:成功返回0,失败返回-1

    P操作:取信号量

    V操作:存信号量

    1. #include <stdio.h>
    2. #include <sys/types.h>
    3. #include <sys/ipc.h>
    4. #include <sys/sem.h>
    5. #include <unistd.h>
    6. // int semget(key_t key, int nsems, int semflg);
    7. // int semctl(int semid, int semnum, int cmd, ...);
    8. // int semop(int semid, struct sembuf *sops, size_t nsops);
    9. union semun {
    10. int val; /* Value for SETVAL */
    11. struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
    12. unsigned short *array; /* Array for GETALL, SETALL */
    13. struct seminfo *__buf; /* Buffer for IPC_INFO
    14. (Linux-specific) */
    15. };
    16. void pHandler(int semid)
    17. {
    18. struct sembuf sop;
    19. sop.sem_num = 0;
    20. sop.sem_op = -1;
    21. sop.sem_flg = SEM_UNDO;
    22. semop(semid,&sop,1);
    23. printf("get key\n");
    24. }
    25. void vHandler(int semid)
    26. {
    27. struct sembuf sop;
    28. sop.sem_num = 0;
    29. sop.sem_op = 1;
    30. sop.sem_flg = SEM_UNDO;
    31. semop(semid,&sop,1);
    32. printf("put back key\n");
    33. }
    34. int main()
    35. {
    36. key_t key;
    37. int semid;
    38. union semun initsem;
    39. int pid;
    40. key = ftok(".",2);
    41. semid = semget(key,1,IPC_CREAT|0666);
    42. initsem.val = 0;
    43. semctl(semid,0,SETVAL,initsem);
    44. pid = fork();
    45. if(pid > 0){
    46. pHandler(semid);
    47. printf("this is father\n");
    48. vHandler(semid);
    49. semctl(semid,0,IPC_RMID);
    50. }else if(pid == 0){
    51. printf("this is child\n");
    52. vHandler(semid);
    53. }else{
    54. printf("fork falied\n");
    55. }
    56. return 0;
    57. }

    3.信号量与共享内存结合

    send.c

    1. #include <stdio.h>
    2. #include <sys/types.h>
    3. #include <sys/ipc.h>
    4. #include <sys/sem.h>
    5. #include <sys/shm.h>
    6. #include <unistd.h>
    7. // int semget(key_t key, int nsems, int semflg);
    8. union semun {
    9. int val; /* Value for SETVAL */
    10. struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
    11. unsigned short *array; /* Array for GETALL, SETALL */
    12. struct seminfo *__buf; /* Buffer for IPC_INFO
    13. (Linux-specific) */
    14. };
    15. void pHandler(int semid)
    16. {
    17. struct sembuf sop;
    18. sop.sem_num = 0;
    19. sop.sem_op = -1;
    20. sop.sem_flg = SEM_UNDO;
    21. semop(semid,&sop,1);
    22. printf("get key\n");
    23. }
    24. void vHandler(int semid)
    25. {
    26. struct sembuf sop;
    27. sop.sem_num = 0;
    28. sop.sem_op = 1;
    29. sop.sem_flg = SEM_UNDO;
    30. semop(semid,&sop,1);
    31. printf("put back key\n");
    32. }
    33. int main()
    34. {
    35. key_t key;
    36. int semid;
    37. union semun initsem;
    38. int shmid;
    39. char *shm;
    40. key = ftok(".",3);
    41. semid = semget(key,1,IPC_CREAT|0666);
    42. printf("semid = %d\n",semid);
    43. initsem.val = 0;
    44. semctl(semid,0,SETVAL,initsem);
    45. shmid = shmget(key,1024*4,IPC_CREAT|0666);
    46. shm = shmat(shmid,0,0);
    47. sprintf(shm,"ccy handsome");
    48. vHandler(semid);
    49. sleep(5);
    50. pHandler(semid);
    51. printf("context:%s\n",shm);
    52. vHandler(semid);
    53. shmdt(shm);
    54. shmctl(shmid,IPC_RMID,NULL);
    55. return 0;
    56. }

    read.c

    1. #include <stdio.h>
    2. #include <sys/types.h>
    3. #include <sys/ipc.h>
    4. #include <sys/sem.h>
    5. #include <sys/shm.h>
    6. #include <unistd.h>
    7. // int semget(key_t key, int nsems, int semflg);
    8. union semun {
    9. int val; /* Value for SETVAL */
    10. struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
    11. unsigned short *array; /* Array for GETALL, SETALL */
    12. struct seminfo *__buf; /* Buffer for IPC_INFO
    13. (Linux-specific) */
    14. };
    15. void pHandler(int semid)
    16. {
    17. struct sembuf sop;
    18. sop.sem_num = 0;
    19. sop.sem_op = -1;
    20. sop.sem_flg = SEM_UNDO;
    21. semop(semid,&sop,1);
    22. printf("get key\n");
    23. }
    24. void vHandler(int semid)
    25. {
    26. struct sembuf sop;
    27. sop.sem_num = 0;
    28. sop.sem_op = 1;
    29. sop.sem_flg = SEM_UNDO;
    30. semop(semid,&sop,1);
    31. printf("put back key\n");
    32. }
    33. int main()
    34. {
    35. key_t key;
    36. int semid;
    37. union semun initsem;
    38. int shmid;
    39. char *shm;
    40. key = ftok(".",3);
    41. semid = semget(key,1,IPC_CREAT|0666);
    42. printf("semid = %d\n",semid);
    43. initsem.val = 0;
    44. semctl(semid,0,SETVAL,initsem);
    45. shmid = shmget(key,1024*4,IPC_CREAT|0666);
    46. shm = shmat(shmid,0,0);
    47. pHandler(semid);
    48. printf("context:%s\n",shm);
    49. vHandler(semid);
    50. pHandler(semid);
    51. sprintf(shm,"thank you for using");
    52. vHandler(semid);
    53. shmdt(shm);
    54. shmctl(shmid,IPC_RMID,NULL);
    55. return 0;
    56. }

  • 相关阅读:
    MySql数据库入门的基本操作
    弹性资源组件elastic-resource设计(二)-集群
    ChatGPT 和 Midjourney 将改变我们的生活,日常工作流程将完全改变并与这些新型工具集成
    HS6621Cx 一款低功耗蓝牙SoC芯片 应用于键盘、鼠标和遥控器消费类产品
    Facebook:数字时代的文化交流平台
    Object构造函数的方法
    Python:实现日历到excel文档
    谈谈什么是数据质量管理
    管理会计学复习题集
    自动驾驶 知识点 Review 2D 感知算法 二(单阶段法 Yolo系列,SSD系列,RetinaNet)
  • 原文地址:https://blog.csdn.net/2303_77402228/article/details/139349958