进程间通信

进程间通信

信号

kill -l 显示常用信号列表

SIGKILL SIGSTOP 两个信号不能被忽略

SYNOPSIS

       #include <signal.h>

       typedef void (*sighandler_t)(int);

       sighandler_t signal(int signum, sighandler_t handler);
       

@signal:信号number

@handler: 传进去一个自定义处理函数,int参数是信号编号


  • SIG_IGN:忽略
  • SIG_DFL:默认处理,具体查看signal(7)

This signal() facility is a simplified interface to the more general sigaction(2) facility.

如果需要传进参数 可以使用sigaction(2)(如果需要使用更多特性)


RETURN 返回上一次的函数指针,如果失败则SIG_ERR会被返回,errno会被设定


Example

子进程退出,父进程 接受到信号,回收子进程。

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>


// 信号处理函数
void signal_fun(int a)
{
    int status;
    printf("收到信号\n");
    //回收子进程,WNOHANG无阻塞 
    waitpid(-1,&status,WNOHANG);
    if (WIFEXITED(status))
    {
        printf("子进程正常退出\n");
    }
}

int main(int argc, char const *argv[])
{
    // 子进程退出时候会向父进程发送SIGCHLD信号
    if(signal(SIGCHLD,signal_fun)==SIG_ERR)
    {
        perror("signal failure");
        exit(EXIT_FAILURE);
    }
    int pid=fork();
    if (pid<0)
    {
        perror("fork failure"); exit(EXIT_FAILURE);
    }

    // 子进程
    if (pid==0)
    {
        printf("this is son\n");
        exit(EXIT_SUCCESS);
    }

    // 父进程
    while(1)
    {
        sleep(1);
    }

    return 0;
}


简单处理方法:显示声明忽略SINGHLD信号

signal(SIGCHLD,SIG_IGN)


IPC对象

消息队列

一 IPC 对象 ---- 消息队列

IPC 对象命令

查看系统中IPC对象

  • ipcs -a 显示所有的IPC对象
  • ipcs -s/-q/-m

删除系统中的IPC对象

  • ipcrm -q/-s/-m ID

第一步:获得key值

key_t ftok(const char *pathname, int proj_id);  

参数:

@pathname 已经存在的文件路径
@proj_id 获取这个整数的低8bit

返回值:

成功返回 key值,失败返回-1


第二步:创建IPC对象

int msgget(key_t key, int msgflg);  

参数:

@key IPC_PRIVATE(用户亲缘关系进程间通信) 或 ftok函数产生key值
@msgflg IPC_CREAT | 0666 或 IPC_CREAT | IPC_EXCL | 0666 (判断IPC对象是否存在)

返回值:

成功返回ID,失败返回-1

注意:

如果对应key值的IPC对象不存在,则创建,如果存在,直接返回IPC对象的ID


第三步.发送消息

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);  

参数

@msqid 消息队列ID
@msgp 需要发送的消息存放的地址
@msgsz 消息正文的大小
@msgflg 0:阻塞的方式发送 IPC_NOWAIT:非阻塞方式调用

返回值:

成功返回0,失败返回-1

结构体体

typedef struct{
    long  msg_type;   //消息类型必须在第一个位置,
    char  mtxt[1024];
    ...
}msg_t;

第四步.接收消息

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);  

参数:

@msqid 消息队列ID
@msgp 存放接收到的消息
@msgsz 正文大小
@msgtyp 消息类型 , 0: 总是从消息队列中提取第一个消息
@msgflg 0:阻塞的方式接收 IPC_NOWAIT:非阻塞方式调用

返回值:

成功返回 接收消息正文大小,失败返回-1


第五步.删除消息队列

int msgctl(int msqid, int cmd, struct msqid_ds *buf); 

参数:

@msgqid 消息队列
@cmd IPC_RMID(删除消息队列) IPC_SET(设置消息队列的属性信息) IPC_STAT(获取消息队列属性信息)
@buf 存放消息队列属性

返回值:

成功返回0,失败返回-1

Example

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>


static void error_judge(int ret,char* mes);
typedef struct msgtype
{
    long msg_type;
    char msg_data[512];
}MSGTYPE;

