当前位置:   article > 正文

Linux进程间通信_linux 进程通信

linux 进程通信

目录

进程间通信的目的

进程间通信的几种方式

无名管道

命名管道

创建键值函数ftok()

消息队列

创建或引用一个消息队列msgget()

向消息队列中添加消息msgsnd()

从消息队列中读取消息msgrcv()

控制消息队列msgctl()

共享内存

创建或引用一个共享内存shmget()

将共享内存链接到当前进程的地址空间shmat()

断开共享内存与当前进程的地址空间的链接shmdt()

控制共享内存shmc

管道、消息队列、共享内存的区别

信号

向某个进程发送信号函数kill()

信号接收函数signal()

高级信号发送函数sigqueue(),比kill()在发射信号时能携带更多信息

高级信号接收函数sigaction(),比signal()在接收信号时能获取更多信息

信号量

 创建一个信号量集semget()

控制信号量集或获取信号量的信息semctl()

信号量控制共享资源的操作,即PV操作semop()

进程间通信的目的

  • 数据传输:一个进程需要将它的数据发送给另一个进程
  • 资源共享:多个进程之间共享同样的资源
  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知它发生了某种事件。
  • 进程控制:有些进程希望完全控制另一个进程的执行(如 Debug进程),此时控制进程希望能够拦截另 一个进程的所有陷入和异常,并能够及时知道它的状态改变

进程间通信的几种方式

无名管道

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <unistd.h>
  4. #include <stdlib.h>
  5. #include <sys/wait.h>
  6. int main()
  7. {
  8. pid_t pid;
  9. int fd[2];
  10. char readBuf[128];
  11. if(pipe(fd) < 0){
  12. perror("pipe");
  13. exit(-1);
  14. }
  15. pid = vfork();
  16. if(pid == 0){
  17. printf("this is child process\n");
  18. close(fd[0]);
  19. write(fd[1],"my name is child process,haozige handsome",128);
  20. printf("child process is done\n");
  21. exit(0);
  22. }else if(pid > 0){
  23. printf("this is father process\n");
  24. wait(NULL);
  25. close(fd[1]);
  26. read(fd[0],readBuf,128);
  27. printf("from child process content is %s\n",readBuf);
  28. printf("father process is done\n");
  29. }else{
  30. printf("fork is fail\n");
  31. perror("fork");
  32. exit(-1);
  33. }
  34. return 0;
  35. }

命名管道

与无名管道不同的是,命名管道可以在没有关系的两个进程间进行通信

由于read()函数在读不到数据时会阻塞,因此我们在读数据的进程创建命名管道,并阻塞等待写数据的进程向命名管道写入数据 ,在创建命名管道时,给管道的权限建议是0600,即可读可写可执行

读数据进程

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/types.h>
  4. #include <sys/stat.h>
  5. #include <fcntl.h>
  6. #include <unistd.h>
  7. #include <string.h>
  8. int main(int argc,char *argv[])
  9. {
  10. if(argc < 2){
  11. printf("missing paramiter -> FIFO");
  12. exit(-1);
  13. }
  14. char readBuf[128];
  15. int readSize = 0;
  16. if(mkfifo(argv[1],0600) < 0){
  17. perror("mkfifo");
  18. exit(-1);
  19. }
  20. int fd = open(argv[1],O_RDONLY);
  21. if(fd < 0){
  22. perror("open");
  23. exit(-1);
  24. }
  25. while(1){
  26. memset(readBuf,'\0',128);
  27. readSize = read(fd,readBuf,128);
  28. printf("read data from FIFO %s, content is %s\n",argv[1],readBuf);
  29. }
  30. close(fd);
  31. return 0;
  32. }

命名管道是类似创建文件的形式创建的  

写数据进程

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/types.h>
  4. #include <sys/stat.h>
  5. #include <fcntl.h>
  6. #include <unistd.h>
  7. #include <string.h>
  8. int main(int argc,char *argv[])
  9. {
  10. if(argc < 2){
  11. printf("missing paramiter -> FIFO");
  12. exit(-1);
  13. }
  14. char Msg[128] = "from write FIFO send jiangxiaoya is pigHead";
  15. int fd = open(argv[1],O_WRONLY);
  16. while(1){
  17. write(fd,Msg,strlen(Msg));
  18. sleep(1);
  19. }
  20. close(fd);
  21. return 0;
  22. }

 当写数据进程启动后,读数据进程不再阻塞,开始读取命名管道的数据

命名管道的应用:

  • shell命令使用FIFO将数据从一条管道传送到另一条时,无需创建中间临时文件。
  • 客户进程-服务器进程应用程中,FIFO用作汇聚点,在客户进程和服务器进程二者之间传递数据。

