无名管道只能用于亲缘间进程的通信,无名管道的大小是64K。无名管道是内核空间实现的机制。
1) Pipe()创建一个管道,这是一个单向的数据通道,可用于进程间通信。
2)数组pipefd用于返回两个指向管道末端的文件描述符。
3)Pipefd[0]指的是管道的读端。Pipefd[1]指的是管道的写入端,写入管道的写入端数据由内核进行缓冲(64k),直到从管道的读取端读取为止。
1.只能用于亲缘间进程的通信
2.无名管道数据半双工的通信的方式
单工 : A -------------->B
半双工 : 同一时刻 A----->B B------>A
全双工 : 同一时刻 A<---->B
3.无名管道的大小是64K
4.无名管道不能够使用lseek函数
5.读写的特点
如果读端存在写管道:有多少写多少,直到写满为止(64k)写阻塞
如果读端不存写管道,管道破裂(SIGPIPE) (可以通过gdb调试看现象)
如果写端存在读管道:有多少读多少,没有数据的时候阻塞等待
如果写端不存在读管道:有多少读多少,没有数据的时候立即返回
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #define ERROR(msg) do{\
- printf("%s %s %d\n", __FILE__, __func__, __LINE__);\
- printf(msg);\
- exit(-1); \
- }while(0)
-
- int main(int argc, char const *argv[])
- {
- pid_t pid;
- int num[2];
- char buff[128] = {0};
-
- if (pipe(num)){
- ERROR("pipe error");
- }
-
- if ((pid = fork()) == -1){
- ERROR("fork error");
- }else if(pid == 0){
- close(num[0]);
- while (1){
- memset(buff, 0, sizeof(buff));
- printf("请输入您要输入的数值>>");
- fgets(buff, sizeof(buff), stdin);
- buff[strlen(buff) -1] = '\0';
- write(num[1], buff, strlen(buff));
-
- if (!strncmp(buff, "quit", 4)){
- break;
- }
-
- }
- close(num[1]);
- exit(EXIT_SUCCESS);
- }else{
- close(num[1]);
- while (1){
- memset(buff, 0, sizeof(buff));
- read(num[0], buff, sizeof(buff));
-
- if (!strncmp(buff, "quit", 4)){
- break;
- }
- printf("%s\n",buff);
- }
- close(num[0]);
- wait(NULL);
- }
-
- return 0;
- }
1)可以用于亲缘间进程的通信,也可以用于非亲缘间的进程的通信。
2)有名管道会创建一个文件,需要通信的进程打开这个文件,产生文件描述符后就可以通信了,有名管道的文件存在内存上。
3)有名管道的大小也是64K,也不能使用lseek函数
1.可以用于任意进程间的通信
2.有名管道数据半双工的通信的方式
3.有名管道的大小是64K
4.有名管道不能够使用lseek函数
5.读写的特点
如果读端存在写管道:有多少写多少,直到写满为止(64k)写阻塞
如果读端不存写管道
1.读权限没有打开,写端在open的位置阻塞
2.读端打开后关闭,管道破裂(SIGPIPE) (可以通过gdb调试看现象)
如果写端存在读管道:有多少读多少,没有数据的时候阻塞等待
如果写端不存在读管道
1.写权限没有打开,读端在open的位置阻塞
2.写端打开后关闭,有多少读多少,没有数据的时候立即返回
mkfifo文件:
- #include
- #include
- #define ERROR(msg) do{\
- printf("%s %s %d\n", __FILE__, __func__, __LINE__);\
- printf(msg);\
- exit(-1); \
- }while(0)
-
- int main(int argc, char const *argv[])
- {
- if (mkfifo("./fifo",0666)){
- ERROR("mkfifo error");
- }
-
- //有名管道没有阻塞,手动加一个阻塞
- getchar();
-
- system("rm ./fifo -rf");
-
- return 0;
- }
write文件:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- #define ERROR(msg) do{\
- printf("%s %s %d\n", __FILE__, __func__, __LINE__);\
- printf(msg);\
- exit(-1); \
- }while(0)
-
- int main(int argc, char const *argv[])
- {
- int fd;
- char buff[128] = {0};
- if ((fd = open("./fifo",O_WRONLY)) == -1){
- ERROR("open fifo error\n");
- }
-
- while(1){
- printf("input >");
- fgets(buff, sizeof(buff), stdin);
- buff[strlen(buff) - 1] = '\0';
-
- write(fd, buff, strlen(buff));
- if(!strncmp("quit",buff,4))break;
- }
- close(fd);
- return 0;
- }
read文件:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- #define ERROR(msg) \
- do \
- { \
- printf("%s %s %d\n", __FILE__, __func__, __LINE__); \
- printf(msg); \
- exit(-1); \
- } while (0)
-
- int main(int argc, char const *argv[])
- {
- int fd;
- char buff[128] = {0};
- if ((fd = open("./fifo", O_RDONLY)) == -1)
- {
- ERROR("open error");
- }
-
- while (1){
- memset(buff, 0, sizeof(buff));
- read(fd, buff, sizeof(buff));
- if (!strncmp("quit",buff,4)){
- break;
- }
-
- printf("%s\n",buff);
- }
- close(fd);
- return 0;
- }
信号是中断的一种软件模拟,中断是基于硬件实现的,信号是基于linux内核实现的。
用户可以给进程发信号,进程可以给进程发信号,内核也可以给进程发信号。进程对
信号的处理方式有三种:捕捉,忽略,默认
- int raise(int sig);
- 功能:给自己(进程或者线程)发信号
- 参数:
- @sig:信号号
- 返回值:成功返回0,失败返回非0
-
- int kill(pid_t pid, int sig);
- 功能:给进程发信号
- 参数:
- @pid:进程号
- pid > 0 :给pid对应的进程发信号
- pid = 0 :给同组的进程发信号
- pid = -1:给所有的有权限操作的进程发送信号,init进程除外
- pid < -1:给-pid对应的同组的进程发信号
- @sig:信号号
- 返回值:成功返回0,失败返回-1置位错误码


