进程间通信函数接口及应用模板小结

打印 上一主题 下一主题

主题 887|帖子 887|积分 2661

进程间通信方式

1.无名管道/有名管道
2.信号
3.共享队列(system V-IPC)
4.共享内存(system V-IPC)
5.信号量(system V-IPC)
6.套接字
无名管道特征

1.文件没有名字,无法使用open
2.只能用于亲缘进程间
3.半双工工作方式:读写端分开
4.写入操作不具有原子性,会被打断,因此只能用于一对一的简单场景
5.不能使用lseek()来定位
相干API
  1. int pipe(int pipefd[2]);
复制代码
应用模板
  1. int main(int argc, char *argv[])
  2. {
  3.     int fd[2];
  4.     if (pipe(fd) == -1)
  5.     {
  6.         fprintf(stderr, "errno:%d,%s", errno, strerror(errno));
  7.         exit(-1);
  8.     }
  9.     pid_t x = fork(); // 创建子进程,继承pipe的描述符
  10.     if (x == 0)       // 子进程
  11.     {
  12.         char *s = "I am the child\n";
  13.         write(fd[1], s, strlen(s)); // 1是读,0是写
  14.     }
  15.     if (x > 0) // 父进程
  16.     {
  17.         char buf[30];
  18.         bzero(buf, sizeof(buf));
  19.         read(fd[0], buf, sizeof(buf));
  20.         printf("from the child:%s", buf);
  21.     }
  22.     close(fd[0]); // 因为无名,所以无法用open,但还是需要close
  23.     close(fd[1]);
  24.     return 0;
  25. }
复制代码
有名管道特征

1.有名字,且存储于普通文件系统中
2.任何有权限的进程都可以使用open函数获取FIFO文件形貌符
4.写入操作具有原子性,支持多写者同时进行写操作且数据不会相互践踏。这是与无名管道的最大区别
5.不能使用lseek()来定位
6.FIFO,开始被写入的数据,开始被读出来
相干API
  1. int mkfifo(const char *pathname, mode_t mode);
复制代码
应用模板
  1. #define FIFO "/tmp/fifo4test"
  2. int main(int argc, char *argv[]) // 发送的进程
  3. {
  4.     if (access(FIFI, F_OK)) // 检查文件是否存在,不存在就创建
  5.     {
  6.         mkfifo(FIFO, 0644);
  7.     }
  8.     int fifo_fd = open(FIFO, O_RDWR);
  9.     char *s = "I am the child\n";
  10.     int n = write(fifo_fd, s, strlen(s)); // 1是读,0是写
  11.     printf("%d bytes have been sended.\n", n);
  12.     close(fifo_fd);
  13.     return 0;
  14. }
  15. int main(int argc, char *argv[]) // 接收的进程
  16. {
  17.     if (access(FIFI, F_OK)) // 检查文件是否存在,不存在就创建
  18.     {
  19.         mkfifo(FIFO, 0644);
  20.     }
  21.     int fifo_fd = open(FIFO, O_RDWR);
  22.     char buf[30];
  23.     bzero(buf, sizeof(buf));
  24.     read(fifo_fd, buf, sizeof(buf));
  25.     printf("from the child:%s", buf);
  26.     close(fifo_fd);
  27.     return 0;
  28. }
复制代码
信号特征

1.大部分信号都是异步
2.linux信号62个:
1~31是非及时,不可靠信号,相应不排队;
假如目标进程没有及时相应,则随后到达的同样的信号会被丢弃;
每个非及时信号都对应一个系统事件。
当进程的挂起信号中含有及时和非及时信号,则会优先相应及时信号并从大到小依次相应
34~64及时,可靠信号,按吸收顺序排队
即使雷同的及时信号被同时发送多次也不会被丢弃,而是依次相应
及时信号没有系统事件与之对应
3.对信号的处置惩罚:壅闭,被捕获并相应(按设置的相应函数或忽略),执行默认动作
相干API
  1. int kill(pid_t pid, int sig);                          // 向pid进程发送信号
  2. void (*sighandler_t)(int);                             // 设置的函数,sighandler_t 被定义为指向一个接收单个 int 参数(即信号编号)并返回 void 的函数的指针。这样的函数通常被称为“信号处理函数”或“信号处理程序”。
  3. sighandler_t signal(int signum, sighandler_t handler); // 接收signum信号,并执行handler信号处理函数,一般和kill配套使用
  4. int raise(int sig);                                    // 给自己发送信号
  5. int pause(void);                                       // 挂起等待信号
  6. sigset_t setset;                                  // 创建信号集
  7. int sigemptyset(sigset_t *set);                   // 清空信号集
  8. int sigfillset(sigset_t *set);                    // 将所有信号添加到信号集中
  9. int sigaddset(sigset_t *set, int signum);         // 将特定信号添加到信号集中
  10. int sigdelset(sigset_t *set, int signum);         // 将特定信号从信号集中删除
  11. int sigismember(const sigset_t *set, int signum); // 判断某特定信号是否在信号集中
  12. int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); // 设置阻塞掩码