int main(int argc, char const *argv[])
{
    MSGTYPE sen_mes={100,"hello zhaohe!"};
    MSGTYPE rec_mes;

    key_t key=ftok("./msg_ipc_client.c",'k');
    error_judge(key,"ftok failure");

    int msqid=msgget(key,IPC_CREAT|0666);
    error_judge(msqid,"msgget failure");


    printf("发送的信息是%s 编号是:%ld \n",sen_mes.msg_data,sen_mes.msg_type);
    
    int ret=msgsnd(msqid,&sen_mes,sizeof(MSGTYPE)-sizeof(long),0);
    error_judge(ret,"msgsnd failure");

    ret=msgrcv(msqid,&rec_mes,sizeof(MSGTYPE)-sizeof(long),100,0);
    error_judge(ret,"msgsnd failure");

    printf("收到的信息是%s 编号是:%ld\n",rec_mes.msg_data,rec_mes.msg_type);

    int msgctl(msqid,IPCRMID,NULL); 
    return 0;
}


static void error_judge(int ret,char* mes)
{
    if (ret<0)
    {
        perror(mes);
        exit(EXIT_FAILURE);
    }
}

共享内存 :内核空间预留出来的一块内存,用于进程间通信

第一步

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

功能:获取共享内存段的ID

参数:

@key IPC_PRIVATE 或 ftok()
@size 申请的共享内存段大小 [4k的倍数]
@shmflg IPC_CREAT | 0666IPC_CREAT | IPC_EXCL
有则打开,没有则创建 返回shemid |第二标示表示 没有创建,但当文件存在的时候返回 -1并将errno设置为EEXIST

返回值:

成功返回ID,失败返回-1

第二步

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

功能:映射共享内存到用户空间
参数:

@shmid 共享内存段ID
@shmaddr NULL:系统自动完成映射
@shmflg SHM_RDONLY:只读 0:读写

返回值:

成功返回映射后的地址,失败返回(void *)-1

其他

int shmdt(const void *shmaddr);  

功能:撤销映射
参数:

@shmaddr 共享内存映射的地址

注意:当一个进程结束的时候,它映射共享内存,会自动撤销映射

int shmctl(int shmid, int cmd, struct shmid_ds *buf);  

功能:根据命令控制共享内存
参数:

@shmid 共享内存段的ID
@cmd

IPC_STAT 获取属性
IPC_SET 设置属性
IPC_RMID删除IPC对象

@buf 保存属性

返回值:

成功返回0,失败返回 -1


注意:当我们调用shmctl删除共享内存的时候,并不会立即删除。只有当共享内存映射 次数为0,才会删除共享内存对象


Example

描述

两个进程一端发送信息,另一端接受信息,通过共享空间实现信息交互,使用两个char的变量模拟信号量进行同步。对模拟信号量的初始化,这里约定有空间空间的创建者进行初始化

写端代码

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
#include <sys/shm.h>
#include <string.h>

#define SHM_SIZE (4096*1)
#define BUFF_SIZE (SHM_SIZE-2*sizeof(char))

static void error_judge(int ret,char* mes);
typedef struct msgtype
{
    char w_flag;
    char r_flag;
    char buff[SHM_SIZE-2*sizeof(char)];
}MSGTYPE;

int main(int argc, char const *argv[])
{
    //构建一个地址,之后接受共享地址
    MSGTYPE *sen_mes;
    int flags=0;

    // 1.首先创建一个key
    key_t key=ftok("./msg_ipc_client.c",'a');
    error_judge(key,"ftok failure");

    // 2.向内核申请共享空间
    int shmid=shmget(key,SHM_SIZE,IPC_CREAT|IPC_EXCL|0666);
    if (shmid<0&&errno!=EEXIST)
    {
        perror("shmget failure");
        exit(EXIT_FAILURE);
    }
    if (shmid<0&&errno==EEXIST)
    {
         shmid=shmget(key,SHM_SIZE,IPC_CREAT|0666);
         flags=1;
    }

    // 3.获得共享空间的在用户空间的地址映射
    sen_mes=shmat(shmid,NULL,0);

    // 初始化信号量  这里是模拟信号量 用来同步两个进程之间的读写顺序 占用cpu资源不推荐
    if (flags==0)
    {
        sen_mes->w_flag=1;
        sen_mes->r_flag=0;
    }

    printf("shmid is %d w_flag is %d r_flag is %d\n",shmid,sen_mes->w_flag,sen_mes->r_flag);
    // sen_mes=shmat(shmid,NULL,0);


    // 4.之后就可以像操作地址空间一样操作共享内存
    while(1)
    {
        if (sen_mes->w_flag==1)
        {
            fgets(sen_mes->buff,BUFF_SIZE,stdin);
            sen_mes->w_flag=0;
            sen_mes->r_flag=1;
            if (strcmp(sen_mes->buff,"quit\n")==0)
            {
                break;
            }
        }
    }

    printf("程序结束\n");

    // 5。释放映射
    shmdt(sen_mes);

    // 6.由创造者删除共享内存
    if (flags==0)
    {
        shmctl(shmid,IPC_RMID,NULL);
    }

    return 0;
}


