Linux进程间通信(下)(共享内存、信号、信号量)

本文详细介绍了Linux进程间通信的三种主要方式:共享内存、信号和信号量。针对共享内存,讲解了创建、映射、释放及管理的步骤;对于信号,阐述了信号的基本概念、处理方法以及kill和sigqueue函数的应用;在信号量部分,解析了semget、semop和semctl函数的使用。通过实例代码展示了如何在实践中应用这些通信机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、共享内存

shmget()创建或打开共享内存

shmat()建立共享内存的映射区

shmdt释放共享内存映射区

shmctl干掉共享内存

查看共享内存

删除共享内存

二、信号

signal()函数收消息

kill()函数发消息

sigaction()函数收消息

sigqueue()函数发消息

三、信号量

1、semget()函数

2、semop()函数

3、semctl()函数


一、共享内存

实现步骤:

a、创建/打开共享内存                           shmget

b、建立共享内存映射区                         shmat

c、往共享内存写入数据                       

d、释放共享内存映射区                         shmdt        

e、干掉共享内存                                    shmctl

#include <sys/shm.h>
// 创建或获取一个共享内存:成功返回共享内存ID,失败返回-1
int shmget(key_t key, size_t size, int flag);
// 连接共享内存到当前进程的地址空间:成功返回指向共享内存的指针,失败返回-1
void *shmat(int shm_id, const void *addr, int flag);
// 断开与共享内存的连接:成功返回0,失败返回-1
int shmdt(void *addr); 
// 控制共享内存的相关信息:成功返回0,失败返回-1
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);

shmget()创建或打开共享内存

#include <sys/ipc.h>
#include <sys/shm.h>

int shmget(key_t key, size_t size, int shmflg);


key_t key:   keyid由ftok获得
size_t size: 必须以1024为单位分配
int shmflg:    共享内存的权限,如:IPC_CREAT|0666
	            IPC_CREAT:如果不存在就创建
	            IPC_EXCL:如果已经存在则返回失败
	           
返回值:成功:返回共享内存标识符,失败:返回-1

shmat()建立共享内存的映射区



SYNOPSIS
#include <sys/types.h>
#include <sys/shm.h>

void *shmat(int shmid, const void *shmaddr, int shmflg);

int shmid:              共享内存标识符
const void *shmaddr:    共享内存映射地址,默认写0
int shmflg:             共享内存具有可读可写权限,默认写0
 
返回值:成功:返回共享内存段映射地址,失败:-1    

shmdt释放共享内存映射区

#include <sys/types.h>
#include <sys/shm.h>
 
int shmdt(const void *shmaddr);
const void *shmaddr:共享内存映射地址
 
返回值:成功0,失败-1

shmctl干掉共享内存

#include <sys/ipc.h>
#include <sys/shm.h>
 
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
 
int shmid:              共享内存标识符
int cmd:                一个指令,一般写删除指令:IPC_RMID
struct shmid_ds *buf:   一个buf,一般写0
 
 

实现共享内存的双方通信

写入端代码如下:

#include <sys/ipc.h>
#include <sys/shm.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//shmw.c
int main()
{
        int shmId;
        char *shmaddr;
        key_t key;
        key=ftok(".",1);
        shmId=shmget(key,1024*4,IPC_CREAT|0666);
     //          shmId= shmget(key,1024*4,IPC_CREAT|0666);
        if(shmId== -1){
                printf("shm creat fail\n");
                exit(-1);
        }


        shmaddr=shmat(shmId,0,0);
        printf("shmat ok!\n");

        strcpy(shmaddr,"jinlibaobao hello");
        sleep(5);

        shmdt(shmaddr);
        shmctl(shmId,IPC_RMID,0);
         return 0;
}

读取端代码如下:

