当前位置:   article > 正文

Linux进程间通信(IPC)的五种方式_linux ipc通讯

linux ipc通讯

进程间通信(IPC)的概念:进程间通信是指在不同进程间进行信息传输或交换,IPC的方式通常有管道,消息队列,信号量,共享存储,Socket,Stream等

  1. 无名管道,UNIX系统最古老的形式

特点:①.半双工,具有固定的读写

②:只能用于父子进程兄弟进程

③:.可以看做是一种普通文件,他的读写可以用read和write等函数,但不是普通文件,他只存在于内存当中

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <string.h>
  4. #include <stdlib.h>
  5. int main(){
  6. int fd[2];
  7. char buf[128];
  8. int pid;
  9. char *writeData = "this is write_data";
  10. if(pipe(fd) == -1){
  11. printf("pipe create failed!\n");
  12. }
  13. pid = fork();
  14. if(pid == 0){
  15. sleep(1);
  16. close(fd[1]);
  17. read(fd[0],buf,128);
  18. printf("read data: %s\n",buf);
  19. exit(0);
  20. }
  21. close(fd[0]);
  22. write(fd[1],writeData,strlen(writeData));
  23. wait(NULL);
  24. return 0;
  25. }
  1. 有名管道

函数有:

mkfifo(path,mod); 创建管道

path路径名,mod模式

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <errno.h>
  5. #include <stdlib.h>
  6. int main(){
  7. if(mkfifo("./file",0600)==-1 && errno == EEXIST){
  8. printf("mkfifo failed!\n");
  9. perror("why");
  10. exit(0);
  11. }
  12. printf("mkfifo success!\n");
  13. return 0;
  14. }

这是以创建一个文件来进行管道传输的,使用这个有名管道进行通信时,要么读,要么写,不能同时

读的代码:

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <errno.h>
  5. #include <stdlib.h>
  6. #include <fcntl.h>
  7. int main(){
  8. char buf[30]={0};
  9. int n_read;
  10. int fd = open("./file",O_RDONLY);
  11. printf("open success\n");
  12. n_read = read(fd,buf,30);
  13. while(n_read > 0){
  14. sleep(1);
  15. n_read = read(fd,buf,30);
  16. printf(buf);
  17. }
  18. close(fd);
  19. return 0;
  20. }

写的代码

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <errno.h>
  5. #include <stdlib.h>
  6. #include <fcntl.h>
  7. #include <string.h>
  8. int main(){
  9. int i;
  10. char *str = "this is write data\n";
  11. int fd = open("./file",O_WRONLY);
  12. printf("open success\n");
  13. for(i = 0; i<5;i++){
  14. write(fd,str,strlen(str));
  15. }
  16. close(fd);
  17. return 0;
  18. }

3.消息队列,是消息的链接表,存放在内核中,一个消息队列由一个标识符即队列ID来标识。

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

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

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

创建队列msgget

接收消息 msgrcv

发送消息 msgsnd

操作队列msgctl

收发消息的函数原型

  1. int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
  2. ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);

代码demo6

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/ipc.h>
  4. #include <sys/msg.h>
  5. #include <string.h>
  6. /*
  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. */
  11. struct msgbuf {
  12. long mtype; /* message type, must be > 0 */
  13. char mtext[128]; /* message data */
  14. };
  15. int main(){
  16. key_t key = ftok(".",1);
  17. int msgId;
  18. struct msgbuf msgread;
  19. //创建队列
  20. if(msgId = msgget(key,IPC_CREAT|0700) == -1){
  21. printf("create queue failed\n");
  22. }
  23.         //接收消息
  24. msgrcv(msgId,&msgread,sizeof(msgread.mtext),888,0);
  25. printf("receive:%s\n",msgread.mtext);
  26. //再发消息
  27. struct msgbuf msgsend = {999,"thanks!"};
  28. msgsnd(msgId,&msgsend,strlen(msgsend.mtext),0);
  29. //删除队列
  30. msgctl(msgId,IPC_RMID,NULL);
  31. return 0;
  32. }