static void error_judge(int ret,char* mes)
{
    if (ret<0)
    {
        perror(mes);
        exit(EXIT_FAILURE);
    }
}

读端代码

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
#include <sys/shm.h>
#include <string.h>

#define SHM_SIZE (4096*1)
#define BUFF_SIZE (SHM_SIZE-2*sizeof(char))
static void error_judge(int ret,char* mes);
typedef struct msgtype
{
    char w_flag;
    char r_flag;
    char buff[SHM_SIZE-2*sizeof(char)];
}MSGTYPE;

int main(int argc, char const *argv[])
{
    MSGTYPE *sen_mes;
    int flags=0;

    key_t key=ftok("./msg_ipc_client.c",'a');
    error_judge(key,"ftok failure");

    int shmid=shmget(key,SHM_SIZE,IPC_CREAT|IPC_EXCL|0666);
    if (shmid<0&&errno!=EEXIST)
    {
        perror("shmget failure");
        exit(EXIT_FAILURE);
    }
    if (shmid<0&&errno==EEXIST)
    {
         shmid=shmget(key,SHM_SIZE,IPC_CREAT|0666);
         flags=1;
    }
    sen_mes=shmat(shmid,NULL,0);

    if (shmid==0)
    {
        sen_mes->w_flag=1;
        sen_mes->r_flag=0;
    }

    printf("shmid is %d w_flag is %d r_flag is %d\n",shmid,sen_mes->w_flag,sen_mes->r_flag);


    while(1)
    {
        if (sen_mes->r_flag==1)
        {
            printf("I have read:%s",sen_mes->buff);
            if (strcmp(sen_mes->buff,"quit\n")==0)
            {
                break;
            }
            sen_mes->w_flag=1;
            sen_mes->r_flag=0;
        }
    }

    printf("程序结束\n");
    shmdt(sen_mes);
    shmctl(shmid,IPC_RMID,NULL);
    return 0;
}


static void error_judge(int ret,char* mes)
{
    if (ret<0)
    {
        perror(mes);
        exit(EXIT_FAILURE);
    }
}

信号灯

头文件

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

(1)创建信号灯集

int semget(key_t key, int nsems, int semflg);  

参数:

@key IPC_PRIVATE , ftok()
@nsems 信号灯集中信号灯的个数
@semflg 常用IPC_CREAT | 0666, IPC_CREAT | IPC_EXCL| 0666
有则打开,没有则创建 返回shemid |第二标示表示 没有创建,但当文件存在的时候返回 -1并将errno设置为EEXIST

返回值:

成功返回ID,失败返回-1

(2)初始化信号灯集中信号灯的值

int semctl(int semid, int semnum, int cmd, ...);  

参数:

@semid 信号灯集的ID
@semnum 信号灯的编号[编号从0开始]
@cmd SETVAL[设置信号灯的值] ,GETVAL(获取信号灯的值),IPC_RMID[删除信号灯集]

返回值:

成功返回0,失败返回-1

(3)PV操作

int semop(int semid, struct sembuf *sops, unsigned nsops);  

功能:完成PV操作

参数:

@semid 信号灯集的ID
@sops 操作方式结构体首地址
@nsops 操作信号灯的个数

返回值:

成功返回0,失败返回-1

