1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > Linux —进程间的五种通信方式—(半双工管道 命名管道 消息队列 信号 共享内存

Linux —进程间的五种通信方式—(半双工管道 命名管道 消息队列 信号 共享内存

时间:2021-07-07 08:12:58

相关推荐

Linux —进程间的五种通信方式—(半双工管道 命名管道 消息队列 信号 共享内存

无名管道pipe(半双工):(仅限同一个程序运行)

创建无名管道会生成特殊文件,只存在于内存中

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>//int pipe(int pipefd[2]);//ssize_t write(int fd, const void *buf, size_t count);int main(){int pid;int fd[2];char buf[128];if(pipe(fd) == -1){ //创建无名管道printf("create pipe failed!\n");}pid = fork();//创建进程if(pid < 0){printf("creat fork failed!\n");}else if(pid > 0){//父进程sleep(3); //延迟3秒printf("This is father!\n");close(fd[0]); //关闭读通道write(fd[1],"Hello , From Father!",strlen("Hello , From Father!"));//写入通道给子进程wait();//等待子进程退出收集exit信号}else{ //子进程printf("This is Child!\n");close(fd[1]);//关闭写通道read(fd[0],buf,128);//读通道父进程发送的数据printf("read from father:%s\n",buf);//打印读取的数据exit(0);//退出信号}return 0;}

运行效果如下: (因为父进程等待3秒,所以子进程先运行,等待3秒后父进程运行)

命名管道fifo:(可不同程序运行)

创建命名管道会以特殊设备文件形式存在于文件系统中,如以下我创建的file

程序A:(mkfifo)创建管道(read)读

#include <sys/types.h>#include <sys/stat.h>#include <stdio.h>#include <errno.h>#include <fcntl.h>// int mkfifo(const char *pathname, mode_t mode);int main(){char buf[30] = {0};int nread = 0;if((mkfifo("./file",0600) == -1) && errno!= EEXIST)//创建命名管道,可读可写{printf("mkfifo file failed!\n");perror("why:");//perror函数获取失败信息}int fd = open("./file",O_RDONLY);//只读的方式打开管道filewhile(1){nread = read(fd,buf,30);//读管道中的数据printf("read=%dbyte from buf:%s\n",nread,buf);//打印读取的字节数,和数据内容}close(fd);//关闭文件return 0;}

程序B:(write)写

#include <sys/types.h>#include <sys/stat.h>#include <stdio.h>#include <errno.h>#include <fcntl.h>#include <string.h>// int mkfifo(const char *pathname, mode_t mode);int main(){int cnt = 0;char *buf = "Ivan hen shuai!"; //要写入的字符串int fd = open("./file",O_WRONLY);//只写的方式打卡fileprintf("write open success\n");while(1){ //写入5次后跳出循环write(fd,buf,strlen(buf));//写入sleep(1);//延时一秒等待写入缓存if(cnt == 5){break;}}close(fd);//关闭文件return 0;}

运行效果如下: (先运行程序A,等待数据写入读数据,再运行程序B,写入数据,最后程序A打印读取的数据)(fiforead为程序A,fifowrite为程序B)

消息队列:msg(可不同程序运行)

再Linux内核中,有专门的结构体链表,来管理和存储消息队列

思路:1、A如何加消息到队列

2、B如何从队列拿到消息

程序A: msgget

#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>#include <stdio.h>#include <string.h>//ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);struct msgbuf {long mtype; /* message type, must be > 0 */char mtext[128]; /* message data */};struct msgbuf readbuf;int main(){int key; key = ftok(".",'z');//通过ftok函数解析成key printf("key=%x\n",key);//每一个消息队列都有一个id号int msgID = msgget(key,IPC_CREAT|0777);//创建/获取消息队列,可读可写可执行if(msgID == -1){printf("get que failed!\n");}//将写入队列的内容读出来,消息类型长度888,编号0msgrcv(msgID,&readbuf,sizeof(readbuf.mtext),888,0);//打印读取的数据printf("read from que:%s\n",readbuf.mtext);//创建数据,是结构体类型,消息类型长度988struct msgbuf sendbuf = {988,"Thank you for reach!\n"};//msgsend函数将结构体的数据写入队列msgsnd(msgID,&sendbuf,strlen(sendbuf.mtext),0);//用完程序,将不用的垃圾队列移除msgctl(msgID,IPC_RMID,NULL);return 0;}

程序B: msgsend(整体和msgget差不多,却决于谁先发送,谁先读)

#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>#include <stdio.h>#include <string.h>//ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);struct msgbuf {long mtype; /* message type, must be > 0 */char mtext[128]; /* message data */};struct msgbuf readbuf;struct msgbuf sendbuf = {888,"This is maessage from queue!\n"};int main(){int key;key = ftok(".",'z');printf("key=%x\n",key);int msgID = msgget(key,IPC_CREAT|0777);if(msgID == -1){printf("get que failed!\n");}//msgsend函数将队列里的数据读出来msgsnd(msgID,&sendbuf,strlen(sendbuf.mtext),0);printf("send over!\n");//发送数据到队列msgrcv(msgID,&readbuf,sizeof(readbuf.mtext),988,0);printf("return from que:%s\n",readbuf.mtext);//讲垃圾队列移除msgctl(msgID,IPC_RMID,NULL);return 0;}

运行效果如下:(先运行程序A,等待数据写入读数据,再运行程序B,写入数据,最后程序AB打印读取的数据)(msgget为程序A,msgsend为程序B)

内存共享:shm(可不同程序运行)

内存共享,顾名思义就是他们可以共享某一个内存

思路:1、先创建/获取一个共享内存

2、文件连接这个内存

3、释放内存

4、将内存移除

程序A: shaget

#include <sys/ipc.h>#include <sys/shm.h>#include <stdio.h>#include <stdlib.h>#include <string.h>// int shmget(key_t key, size_t size, int shmflg);int main(){int shmID;key_t key;char *shmaddr;key = ftok(".",1);//获取key值shmID = shmget(key,1024*4,IPC_CREAT|0666);//创建共享内存,可读可写,生成一个内存的id号if(shmID == -1){printf("get failed!\n");} /*连接这个内存,第一个0是默认,第二行0是代表映射进去的内容可读可写生成一个指针变量,并指向内存*/shmaddr = shmat(shmID,0,0);printf("shmat ok!\n");//连接成功strcpy(shmaddr,"Ivan nb123!");//将字符串写入指针变量sleep(5);//等待5秒shmdt(shmaddr);//释放内存shmctl(shmID,IPC_RMID,0);//将内存移除printf("quit\n");return 0;}

程序B: sharead

#include <sys/ipc.h>#include <sys/shm.h>#include <stdio.h>#include <stdlib.h>#include <string.h>// int shmget(key_t key, size_t size, int shmflg);int main(){int shmID;key_t key;char *shmaddr;key = ftok(".",1);shmID = shmget(key,1024*4,0);//获取if(shmID == -1){printf("get failed!\n");}shmaddr = shmat(shmID,0,0);//连接这个内存printf("data:%s\n",shmaddr);//将内存中的数据打印出来printf("shmat ok!\n");// sleep(5);shmdt(shmaddr);//释放内存//shmctl(key,IPC_RMID,0);//因为程序已经有了移除内存操作,所以这里不用printf("quit\n");return 0;}

运行效果如下:(先运行程序A,等待数据写入读数据到内存,再运行程序B,读取数据,最后程序B打印读取的数据)(shmg为程序A,shmr为程序B)

信号:sig(可不同程序运行)

信号对于Linux来说,就像单片机中的硬件中断,这个信号为“软中断”

信号处理有三种方式:忽略,捕捉信号,默认动作

(SIGKILL和SIGSTOP不能被忽略,他们是ctl+C和ctl+Z)

每一个信号都有名字和编号,都已SIG开头

用kill -l来查看linux中有哪些信号

比如说当A程序无法退出时,我们可以再cmd另一端调用这个信号

如KILL (pid:-9,就是SIGKILL)(A程序的pid号)

前提是我们让程序A绑定了这个KILL这个信号来杀死这个进程

以上是低级信号的操作,下面直接上高级信号操作:

直接上代码demo

程序A: sigaction收信号

#include <signal.h>#include <stdio.h>//int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);//函数指针,第一个为程序的num,第二个为结构体,第三个非空可接收数据void handler(int signum, siginfo_t *info, void *context){printf("get signum:%d\n",signum);//打印出程序的numif(context != NULL){//非空可接收数据printf("get data:%d\n",info->si_int);//打印出结构体里面的整型数据printf("get data:%d\n",info->si_value.sival_int);//可自选printf("form:%d\n",info->si_pid);//将程序B的pid打印出来}}int main(){struct sigaction act;printf("pid = %d\n",getpid());//打印出本程序的pid号act.sa_sigaction = handler;//额外接收数据,为函数指针act.sa_flags= SA_SIGINFO;//收信号默认//第一参数是自定义信号量,第二个是一个结构体,绑定函数,第三个为备份,可NULLsigaction(SIGUSR1,&act,NULL);//处理信号函数注册while(1);return 0;}

程序B: sigaqueue发信号

#include <signal.h>#include <stdio.h>//int sigqueue(pid_t pid, int sig, const union sigval value);int main(int argc,char **argv){int signum;int pid;signum = atoi(argv[1]); //因为signum为整型,调用atoi来强制转换ASI码pid = atoi(argv[2]); //因为pid为整型 ,调用atoi来强制转换ASI码union sigval value;value.sival_int = 100;sigqueue(pid,signum,value); //发信号,第三个参数为联合体,存储要发的数据printf("pid=%d,done\n",getpid());return 0;}

运行效果如下:(程序B为发送信号,程序A为接收信号)

信号量

它不是一个通信方式,它是一个以锁的形式附加在以上其他通信方式的用法

它来决定谁先运行,谁后运行,

可理理解为:

钥匙:信号量,信号量集

房间:临界资源(打印机)

有两个操作:

1、P操作(pgetkey)拿钥匙

2、V操作(vputbackkey)把钥匙放回去

上代码理解:

#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>#include <stdio.h>//int semget(key_t key, int nsems, int semflg);//int semop(int semid, struct sembuf *sops, unsigned nsops)union semun {int val; /* Value for SETVAL */struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */unsigned short *array; /* Array for GETALL, SETALL */struct seminfo *__buf; /* Buffer for IPC_INFO(Linux-specific) */};void pGetKey(int id){struct sembuf sops;sops.sem_num = 0;//信号量的编号sops.sem_op = -1; //取钥匙操作sops.sem_flg = SEM_UNDO;//当进程自动终止的时候,取消对锁的操作(阻塞)semop(id, &sops, 1);printf("get keys!\n");}void vPutBackKey(int id){struct sembuf sops;sops.sem_num = 0;//信号量的编号sops.sem_op = 1; //取钥匙操作sops.sem_flg = SEM_UNDO;//当进程自动终止的时候,取消对锁的操作(阻塞)semop(id, &sops, 1);printf("put back keys!\n");}int main(){int semid;key_t key;key = ftok(".",2);//信号量集合中有一个信号量 semid = semget(key,1,IPC_CREAT|0666);//创建/获取信号量union semun initsem;initsem.val = 0;//操作第0个信号量 编号0semctl(semid,0,SETVAL,initsem);//SETVAL设置信号量的值,设置为initsemint pid = fork();if(pid > 0){//去拿锁pGetKey(semid);printf("This is Father\n");//锁放回去vPutBackKey(semid);semctl(semid,0,IPC_RMID);//销毁锁}else if(pid == 0){//这里不拿锁,因为初始状态没有锁,等父进程不执行,来到这里printf("This is Child\n");//这时调用拿锁操作,锁池就有一把锁vPutBackKey(semid);}else{printf("fork error!\n");}return 0;}65,1 底端

运行效果如下:(因为初始化没有钥匙,父进程没有拿到钥匙,子进程先运行,然后将钥匙放入,父进程拿到钥匙再运行)

Linux —进程间的五种通信方式—(半双工管道 命名管道 消息队列 信号 共享内存) 外加信号量。直接上代码:

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。