复制代码
应用模板
  1. void handler(int sig)
  2. // sig是触发该处理函数的信号值;
  3. // 子进程会不会继承父进程的信号响应函数和阻塞状态,每个进程都有自己的信号屏蔽字(signal mask),用于控制哪些信号在当前是阻塞的(即不会被立即处理)。
  4. // 而且不同的信号能共享一个处理函数
  5. {
  6.     printf("This is a test4signal.sig:%d\n", sig);
  7. }
  8. int main(int argc, char const *argv[])
  9. {
  10.     sigset_t set;                       // 1.创建一个信号集
  11.     sigemptyset(&set);                  // 清空信号集
  12.     sigaddset(&set, SIGINT);            // 2.把需要屏蔽的信号加入到该集合中
  13.     sigprocmask(SIG_BLOCK, &set, NULL); // 3.需要设置该集合的阻塞属性,第一个参数如果是SIG_UNBLOCK,就解除阻塞
  14.     signal(SIGUSR1, handler);
  15.     // 接收信号,第二个参数设置成SIG_IGN是忽略  SIG_DEL是删除
  16.     pause(); // 暂停进程,等待信号。此处可以用while(1),一直等待信号
  17.     return 0;
  18. }
复制代码


消息队列,共享内存,信号量 (统称为system-V IPC)

1.消息队列,提供带有数据标识的特殊管道
2.共享内存,提供一块物理内存多次映射到差别的进程假造空间
3.信号量,对进程或线程的资源进行管理
消息队列相干API
  1. key_t ftok(const char *pathname, int proj_id);                                // 指定路径和未使用过的整数
  2. int msgget(key_t key, int msgflg);                                            // 获取消息队列的ID
  3. int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);            // 发送消息
  4. ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); // 接收消息
  5. int msgctl(int msqid, int cmd, struct msqid_ds *buf);                         // 设置消息队列的属性
复制代码
应用模板
  1. // 应用模板:
  2. #define PROJ_PATH "."
  3. #define PROJ_ID 10
  4. #define A2B 1L    // 定义消息标识
  5. #define MSGSZ 100 // 定义消息长度
  6. ;
  7. struct msgbuf // 定义带标识的消息结构体
  8. {
  9.     long mtype;
  10.     char mtext[MSGSZ]; // 消息最大一般是16384bytes
  11. };
  12. int main()
  13. {
  14.     key_t key = ftok(PROJ_PATH, PROJ_ID);
  15.     int msgid = msgget(key, IPC_CREAT | 0644);
  16.     struct msgbuf buf;
  17.     bzero(&buf, sizeof(buf));
  18.     buf.mtype = A2B;
  19.     strcpy(buf.mtext, "This is a msg test.\n");
  20.     msgsnd(msgid, &buf, strlen(buf.mtext), MSGSZ);
  21.     // 错误处理此处忽略,实际项目需要考虑
  22.     //  ================下面为从消息队列读取消息============//
  23.     struct msgbuf rec;
  24.     bzero(&rec, sizeof(rec));
  25.     msgrcv(msgid, &buf, MSGSZ, A2B, 0); // 0表示等待消息,默认阻塞
  26.     printf("recive the message:%s", rec.mtext);
  27.     //  ================从消息队列读取消息============//
  28.     return 0;
  29. }
复制代码
共享内存相干API
  1. key_t ftok(const char *pathname, int proj_id);           // 指定路径和未使用过的整数
  2. int shmget(key_t key, size_t size, int shmflg);          // 获取的ID
  3. void *shmat(int shmid, const void *shmaddr, int shmflg); // 对共享内存进行映射
  4. int shmdt(const void *shmaddr);                          // 解除映射
  5. int shmctl(int msqid, int cmd, struct msqid_ds *buf);    // 设置共享内存的属性
复制代码
信号量相干API
  1. key_t ftok(const char *pathname, int proj_id);           // 指定路径和未使用过的整数
  2. int semget(key_t key, int nsems, int semflg);            // 获取信号量ID
  3. int semop(int semid, struct sembuf *sops, size_t nsops); // 对信号量进行P/V操作
  4. int semctl(int semid, int semnum, int cmd, ...);         // 获取或设置信号量的相关属性
