1.消息队列key的获取
在程序中若要使用消息队列,必须要能够知道消息队列key,因为应用进程无法直接访问内核消息队列中的数据结构,因此需要一个消息队列的标识,让应用进程知道当前操作的是哪一个消息队列,同时也要保证每一个消息队列key值的唯一性。
2.通过 ftok函数获取
key_t ftok(const char *pathname,int id);
key_t key;
key=ftok(".",1);
该函数通过一个路径名称映射出一个消息队列key(我的理解是使用路径映射的方式比较容易获取一个唯一的消息队列key)当然也可以直接使用0x1234 代替key (这里看自己怎样写就好了没有固定的写法)。
IPC_CREAT:若消息队列不存在创建一个新的消息队列,若存在则返回存在的消息队列。对于(msgflg)其他的用法大家可以根据man msgflg手册里面看看就好了自己会怎么用就行不需要刻意去背。
对于消息队列的操作主要就是 对 msgget msgrcv msgsnd 的操作
我就把自己的一些理解写出来帮助自己学习,希望也能帮助到更多的伙伴(上代码吧 哈哈)。
//接受消息
#include
#include
#include
#include
#include
//int msgget(key_t key, int msgflg);
// int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
struct msgbuf
{
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
int main()
{
struct msgbuf readbuf;
int msgid;
key_t key;
key=ftok(".",1);
msgid= msgget(key,IPC_CREAT|0777);//打开权限
if(msgid==-1){
printf("Creat failed\n");
}
msgrcv(msgid,&readbuf,sizeof(readbuf.mtext),888,0);
printf("read :%s\n",readbuf.mtext);
return 0;
}
-- INSERT --
//发送消息
#include
#include
#include
#include
#include
//int msgget(key_t key, int msgflg);
// int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
struct msgbuf
{
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
int main()
{
struct msgbuf sendbuf={
888,"thi is message"};
int msgid;
key_t key;
key=ftok(".",1);
msgid= msgget(key,IPC_CREAT|0777);
if(msgid==-1){
printf("Creat failed\n");
}
msgsnd(msgid,&sendbuf,strlen(sendbuf.mtext),0);
return 0;
}
##二、 内存共享
1.实现进程间通信最简单也是最直接的方法就是共享内存——为参与通信的多个进程在内存中开辟一个共享区。由于进程可以直接对共享内存进行读写操作,因此这种通信方式效率特别高,但其弱点是,它没有互斥机制,需要信号量之类的手段来配合。
在对内存操作过程中就不仔细写了总结的话就是
a. shmget()创建共享内存 失败返回-1,成功返回共享内存的ID
b. shmat() 映射把该共享内存连接到进程上,即要把待使用的共享内存映射到进程空间。
c. 数据区域 这里可以通过strcpy()函数,该我们要输入的数据写入到该进程中 在之前可以通过一个char *shmaddr 获取我们输入的数据(在后面代码中可以体会到)。
d.shmdt() 释放共享内存 断开共享内存与进程的连接
e.shmctl()因为内存不会随着程序的结束而自动消除,要么调用shmctl函数删除,要么手动使用ipcrm -m shmid 删除,否则会一直保存在系统中。
代码展示(啦啦啦啦)。
//创建写入消息
#include
#include
#include
#include
//int shmget(key_t key, size_t size, int shmflg);
int main()
{
int shmid;
char *shmaddr;
key_t key;
key=ftok(".",1);
shmid= shmget(key,1024*4,IPC_CREAT|0666);//ke du ke xie
if(shmid==-1){
printf("shmget error\n");
exit(-1);
}
shmaddr= shmat(shmid,0,0);//ying shen
printf("shmat ok\n");
strcpy(shmaddr,"xiao yin");
sleep(5);
shmdt(shmaddr);
shmctl(shmid,IPC_RMID,0);
printf("quit\n");
return 0;
}
//读取 数据
#include
#include
#include
#include
//int shmget(key_t key, size_t size, int shmflg);
int main()
{
int shmid;
char *shmaddr;
key_t key;
key=ftok(".",1);
shmid= shmget(key,1024*4,0);
if(shmid==-1){
printf("shmget error\n");
exit(-1);
}
shmaddr= shmat(shmid,0,0);//ying shen
printf("shmat ok\n");
printf("data:%s\n",shmaddr);
sleep(5);
shmdt(shmaddr);
printf("quit\n");
return 0;
}
1.信号的概念
信号是linux系统为例响应一些状况而产生的事件,进程收到信号后应该采取相应的动作。
2.哪些情况会引发信号
1.键盘事件 ctrl +c ctrl +
2.非法内存 如果内存管理出错,系统就会发送一个信号进行处理
3.硬件故障 同样的,硬件出现故障系统也会产生一个信号
4.环境切换 比如说从用户态切换到其他态,状态的改变也会发送一个信号,这个信号会告知给系统
1.单个信号处理
#include
#include
// typedef void (*sighandler_t)(int);
// sighandler_t signal(int signum, sighandler_t handler);
void handler(int signum)
{
printf("get signum ;%d\n",signum);
printf("never quit\n");
}
int main()
{
signal(SIGINT,handler);
while(1);
return 0;
}
运行结果
CLC@Embed_Learn:~$ gcc f9.c
CLC@Embed_Learn:~$
CLC@Embed_Learn:~$ ./a.out
^Cget signum ;2
never quit
^Cget signum ;2
never quit
^Cget signum ;2
never quit
^Z
[2]+ Stopped ./a.out
CLC@Embed_Learn:~$
2.多个信号
#include
#include
// typedef void (*sighandler_t)(int);
// sighandler_t signal(int signum, sighandler_t handler);
void handler(int signum)
{
printf("get signum ;%d\n",signum);
switch(signum)
{
case 2:
printf("SIGINT\n");
break;
case 9:
printf("SIGKILL\n");
break;
case 10:
printf("SIGUSR1\n");
break;
}
printf("never quit\n");
}
int main()
{
signal(SIGINT,handler);
signal(SIGKILL,handler);
signal(SIGUSR1,handler);
while(1);
return 0;
}
~
通过发送指令发杀死进程
查看运行的进程号
ps -aux|grep a.out
通过kill(signum,pid)来发送消息
注意在执行这个代码时将上一个同时运行,同样先查看运行进程号 ps -aux|grep a.out
#include
#include
#include
int main(int argc,char **argv)
{
int signum;
int pid;
signum=atoi(argv[1]);//atoi将字符转换成整型
pid=atoi(argv[2]);
printf("send %d pid %d\n",signum,pid);
kill(signum,pid);
printf("send signal ok\n");
return 0;
}
// 发送指令
CLC@Embed_Learn:~$ ./a.out 9 10209
1、创建:semget函数
函数原型:int semget(key_t key, int num_sems:, int sem_flags);
作用:第一次使用时创建信号量,以后使用时获取信号量。
参数:
key:一个整型值对应内核中一个信号量对象,可以自己指定,不同信号量的key值不一样。不相关的进程可以通过它访问同一个信号量。程序对所有信号量的访问都是间接的,它先提供一个键,再由系统生成一个响应的信号标识符。
num_sems:信号量的数目。
sem_flags:设置一组标志,与open函数的标志非常相似,包括信号量的权限等。IPC_CREAT标志是创建或者使用已有的信号量。
而IPC_CREATE和IPC_EXCL结合使用可以确保创建出的是一个新的、唯一的信号量,如果该信号量已存在,它将返回一个错误。
创建时给出的权限可以是:0600。
2、初始化、删除 :semctl(cmd)
作用:对信号量值进行修改。
函数原型:int semctl(int sem_id, int sem_num, int command, …);
sem_id:信号量id。
sem_num:信号量的下标,从0开始。
command:具体的操作命令,有SETVAL(设置初值)、IPC_RMID(移除信号量)
最后一个参数可以有也可以没有,如果有的话。它将会是一个union semun结构,包含以下几个成员:
union semun
{
int val;
struct semid_ds *buf;
usigned short *array;
}
一般只使用val这个成员,来为信号量赋初值。当信号量值为0时,进程会阻塞运行。
看一下信号量实现的具体代码操作吧(冲 鸭)
#include
#include
#include
#include
#include
#include
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *_buf;
};
void pGetKey(int id)
{
struct sembuf set;
set.sem_num=0;
set.sem_op=-1;
set.sem_flg=SEM_UNDO;
semop(id,&set,1);
printf("getkey\n");
}
void PutBackKey(int id)
{
struct sembuf set;
set.sem_num=0;
set.sem_op=1;
set.sem_flg=SEM_UNDO;
semop(id,&set,1);
printf("putbackkey\n");
}
int main(int argc,char const *argv[])
{
key_t key;
int semid;
key=ftok(".",2);
// 只传入一个信号量
semid=semget(key,1,IPC_CREAT|0666);//获取信号量
union semun initsem;
initsem.val=0;
//操作第0个信号量
semctl(semid,0,SETVAL,initsem);//初始化
//SETVAL 设置信号量的值 initsem
int pid=fork();
if(pid>0){
// 去拿锁
pGetKey(semid);
printf("this is father\n");
PutBackKey(semid);//放锁
//销毁
semctl(semid,0,IPC_RMID);
}
else if(pid==0){
printf("this is child\n");
PutBackKey(semid);
}
else{
printf("fork error\n");
}
} 105,0-1 Bot
运行结果
对于拿锁和放锁在父子进程中的具体操作大家可以将其位置调换在看一下运行结果。
this is child
putbackkey
getkey
this is father
putbackkey
好了到这里关于linux下的通信进程的基本操作就学完了对于管道之间的通信在前面写过了需要的伙伴可以去看看(记录自己的学习过程)加油。