创建键值函数ftok()

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/types.h>
  4. #include <sys/ipc.h>
  5. int main(int argc,char *argv[])
  6. {
  7. if(argc < 2){
  8. printf("missing parameter -> keyfile\n");
  9. exit(-1);
  10. }
  11. key_t key;
  12. key = ftok(argv[1],'z');
  13. if(key == -1){
  14. perror("ftok");
  15. exit(-1);
  16. }else{
  17. printf("key value is %d\n",key);
  18. }
  19. key = ftok(argv[1],159);
  20. if(key == -1){
  21. perror("ftok");
  22. exit(-1);
  23. }else{
  24. printf("key value is %d\n",key);
  25. }
  26. return 0;
  27. }

消息队列

消息队列是一个存储信息的链表,全双工通信,进程终止后,数据不会丢式 

创建或引用一个消息队列msgget()

向消息队列中添加消息msgsnd()

从消息队列中读取消息msgrcv()

控制消息队列msgctl()

 

 向消息队列添加各种消息的sendMsg进程

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/types.h>
  4. #include <sys/ipc.h>
  5. #include <sys/msg.h>
  6. #include <string.h>
  7. struct Msgbuf
  8. {
  9. long mtype;
  10. char mtext[128];
  11. };
  12. int main(int argc,char *argv[])
  13. {
  14. key_t key;
  15. key = ftok("./keyfile.txt",'z');
  16. if(key == -1){
  17. perror("ftok");
  18. exit(-1);
  19. }
  20. int msgID = msgget(key,IPC_CREAT|0777);
  21. if(msgID == -1){
  22. perror("msgget");
  23. exit(-1);
  24. }
  25. struct Msgbuf sendMsg = {
  26. 130,
  27. "this is sendMsg process"
  28. };
  29. struct Msgbuf sendMsg1 = {
  30. 181,
  31. "haozige handsome"
  32. };
  33. struct Msgbuf sendMsg2 = {
  34. 166,
  35. "jiangxiaoya pigHand"
  36. };
  37. msgsnd(msgID,&sendMsg,strlen(sendMsg.mtext),0);
  38. msgsnd(msgID,&sendMsg1,strlen(sendMsg1.mtext),0);
  39. msgsnd(msgID,&sendMsg2,strlen(sendMsg2.mtext),0);
  40. struct Msgbuf receive;
  41. memset(&receive,0,sizeof(receive));
  42. msgrcv(msgID,&receive,sizeof(receive.mtext),2349,0);
  43. printf("this is sendMsg process,receive msg from messageQueue,content is %s\n",receive.mtext);
  44. //msgctl(msgID,IPC_RMID,NULL);
  45. return 0;
  46. }

根据消息字段读取消息队列中的消息的receive进程,要使用同一个键值才能访问到同一个消息队列 

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/types.h>
  4. #include <sys/ipc.h>
  5. #include <sys/msg.h>
  6. #include <string.h>
  7. #include <unistd.h>
  8. struct Msgbuf
  9. {
  10. long mtype;
  11. char mtext[128];
  12. };
  13. int main(int argc,char *argv[])
  14. {
  15. key_t key;
  16. key = ftok("./keyfile.txt",'z');
  17. if(key == -1){
  18. perror("ftok");
  19. exit(-1);
  20. }
  21. int msgID = msgget(key,IPC_CREAT|0777);
  22. if(msgID == -1){
  23. perror("msgget");
  24. exit(-1);
  25. }
  26. struct Msgbuf sendMsg = {
  27. 2349,
  28. "this is receiveMsg process"
  29. };
  30. msgsnd(msgID,&sendMsg,strlen(sendMsg.mtext),0);
  31. struct Msgbuf receive;
  32. struct Msgbuf receive1;
  33. struct Msgbuf receive2;
  34. memset(&receive,0,sizeof(receive));
  35. memset(&receive1,0,sizeof(receive1));
  36. memset(&receive2,0,sizeof(receive2));
  37. msgrcv(msgID,&receive,sizeof(receive.mtext),130,0);
  38. msgrcv(msgID,&receive1,sizeof(receive1.mtext),181,0);
  39. msgrcv(msgID,&receive2,sizeof(receive2.mtext),166,0);
  40. printf("this is receiveMsg process,receive msg from messageQueue,content is %s\n",receive.mtext);
  41. sleep(5);
  42. printf("this is receiveMsg process,receive msg from messageQueue,content is %s\n",receive1.mtext);
  43. sleep(5);
  44. printf("this is receiveMsg process,receive msg from messageQueue,content is %s\n",receive2.mtext);
  45. msgctl(msgID,IPC_RMID,NULL);
  46. return 0;
  47. }

 

共享内存

创建或引用一个共享内存shmget()

将共享内存链接到当前进程的地址空间shmat()

断开共享内存与当前进程的地址空间的链接shmdt()

