文章目录
参考链接
链接:https://www.jianshu.com/p/f445bfeea40a
一、共享内存
共享内存,顾名思义,就是两个或多个进程都可以访问的同一块内存空间,一个进程对这块空间内容的修改可为其他参与通信的进程所看到的。
显然,为了达到这个目的,就需要做两件事:一件是在内存划出一块区域来作为共享区;另一件是把这个区域映射到参与通信的各个进程空间。
原型
获取创建共享内存
int shmget(key_t key, size_t size, int shmflg);
挂载映射到进程上
void *shmat(int shmid, const void *shmaddr, int shmflg);
如果一个进程已创建或打开一个共享内存,则在需要使用它时,要调用函数shmat()把该共享内存连接到进程上,即要把待使用的共享内存映射到进程空间。函数shmat()通过系统调用sys_shmat()实现。
释放连接
int shmdt(const void *shmaddr);
删除共享内存
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
代码示例
实现两个进程的通信
写
#include <sys/ipc.h>
#include <sys/shm.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
// int shmget(key_t key, size_t size, int shmflg);
int main()
{
char *shmaddr;
key_t key;
key=ftok(".",1); //key还是用ftok函数生成比较专业一点
int shmid;
shmid=shmget(key,1024*4,IPC_CREAT|0600); //创建共享内存时,大小是以M为基本的 最小也得1M
if(shmid==-1){
printf("shmget failed!\n");
exit(-1);
}
shmaddr=shmat(shmid,0,0); //映射 连接进程
printf("shmat ok!\n");
strcpy(shmaddr,"jia you ao li gei!!!"); //往映射区写入
sleep(5); //给5秒时间读取
shmdt(shmaddr); //释放映射
shmctl(shmid,IPC_RMID,0); //删除共享区域
printf("quit \n");
return 0;
}
读
#include <sys/ipc.h>
#include <sys/shm.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
// int shmget(key_t key, size_t size, int shmflg);
int main()
{
char *shmaddr;
key_t key;
key=ftok(".",1);
int shmid;
shmid=shmget(key,1024*4,0); //因为是读 不需要创建 就写0
if(shmid==-1){
printf("shmget failed!\n");
exit(-1);
}
shmaddr=shmat(shmid,0,0);
printf("shmat ok!\n");
printf("read from w:%s\n",shmaddr); //直接将映射区的内容打印出来 实现读
shmdt(shmaddr);
printf("quit \n");
return 0;
}


查看共享内存的指令
ipcs -m
指令删除共享内存
ipcrm -m + shmid
二、信号
什么是信号
对于 Linux来说,实际信号是软中断,许多重要的程序都需要处理信号。信号,为 Linux 提供了一种处理异步事件的方法。比如,终端用户输入了 ctrl+c 来中断程序,会通过信号机制停止一个程序。
指令 kill -l 查看信号名字及序号