代码demo7

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/ipc.h>
  4. #include <sys/msg.h>
  5. #include <string.h>
  6. /*
  7. nt 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. */
  11. struct msgbuf {
  12. long mtype; /* message type, must be > 0 */
  13. char mtext[128]; /* message data */
  14. };
  15. int main(){
  16. key_t key = ftok(".",1);
  17. int msgId;
  18. struct msgbuf msgwrite = {888,"aaa"};
  19. //创建队列
  20. if(msgId = msgget(key,IPC_CREAT|0700) == -1){
  21. printf("create queue failed\n");
  22. }
  23. //发送消息
  24.         msgsnd(msgId,&msgwrite,strlen(msgwrite.mtext),0);
  25. //再接收消息
  26. struct msgbuf msgrece;
  27. msgrcv(msgId,&msgrece,sizeof(msgrece.mtext),999,0);
  28. printf("rece:%s\n",msgrece.mtext);
  29. //删除队列
  30. msgctl(msgId,IPC_RMID,NULL);
  31. return 0;
  32. }

4.共享内存

//创建或获取一个共享内存,成功则返回ID,失败返回-1

//映射共享内存(连接当前进程的地址空间和共享内存,成功返回指向共享内存的指针,失败返回-1)

//写入东西,就相当于直接copy内容到当前进程的映射地址即可

//断开连接共享内存

//删除共享内存

下面这两个是命令行指令

ipcs -m 在控制台能够查看现在共享内存的使用情况,

ipcrm -m XX 删除shmid == XX的这个共享内存

5.信号

5.1信号低级版

有分低级版和高级版

低级版就是没有进行消息的传输

代码如下

demo10,对命令行命令kill进行替换 ,可以使用kill -l 查看详细的对应字段

  1. #include <stdio.h>
  2. #include <signal.h>
  3. void handler(int signum){
  4. printf("input sinnum is : %d",signum);
  5. switch(signum){
  6. case 2:
  7. printf("SIGINT\n");
  8. break;
  9. case 9:
  10. printf("SIGKILL\n");
  11. break;
  12. case 10:
  13. printf("SIGUSR1\n");
  14. break;
  15. }
  16. printf("can't kill\n");
  17. }
  18. int main(){
  19. signal(SIGINT,handler);
  20. signal(SIGKILL,handler);
  21. signal(SIGUSR1,handler);
  22. while(1);
  23. return 0;
  24. }

demo11 ,下面的代码功能是,运行程序时传入signum和pid参数,对应的是你要在命令行执行的

kill -XX XX的指令 例如 kill -2 进程ID,原本执行的功能是中断,但替换后不会执行原有功能,而是执行我们自己编写的handler的功能

  1. #include <stdio.h>
  2. #include <signal.h>
  3. #include <sys/types.h>
  4. int main(int argc,char **argv){
  5. int signum;
  6. int pid;
  7. char cmd[128] = {0};
  8. //这里要把数组里的字符串转换为int,用atoi char to int
  9. signum = atoi(argv[1]);
  10. pid = atoi(argv[2]);
  11. sprintf(cmd,"kill -%d %d",signum,pid);
  12. printf(cmd);
  13. system(cmd);
  14. return 0;
  15. }

5.2信号高级版

就是加入的消息的传输,个人认为有点操蛋。直接上代码吧。

函数原型

  1. //函数原型
  2. //int sigaction(int signum, const struct sigaction *act,
  3. // struct sigaction *oldact);
  4. /**
  5. *
  6. *struct sigaction {
  7. void (*sa_handler)(int);
  8. void (*sa_sigaction)(int, siginfo_t *, void *);
  9. sigset_t sa_mask;
  10. int sa_flags;
  11. void (*sa_restorer)(void);
  12. };
  13. */