1.在上述的信号中只有SIGKILL和SIGSTOP两个信号不能被捕捉也不能被忽略
2.SIGCHLD,当子进程结束的时候,父进程收到这个SIGCHLD的信号
- #include
- #include
- #include
- #define ERROR(msg) do{\
- printf("%s %s %d\n", __FILE__, __func__, __LINE__);\
- printf(msg);\
- exit(-1); \
- }while(0)
-
- void handle(int num)
- {
- if (num == SIGINT){
- printf("我收到一个ctrl+c的信号\n");
- }
- }
-
- int main(int argc, char const *argv[])
- {
- //捕捉
- if (signal(SIGINT, handle) == SIG_ERR){
- ERROR("register signal error");
- }
- //忽略
- if (signal(SIGINT,SIG_IGN) == SIG_ERR){
- ERROR("register signal error");
- }
- //默认
- if (signal(SIGINT,SIG_DFL) == SIG_ERR){
- ERROR("register signale");
- }
-
-
- while(1);
- return 0;
- }
- #include
- #include
- #include
- #include
- #include
-
- #define ERROR(msg) do{\
- printf("%s %s %d\n", __FILE__, __func__, __LINE__);\
- printf(msg);\
- exit(-1); \
- }while(0)
-
- void handle(int num)
- {
- if (num == SIGPIPE){
- printf("捕捉到一条管道破裂的消息\n");
- }
- }
-
- int main(int argc, char const *argv[])
- {
- int num[2];
- char buff[32] = "123";
-
- if(pipe(num)){
- ERROR("pipe error");
- }
-
- if (signal(SIGPIPE,handle) == SIG_ERR){
- ERROR("signal error");
- }
-
- close(num[0]);
-
- write(num[1],buff,strlen(buff));
- return 0;
- }
- #include
- #include
- #include
- #include
- #include
-
- void signal_handle(int signo)
- {
- printf("我是父进程,收到了子进程退出的信号,为它回收资源\n");
- waitpid(-1,NULL,WNOHANG); //非阻塞方式回收资源
- printf("为子进程回收资源成功\n");
- raise(SIGKILL); //给父进程发送信号,结束父进程
- }
-
- int main(int argc,const char * argv[])
- {
- pid_t pid;
-
- pid = fork();
- if(pid == -1){
- ERROR("fork error");
- }else if(pid == 0){
- sleep(5);
- printf("子进程执行结束了\n");
- exit(EXIT_SUCCESS);
- }else{
- if(signal(SIGCHLD,signal_handle)==SIG_ERR)
- ERROR("signal error");
-
- while(1);
- }
-
- return 0;
- }
- #include
- #include
- #include
- #include
-
- #define ERROR(msg) do{\
- printf("%s %s %d\n", __FILE__, __func__, __LINE__);\
- printf(msg);\
- exit(-1); \
- }while(0)
-
- void handle(int num)
- {
- if (num == SIGALRM){
- printf("自动出牌\n");
- }
- alarm(3);
- }
-
- int main(int argc, char const *argv[])
- {
- char ch;
- if (signal(SIGALRM,handle) == SIG_ERR){
- ERROR("signale error");
- }
-
- alarm(3);
-
- while (1){
- printf("请输入您要出的牌>>");
- ch = getchar();
- getchar();
- printf("%c\n",ch);
- alarm(3);
- }
- return 0;
- }
(1)消息队列
(2)共享内存
(3)信号灯集
ipcs -q //查看消息队列的命令
ipcs -m //查看共享内存的命令
ipcs -s //查看信号灯集的命令
ipcrm -q msqid //删除消息队列命令
ipcrm -m shmid //删除共享内存命令
ipcrm -s semid //删除信号灯集的命令
消息队列也是借助内核实现的,A进程将消息放到消息队列中,队列中的消息
有消息的类型和消息的正文。B进程可以根据想取的消息的类型从消息队列中
将消息读走。消息队列默认的大小是16384个字节。如果消息队列中的消息满了,
A进程还想往队列中发消息,此时A进程阻塞。
- #include
- #include
- #include
- #include
- #include
- #include
-
- #define ERROR(msg) do{\
- printf("%s %s %d\n", __FILE__, __func__, __LINE__);\
- printf(msg);\
- exit(-1); \
- }while(0)
-
- int main(int argc, char const *argv[])
- {
- key_t key;
- struct stat st;
-
- if ((key = ftok("/home/linux",'w')) == -1){
- ERROR("ftok error");
- }
-
- printf("key=%#x\n",key);
-
- if (stat("/home/linux",&st)){
- ERROR("stat error");
- }
-
- printf("pro_id=%#x,devno=%#lx,ino=#=%#lx\n",'w',st.st_dev,st.st_ino);
- return 0;
- }
结果图:

头文件:
- #ifndef __MYHEAD_H__
- #define __MYHEAD_H__
-
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- #define PRINT_ERR(msg) do{\
- printf("%s %s %d\n", __FILE__, __func__, __LINE__);\
- printf(msg);\
- exit(-1); \
- }while(0)
-
-
- #define MSGSIZE (sizeof(msg_t)-sizeof(long))
-
- typedef struct mubuf{
- long mtype;
- char text[512];
- }msg_t;
-
- #endif
发送方:
- #include "myhead.h"
-
- int main(int argc, char const *argv[])
- {
- key_t key;
- int msqid;
- msg_t msg ={
- .mtype = 100,
- };
-
- if((key = ftok("/home/linux/",'r'))==-1)
- PRINT_ERR("ftok get key error");
-
- if((msqid = msgget(key,IPC_CREAT|0666))==-1)
- PRINT_ERR("create msg queue error");
-
- while (1){
- memset(msg.text,0,sizeof(msg.text));
- fgets(msg.text,MSGSIZE,stdin);
- msg.text[strlen(msg.text) - 1] = '\0';
-
- msgsnd(msqid, &msg, MSGSIZE, 0);
-
- if (!strncmp(msg.text,"quit",4)){
- break;
- }
- }
-
- msgctl(msqid, IPC_RMID, NULL);
- return 0;
- }
接受方:
- #include "myhead.h"
-
- int main(int argc, char const *argv[])
- {
- key_t key;
- int msgqid;
- msg_t msg;
-
- if ((key = ftok("/home/linux",'r')) == -1){
- PRINT_ERR("ftok error");
- }
-
- if ((msgqid = msgget(key, IPC_CREAT|0666)) == -1){
- PRINT_ERR("msgget error");
- }
-
- while (1){
- memset(msg.text, 0, sizeof(msg.text));
- msgrcv(msgqid, &msg, MSGSIZE,0,0);
-
- if (!strncmp("quit",msg.text,4)){
- break;
- }
-
- printf("%s\n",msg.text);
- }
-
- msgctl(msgqid,IPC_RMID,NULL);
- return 0;
- }
-
头文件:
- #ifndef __MSGQUE_H__
- #define __MSGQUE_H__
-
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- typedef struct msgbuf {
- long id;
- char name[30];
- char sex;
- int age;
- }msg_t;
-
- #define MSGSIZE (sizeof(msg_t)-sizeof(long))
-
- #endif
发送方:
- #include "msgqueue.h"
- #include
-
- int main(int argc, const char* argv[])
- {
- key_t key;
- int msqid;
-
- if ((key = ftok("/home/linux/", 'r')) == -1)
- PRINT_ERR("ftok get key error");
-
- if ((msqid = msgget(key, IPC_CREAT | 0666)) == -1)
- PRINT_ERR("create msg queue error");
-
- msg_t m1 = {
- .id = 1,
- .name = "zhangsan",
- .sex = 'm',
- .age = 30,
- };
- msgsnd(msqid, &m1, MSGSIZE, 0);
- msg_t m2 = {
- .id = 2,
- .name = "lisi",
- .sex = 'w',
- .age = 18,
- };
- msgsnd(msqid, &m2, MSGSIZE, 0);
- msg_t m3 = {
- .id = 3,
- .name = "wangwu",
- .sex = 'm',
- .age = 22,
- };
- msgsnd(msqid, &m3, MSGSIZE, 0);
-
-
- // msgctl(msqid, IPC_RMID, NULL);
-
- return 0;
- }
接受方:
- #include "msgqueue.h"
-
- int main(int argc, const char* argv[])
- {
- key_t key;
- int msqid;
- msg_t msg;
-
-
- if ((key = ftok("/home/linux/", 'r')) == -1)
- PRINT_ERR("ftok get key error");
-
- if ((msqid = msgget(key, IPC_CREAT | 0666)) == -1)
- PRINT_ERR("create msg queue error");
-
-
- memset(&msg, 0, sizeof msg);
- msgrcv(msqid, &msg, MSGSIZE, atoi(argv[1]), 0);
- printf("id=%ld,name=%s,sec=%c,age=%d\n",msg.id,msg.name,msg.sex,msg.age);
-
- // msgctl(msqid, IPC_RMID, NULL);
-
- return 0;
- }
结果图:

共享内存:在内核空间创建共享内存,让用户的A和B进程都能够访问到。通过这块内存
进行数据的传递。共享内存所有的进程间通信中效率最高的方式(不需要来回拷贝数据),共享内存的大小为 4k整数倍。
接受方:
- #include
- #include
- #include
- #include
- #include
- #include
-
- #define PRINT_ERR(msg) do{\
- printf("%s %s %d\n", __FILE__, __func__, __LINE__);\
- printf(msg);\
- exit(-1); \
- }while(0)
-
- int main(int argc, char const *argv[])
- {
- key_t key;
- int shmid;
- char* over;
-
- if ((key = ftok("/home/linux", 'r')) == -1){
- PRINT_ERR("ftok error");
- }
-
- if ((shmid = shmget(key, 4096, IPC_CREAT|0666)) == -1){
- PRINT_ERR("shemget error");
- }
-
- if ((over = shmat(shmid, NULL, 0)) == (void*)-1){
- PRINT_ERR("shmat error");
- }
-
- while (1){
- if (!strncmp("quit", over, 4))break;
- getchar();
-
- printf("%s\n",over);
- }
-
- if (shmdt(over)){
- PRINT_ERR("shmdt error");
- }
-
- shmctl(shmid,IPC_RMID,NULL);
- return 0;
- }
发送方:
- #include
- #include
- #include
- #include
- #include
- #include
-
- #define PRINT_ERR(msg) do{\
- printf("%s %s %d\n", __FILE__, __func__, __LINE__);\
- printf(msg);\
- exit(-1); \
- }while(0)
-
- int main(int argc, char const *argv[])
- {
- key_t key;
- int shmid;
- char* over;
-
- if ((key = ftok("/home/linux", 'r')) == -1){
- PRINT_ERR("ftok error");
- }
-
- if ((shmid = shmget(key, 4096, IPC_CREAT|0666)) == -1){
- PRINT_ERR("shmget error\n");
- }
-
- if ((over = shmat(shmid, NULL, 0)) == (void*)-1){
- PRINT_ERR("shmat error\n");
- }
-
- while (1){
- printf("请输入>>");
- fgets(over,4096,stdin);
- over[strlen(over) - 1] = '\0';
-
- if (!strncmp("quit",over,4)){
- break;
- }
- }
-
- if (shmdt(over)){
- PRINT_ERR("shmdt error\n");
- }
-
- shmctl(shmid,IPC_RMID,NULL);
- return 0;
- }
信号量:又叫信号灯集,它是实现进程间同步的机制。在一个信号灯集中可以有
很多的信号灯,这些信号灯它们的工作相关不干扰。一般使用的时候使用的是二值
信号灯。
sem.h
- #ifndef __SEM_H__
- #define __SEM_H__
-
- int mysem_init(int nsems);
- int P(int semid, int semnum);
- int V(int semid, int semnum);
- int sem_del(int semid);
-
- #endif
- #include
-
- union semun {
- int val; /* Value for SETVAL */
- struct semid_ds* buf; /* Buffer for IPC_STAT, IPC_SET */
- };
-
- int semnum_init_value(int semid, int which, int value)
- {
- union semun sem = {
- .val = value,
- };
- if (semctl(semid, which, SETVAL, sem) == -1)
- PRINT_ERR("semctl int value error");
- return 0;
- }
-
- //初始化信号灯集
- int mysem_init(int nsems)
- {
- key_t key;
- int semid;
- // 1.通过ftok获取键值
- if ((key = ftok("/home/linux/", 'g')) == -1)
- PRINT_ERR("get key error");
- // 2.如果不选择就创建信号灯集,如果存在返回已存在的错误
- if ((semid = semget(key, nsems, IPC_CREAT | IPC_EXCL | 0666)) == -1) {
- if (errno == EEXIST) {
- //如果已存在,这里调用semget,直接返回semid
- semid = semget(key, nsems, IPC_CREAT | 0666);
- } else {
- PRINT_ERR("create sem error");
- }
- } else {
- // 3.初始化信号灯集中的信号灯
- for (int i = 0; i < nsems; i++) {
- semnum_init_value(semid, i, !i);
- }
- }
-
- return semid;
- }
-
- //申请资源
- int P(int semid, int semnum)
- {
- struct sembuf buf = {
- .sem_num = semnum,
- .sem_op = -1,
- .sem_flg = 0,
- };
-
- if (semop(semid, &buf, 1))
- PRINT_ERR("request resource error");
-
- return 0;
- }
- //释放资源
- int V(int semid, int semnum)
- {
- struct sembuf buf = {
- .sem_num = semnum,
- .sem_op = 1,
- .sem_flg = 0,
- };
-
- if (semop(semid, &buf, 1))
- PRINT_ERR("free resource error");
-
- return 0;
- }
- //删除信号灯集
- int sem_del(int semid)
- {
- semctl(semid,0,IPC_RMID);
- }
写端:
- #include
- #include "sem.h"
-
- int main(int argc, const char* argv[])
- {
- key_t key;
- int shmid,semid;
- char* waddr;
- //0.信号量的初始化
- semid = mysem_init(2);
- if(semid == -1){
- printf("sem init error");
- return -1;
- }
- // 1.获取key
- if ((key = ftok("/home/linux", 'p')) == -1)
- PRINT_ERR("get key error");
- // 2.创建共享内存
- if ((shmid = shmget(key, 4096, IPC_CREAT | 0666)) == -1)
- PRINT_ERR("create share memory error");
- // 3.将共享内存映射到用户空间
- if ((waddr = shmat(shmid, NULL, 0)) == (void*)-1)
- PRINT_ERR("shmat error");
- printf("waddr = %p\n", waddr);
- // 4.写操作
- while (1) {
- P(semid,0);
- printf("input > ");
- fgets(waddr, 4096, stdin);
- waddr[strlen(waddr) - 1] = '\0';
- if (strncmp(waddr, "quit", 4) == 0)
- break;
- V(semid,1);
- }
- // 5.取消地址映射
- if (shmdt(waddr))
- PRINT_ERR("shmdt error");
-
- // 6.删除共享内存
- if (shmctl(shmid, IPC_RMID, NULL))
- PRINT_ERR("shmrm error");
- //7.删除信号量
- sem_del(semid);
- return 0;
- }
读端口:
- #include "sem.h"
- #include
- int main(int argc, const char* argv[])
- {
- key_t key;
- int shmid, semid;
- char* raddr;
- // 0.信号量的初始化
- semid = mysem_init(2);
- if (semid == -1) {
- printf("sem init error");
- return -1;
- }
- // 1.获取key
- if ((key = ftok("/home/linux", 'p')) == -1)
- PRINT_ERR("get key error");
- // 2.创建共享内存
- if ((shmid = shmget(key, 4096, IPC_CREAT | 0666)) == -1)
- PRINT_ERR("create share memory error");
-
- // 3.将共享内存映射到用户空间
- if ((raddr = shmat(shmid, NULL, 0)) == (void*)-1)
- PRINT_ERR("shmat error");
-
- printf("waddr = %p\n", raddr);
- // 4.读操作
- while (1) {
- P(semid,1);
- printf("raddr = %s\n", raddr);
- if (strncmp(raddr, "quit", 4) == 0)
- break;
- V(semid,0);
- }
- // 5.取消地址映射
- if (shmdt(raddr))
- PRINT_ERR("shmdt error");
-
- // 6.删除共享内存
- shmctl(shmid, IPC_RMID, NULL);
-
- //7.删除信号量
- sem_del(semid);
- return 0;
- }