#include <sys/ipc.h>
#include <sys/shm.h>
#include<stdio.h>
#include<string.h>
#include <stdlib.h>
//shmr.c
int main()
{
     
        int shmId;
        char *shmaddr;
        key_t key;
        key=ftok(".",1);

         shmId= shmget(key,1024*4,0);
        if(shmId == -1){
                printf("shm creat fail\n");
                exit(-1);
        }


        shmaddr=shmat(shmId,0,0);
        printf("shm ok!\n");

        printf("context is %s\n",shmaddr);


        shmdt(shmaddr);
        printf("quit");

         return 0;
}
             

 查看共享内存

ipcs -m

上面的写入端代码要注释shmctl函数或提前退出不执行shmctl才可以查看具体共享内存号 

删除共享内存

ipcs -m xxx

xxx为shmid号码

二、信号

        对于 Linux来说,实际信号是软中断,许多重要的程序都需要处理信号。信号,为 Linux 提供了一种处理异步事件的方法。比如,终端用户输入了 ctrl+c 来中断程序,会通过信号机制停止一个程序。

信号概述

        信号的名字和编号:每个信号都有一个名字和编号,这些名字都以“SIG”开头,例如SIGIO、SIGCHLD等等

查看信号:kill -l

信号的处理方法:忽略、捕捉、默认动作、杀死进程

忽略信号(SIG_IGN):大多数信号可以使用这个方式来处理,但是有两种信号不能被忽略(分别是SIGKILL和SIGSTOP)

捕捉信号:需要告诉内核,用户希望如何处理某一种信号,说白了就是写一个信号处理函数,然后把这个函数告诉内核

系统默认动作:对于每个信号来说,系统都对应由默认的处理动作,当发生了该信号,系统会自动执行。

杀死进程:

   a.out指正在运行的程序

(1)查看进程号:ps -aux|grep a.out 

(2)kill -9 进程号

 比如我们要杀杀死进程a.out,则先查询a.out的进程号,再配合kill -9杀死进程

入门:收消息signa,发消息kill

高级:收消息sigaction,发消息sigqueue

signal()函数收消息

        功能设置某个信号的捕捉行为

#include <signal.h>
 
typedef void (*sighandler_t)(int);
 
sighandler_t signal(int signum, sighandler_t handler);
 
int signum:             表示要捕捉的信号
sighandler_t handler:   一个自定义的处理信号的函数,信号的编号为这个自定义函数的参数
 
参数handler表示信号的处理方式,有三种情况:
(1)SIG_IGN:忽略参数signum所指的信号。signal(SIGINT,SIG_IGN );把ctrl+c信号忽略
(2)一个自定义的处理信号的函数,信号的编号为这个自定义函数的参数。
(3)SIG_DFL:恢复参数signum所指信号的处理方法为默认值。 一般不关心signal的返回值。
 
返回值:成功返回信号处理程序的先前值,失败返回 SIG_ERR 错误并设置 errno。

 举例,实现按下键盘ctrl+c无法终止程序

#include <signal.h>
#include<stdio.h>
        //typedef void (*sighandler_t)(int);
         // sighandler_t signal(int signum, sighandler_t handler);
        void handler(int signum){
                printf("signum is %d\n",signum);
                printf("never quit\n");
        }
int main(){

        signal(SIGINT,handler);//注册捕捉Ctrl+c的信号

        while(1);//一直循环不退出程序

        return 0;
}

 kill()函数发消息

        功能把信号sig发送给目标进程pid

#include <sys/types.h>
#include <signal.h>
 
kill()把信号sig发送给目标进程pid
 
int kill(pid_t pid, int sig);
 
pid_t pid:   代表当前进程的进程号
int sig:     代表信号的编号,如SIGINT的编号是2
 
返回值:成功返回0,失败返回-1并设置errno:

实现发送信号杀死a.out进程

#include <sys/types.h>
#include <signal.h>
#include<stdio.h>
//       int kill(pid_t pid, int sig);
int main(int argc,char **argv){
        int pid;
        int signum;
        signum=atoi(argv[1]);//把指令的第二项转为数字,要不然是字符串

        pid=atoi(argv[2]);//把指令的第三项转为数字
        printf("signum=%d pid=%d\n",signum,pid);//打印信号码、进程号
        kill(pid,signum);//把信号发给这个pid的进程
        printf("kill send ok!\n");
        return 0;
}

 sigaction()函数收消息

