黄骅做网站的电话,网站开发哪家公司比较好,什么网站可以做公共基础知识,wordpress标签云页面进程间通信#xff08;IPC#xff09;介绍
进程间通信#xff08;IPC#xff0c;InterProcess Communication#xff09;是指在不同的进程之间传播或交换信息。IPC 的方式包括管道#xff08;无名管道和命名管道#xff09;、消息队列、信号量、共享内存、Socket、Stre…进程间通信IPC介绍
进程间通信IPCInterProcess Communication是指在不同的进程之间传播或交换信息。IPC 的方式包括管道无名管道和命名管道、消息队列、信号量、共享内存、Socket、Streams 等。其中 Socket 和 Streams 支持不同主机上的两个进程间通信。
1. 管道
1.1 无名管道
1.1.1 特点
它是半双工的即数据只能在一个方向上流动具有固定的读端和写端。它只能用于父子进程之间的通信。管道是创建在内存中进程结束空间释放管道不复存在。对于它的读写可以使用普通的 read、write 等函数。
1.1.2 函数原型
#include unistd.h
int pipe(int pipefd[2]);1.1.3 返回值
成功返回 0失败返回 -1。当一个管道建立时它会创建两个文件描述符fd[0] 为读而打开fd[1] 为写而打开。
1.1.4 无名管道代码示例
#include stdio.h
#include stdlib.h
#include string.h
#include unistd.hint main() {int fd[2];int pid;char buf[128];if (pipe(fd) -1) {printf(create pipe fail\n);}pid fork();if (pid 0) {printf(create child fail\n);} else if (pid 0) {sleep(3);printf(this is father\n);close(fd[0]);write(fd[1], hello from father, strlen(hello from father));wait(NULL);} else {printf(this is child\n);close(fd[1]);read(fd[0], buf, 128);printf(read %s \n, buf);exit(0);}return 0;
}注意管道的特性管道里没有数据时会阻塞。
1.2 命名管道
1.2.1 特点
命名管道可以在无关的进程之间交换数据与无名管道不同。命名管道有路径名与之相关联它以一种特殊设备文件形式存在于文件系统中。
1.2.2 函数原型
#include sys/types.h
#include sys/stat.h
int mkfifo(const char *pathname, mode_t mode);1.2.3 命名管道代码示例
read.c
#include sys/types.h
#include sys/stat.h
#include stdio.h
#include errno.h
#include fcntl.h
#include unistd.h
#include string.hint main() {int fd;char buf[128] {0};if (mkfifo(./file, 0600) -1 errno ! EEXIST) {printf(mkfifo fail\n);perror(why);}fd open(./file, O_RDONLY);printf(read open success\n);while (1) {int n_read read(fd, buf, 128);printf(read %d byte, context %s \n, n_read, buf);}close(fd);return 0;
}write.c
#include sys/types.h
#include sys/stat.h
#include stdio.h
#include errno.h
#include fcntl.h
#include unistd.h
#include string.hint main() {int fd;char *str message from fifo;fd open(./file, O_WRONLY);printf(write open success\n);while (1) {write(fd, str, strlen(str));sleep(1);}close(fd);return 0;
}2. 消息队列
消息队列是信息的链接表存放在内核中。一个消息队列由一个标识符即队列 ID来标识。
2.1 特点
消息队列是面向记录的其中的消息具有特定的格式以及特定的优先级。消息队列独立于发送与接收进程。进程终止时消息队列及内容并不会删除。消息队列可以实现信息的随机查询消息不一定要以先进先出的次序读取也可以按消息的类型读取。
2.2 函数原型
#include sys/types.h
#include sys/ipc.h
#include sys/msg.hint msgget(key_t key, int msgflg);
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);2.3 消息队列代码示例
send.c
#include stdio.h
#include sys/types.h
#include sys/ipc.h
#include sys/msg.h
#include string.hstruct msgbuf {long mtype;char mtext[128];
};int main() {struct msgbuf sendBuf {888, this is msg from queue};struct msgbuf readBuf;key_t key ftok(., 1);printf(key%d\n, key);int msgId msgget(key, IPC_CREAT | 0777);if (msgId -1) {perror(why:);}while (1) {msgsnd(msgId, sendBuf, strlen(sendBuf.mtext), 0);sleep(1);printf(write success\n);msgrcv(msgId, readBuf, sizeof(readBuf.mtext), 988, 0);sleep(1);printf(read from queue: %s\n, readBuf.mtext);}msgctl(msgId, IPC_RMID, NULL);return 0;
}read.c
#include stdio.h
#include sys/types.h
#include sys/ipc.h
#include sys/msg.h
#include string.hstruct msgbuf {long mtype;char mtext[128];
};int main() {struct msgbuf readBuf;struct msgbuf sendBuf {988, this is msg from father};key_t key ftok(., 1);printf(key%d\n, key);int msgId msgget(key, IPC_CREAT | 0777);if (msgId -1) {perror(why:);}while (1) {msgrcv(msgId, readBuf, sizeof(readBuf.mtext), 888, 0);sleep(1);printf(read from queue: %s\n, readBuf.mtext);msgsnd(msgId, sendBuf, strlen(sendBuf.mtext), 0);sleep(1);printf(write success\n);}msgctl(msgId, IPC_RMID, NULL);return 0;
}3. 信号
3.1 信号的处理
信号的处理有三种方法忽略、捕捉和默认动作。具体的信号默认动作可以使用 man 7 signal 来查看系统的具体定义。
3.2 信号处理函数的注册
信号处理函数的注册可以分为入门版和高级版
入门版signal 函数高级版sigaction 函数
3.3 信号处理发送函数
信号发送函数也分为入门版和高级版
入门版kill高级版sigqueue
3.4 signal 函数原型及示例
#include signal.htypedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);示例
#include signal.h
#include stdio.hvoid handler(int signum) {printf(get signum %d\n, signum);switch (signum) {case SIGINT:printf(SIGINT\n);break;case SIGKILL:printf(SIGKILL\n);break;case SIGUSR1:printf(SIGUSR1\n);break;}
}int main() {signal(SIGINT, handler);signal(SIGKILL, handler);signal(SIGUSR1, handler);while (1);return 0;
}3.5 kill 函数原型及示例
#include sys/types.h
#include signal.h
int kill(pid_t pid, int sig);示例
#include stdio.h
#include stdlib.h
#include sys/types.h
#include signal.hint main(int argc, char **argv) {int signum atoi(argv[1]);int pid atoi(argv[2]);printf(signum %d , pid %d \n, signum, pid);kill(pid, signum);printf(send signal ok\n);return 0;
}4. 信号量
信号量semaphore是一个计数器用于实现进程间的互斥与同步而不是用于存储进程间通信数据。
4.1 特点
信号量用于进程间同步若要在进程间传递数据需要结合共享内存。信号量基于操作系统的 PV 操作程序对信号量的操作都是原子操作。支持信号量组。
4.2 函数原型
#include sys/types.h
#include sys/ipc.h
#include sys/sem.hint semget(key_t key, int nsems, int semflg);
int semop(int semid, struct sembuf *sops, unsigned nsops);
int semctl(int semid, int semnum, int cmd, ...);4.3 信号量代码示例
#include sys/types.h
#include sys/ipc.h
#include sys/sem.h
#include stdio.h//联合体用于 semctl 初始化
union semun {int val;struct semid_ds *buf;unsigned short *array;
};void pGetKey(int id) {struct sembuf set;set.sem_num 0;set.sem_op -1;set.sem_flg SEM_UNDO;semop(id, set, 1);printf(get key\n);
}void pPutBackKey(int id) {struct sembuf set;set.sem_num 0;set.sem_op 1;set.sem_flg SEM_UNDO;semop(id, set, 1);printf(put back the key\n);
}int main() {int semid;key_t key ftok(., 2);//1. 获取或创建信号量semid semget(key, 1, IPC_CREAT | 0666);union semun initsem;initsem.val 0;//2. 初始化信号量semctl(semid, 0, SETVAL, initsem);int pid fork();if (pid 0) {//4. 拿锁pGetKey(semid);printf(this is father\n);//5. 还锁pPutBackKey(semid);//6. 销毁锁semctl(semid, 0, IPC_RMID);} else if (pid 0) {printf(this is child\n);//3. 放锁pPutBackKey(semid);} else {printf(fork error\n);}return 0;
}5. 共享内存
共享内存shared memory指两个或多个进程共享一个给定的存储区。
5.1 特点
共享内存是最快的一种 IPC因为进程是直接对内存进行存储而不需要任何数据的拷贝。只能单独一个进程写或读如果 A 和 B 进程同时写会造成数据的混乱需要搭配信号量来使用。
5.2 函数原型
#include sys/types.h
#include sys/ipc.h
#include sys/shm.hint shmget(key_t key, size_t size, int shmflg);
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
int shmctl(int shmid, int cmd, struct shmid_ds *buf);5.3 共享内存代码示例
shm_write.c
#include stdio.h
#include stdlib.h
#include string.h
#include sys/ipc.h
#include sys/shm.hint main() {int shmid;char *shmaddr;key_t key ftok(., 1);// 创建共享内存shmid shmget(key, 1024 * 4, IPC_CREAT | 0600);if (shmid -1) {printf(create shm fail\n);exit(-1);}// 连接映射共享内存shmaddr shmat(shmid, 0, 0);printf(shmat OK\n);// 将数据拷贝到共享内存strcpy(shmaddr, hello world\n);sleep(5); // 等待 5 秒避免一下子断开连接。等待另外一个进程读完。// 断开共享内存连接shmdt(shmaddr);// 删除共享内存shmctl(shmid, IPC_RMID, 0);printf(quit\n);return 0;
}shm_get.c
#include stdio.h
#include stdlib.h
#include string.h
#include sys/ipc.h
#include sys/shm.hint main() {int shmid;char *shmaddr;key_t key ftok(., 1);// 打开共享内存shmid shmget(key, 1024 * 4, 0);if (shmid -1) {printf(create shm fail\n);exit(-1);}// 连接并映射共享内存shmaddr shmat(shmid, 0, 0);printf(get from shm_write message is: %s, shmaddr);// 断开共享内存连接shmdt(shmaddr);printf(quit\n);return 0;
}6. 结合消息队列、共享内存、信号量的示例
6.1 代码示例
get.c
#include stdio.h
#include stdlib.h
#include sys/shm.h
#include sys/sem.h
#include sys/msg.h
#include string.h// 消息队列结构
struct msg_form {long mtype;char mtext;
};// 联合体用于 semctl 初始化
union semun {int val;struct semid_ds *buf;unsigned short *array;
};// 初始化信号量
int init_sem(int sem_id, int value) {union semun tmp;tmp.val value;if (semctl(sem_id, 0, SETVAL, tmp) -1) {perror(Init Semaphore Error);return -1;}return 0;
}// P 操作
int sem_p(int sem_id) {struct sembuf sbuf;sbuf.sem_num 0;sbuf.sem_op -1;sbuf.sem_flg SEM_UNDO;if (semop(sem_id, sbuf, 1) -1) {perror(P operation Error);return -1;}return 0;
}// V 操作
int sem_v(int sem_id) {struct sembuf sbuf;sbuf.sem_num 0;sbuf.sem_op 1;sbuf.sem_flg SEM_UNDO;if (semop(sem_id, sbuf, 1) -1) {perror(V operation Error);return -1;}return 0;
}// 删除信号量集
int del_sem(int sem_id) {union semun tmp;if (semctl(sem_id, 0, IPC_RMID, tmp) -1) {perror(Delete Semaphore Error);return -1;}return 0;
}// 创建一个信号量集
int create_sem(key_t key) {int sem_id;if ((sem_id semget(key, 1, IPC_CREAT | 0666)) -1) {perror(semget error);exit(-1);}init_sem(sem_id, 1); // 初值设为 1 资源未占用return sem_id;
}int main() {key_t key;int shmid, semid, msqid;char *shm;struct shmid_ds buf1; // 用于删除共享内存struct msqid_ds buf2; // 用于删除消息队列struct msg_form msg; // 消息队列用于通知对方更新了共享内存// 获取 key 值if ((key ftok(., z)) 0) {perror(ftok error);exit(1);}// 创建共享内存if ((shmid shmget(key, 1024, IPC_CREAT | 0666)) -1) {perror(Create Shared Memory Error);exit(1);}// 连接共享内存shm (char *)shmat(shmid, 0, 0);if ((int)shm -1) {perror(Attach Shared Memory Error);exit(1);}// 创建消息队列if ((msqid msgget(key, IPC_CREAT | 0777)) -1) {perror(msgget error);exit(1);}// 创建信号量semid create_sem(key);// 读数据while (1) {msgrcv(msqid, msg, 1, 888, 0); // 读取类型为 888 的消息if (msg.mtext q) // quit - 跳出循环break;if (msg.mtext r) // read - 读共享内存{sem_p(semid);printf(%s\n, shm);sem_v(semid);}}// 断开连接shmdt(shm);// 删除共享内存、消息队列、信号量shmctl(shmid, IPC_RMID, buf1);msgctl(msqid, IPC_RMID, buf2);del_sem(semid);return 0;
}send.c
#include stdio.h
#include stdlib.h
#include sys/shm.h
#include sys/sem.h
#include sys/msg.h
#include string.h// 消息队列结构
struct msg_form {long mtype;char mtext;
};// 联合体用于 semctl 初始化
union semun {int val;struct semid_ds *buf;unsigned short *array;
};// P 操作
int sem_p(int sem_id) {struct sembuf sbuf;sbuf.sem_num 0;sbuf.sem_op -1;sbuf.sem_flg SEM_UNDO;if (semop(sem_id, sbuf, 1) -1) {perror(P operation Error);return -1;}return 0;
}// V 操作
int sem_v(int sem_id) {struct sembuf sbuf;sbuf.sem_num 0;sbuf.sem_op 1;sbuf.sem_flg SEM_UNDO;if (semop(sem_id, sbuf, 1) -1) {perror(V operation Error);return -1;}return 0;
}int main() {key_t key;int shmid, semid, msqid;char *shm;struct msg_form msg;int flag 1; // while 循环条件// 获取 key 值if ((key ftok(., z)) 0) {perror(ftok error);exit(1);}// 获取共享内存if ((shmid shmget(key, 1024, 0)) -1) {perror(shmget error);exit(1);}// 连接共享内存shm (char *)shmat(shmid, 0, 0);if ((int)shm -1) {perror(Attach Shared Memory Error);exit(1);}// 创建消息队列if ((msqid msgget(key, 0)) -1) {perror(msgget error);exit(1);}// 获取信号量if ((semid semget(key, 0, 0)) -1) {perror(semget error);exit(1);}// 写数据printf(***************************************\n);printf(* IPC *\n);printf(* Input r to send data to server. *\n);printf(* Input q to quit. *\n);printf(***************************************\n);while (flag) {char c;printf(Please input command: );scanf(%c, c);switch (c) {case r:printf(Data to send: );sem_p(semid); // 访问资源scanf(%s, shm);sem_v(semid); // 释放资源// 清空标准输入缓冲区while ((c getchar()) ! \n c ! EOF);msg.mtype 888;msg.mtext r; // 发送消息通知服务器读数据msgsnd(msqid, msg, sizeof(msg.mtext), 0);break;case q:msg.mtype 888;msg.mtext q;msgsnd(msqid, msg, sizeof(msg.mtext), 0);flag 0;break;default:printf(Wrong input!\n);// 清空标准输入缓冲区while ((c getchar()) ! \n c ! EOF);}}// 断开连接shmdt(shm);return 0;
}7. 对比总结
通过上述对比可以看出各种 IPC 方式各有优劣选择合适的方式进行进程间通信可以提高程序的效率和可靠性。