控制共享内存shmctl

 创建一块共享内存,writeShm进程每隔1秒向共享内存写入一个字母,readShm进程每隔一秒从共享内存读取一个字母,当从共享内存连续10s读不到数据后,会删除该共享内存

my_writeShm进程:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/types.h>
  4. #include <sys/ipc.h>
  5. #include <unistd.h>
  6. #include <string.h>
  7. #include <sys/shm.h>
  8. int main(int argc,char *argv[])
  9. {
  10. key_t key = ftok(".",'z');
  11. if(key == -1){
  12. perror("ftok");
  13. exit(-1);
  14. }
  15. int shmID = shmget(key,1024*4,IPC_CREAT|0666);
  16. if(shmID == -1){
  17. perror("shmget");
  18. exit(-1);
  19. }
  20. char *tmp = (char *)shmat(shmID,NULL,0);
  21. memset(tmp,'\0',1024*4);
  22. char *str = "abcdefghijklmnopqrstuvwxyz";
  23. while(*str != '\0'){
  24. sprintf(tmp,"%c",*str);
  25. sleep(1);
  26. *str++;
  27. }
  28. memset(tmp,'\0',1024*4);
  29. shmdt(tmp);
  30. //shmctl(shmID,IPC_RMID,NULL);
  31. return 0;
  32. }

my_readShm进程: 

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/types.h>
  4. #include <sys/ipc.h>
  5. #include <unistd.h>
  6. #include <string.h>
  7. #include <sys/shm.h>
  8. int main(int argc,char *argv[])
  9. {
  10. key_t key = ftok(".",'z');
  11. if(key == -1){
  12. perror("ftok");
  13. exit(-1);
  14. }
  15. int shmID = shmget(key,1024*4,IPC_CREAT|0666);
  16. if(shmID == -1){
  17. perror("shmget");
  18. exit(-1);
  19. }
  20. char *tmp = (char *)shmat(shmID,NULL,0);
  21. while(1){
  22. printf("shm content is %s\n",tmp);
  23. sleep(1);
  24. if(strlen(tmp) == 0){
  25. sleep(10);
  26. if(strlen(tmp) == 0){
  27. break;
  28. }
  29. }
  30. }
  31. shmdt(tmp);
  32. shmctl(shmID,IPC_RMID,NULL);
  33. return 0;
  34. }

管道、消息队列、共享内存的区别

将三者进程间的通信看作纸条上的通信

  • 管道:一方拿走这个纸条进行写内容,另一方只能等对方写完这个纸条才能拿走这个纸条进行读内容,半双工通信
  • 消息队列:在一个纸箱存放着多张纸条,纸条上有号码标注,每一张纸条都是独一无二,一方拿走了一张纸条进行写内容或读内容,同时另一方也拿走了一张纸条进行写内容或读内容,操作完后会把纸条放回纸箱中,全双工通信
  • 共享内存:双方同时拿着纸条,一方在纸条写内容,另一方实时在纸条上读内容(实时性)

信号

向某个进程发送信号函数kill()

信号接收函数signal()

使用signal()函数实现Linux定时器,每隔2s输出一个字符串,使用kill()关闭这个定时器

signal()进程

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <sys/time.h>
  5. #include <signal.h>
  6. static int i = 0;
  7. void time_handle(int signum)
  8. {
  9. i++;
  10. if(i == 2000){
  11. printf("2s to hello haozige,my process pid is %d\n",getpid());
  12. i = 0;
  13. }
  14. }
  15. int main()
  16. {
  17. struct itimerval tmp;
  18. tmp.it_value.tv_sec = 0;
  19. tmp.it_value.tv_usec = 500*1000;
  20. tmp.it_interval.tv_sec = 0;
  21. tmp.it_interval.tv_usec = 500;
  22. if(setitimer(ITIMER_REAL,&tmp,NULL) == -1){
  23. perror("setitimer");
  24. exit(-1);
  25. }//SIGALRM
  26. signal(SIGALRM,time_handle);
  27. while(1);
  28. return 0;
  29. }

相关结构体

 

 

 kill()进程

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <sys/types.h>
  5. #include <signal.h>
  6. int main(int argc,char *argv[])
  7. {
  8. if(argc < 2){
  9. printf("missing parameter -> process pid\n");
  10. exit(-1);
  11. }
  12. pid_t pid = atoi(argv[1]);
  13. kill(pid,SIGINT);
  14. sleep(1);
  15. printf("pid is %d process is done!\n",pid);
  16. return 0;
  17. }

高级信号发送函数sigqueue(),比kill()在发射信号时能携带更多信息

 

高级信号接收函数sigaction(),比signal()在接收信号时能获取更多信息

sigset_t sa_mask:

 int sa_flags:

 siginfo_t 结构:保存的其他信息

进程A使用sigqueue向进程B发送信号和携带信息:年龄21和年龄20两个整型数,进程B收到该信号后进行处理

