思维导图:
消息队列原理:
进程在内核空间中维护出消息队列,每个进程借由消息队列的msgid对消息队列进行读写操作,消息队列中的信息是一个结构体,存储着消息的种类(身份证)和消息的内容,多个进程间可以通过消息的种类来进行通讯,例如:进程A放了一个种类为A的信息,进程B只读取种类为A的信息,通过这种方式实现了进程间的定向信息传输。
共享内存原理:
共享内存第一步是先将一块物理内存映射到内核空间,在内核空间维护出一块共享内存,然后每个进程可以通过指针的方式共同访问这块内存空间,并进行读写操作,实现进程通信。类似malloc用法。
使用消息列队实现两个进程间接受发送信息
发送端:
#include
typedef struct m
{
long type;
char text[1024];
}sake;
#define SIZE sizeof(struct m)-sizeof(long)
int main(int argc, const char *argv[])
{
key_t key;
if((key=ftok("/",'a'))==-1)
{
perror("");
return -1;
}
int msgid=0;
if((msgid=msgget(key,IPC_CREAT|0664))==-1)
{
perror("");
return -1;
}
pid_t pid;
pid=fork();
if(pid>0)
{
sake message;
while(1)
{
message.type=1;
printf("传送的信息:");
scanf("%s",message.text);
printf("%s\n",message.text);
msgsnd(msgid,&message,SIZE,0);
if(strcmp(message.text,"quit")==0)
{
break;
}
}
}
else if(pid==0)
{
sake message;
while(1)
{
msgrcv(msgid,&message,SIZE,2,0);
if(strcmp(message.text,"quit")==0)
{
break;
}
printf("接收到的消息为:%s\n",message.text);
}
}
return 0;
}
接收端:
#include
typedef struct m
{
long type;
char text[1024];
}sake;
#define SIZE sizeof(sake)-sizeof(long)
int main(int argc, const char *argv[])
{
key_t key;
if((key=ftok("/",'a'))==-1)
{
perror("");
return -1;
}
int msgid;
if((msgid=msgget(key,IPC_CREAT|0664))==-1)
{
perror("");
return -1;
}
pid_t pid;
pid=fork();
if(pid>0)
{
sake message;
while(1)
{
message.type=2;
printf("传送的信息:");
scanf("%s",message.text);
msgsnd(msgid,&message,SIZE,0);
if(strcmp(message.text,"quit")==0)
{
break;
}
}
}
else if(pid==0)
{
sake message;
while(1)
{
msgrcv(msgid,&message,SIZE,1,0);
if(strcmp(message.text,"quit")==0)
{
break;
}
printf("接收到的消息为:%s\n",message.text);
}
}
if(msgctl(msgid,IPC_RMID,NULL)==-1)
{
perror("");
return -1;
}
return 0;
}
复现消息列队代码:
发送端:
#include
typedef struct message
{
long mtype;
char mtext[1024];
}sake;
#define SIZE sizeof(sake)-sizeof(long)
int main(int argc, const char *argv[])
{
key_t key;
if((key=ftok("/",'a'))==-1)
{
perror("");
return -1;
}
int msgq=0;
if((msgq=msgget(key,IPC_CREAT|0664))==-1)
{
perror("");
return -1;
}
sake msg;
while(1)
{
long mt=0;
printf("请输入消息类型:");
scanf("%ld",&mt);
msg.mtype=mt;
char buf[1024];
printf("请输入发送的消息:");
scanf("%s",buf);
strcpy(msg.mtext,buf);
msgsnd(msgq,&msg,SIZE,0);
printf("operation success\n");
}
if(msgctl(msgq,IPC_RMID,NULL)==-1)
{
printf("error\n");
return -1;
}
return 0;
}
接收端:
#include
typedef struct message
{
long mtype;
char mtext[1024];
}sake;
#define SIZE sizeof(sake)-sizeof(long)
int main(int argc, const char *argv[])
{
key_t key;
if((key=ftok("/",'a'))==-1)
{
perror("");
return -1;
}
int msgq=0;
if((msgq=msgget(key,IPC_CREAT|0664))==-1)
{
perror("");
return -1;
}
sake msg;
while(1)
{
msgrcv(msgq,&msg,SIZE,1,0);
printf("收到的数据为:%s\n",msg.mtext);
}
return 0;
}
复现共享内存代码
发送端:
#include
#define PAGE_SIZE 4096
int main(int argc, const char *argv[])
{
key_t key;
if((key=ftok("/",'a'))==-1)
{
perror("");
return -1;
}
int shmid;
if((shmid=shmget(key,4096,IPC_CREAT|0664))==-1)
{
perror("");
return -1;
}
char *ptr=NULL;
ptr=(char *)shmat(shmid,NULL,0);
while(1)
{
fgets(ptr,PAGE_SIZE,stdin);
ptr[strlen(ptr)-1]='\0';
if(strcmp(ptr,"quit")==0)
{
break;
}
}
if(shmdt(ptr)==-1)
{
perror("");
return -1;
}
if(shmctl(shmid,IPC_RMID,NULL)==-1)
{
perror("");
return -1;
}
return 0;
}
接收端:
#include
#define PAGE_SIZE 4096
int main(int argc, const char *argv[])
{
key_t key;
if((key=ftok("/",'a'))==-1)
{
perror("");
return -1;
}
int shmid;
if((shmid=shmget(key,4096,IPC_CREAT|0664))==-1)
{
perror("");
return -1;
}
char *ptr=NULL;
ptr=(char *)shmat(shmid,NULL,0);
while(1)
{
printf("收到的数据为%s\n",ptr);
sleep(3);
if(strcmp(ptr,"quit")==0)
{
break;
}
}
if(shmdt(ptr)==-1)
{
perror("");
return -1;
}
return 0;
}
面试题整理:
进程和线程的区别是什么:
相同点:他们都遵循时间片轮询,上下文切换原则,
不同点:进程是资源分配的最小单位,线程是任务执行的最小单位
在一个进程中的线程共享进程的资源,而进程之间相互独立
线程是进程的执行单元,一个进程可以包含多个线程
如何在Linux系统中实现同步
实现同步的方式主要有两种,一种是无名信号量。无名信号量本质上是个临界资源,所有线程在执行前会去访问自己对应的无名信号量,在别的线程将自己的无名信号量增加后方可执行。
第二种是同步互斥机制中的条件变量,它结合了互斥锁思想,所有线程先获取锁资源,然后进入到消息队列后将锁资源打开供其他线程进入队列,等到条件变量被唤醒,再同时让所有再队列中的线程去获取锁资源
互斥的概念及实现
互斥指同一时刻只有一个线程可以执行,没有先后顺序,谁抢到资源谁先执行
主要依靠互斥锁进行工作,互斥锁本质上是个临界资源,所有线程再执行前先访问锁资源,访问到了便将其上锁,其他线程无法访问到锁资源。等到线程结束再释放锁资源供下次争抢。
fork()调用后,父进程和子进程的执行顺序是什么样的,他们如何共享资源
没有执行顺序,遵循时间片轮询上下文切换原则
子进程会拷贝父进程的资源,在fork语句之前的资源子进程都能使用,且相互独立
什么是僵尸进程?如何避免?
僵尸进程是指子进程已经结束但是父进程未回收其资源
可以利用signal函数,当子进程结束后,子进程会对父进程发送SIG_CHLD信号,父进程可以利用signal函数捕获这种信号,然后利用回调函数回收子进程资源。
Linux中进程间的通信方式
无名管道,有名管道,信号
消息队列,共享内存,信号量(信号灯集)
套接字