思维导图
1.使用消息队列完成两个进程之间的相互通信
进程A:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<header.h>
#define SIZE (sizeof(struct msgbuf)-sizeof(long))
struct msgbuf
{
long mtype; //消息类型
char mtext_1[1024];//消息正文
char mtext_2[1024];
};
int main(int argc, const char *argv[])
{
//创建key值
key_t key=0;
if((key=ftok("/",'a'))==-1)
{
perror("ftok error");
return -1;
}
printf("key=%#x\t",key);
//使用key值创建消息队列
int msqid=0;
if((msqid=msgget(key,IPC_CREAT|0664))==-1)
{
perror("msgget error");
return -1;
}printf("msqid=%d\n",msqid);
//创建进程
pid_t pid=fork();
if(pid>0)
{
//父进程写入数据
struct msgbuf buf={.mtype=100};
while(1)
{
scanf(" %s",buf.mtext_1);
msgsnd(msqid,&buf,SIZE,0); //0表示阻塞
if(strcmp(buf.mtext_1,"quit")==0)
break;
printf("发送成功\n");
}
exit(EXIT_SUCCESS);
}else if(pid==0)
{
//子进程读取数据
struct msgbuf buf={.mtype=200};
while(1)
{
msgrcv(msqid,&buf,SIZE,200,0); //200指取mtype的值
//0表示阻塞
if(strcmp(buf.mtext_2,"quit")==0)
break;
printf("收到了:%s\n",buf.mtext_2);
}
wait(NULL);
//删除消息队列
if(msgctl(msqid,IPC_RMID,NULL)==-1)
{
perror("msgctl error");
return -1;
}
}else
{
perror("pid1 create error");
return -1;
}
return 0;
}
进程B:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<header.h>
#define SIZE (sizeof(struct msgbuf)-sizeof(long))
struct msgbuf
{
long mtype; //消息类型
char mtext_1[1024];//消息正文
char mtext_2[1024];
};
int main(int argc, const char *argv[])
{
//创建key值
key_t key=0;
if((key=ftok("/",'a'))==-1)
{
perror("ftok error");
return -1;
}
printf("key=%#x\t",key);
//使用key值创建消息队列
int msqid=0;
if((msqid=msgget(key,IPC_CREAT|0664))==-1)
{
perror("msgget error");
return -1;
}printf("msqid=%d\n",msqid);
//创建进程
pid_t pid=fork();
if(pid==0)
{
//子进程写入数据
struct msgbuf buf={.mtype=200};
while(1)
{
scanf(" %s",buf.mtext_2);
msgsnd(msqid,&buf,SIZE,0); //0表示阻塞
if(strcmp(buf.mtext_2,"quit")==0)
break;
printf("发送成功\n");
}
exit(EXIT_SUCCESS);
}else if(pid>0)
{
//父进程读取数据
struct msgbuf buf={.mtype=100};
while(1)
{
msgrcv(msqid,&buf,SIZE,100,0); //100指取mtype100的值
//0表示阻塞
if(strcmp(buf.mtext_1,"quit")==0)
break;
printf("收到了:%s\n",buf.mtext_1);
}
wait(NULL);
}else
{
perror("pid1 create error");
return -1;
}
return 0;
}
2.将信号通信相关代码重新实现一遍
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<header.h>
//定义信号处理函数
void handler(int signo)
{
if(signo==SIGUSR1)
{
puts("父进程:逆子何至于此,那就跟你一起去了吧");
raise(SIGKILL);
}
}
int main(int argc, const char *argv[])
{
//定义进程号
pid_t pid=fork();
if(pid>0)
{
//父进程
//将SIGUSR1信号绑定
if(signal(SIGUSR1,handler)==SIG_ERR)
{
perror("signal error");
return -1;
}
while(1)
{
puts("父进程:我真的很想再活五百年");
usleep(500000);
}
}
else if(pid==0)
{
//子进程
puts("子进程:人生得意须尽欢,莫使金樽空对月");
sleep(3);
puts("子进程:我看透了红尘,父亲和我一起去了吧");
//向父进程发送一个信号
kill(getppid(),SIGUSR1);
//退出进程
exit(EXIT_SUCCESS);
}
else
{
perror("fork error");
return -1;
}
return 0;
}
3.将共享内存的相关代码重新实现一遍
shmsnd:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<header.h>
#define PAGE_SIZE 4096
int main(int argc, const char *argv[])
{
//1.成key值
key_t key=-1;
if((key=ftok("/",'t'))==-1)
{
perror("ftok error");
return =1;
}
printf("key=%#x\n",key);
//2.将物理内存创建处共享内存段
int shmid=0;
if((shmid=shmget(key,PAGE_SIZE,IPC_CREAT))==-1)
{
perror("shmget error");
return -1;
}
printf("shmid=%d\n",shmid);
//3.将共享内存段地址映射到用户空间
//NULL表示让系统自动选择页的分段
//0表示当前进程对共享内存具有读写功能
char *addr=(char *)shmat(shmid,NULL,0);
if(addr==(void *)-1)
{
perror("shmat error");
return -1;
}
printf("addr=%p\n",addr);;
//4.操作共享映射
//char buf[128]="";
while(1)
{
fgets(addr,PAGE_SIZE,stdin);//从终端输入数据
addr[strlen(addr)-1]='\0';
if(strcmp(addr,"quit")==0)
break;
}
//5.取消映射
return 0;
}
shmrcv:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<header.h>
#define PAGE_SIZE 4096
int main(int argc, const char *argv[])
{
//1.成key值
key_t key=-1;
if((key=ftok("/",'t'))==-1)
{
perror("ftok error");
return =1;
}
printf("key=%#x\n",key);
//2.将物理内存创建处共享内存段
int shmid=0;
if((shmid=shmget(key,PAGE_SIZE,IPC_CREAT))==-1)
{
perror("shmget error");
return -1;
}
printf("shmid=%d\n",shmid);
//3.将共享内存段地址映射到用户空间
//NULL表示让系统自动选择页的分段
//0表示当前进程对共享内存具有读写功能
char *addr=(char *)shmat(shmid,NULL,0);
if(addr==(void *)-1)
{
perror("shmat error");
return -1;
}
printf("addr=%p\n",addr);;
//4.操作共享映射
//char buf[128]="";
while(1)
{
printf("共享内存中的数据为:%s\n",addr);
sleep(1);
addr[strlen(addr)-1]='\0';
if(strcmp(addr,"quit")==0)
break;
}
//5.取消映射
if(shmdt(addr)==-1)
{
perror("shmdt error");
return -1;
}
//6.删除共享内存
if(shmctl(shmid,IPC_RMID,NULL)==-1)
{
perror("shmctl error");
return -1;
}
return 0;
}
面试:
一.线程和进程的区别
不同:
1.进程是资源分配的最小单位,线程是执行任务的最小单位
2.进程分配4G的虚拟内存,0-3G独立的用户内存,3-4G共享的内核空间,线程占用8K内存
3.进程之间的资源相互独立;同一进程中的线程共享进程的资源,可能会产生资源抢占现象(竞态)
4.线程是进程的执行单元,一个进程中可包含多个线程
同:
1.都能实现多任务并发执行
2.都遵循时间片轮巡调度上下文切换
二.如何实现线程同步,举例说明
同步指多个线程有先后顺序的执行,同步是为了防止多任务并发执行时发生的竞态,保护临界资源
实现:无名信号量,条件变量。
无名信号量:本质是维护了一个value值,当每个线程要执行时,先申请该value值。如果申请成功,则value减1;如果申请时value为0,则在申请处阻塞,直到另一个线程将value值增加到大于0。
三.互斥的概念以及实现
互斥指多个线程同一时间只允许一个执行
实现:互斥锁,本质是一个临界资源,当某个线程抢到该资源后,其他线程只能处于等待状态,直到该线程释放了锁资源。
四.fork()调用后,父子进程的执行顺序?如何共享资源?
没有先后顺序。创建子进程后,子进程会继承父进程创建子进程之前的所有资源
五.什么是僵尸进程,如何避免
僵尸进程指子进程退出后,父进程没有为其回收资源
子进程退出时会发出SIGCHLD信号,父进程可以捕获该信号然后将该进程回收
六.linux支持的进程间的通信机制
七种:无名管道、有名管道、信号、消息队列、共享内存、信号灯集、套接字。
七.消息队列在进程间通信是如何工作的
允许一个或多个进程写入消息,并由一个或多个进程读取,该消息读取后就没有了。当程序关闭后,如果消息没有被读取,消息还存在。