进程间通信

进程间通信

信号

kill -l 显示常用信号列表

SIGKILL SIGSTOP 两个信号不能被忽略

SYNOPSIS

       #include 

       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 
#include 
#include 
#include 
#include 
#include 


// 信号处理函数
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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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 
#include 

(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 
#include 
#include 
#include 
#include 
#include 


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);
}

你可能感兴趣的:(进程间通信)