进程间通信方式
1.无名管道/有名管道
2.信号
3.共享队列(system V-IPC)
4.共享内存(system V-IPC)
5.信号量(system V-IPC)
6.套接字
无名管道特征
1.文件没有名字,无法使用open
2.只能用于亲缘进程间
3.半双工工作方式:读写端分开
4.写入操作不具有原子性,会被打断,因此只能用于一对一的简单场景
5.不能使用lseek()来定位
相干API
应用模板
- int main(int argc, char *argv[])
- {
- int fd[2];
- if (pipe(fd) == -1)
- {
- fprintf(stderr, "errno:%d,%s", errno, strerror(errno));
- exit(-1);
- }
- pid_t x = fork(); // 创建子进程,继承pipe的描述符
- if (x == 0) // 子进程
- {
- char *s = "I am the child\n";
- write(fd[1], s, strlen(s)); // 1是读,0是写
- }
- if (x > 0) // 父进程
- {
- char buf[30];
- bzero(buf, sizeof(buf));
- read(fd[0], buf, sizeof(buf));
- printf("from the child:%s", buf);
- }
- close(fd[0]); // 因为无名,所以无法用open,但还是需要close
- close(fd[1]);
- return 0;
- }
复制代码 有名管道特征
1.有名字,且存储于普通文件系统中
2.任何有权限的进程都可以使用open函数获取FIFO文件形貌符
4.写入操作具有原子性,支持多写者同时进行写操作且数据不会相互践踏。这是与无名管道的最大区别
5.不能使用lseek()来定位
6.FIFO,开始被写入的数据,开始被读出来
相干API
- int mkfifo(const char *pathname, mode_t mode);
复制代码 应用模板
- #define FIFO "/tmp/fifo4test"
- int main(int argc, char *argv[]) // 发送的进程
- {
- if (access(FIFI, F_OK)) // 检查文件是否存在,不存在就创建
- {
- mkfifo(FIFO, 0644);
- }
- int fifo_fd = open(FIFO, O_RDWR);
- char *s = "I am the child\n";
- int n = write(fifo_fd, s, strlen(s)); // 1是读,0是写
- printf("%d bytes have been sended.\n", n);
- close(fifo_fd);
- return 0;
- }
- int main(int argc, char *argv[]) // 接收的进程
- {
- if (access(FIFI, F_OK)) // 检查文件是否存在,不存在就创建
- {
- mkfifo(FIFO, 0644);
- }
- int fifo_fd = open(FIFO, O_RDWR);
- char buf[30];
- bzero(buf, sizeof(buf));
- read(fifo_fd, buf, sizeof(buf));
- printf("from the child:%s", buf);
- close(fifo_fd);
- return 0;
- }
复制代码 信号特征
1.大部分信号都是异步
2.linux信号62个:
1~31是非及时,不可靠信号,相应不排队;
假如目标进程没有及时相应,则随后到达的同样的信号会被丢弃;
每个非及时信号都对应一个系统事件。
当进程的挂起信号中含有及时和非及时信号,则会优先相应及时信号并从大到小依次相应
34~64及时,可靠信号,按吸收顺序排队
即使雷同的及时信号被同时发送多次也不会被丢弃,而是依次相应
及时信号没有系统事件与之对应
3.对信号的处置惩罚:壅闭,被捕获并相应(按设置的相应函数或忽略),执行默认动作
相干API
- int kill(pid_t pid, int sig); // 向pid进程发送信号
- void (*sighandler_t)(int); // 设置的函数,sighandler_t 被定义为指向一个接收单个 int 参数(即信号编号)并返回 void 的函数的指针。这样的函数通常被称为“信号处理函数”或“信号处理程序”。
- sighandler_t signal(int signum, sighandler_t handler); // 接收signum信号,并执行handler信号处理函数,一般和kill配套使用
- int raise(int sig); // 给自己发送信号
- int pause(void); // 挂起等待信号
- sigset_t setset; // 创建信号集
- int sigemptyset(sigset_t *set); // 清空信号集
- int sigfillset(sigset_t *set); // 将所有信号添加到信号集中
- int sigaddset(sigset_t *set, int signum); // 将特定信号添加到信号集中
- int sigdelset(sigset_t *set, int signum); // 将特定信号从信号集中删除
- int sigismember(const sigset_t *set, int signum); // 判断某特定信号是否在信号集中
- int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); // 设置阻塞掩码
复制代码 应用模板
- void handler(int sig)
- // sig是触发该处理函数的信号值;
- // 子进程会不会继承父进程的信号响应函数和阻塞状态,每个进程都有自己的信号屏蔽字(signal mask),用于控制哪些信号在当前是阻塞的(即不会被立即处理)。
- // 而且不同的信号能共享一个处理函数
- {
- printf("This is a test4signal.sig:%d\n", sig);
- }
- int main(int argc, char const *argv[])
- {
- sigset_t set; // 1.创建一个信号集
- sigemptyset(&set); // 清空信号集
- sigaddset(&set, SIGINT); // 2.把需要屏蔽的信号加入到该集合中
- sigprocmask(SIG_BLOCK, &set, NULL); // 3.需要设置该集合的阻塞属性,第一个参数如果是SIG_UNBLOCK,就解除阻塞
- signal(SIGUSR1, handler);
- // 接收信号,第二个参数设置成SIG_IGN是忽略 SIG_DEL是删除
- pause(); // 暂停进程,等待信号。此处可以用while(1),一直等待信号
- return 0;
- }
复制代码
消息队列,共享内存,信号量 (统称为system-V IPC)
1.消息队列,提供带有数据标识的特殊管道
2.共享内存,提供一块物理内存多次映射到差别的进程假造空间
3.信号量,对进程或线程的资源进行管理
消息队列相干API
- key_t ftok(const char *pathname, int proj_id); // 指定路径和未使用过的整数
- int msgget(key_t key, int msgflg); // 获取消息队列的ID
- int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); // 发送消息
- ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); // 接收消息
- int msgctl(int msqid, int cmd, struct msqid_ds *buf); // 设置消息队列的属性
复制代码 应用模板
- // 应用模板:
- #define PROJ_PATH "."
- #define PROJ_ID 10
- #define A2B 1L // 定义消息标识
- #define MSGSZ 100 // 定义消息长度
- ;
- struct msgbuf // 定义带标识的消息结构体
- {
- long mtype;
- char mtext[MSGSZ]; // 消息最大一般是16384bytes
- };
- int main()
- {
- key_t key = ftok(PROJ_PATH, PROJ_ID);
- int msgid = msgget(key, IPC_CREAT | 0644);
- struct msgbuf buf;
- bzero(&buf, sizeof(buf));
- buf.mtype = A2B;
- strcpy(buf.mtext, "This is a msg test.\n");
- msgsnd(msgid, &buf, strlen(buf.mtext), MSGSZ);
- // 错误处理此处忽略,实际项目需要考虑
- // ================下面为从消息队列读取消息============//
- struct msgbuf rec;
- bzero(&rec, sizeof(rec));
- msgrcv(msgid, &buf, MSGSZ, A2B, 0); // 0表示等待消息,默认阻塞
- printf("recive the message:%s", rec.mtext);
- // ================从消息队列读取消息============//
- return 0;
- }
复制代码 共享内存相干API
- key_t ftok(const char *pathname, int proj_id); // 指定路径和未使用过的整数
- int shmget(key_t key, size_t size, int shmflg); // 获取的ID
- void *shmat(int shmid, const void *shmaddr, int shmflg); // 对共享内存进行映射
- int shmdt(const void *shmaddr); // 解除映射
- int shmctl(int msqid, int cmd, struct msqid_ds *buf); // 设置共享内存的属性
复制代码 信号量相干API
- key_t ftok(const char *pathname, int proj_id); // 指定路径和未使用过的整数
- int semget(key_t key, int nsems, int semflg); // 获取信号量ID
- int semop(int semid, struct sembuf *sops, size_t nsops); // 对信号量进行P/V操作
- int semctl(int semid, int semnum, int cmd, ...); // 获取或设置信号量的相关属性
复制代码 由于systemV IPC 的信号量用的较少,且进程间通信一样平常可以用POSIX的信号量取代,因此一起简要总结后者。
POSIX的信号量分为有名和无名:有名信号量,是一种特殊的文件,一样平常会放在系统的特殊文件系统/dev/shm中,差别进程间需要约定一个雷同的名字,就能通过这种有名信号量来相互协调;无名信号量,一样平常用于一个进程内线程间的同步互斥,由于线程共享一个内存空间。
POSIX有名信号量API
- sem_t *sem;
- sem_t *sem_open(const char *name, int oflag);
- sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value); // 创建/打开一个有名信号量
- int sem_wait(sem_t *sem);
- int sem_trywait(sem_t *sem);
- int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); // 申请资源,即P操作
- int sem_post(sem_t *sem); // 释放资源,即V操作
- int sem_close(sem_t *sem); // 关闭
- int sem_unlink(const char *name); // 删除有名信号量文件
复制代码 POSIX无名信号量API
- sem_t *sem;
- int sem_init(sem_t *sem, int pshared, unsigned int value);
- int sem_wait(sem_t *sem);
- int sem_trywait(sem_t *sem);
- int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); // 申请资源,即P操作
- int sem_post(sem_t *sem); // 释放资源,即V操作
- int sem_destroy(sem_t *sem);
- /*
复制代码 有名信号量和共享内存应用模板
- #define PROJ_PATH "."
- #define PROJ_ID 10
- #define SEMNAME "sem4test" // 定义消息标识
- #define SHMSZ 100 // 定义消息长度
- int main()
- {
- key_t key = ftok(PROJ_PATH, PROJ_ID); // 指定路径和未使用过的整数
- int shm_id = shmget(key, SHMSZ, IPC_CREAT | 0644); // 获取共享内存的ID
- char *shmaddr = shmat(shm_id, NULL, 0); // 对共享内存进行映射
- // 创建POSIX有名信号量
- sem_t *s;
- s = sem_open(SEMNAME, O_CREAT, 0644, 0);
- while (1)
- {
- fgets(shmaddr, SHMSZ, stdin);
- sem_post(s); // 向信号量释放资源,即每次进行操作结束后资源量+1
- }
- sem_unlink(SEMNAME);
- //=========================下半部分为接收信号=====================//
- while (1)
- {
- sem_post(s); // 向信号量申请资源,当信息被读完,即信息量为0时被阻塞
- printf("recive message:%s", shmaddr);
- }
- //=========================接收信号=====================//
- sem_close(s);
- return 0;
- }
复制代码 尤其注意,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企服之家,中国第一个企服评测及商务社交产业平台。 |