复制代码
由于systemV IPC 的信号量用的较少,且进程间通信一样平常可以用POSIX的信号量取代,因此一起简要总结后者。
POSIX的信号量分为有名和无名:有名信号量,是一种特殊的文件,一样平常会放在系统的特殊文件系统/dev/shm中,差别进程间需要约定一个雷同的名字,就能通过这种有名信号量来相互协调;无名信号量,一样平常用于一个进程内线程间的同步互斥,由于线程共享一个内存空间。
POSIX有名信号量API
  1. sem_t *sem;
  2. sem_t *sem_open(const char *name, int oflag);
  3. sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value); // 创建/打开一个有名信号量
  4. int sem_wait(sem_t *sem);
  5. int sem_trywait(sem_t *sem);
  6. int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); // 申请资源,即P操作
  7. int sem_post(sem_t *sem);                                          // 释放资源,即V操作
  8. int sem_close(sem_t *sem);                                         // 关闭
  9. int sem_unlink(const char *name);                                  // 删除有名信号量文件
复制代码
POSIX无名信号量API
  1. sem_t *sem;
  2. int sem_init(sem_t *sem, int pshared, unsigned int value);
  3. int sem_wait(sem_t *sem);
  4. int sem_trywait(sem_t *sem);
  5. int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); // 申请资源,即P操作
  6. int sem_post(sem_t *sem);                                          // 释放资源,即V操作
  7. int sem_destroy(sem_t *sem);
  8. /*
复制代码
有名信号量和共享内存应用模板
  1. #define PROJ_PATH "."
  2. #define PROJ_ID 10
  3. #define SEMNAME "sem4test" // 定义消息标识
  4. #define SHMSZ 100          // 定义消息长度
  5. int main()
  6. {
  7.     key_t key = ftok(PROJ_PATH, PROJ_ID);              // 指定路径和未使用过的整数
  8.     int shm_id = shmget(key, SHMSZ, IPC_CREAT | 0644); // 获取共享内存的ID
  9.     char *shmaddr = shmat(shm_id, NULL, 0);            // 对共享内存进行映射
  10.     // 创建POSIX有名信号量
  11.     sem_t *s;
  12.     s = sem_open(SEMNAME, O_CREAT, 0644, 0);
  13.     while (1)
  14.     {
  15.         fgets(shmaddr, SHMSZ, stdin);
  16.         sem_post(s); // 向信号量释放资源,即每次进行操作结束后资源量+1
  17.     }
  18.     sem_unlink(SEMNAME);
  19.     //=========================下半部分为接收信号=====================//
  20.     while (1)
  21.     {
  22.         sem_post(s); // 向信号量申请资源,当信息被读完,即信息量为0时被阻塞
  23.         printf("recive message:%s", shmaddr);
  24.     }
  25.     //=========================接收信号=====================//
  26.     sem_close(s);
  27.     return 0;
  28. }
复制代码
尤其注意,System V的信号量和POSIX的信号量不是一个概念,它们之间存在明显的区别。以下是两者之间的主要区别:

  • 来源与标准:
System V信号量:来源于Unix操作系统的一个分支,即System V版本。
POSIX信号量:来源于“可移植操作系统接口(Portable Operating System Interface)”标准,这是一个由电气与电子工程学会(IEEE)开发,并由ISO(国际标准化组织)和IEC(国际电工委员会)采纳的国际标准。

  • 使用场景:
System V信号量:常用于进程间的同步。
POSIX信号量:常用于线程间的同步,但也可以用于进程间同步,特别是当使用有名信号量时。

  • 实现与复杂性:
System V信号量:使用相对复杂,通常涉及多个步骤和结构体(如struct sembuf)。
POSIX信号量:使用相对简单,通过单一的sem_open调用即可完成信号量的创建、初始化和权限设置。

  • 存储位置:
System V信号量:基于内核,存放在内核空间中。
POSIX信号量:基于内存,信号量值通常存放在共享内存中,有名信号量通过文件系统(如/dev/shm)中的特殊文件来表现。

  • 信号量范例:
System V信号量:通常作为信号量聚集存在,每个聚集可以包罗多个信号量。
POSIX信号量:有两种范例——有名信号量和无名信号量。有名信号量通过IPC名字进行进程间同步,无名信号量则通常用于线程间同步。

  • 信号量操作:
System V信号量:通过semop等系统调用来操作信号量。
POSIX信号量:通过sem_wait(P操作)、sem_post(V操作)等函数来操作信号量。

  • 头文件与接口:
System V信号量:使用头文件,接口函数包括semget、semop等。
POSIX信号量:使用头文件,接口函数包括sem_open、sem_wait、sem_post等。
剩下一项套接字,一样平常用于网络编程,即差别主机间的进程通信,后续补充。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

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

本版积分规则

南飓风

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表