当前位置:   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/2023面试高手/article/detail/560220
推荐阅读
相关标签
  

闽ICP备14008679号