有名管道可以在任意两个进程之间通信
管道文件只有两种打开方式:只读 O_RDONLY 只写 O_WRONLY
一个进程往管道里面写入数据
- #include
- #include
- #include
- #include
- #include
- #include
- int main()
- {
- int fdw=open("./FIFO",O_WRONLY);
- assert(fdw!=-1);
- printf("fdw=%d\n",fdw);
- while(1)
- {
- printf("input:\n");
- char buff[128]={0};
- fgets(buff,128,stdin);
- if(strncmp(buff,"end",3)==0)
- {
- break;
- }
- write(fdw,buff,strlen(buff));
- }
- close(fdw);
- }
另外一个进程从管道里面读出数据
- #include
- #include
- #include
- #include
- #include
- #include
- int main()
- {
- int fdw=open("./FIFO",O_RDONLY);
- assert(fdw!=-1);
- printf("fdw=%d\n",fdw);
- while(1)
- {
- char buf[128]={0};
- if(read(fdw,buf,127)==0)
- {
- break;
- }
- printf("read=%s\n",buf);
- }
- close(fdw);
- }
结果

注意
需要两个进程同时进行,至少一个读一个写,保证读和写都得有。
当写端关闭,读端自动返回0。
如果读端关闭,系统会给写端发送信号--13,引起异常。

加上这段代码,当读端结束后,写端会打印系统返回的信号。

int pipe(int fds[2]);pipe()成功返回 0,失败返回-1fds[0]是管道读端的描述符fds[1]是管道写端的描述符
- #include
- #include
- #include
- #include
- #include
- int main()
- {
- int fd[2];
- assert(pipe(fd)!=-1);
- pid_t pid =fork();
- assert(pid!=-1);
- if(pid ==0)
- {
- close(fd[1]);
- char buff[128]={0};
- read(fd[0],buff,127);
- printf("child read:%s\n",buff);
- close(fd[0]);
- }
- else
- {
- close(fd[0]);
- write(fd[1],"hello",5);
- close(fd[1]);
- }
- }
单工:方向固定
半双工:一次只允许一方到另一方传递
全双工:允许同时进行双方传递

A和B去打印,需求是A,B同时打印次数为偶数
- #include
- #include
- #include
- #include
- #include
- int main()
- {
- for(int i=0;i<5;i++)
- {
- printf("A");
- fflush(stdout);
- int n=rand()%3;
- sleep(n);
- printf("A");
- fflush(stdout);
- n=rand()%3;
- sleep(n);
- }
- }
- #include
- #include
- #include
- #include
- #include
- int main()
- {
- for(int i=0;i<5;i++)
- {
- printf("B");
- fflush(stdout);
- int n=rand()%3;
- sleep(n);
- printf("B");
- fflush(stdout);
- n=rand()%3;
- sleep(n);
- }
- }
结果