处理方法
信号的处理有三种方法,分别是:忽略、捕捉和默认动作
忽略信号,大多数信号可以使用这个方式来处理,但是有两种信号不能被忽略(分别是 SIGKILL和SIGSTOP)。因为他们向内核和超级用户提供了进程终止和停止的可靠方法,如果忽略了,那么这个进程就变成了没人能管理的的进程,显然是内核设计者不希望看到的场景
捕捉信号,需要告诉内核,用户希望如何处理某一种信号,说白了就是写一个信号处理函数,然后将这个函数告诉内核。当该信号产生时,由内核来调用用户自定义的函数,以此来实现某种信号的处理。
系统默认动作,对于每个信号来说,系统都对应由默认的处理动作,当发生了该信号,系统会自动执行。
捕捉信号里handler(信号处理函数)分两个版本
1.入门版:函数signal
2.高级版:函数sigaction
信号处理发送函数
1.入门版 kill
2.高级版 sigqueue
入门版
signal 的函数原型
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
根据函数原型可以看出由两部分组成,一个是真实处理信号的函数,另一个是注册函数了。
对于sighandler_t signal(int signum, sighandler_t handler);函数来说,signum 显然是信号的编号,handler 是中断函数的指针。
同样,typedef void (*sighandler_t)(int);中断函数的原型中,有一个参数是 int 类型,显然也是信号产生的类型,方便使用一个函数来处理多个信号。
编写入门版信号处理函数
#include <signal.h>
#include<stdio.h>
// 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("SIGUSER1\n");
break;
}
printf("never quit!\n");
}
int main()
{
signal(SIGINT,handler); //捕捉crtl c 然后执行handler函数
signal(SIGKILL,handler);
signal(SIGUSR1,handler);
while(1);
return 0;
}
入门版发送函数
原型
int kill(pid_t pid, int sig);
#include <signal.h>
#include<stdio.h>
#include <sys/types.h>
int main(int argc,char **argv) //这里用以前的老方法 argc v 收集指令
{
int signum;
int pid;
char cmd[128]={0};
signum=atoi(argv[1]); //需注意 argv是个字符串 而指令输入的是整型 需要atoi函数进行转换
pid=atoi(argv[2]);
printf("signum=%d,pid=%d\n",signum,pid);
// kill(pid,signum); 第一种方法 调用kill函数
sprintf(cmd,"kill -%d %d",signum,pid);
system(cmd); //第二种调用system 但需要sprintf配合
printf("send signal ok!\n");
return 0;
}
SIG_IGN宏可以将信号忽略
高级版
我们已经成功完成了信号的收发,那么为什么会有高级版出现呢?其实之前的信号存在一个问题就是,虽然发送和接收到了信号,可是总感觉少些什么,既然都已经把信号发送过去了,为何不能再携带一些数据呢?
正是如此,我们需要另外的函数来通过信号传递的过程中,携带一些数据。咱么先来看看发送的函数吧。
sigaction 的函数原型
include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
struct sigaction {
void (*sa_handler)(int); //信号处理程序,不接受额外数据,SIG_IGN 为忽略,SIG_DFL 为默认动作
void (*sa_sigaction)(int, siginfo_t *, void *); //信号处理程序,能够接受额外数据和sigqueue配合使用
sigset_t sa_mask;//阻塞关键字的信号集,可以再调用捕捉函数之前,把信号添加到信号阻塞字,信号捕捉函数返回之前恢复为原先的值。
int sa_flags;//影响信号的行为SA_SIGINFO表示能够接受数据
};
//回调函数句柄sa_handler、sa_sigaction只能任选其一
sigaction 是一个系统调用,根据这个函数原型,我们不难看出,在函数原型中,第一个参数signum应该就是注册的信号的编号;第二个参数act如果不为空说明需要对该信号有新的配置;第三个参数oldact如果不为空,那么可以对之前的信号配置进行备份,以方便之后进行恢复。
处理函数
#include <signal.h>
#include <stdio.h>
//int sigaction(int signum, const struct sigaction *act,
// struct sigaction *oldact);
void handler(int signum, siginfo_t *info, void *context)
{ //第一个参数 信号的kill 数值 第二个为指针 存放传递数据的值
//第三个 如果为空 说明没有内容
printf("the signum is:%d\n",signum);
if(context !=NULL){
printf("get data=%d\n",info->si_int);
printf("get data=%d\n",info->si_value.sival_int);
}
}
int main()
{
struct sigaction act;
act.sa_sigaction=handler; //处理函数
act.sa_flags=SA_SIGINFO; //表示能够接受数据
printf("pid =%d\n",getpid());
sigaction(SIGUSR1,&act,NULL); //第一个参数为 处理的信号
//第二个为sigaction结构体 ,备份 不需要就NULL
while(1);
return 0;
}
~

发送
#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]);
pid=atoi(argv[2]);
union sigval value; //联合体 负责存放传送数据
value.sival_int=100;
sigqueue(pid,signum,value); //进程号 信号值 传递内容联合体
printf("done!\n");
return 0;
}
发送字符串只能在 共享内存或者 同一进程下 才可以发送
三、信号量
信号量(semaphore)与已经介绍过的IPC结构不同,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。
1、特点
1.信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。
2.信号量基于操作系统的PV操作,程序对信号量的操作都是原子操作。
3.每次对信号量的PV操作不仅限于对信号量加1或减1,而且可以加减任一正整数。
4.支持信号量组。
2、原型
最简单的信号量是只能取0和1的变量,这也是信号量最常见的一种形式,叫做二值信号量。而可以取多个正整数的信号量被称为通用信号量。
Linux下的信号量函数都是在通用的信号量数组上进行操作,而不是在一个单一的二值信号量上进行操作。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
//创建或获取一个信号量组,若成功返回信号量集ID,失败返回-1
int semop(int semid, struct sembuf *sops, unsigned nsops);
//对信号量组进行操作,改变信号量的值;成功返回0,失败返回-1
int semctl(int semid, int semnum, int cmd, ...);
//控制信号量的相关信息
3、代码示例
#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 semctl(int semid, int semnum, int cmd, ...);
//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 pgk(int id) //接受信号量id参数
{
struct sembuf sops;
sops.sem_num=0; //信号量的编号
sops.sem_op=-1; //拿走key 数量-1
sops.sem_flg=SEM_UNDO; //一共两个flg UNDO NOWAIT 用NOWAIT就没有意义了
semop(id,&sops,1); //第三个参数表示第二个参数的个数 因为信号集合1——多
printf("get key\n");
}
void vbk(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 key\n");
}
int main()
{
key_t key;
key=ftok(".",2);
int semid;
union semun initsem;
initsem.val=0; //信号量数量
semid=semget(key,1,IPC_CREAT|0666);//创建/获取信号量
//第二个参数 信号集里有1个信号量
semctl(semid,0,SETVAL,initsem);//初始化信号量
//第二个参数 初始化第1个信号量 第三四个参数设置信号量的值
int pid=fork();
if(pid>0){
pgk(semid);//先拿key 但是由于信号量初始化的时候为0 没有key只能阻塞至有key再运行
printf("this is father!\n");
vbk(semid);
}
else if(pid==0){
printf("this is child\n");
vbk(semid);
//先返回key 如果父子进程都是pv操作 则无法控制先后
}
else{
printf("fork failed\n");
}
return 0;
}
6425

被折叠的 条评论
为什么被折叠?