具体使用代码

接收端:

  1. #include <stdio.h>
  2. #include <signal.h>
  3. #include <stdlib.h>
  4. void handler(int signum,siginfo_t *info,void *context){
  5. printf("get signum : %d\n",signum);
  6. if(context != NULL){
  7. printf("siginfo1 : %d\n",info->si_int);
  8. printf("siginfo2 : %d\n",info->si_value.sival_int);
  9. }
  10. }
  11. int main(){
  12. struct sigaction act;
  13. act.sa_sigaction = handler;
  14. act.sa_flags = SA_SIGINFO;
  15. sigaction(SIGUSR1,&act,NULL);
  16. while(1);
  17. return 0;
  18. }

发送端:

  1. #include <stdio.h>
  2. #include <signal.h>
  3. int main(int argc,char **argv){
  4. int signum;
  5. int pid;
  6. signum = atoi(argv[1]);
  7. pid = atoi(argv[2]);
  8. union sigval value;
  9. value.sival_int = 100;
  10. sigqueue(pid,signum,value);
  11. printf("over\n");
  12. return 0;
  13. }

6.信号量集

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

②信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。(P操作:拿锁。V操作:放回锁)

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

④支持信号量组。

  1. //创建或获取一个信号量组,成功会返回信号量集 ID ,失败返回 -1
  2. int semget(key_t key, int nsems, int semflg);
  3. //对信号量组进行操作,改变信号量的值,成功返回 0,失败返回 -1 (用于 PV 操作)
  4. int semop(int semid, struct sembuf *sops, unsigned nsops);
  5. //控制信号量的相关信息 (用于给信号量初始化)
  6. int semctl(int semid, int semnum, int cmd, ...);

示例代码:

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/ipc.h>
  4. #include <sys/sem.h>
  5. #include <stdlib.h>
  6. //int semget(key_t key, int nsems, int semflg);
  7. union semun {
  8. int val; /* Value for SETVAL */
  9. struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
  10. unsigned short *array; /* Array for GETALL, SETALL */
  11. struct seminfo *__buf; /* Buffer for IPC_INFO
  12. (Linux-specific) */
  13. };
  14. //
  15. 对信号量组进行操作,改变信号量的值,成功返回 0,失败返回 -1 (用于 PV 操作)
  16. //int semop(int semid, struct sembuf *sops, unsigned nsops);
  17. //拿锁
  18. void getKey(int semid){
  19. struct sembuf mysem1;
  20. mysem1.sem_num = 0;
  21. mysem1.sem_op = -1;
  22. mysem1.sem_flg = SEM_UNDO;
  23. semop(semid,&mysem1,1);
  24. printf("getkey\n");
  25. }
  26. //还锁
  27. void putKey(int semid){
  28. struct sembuf mysem1;
  29. mysem1.sem_num = 0;
  30. mysem1.sem_op = 1;
  31. mysem1.sem_flg = SEM_UNDO;
  32. semop(semid,&mysem1,1);
  33. printf("putKey\n");
  34. }
  35. int main(){
  36. //创建一个信号量集
  37. key_t key;
  38. int semid = semget(key,0,IPC_CREAT|0700);
  39. //int semctl(int semid, int semnum, int cmd, ...);
  40. //初始化信号量集
  41. union semun initsem;
  42. initsem.val = 0;
  43. semctl(semid,0,SETVAL,initsem);
  44. //用父子进程来操作这个信号量
  45. int pid = fork();
  46. if(pid == 0){
  47. printf("this is son\n");
  48. //拿锁
  49. getKey(semid);
  50. exit(0);
  51. }
  52. printf("this is father\n");
  53. //还锁
  54. putKey(semid);
  55. wait();
  56. //拿锁
  57. getKey(semid);
  58. //还锁
  59. putKey(semid);
  60. return 0;
  61. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/273119
推荐阅读
相关标签
  

闽ICP备14008679号