在B未结束时A已经出现了,所以表示一个进程未结束另一个进程已经开始了。
为解决2.1的问题,我们增加一个信号量,值为1,当执行A时p操作-1,值为0,这时表明没有资源可以使用,进程B无法运行,当A结束后v操作+1,这时进程B就可以有资源去使用,执行B资源然后同上,这样就解决了两个进程依次执行的
int semget(key_t key, int nsems, int semflg);semget()创建或者获取已存在的信号量semget()成功返回信号量的 ID, 失败返回-1key:两个进程使用相同的 key 值,就可以使用同一个信号量nsems:内核维护的是一个信号量集,在新建信号量时,其指定信号量集中信号量的个数semflg 可选: IPC_CREAT IPC_EXCL
int semop( int semid, struct sembuf *sops, unsigned nsops);semop()对信号量进行改变,做 P 操作或者 V 操作semop()成功返回 0,失败返回-1struct sembuf{unsigned short sem_num; //指定信号量集中的信号量下标short sem_op; //其值为-1,代表 P 操作,其值为 1,代表 V 操作short sem_flg; //SEM_UNDO}
2.2.1.3 semget()
int semctl( int semid, int semnum, int cmd, ...);semctl()控制信号量semctl()成功返回 0,失败返回-1cmd 选项: SETVAL IPC_RMIDunion semun{int val;struct semid_ds *buf;unsigned short *array;struct seminfo *_buf;};
- #include "sem.h"
-
- static int semid = -1;
- void sem_init()
- {
- semid = semget((key_t)1234,1,IPC_CREAT|IPC_EXCL|0600);//全新创建信号量,如果存在就失败
- if ( semid == -1 )//失败,表示已存在
- {
- semid = semget((key_t)1234,1,0600);//获取已存在的信号量id
- if ( semid == -1)
- {
- printf("semget err\n");
- }
- }
- else//全新创建成功,那么要进行初始化
- {
- union semun a;
- a.val = 1;//信号量的初始值
- if ( semctl(semid,0,SETVAL,a) == -1)//设置初始值
- {
- printf("semctl err\n");
- }
- }
- }
- void sem_p()
- {
- struct sembuf buf;
- buf.sem_num = 0;
- buf.sem_op = -1;//p
- buf.sem_flg = SEM_UNDO;
-
- if ( semop(semid,&buf,1) == -1)
- {
- printf("semop p err\n");
- }
- }
- void sem_v()
- {
- struct sembuf buf;
- buf.sem_num = 0;
- buf.sem_op = 1;//v
- buf.sem_flg = SEM_UNDO;
-
- if ( semop(semid,&buf,1) == -1)
- {
- printf("semop v err\n");
- }
- }
- void sem_destroy()
- {
- if ( semctl(semid,0,IPC_RMID) == -1)
- {
- printf("semctl destroy err\n");
- }
- }
给2.1中的a.c b.c 添加信号量就会输出下图

两个进程在进行通讯时,共同使用同一块内存。
int shmget(key_t key, size_t size, int shmflg);shmget()用于创建或者获取共享内存shmget()成功返回共享内存的 ID, 失败返回-1key: 不同的进程使用相同的 key 值可以获取到同一个共享内存size: 创建共享内存时,指定要申请的共享内存空间大小shmflg: IPC_CREAT IPC_EXCL
void * shmat( int shmid, const void *shmaddr, int shmflg);shmat()将申请的共享内存的物理内存映射到当前进程的虚拟地址空间上shmat()成功返回返回共享内存的首地址,失败返回 NULLshmaddr:一般给 NULL,由系统自动选择映射的虚拟地址空间shmflg: 一般给 0, 可以给 SHM_RDONLY 为只读模式,其他的为读写
int shmdt( const void *shmaddr);shmdt()断开当前进程的 shmaddr 指向的共享内存映射shmdt()成功返回 0, 失败返回-1
int shmctl( int shmid, int cmd, struct shmid_ds *buf);
shmctl()控制共享内存shmctl()成功返回 0,失败返回-1cmd: IPC_RMID 32. *

这里的共享内存的实现方式个人感觉和无名管道很像,都是使用两个信号量来控制输入输出进程的执行,只不过无名管道的信号量是来源于fork()复制来的。
这个是进程1的实现,主要就是往共享内存里面传输数据
- #include
- #include
- #include
- #include
- #include
- #include "sem.h"
-
- int main()
- {
- int shmid = shmget((key_t)1234,128,IPC_CREAT|0600);
- if ( shmid == -1 )
- {
- printf("shmget err\n");
- exit(1);
- }
-
- char* s = (char*)shmat(shmid,NULL,0);
- if ( s == (char*)-1)
- {
- printf("shmat err\n");
- exit(1);
- }
-
- sem_init();
- while( 1 )
- {
- printf("input\n");
- char buff[128] = {0};
- fgets(buff,128,stdin);
-
- sem_p(SEM1);
- strcpy(s,buff);
- sem_v(SEM2);
-
- if ( strncmp(buff,"end",3) == 0)
- {
- break;
- }
- }
-
- shmdt(s);
- }
在进程1结束后我们只用断开进程1和共享内存之间的映射就行,不需要结束共享内存,因为后续还会有其他进程使用这个共享内存。
这个是进程2的实现方式,主要就是从共享内存中读出数据
- #include
- #include
- #include
- #include
- #include
- #include "sem.h"
-
- int main()
- {
- int shmid = shmget((key_t)1234,128,IPC_CREAT|0600);
- if ( shmid == -1 )
- {
- printf("shmget err\n");
- exit(1);
- }
-
- char * s = (char*)shmat(shmid,NULL,0);
- if ( s == (char*)-1)
- {
- printf("shmat err\n");
- exit(1);
- }
-
- sem_init();
- while( 1 )
- {
- sem_p(SEM2);
- if ( strncmp(s,"end",3) == 0 )
- {
- break;
- }
-
- printf("read:%s\n",s);
- sem_v(SEM1);
- }
-
- shmdt(s);
- shmctl(shmid,IPC_RMID,NULL);
- sem_destroy();
- }
这里如果直接去实现就会无限迅速读入读出,所以需要我们引入信号量加以管制,和2的代码很像,只需要大家看得懂我前面画的图,实现这个功能并不复杂。

根据前面的数字类型进行分类。
int msgget(key_t key, int msqflg);msgget()创建或者获取一个消息队列msgget()成功返回消息队列 ID,失败返回-1msqflg: IPC_CREAT
int msgsnd( int msqid, const void *msqp, size_t msqsz, int msqflg);msgsnd()发送一条消息,消息结构为:struct msgbuf{long mtype; // 消息类型, 必须大于 0 必须有char mtext[1]; // 消息数据};msgsnd()成功返回 0, 失败返回-1msqsz: 指定 mtext 中有效数据的长度msqflg:一般设置为 0 可以设置 IPC_NOWAIT
ssize_t msgrcv( int msqid, void *msgp, size_t msqsz, long msqtyp, int msqflg);msgrcv()接收一条消息msgrcv()成功返回 mtext 中接收到的数据长度, 失败返回-1msqtyp: 指定接收的消息类型,类型可以为 0msqflg: 一般设置为 0 可以设置 IPC_NOWAIT
int msgctl( int msqid, int cmd, struct msqid_ds *buf);msgctl()控制消息队列msgctl()成功返回 0,失败返回-1cmd: IPC_RMID
- #include
- #include
- #include
- #include
- #include
- #include
- struct message
- {
- long int type;//固定
- char buff[32];
- };
- int main()
- {
- int msgid=msgget((key_t)1234,IPC_CREAT|0600);
- if(msgid==-1)
- {
- printf("msgget err\n");
- exit(1);
- }
- struct message dt;
- dt.type=1;
- strcpy(dt.buff,"hello1");
- msgsnd(msgid,&dt,32,0);
-
- }
进程b
- #include
- #include
- #include
- #include
- #include
- #include
- struct message
- {
- long int type;//固定
- char buff[32];
- };
- int main()
- {
- int msgid=msgget((key_t)1234,IPC_CREAT|0600);
- if(msgid==-1)
- {
- printf("msgget err\n");
- exit(1);
- }
- struct message dt;
- msgrcv(msgid,&dt,32,1,0);//0代表不区分类型
- printf("read message:%s\n",dt.buff);
-
- }