struct sembuf   
{  
    unsigned short sem_num;  /* semaphore number */  
    short     sem_op;   /* semaphore operation  */  
    short     sem_flg;  /* operation flags */  
};  

sem_num :

信号集信号编号

sem_op :

<1>0 等待信号灯的值变成0
<2>1 释放资源,V操作
<3>-1 申请资源,P操作


sem_flg:

0 : 阻塞方式
IPC_NOWAIT : 非阻塞方式调用
SEM_UNDO : 进程结束的时候,它申请的资源自动释放


Example

#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>


static void error_judge(int ret,char* mes);
void p(int sem_id,int sem_num);
void v(int sem_id,int sem_num);

void release(int sem_id);

void prf_sem(int sem_id,int sem_num);

int main(int argc, char const *argv[])
{

    key_t key=ftok("./msg_ipc_client.c",'a');
    error_judge(key,"ftok failure");

    // int semget(key_t key, int nsems, int semflg);
    //根据key数值创建一个包含两个信号的信号集
    int sem_id=semget(key,2, IPC_CREAT | 0666);
    error_judge(sem_id,"semget failure");

    // int semctl(int sem_id, int semnum, int cmd, ...);
    // 将第一个信号的数字初始化为1
    int ret=semctl(sem_id, 0, SETVAL,1);
    error_judge(ret,"semctl failure");
    prf_sem(sem_id,0);

    // 将第一个信号的数字初始化为2
    ret=semctl(sem_id, 1, SETVAL,2);
    error_judge(ret,"semctl failure");
    prf_sem(sem_id,1);

    p(sem_id,0);
    prf_sem(sem_id,0);

    v(sem_id,0);
    prf_sem(sem_id,0);


    p(sem_id,1);
    prf_sem(sem_id,1);

    p(sem_id,1);
    prf_sem(sem_id,1);



    // p(sem_id,0);





    printf("this is 信号集实验\n");
    release(sem_id);
    return 0;
}


void p(int sem_id,int sem_num)
{
    struct sembuf sem;

    sem.sem_num = sem_num;
    sem.sem_op  = -1;
    sem.sem_flg = 0;

    if(semop(sem_id,&sem,1) < 0)
    {
        release(sem_id);
        error_judge(-1,"semop:fun_p");
    }
}

void v(int sem_id,int sem_num)
{
    struct sembuf sem;

    sem.sem_num = sem_num;
    sem.sem_op  = 1;
    sem.sem_flg = 0;

    if(semop(sem_id,&sem,1) < 0)
    {
        release(sem_id);
        error_judge(-1,"semop:fun_v");
    }
}


static void error_judge(int ret,char* mes)
{
    if (ret<0)
    {
        perror(mes);
        exit(EXIT_FAILURE);
    }
}


void prf_sem(int sem_id,int sem_num)
{
    unsigned short sem_value=0;
    sem_value=semctl(sem_id, sem_num, GETVAL,&sem_value);
    printf("sem_id:%d num:%d value:%d\n",sem_id,sem_num,sem_value);
}

void release(int sem_id)
{
    semctl(sem_id, 0,IPC_RMID);
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容

  • 消息队列 消息队列是在内核中实现的,并且是具有一定的优先级的一种进程间通信模型 POSIX PIC消息队列 在un...
    秋风弄影阅读 1,157评论 0 0
  • 两种方式:1. 管道 2. 消息队列 管道 就像现实中的水管,水就像数据 管道是一种半双工的通信方式 数据只能单向...
    Yojiaku阅读 159评论 0 0
  • 一.管道机制(pipe) 1.Linux的fork操作 在计算机领域中,尤其是Unix及类Unix系统操作系统中,...
    Geeks_Liu阅读 3,686评论 1 9
  • 不说话,先看图 没错,这就是潮汕牛肉火锅用的牛肉了!吃过的一看,肯定会流口水的。然而我不是来介绍牛肉火锅的,我是在...
    煜彬Steven阅读 278评论 2 0
  • 是你让我看见干枯沙漠开出花一朵, 是你让我想要每天为你写一首情歌, 用最浪漫的副歌,你也轻轻的符合, 眼神坚定着我...
    胡彬123阅读 299评论 0 0