进程A:sigqueue()

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <sys/types.h>
  5. #include <signal.h>
  6. int main(int argc,char *argv[])
  7. {
  8. if(argc < 2){
  9. printf("missing parameter -> process pid\n");
  10. exit(-1);
  11. }
  12. pid_t pid = atoi(argv[1]);
  13. int tmp = 0;
  14. union sigval msg;
  15. msg.sival_int = 21;
  16. while(1){
  17. sigqueue(pid,SIGUSR1,msg);
  18. tmp++;
  19. if(tmp == 5){
  20. msg.sival_int = 19;
  21. }
  22. if(tmp == 10){
  23. kill(pid,SIGINT);
  24. break;
  25. }
  26. sleep(1);
  27. }
  28. return 0;
  29. }

进程B:sigaction() 

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <sys/time.h>
  5. #include <signal.h>
  6. static int i = 0;
  7. void time_handle(int signum,siginfo_t *tmp,void *context)
  8. {
  9. if(context != NULL){
  10. printf("msg from pid %d process\n",tmp->si_pid);
  11. printf("age is %d\n",tmp->si_value.sival_int);
  12. printf("age is %d\n",tmp->si_int);
  13. }else{
  14. printf("Segment errors may occur\n");
  15. printf("1%s",(char *)context);
  16. }
  17. }
  18. int main()
  19. {
  20. struct sigaction sigMsg;
  21. printf("my process pid is %d\n",getpid());
  22. sigMsg.sa_flags = SA_SIGINFO;
  23. sigMsg.sa_sigaction = time_handle;
  24. sigemptyset(&sigMsg.sa_mask);//初始化信号集,清空该信号集里的所有信号
  25. sigaction(SIGUSR1,&sigMsg,NULL);
  26. while(1);
  27. return 0;
  28. }

信号量

 信号量并非单个非负值,必须定义为多个信号量的集合,称为信号量集

 创建一个信号量集semget()

控制信号量集或获取信号量的信息semctl()

 

 

信号量控制共享资源的操作,即PV操作semop()

 

 信号量保证子进程先运行,父进程后运行,同理子进程先获得临界资源,父进程后获得临界资源

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/types.h>
  4. #include <sys/ipc.h>
  5. #include <sys/ipc.h>
  6. #include <sys/sem.h>
  7. #include <unistd.h>
  8. //semctl()参数4,保存信号量的信息或者设置信号量的信息
  9. union semun {
  10. int val;
  11. struct semid_ds *buf;
  12. unsigned short *array;
  13. struct seminfo *__buf;
  14. };
  15. //p操作
  16. void handle_P(int id)
  17. {
  18. struct sembuf pBuf;
  19. pBuf.sem_num = 0;
  20. pBuf.sem_op = -1;
  21. pBuf.sem_flg = SEM_UNDO;
  22. if(semop(id,&pBuf,1) < 0){
  23. perror("semop");
  24. exit(-1);
  25. }
  26. }
  27. //v操作
  28. void handle_V(int id)
  29. {
  30. struct sembuf vBuf;
  31. vBuf.sem_num = 0;
  32. vBuf.sem_op = 1;
  33. vBuf.sem_flg = SEM_UNDO;
  34. if(semop(id,&vBuf,1) < 0){
  35. perror("semop");
  36. exit(-1);
  37. }
  38. }
  39. int main(int argc,char *argv[])
  40. {
  41. key_t key = ftok(".",'z');
  42. if(key == -1){
  43. perror("ftok");
  44. exit(-1);
  45. }
  46. int semID = semget(key,1,IPC_CREAT|0666);
  47. if(semID < 0){
  48. perror("semget");
  49. exit(-1);
  50. }
  51. union semun tmp;
  52. tmp.val = 0; //信号量初始值
  53. if(semctl(semID,0,SETVAL,tmp) < 0){
  54. perror("semctl");
  55. exit(-1);
  56. }
  57. pid_t pid = fork();
  58. if(pid > 0){
  59. //如果父进程先运行,由于信号量初始值为0,执行p操作后父进程进入阻塞状态
  60. printf("father process is wait\n");
  61. handle_P(semID);//p操作
  62. printf("child process lets father process cancel wait\n");
  63. semctl(semID,0,IPC_RMID);
  64. }else if(pid == 0){
  65. //子进程运行
  66. printf("child process function\n");
  67. //5s后执行v操作唤醒父进程
  68. printf("wake up the father process after 5s\n");
  69. int i;
  70. for(i=1;i<6;i++){
  71. sleep(1);
  72. printf("%d\n",i);
  73. }
  74. handle_V(semID);//v操作
  75. }else{
  76. perror("fork");
  77. exit(-1);
  78. }
  79. return 0;
  80. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/256285
推荐阅读
相关标签
  

闽ICP备14008679号