#include <signal.h>
 
int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);
 
int signum:                     要捕获的信号
const struct sigaction *act:    捕捉信号后想要干嘛,是一个结构体
struct sigaction *oldact:       输出先前信号的处理方式,默认为NULL
struct sigaction {
    void (*sa_handler)(int);
    void (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t sa_mask;
    int sa_flags;
    void (*sa_restorer)(void);
}
 
void (*sa_sigaction)(int, siginfo_t *, void *): 是一个函数
int sa_flags:                                    接收数据
#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){

        printf("get data=%d\n",info->si_int);
        printf("get data=%d\n",info->si_value.sival_int);
        printf("from %d\n",info->si_pid);

}
int main(){

        struct sigaction act;
        printf("pid is %d\n",getpid());
        act.sa_sigaction=handler;
        act.sa_flags=SA_SIGINFO;

        sigaction(SIGUSR1,&act,NULL);
        while(1);
        return 0;
}

sigqueue()函数发消息

#include <signal.h>
 
int sigqueue(pid_t pid, int sig, const union sigval value);
 
pid_t pid:                    接收指定信号的进程pid,如9、10
int sig:                      确定即将发送的信号
const union sigval value:     是一个联合结构体union sigval,指定了信号传递的参数
 
#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("%ddone\n",getpid());
        return 0;
}


三、信号量

        信号量跟之前的管道、消息队列、共享内存、信号四种不同,他不涉及数据,负责管理临界资源。

1、semget()函数

创建或获取一个信号量组:若成功返回信号量集ID,失败返回-1

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
 
int semget(key_t key, int nsems, int semflg);
 
key_t key:   一个key值
int nsems:   信号量的个数
int semflg:  获取信号量的权限,如果没有就创建一个信号量

2、semop()函数

完成对信号量的P操作或V操作

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
 
取钥匙
int semop(int semid, struct sembuf *sops, unsigned nsops);
 
int semid:            信号量的id值
struct sembuf *sops:  PV操作的具体内容结构体
unsigned nsops:       操作结构的数量,大于或等于1
 
返回值——成功返回0。错误返回-1,errno表示错误

3、semctl()函数

控制信号量的信息:删除信号量或初始化信号量

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
 
int semctl(int semid, int semnum, int cmd, ...);
 
int semid:   要操作信号量的id
int semnum:  要操作第几个信号量
int cmd:     默认写SETVAL,设置信号量的值

信号量拿钥匙、取钥匙代码实战 

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include<stdio.h>
#include <unistd.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) */
};
//封装P操作拿锁
void pGetKey(int semid){

        struct sembuf sops;

        sops.sem_num = 0;        /* Operate on semaphore 0 */
        sops.sem_op = -1;         /* Wait for value to equal 0 */
        sops.sem_flg = SEM_UNDO;
        semop(semid,&sops,1);

        printf("get the key\n");

}
//封装V操作换锁
void vPutKey(int semid){

        struct sembuf sops;

        sops.sem_num = 0;        /* Operate on semaphore 0 */
        sops.sem_op = 1;         /* Wait for value to equal 0 */
        sops.sem_flg = SEM_UNDO;
        semop(semid,&sops,1);

        printf("put the key\n");

}

int main(){

        int semid;
        key_t key;
        key=ftok(".",1);
        semid=semget(key,1,IPC_CREAT|0666);//获取/创建信号量。1:信号量集合有一个信号量
  
        union semun initsem;
        initsem.val=1;
        semctl(semid,0,SETVAL,initsem);//初始化信号量,
        int  pid;
        pid=fork();
        if(pid>0){
                //去拿锁
                pGetKey(semid);
                printf("this is a father\n");
                //去换锁
                vPutKey(semid);
                semctl(semid,0,IPC_RMID);
        }else if(pid==0){

                printf("this is a child\n");
                vPutKey(semid);
        }else {

                printf("creat fail\n");
        }



        return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值