进程通信相关函数及实践应用

125 阅读8分钟

4月「掘金·日新计划」第7天

一、通信方式实践使用

2.1、管道

2.1.1、pipe

#include <unistd.h>
int pipe(int fd[2]);

返回值:成功0,失败-1设置erron

成功建立2文件描述符,fd[0]读,fd[1]写

用法举例:

  1. 建立管道:pipe(fd),判断是否成功
  2. 创建子进程:fork,判断是否成功
  3. 在父子进程读写:父进程写,子进程读
  4. 父:close(fd[0]),write(fd[1],内容,个数) 子:close(fd[1]),read(fd[0],,)
  5. :写时要关闭读管道,读时要关闭写管道 读写方向一但固定就很少改变。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main ()
{
        pid_t pid;
        int fd[2];
        char a[10] = {0};
        if(pipe(fd) == -1)
        {
                perror("pipe");
                exit(-1);
        }
        pid = fork();
        if(pid < 0)
        {
                perror("fork");
                exit(-1);
        }
        else if(pid == 0)
        {
                close(fd[1]);
                read(fd[0],a,strlen("xiao wei"));
                printf("%s\n",a);
                exit(0);
        }
        else
        {
                close(fd[0]);
                write(fd[1],"xiao wei",strlen("xiao wei"));
                wait(NULL);
        }
        return 0;
}

2.1.2、mkfifo

int mkfifo(const char* pathname,mode_t mode)

创建fifo管道文件p文件类型

返回值:成功0 ,失败-1,可以用perror查看失败原因 参数:

  1. 和open一样,文件路径名,0600(读写)

  2. 创建后使用open打开文件

    1. 可设置是否阻塞O_NONBLOCK,默认是阻塞(一般阻塞,不然失去意义)。

用法举例:

  1. 读进程

    1. open打开管道
    2. read阻塞等待write(要用while)
    3. 输出读的数据
    4. close关闭管道
  2. 写进程

    1. open打开管道
    2. write写数据
    3. close关闭管道
  3. 注:管道文件数据读后立马清空,先进先出

/*读代码*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
int main ()
{
        int cnt = 3;
        int fd;
        char a[14] = {0};
        if(mkfifo("./fifo",0600) == -1 && errno != EEXIST)
        {
                perror("pipe");
                exit(-1);
        }
        fd = open("./fifo",O_RDONLY);
        if(fd == -1)
        {
                perror("open");
        }
        while(cnt)
        {
                read(fd,a,13);
                printf("read:%s\n",a);
                cnt--;
        }
        close(fd);
        return 0;
}
/*写代码*/
int main ()
{
        int cnt = 3;
        int fd;
        char *a = "xiao wei ...";
        fd = open("./fifo",O_WRONLY);
        if(fd == -1)
        {
                perror("open");
                exit(-1);
        }
        while(cnt)
        {
                write(fd,a,strlen(a));
                sleep(3);
                cnt--;
        }
        close(fd);
        return 0;
}
/*运行结果*/
/*
读进程那边输出:
read:xiao wei ...
read:xiao wei ...
read:xiao wei ...
写进程没3秒发数据一次,发3次后退出程序
*/

2.2、msgget,msgsnd,msgrcv,msgctl,ftok

#include <sys/msg.h>
int msgget(key_t key,int flag)

功能:创建或打开消息队列,如果有就打开,没有就创建

参数:

  1. key键值(哪一个队列),可以用ftok函数,也可以直接0x1234
  2. 创建队列IPC_CREAT,打开方式可读可写可执行0777

返回值:返回队列id,失败-1

int msgsnd(int msqid,const void*ptr,size_t size,int flag)

功能:添加消息

参数:

  1. 队列号key

  2. 添加的消息内容,一般用结构体指针

    struct msgbuf
    {
         long mtype;
         char mtext[123];
    }
    
  3. 个数strlen,结构体的mtext大小

  4. 0非阻塞

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

int msgrcv(int msqid,void*ptr,size_t size,long type,int flag)

功能:读取消息

参数:

  1. 队列号key
  2. 结构体指针
  3. 个数strlen,结构体的mtext大小
  4. 消息类型,结构体的myupe(就是队列的哪一个结构体数据)
  5. 0阻塞等待

返回值:成功返回数据长度,失败-1

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

功能:控制消息队列,释放移除队列

参数:

  1. 队列号key
  2. 常用IPC_RMID,释放移除队列
  3. 通常NULL

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

key_t ftok(const char*fname,int id)

功能:返回key,通常用在消息队列

参数:

  1. 当前目录,索引值(inode号码)
  2. id,随便一个数

返回值:key,队列号

用法举例:

  1. 读进程

    1. 创建结构体
    2. 创建打开队列
    3. 接受消息
    4. 移除队列
  2. 写进程

    1. 创建结构体
    2. 创建打开队列
    3. 发送消息
    4. 移除队列
/*读代码*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct msgbuf {
        long mtype;       /* message type, must be > 0 */
        char mtext[128];    /* message data */
};
int main()
{
        struct msgbuf readbuf;
        memset(&readbuf,0,sizeof(struct msgbuf));
        key_t key;
        key = ftok(".",'z');
        int msgid = msgget(key,IPC_CREAT|0777);
        if(msgid == -1)
        {
                printf("get que failuer\n");
        }
        msgrcv(msgid,&readbuf,sizeof(readbuf.mtext),666,0);
        printf("readbuf:%s\n",readbuf.mtext);
        msgctl(msgid,IPC_RMID,NULL);
        return 0;
}
/*写代码*/
struct msgbuf {
        long mtype;       /* message type, must be > 0 */
        char mtext[128];    /* message data */
};
int main()
{
        struct msgbuf writebuf = {666,"xiao wei..."};
        key_t key;
        key = ftok(".",'z');
        int msgid = msgget(key,IPC_CREAT|0777);
        if(msgid == -1)
        {
                printf("get que failuer\n");
        }
        msgsnd(msgid,&writebuf,strlen(writebuf.mtext),0);
        msgctl(msgid,IPC_RMID,NULL);
        return 0;
}
/*运行结果*/
/*
先运行读
在运行写
读端输出
readbuf:xiao wei...
后结束程序
*/

2.3、shmget,shmat,shmdt,shmctl

int shmget(key_t key,size_t size,int flag)

功能:创建打开共享内存

参数:

  1. key,用ftok
  2. 创建内存大小,必须以m为单位
  3. 创建共享内存IPC_CREAT,可读写0666

返回值:成功返回id,失败-1

void *shmat(int shm_id,const void* addr,int flag)

功能:连接共享内存地址空间

参数:

  1. 共享内存id
  2. 0
  3. 0

返回值:成功地址void可以是任何类型例如结构体,失败-1

注:这个指针不需要free释放

int shmdt(void* addr)

功能:断开连接

参数:地址

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

int shmctl(int shm_id,int cmd,struct shmid_ds* buf)

功能:控制共享内存,释放

参数:

  1. id
  2. IPC_RMID
  3. 0

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

size_t getpagesize(void);

功能:返回一分页的大小,单位为字节(byte)。此为系统的分页大小,不一定会和硬件分页大小相同。

用法举例:

  1. 创建打开共享内存

  2. 连接映射共享内存

  3. 读写数据(用memcpy,strcpy) 尤其在接收端,必须要拷贝到本地的结构体中,本地化才行!!!

    不要直接操作共享内存内容 原文链接:blog.csdn.net/shine_journ…

  4. 断开连接

  5. 释放干掉共享内存

//发
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
struct student{
    int age;
    char name[12];
};
​
int main(int argc, char **argv)
{
    int shm_id;
    struct student *shmatadd;
    struct student stu = {
        .age = 24,
        .name = "xiaowei",
    };
    key_t key;
    key = ftok(".",'a');
    shm_id = shmget(key,getpagesize(),IPC_CREAT|0666);
    if(shm_id == -1) {
        perror("shmget()");
    }
    shmatadd = (struct student *)shmat(shm_id,0,0);
    printf("shmat ok\n");   
    memcpy(shmatadd, &stu, sizeof(stu));
    sleep(5);
​
    shmdt(shmatadd);
    shmctl(shm_id,IPC_RMID,0);
    return 0;
}
​
//收
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>
struct student{
    int age;
    char name[12];
};
​
int main(int argc, char **argv)
{
    int shm_id;
    struct student *shmatadd;
    struct student stu;
    key_t key;
    key = ftok(".",'a');
    shm_id = shmget(key,getpagesize(),IPC_CREAT|0666);
    if(shm_id == -1) {
        perror("shmget()");
    }
    shmatadd = (struct student *)shmat(shm_id,0,0);
    memcpy(&stu, shmatadd, sizeof(stu));
    printf("shmat ok\n");
    printf("stu.age=%d\n",stu.age);
    printf("stu.name=%s\n",stu.name);
    shmdt(shmatadd);
    printf("quit\n");
    return 0;
}

2.4、信号

2.4.1、signal,kill,atoi,sprintf

typedef void(*sighandler_t)(int)    //函数指针类型
sighandler_t signal(int signum,sighandler_t handler)

功能:信号注册,信号忽略

参数:

  1. 信号编号
  2. 处理函数,函数指针类型 SIG_ICN忽略信号
int kill(pid_t pid,int sig)
atoi()//char转int
sprintf()//构造字符串,参数:字符串地址,和printf一样参数

功能:发送信号

参数:pid,信号

2.4.2、sigaction,sigqueue

int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

功能:

参数:

  1. 信号编号

  2. sigaction结构体指针,一般用法,配置24

    struct sigaction {
            void     (*sa_handler)(int);
            void     (*sa_sigaction)(int, siginfo_t *, void *);
            sigset_t   sa_mask;
            int        sa_flags;
            void     (*sa_restorer)(void);
    };
    
    1. 处理函数,和signal的一样,不能传递消息

    2. 处理函数,可以传递信息的,3个参数

      1. 整形数,信号编号

      2. 结构体

        siginfo_t {
                 int      si_signo;     /* Signal number */
                 int      si_errno;     /* An errno value */
                 int      si_code;      /* Signal code */
                 int      si_trapno;    /* Trap number that caused
                                           hardware-generated signal
                                           (unused on most architectures) */
                 pid_t    si_pid;       /* Sending process ID */
                 uid_t    si_uid;       /* Real user ID of sending process */
                 int      si_status;    /* Exit value or signal */
                 clock_t  si_utime;     /* User time consumed */
                 clock_t  si_stime;     /* System time consumed */
                 sigval_t si_value;     /* Signal value */
                 int      si_int;       /* POSIX.1b signal */
                 void    *si_ptr;       /* POSIX.1b signal */
                 int      si_overrun;   /* Timer overrun count;
                                           POSIX.1b timers */
                 int      si_timerid;   /* Timer ID; POSIX.1b timers */
                 void    *si_addr;      /* Memory location which caused fault */
                 long     si_band;      /* Band event (was int in
                                           glibc 2.3.2 and earlier) */
                 int      si_fd;        /* File descriptor */
                 short    si_addr_lsb;  /* Least significant bit of address
                                           (since Linux 2.6.32) */
                 void    *si_lower;     /* Lower bound when address violation
                                           occurred (since Linux 3.19) */
                 void    *si_upper;     /* Upper bound when address violation
                                           occurred (since Linux 3.19) */
                 int      si_pkey;      /* Protection key on PTE that caused
                                           fault (since Linux 4.6) */
                 void    *si_call_addr; /* Address of system call instruction
                                           (since Linux 3.5) */
                 int      si_syscall;   /* Number of attempted system call
                                           (since Linux 3.5) */
                 unsigned int si_arch;  /* Architecture of attempted system call
                                           (since Linux 3.5) */
        }
        
        1. pid

        2. si_init,int数据

        3. si_valut 联合体

          union sigval{
              int sival_int;
              void *sival_ptr;
          };
          
          1. 整形,字符串,结构体等
      3. 指针,空无数据,非空有数据

    3. 是否阻塞,默认阻塞,处理这个信号的时候不处理其他信号

    4. SA_SIGINFO,收数据必须设置为这个宏

    5. 一般不用

  3. sigaction结构体指针,备份原操作,可以NULL

 int sigqueue(pid_t pid, int sig, const union sigval value);

功能:发送消息,信号

参数:

  1. 发给谁pid
  2. 发什么信号
  3. 数据联合体(整形,字符,结构体都可以)

用法举例:

  1. 2个程序,一个传信号,一个收信号
  2. 传,用完整main,2个参数转int,给谁传,传什么信号
  3. 收,打印,谁传的pid,传的数据是什么结构体(进程之间不能传递指针,所以无法实现)传int
  4. 进程之间,无法传递指针:blog.csdn.net/Dontla/arti…
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>void handler(int signum, siginfo_t *info, void *context)
{
        printf("get signum %d\n",signum);
        if(context != NULL){
                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 buf;
​
        buf.sa_sigaction = handler;
        buf.sa_flags = SA_SIGINFO;
​
        sigaction(SIGUSR1,&buf,NULL);
​
        while(1);
        return 0;
}
/*输出
get signum 10
get data:10
get data:10
from:48116
*/#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc,char **argv)
{
        int signum;
        int pid;
        union sigval value;
        value.sival_int = 10;
        signum = atoi(argv[1]);
        pid = atol(argv[2]);
        sigqueue(pid,signum,value);
        printf("%ddone\n",getpid());
        return 0;
}

2.5、semget,semop,semctl

int semget(key_t key,int num_sems,int sem_flags)

功能:创建,获取信号集

参数:

  1. key
  2. 信号量个数
  3. IPC_CREAT|0666,和共享内存的一样

返回值:信号量集合id

int semop(int semid,struct sembuf *sops,size_t numops)

功能:操作信号量组,改变信号量值

参数:

  1. 信号量集id

  2. 结构体指针,可以操作多个信号量,就是指针数组

    1. sem_num,信号量编号
    2. sem_op,原来的钥匙,-1 或 1
    3. sem_flg,SEM_UNDO,拿不到锁阻塞
  3. 操作几个信号量

int semctl(int semid,int sem_num,int cmd,...)

功能:控制信号量(设置信号量值,几把锁)

参数:

  1. 信号量集id

  2. 操作第几个信号量

  3. 很多宏

    1. SETVAL设置信号量值
    2. IPC_RMID销毁
  4. 联合体,union semun,开头要定义

    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) */
               };
    
    1. val,初始化信号量值,几把锁

用法举例:

  1. 创建信号量组
  2. 初始化信号量
  3. 创建父子进程
  4. 写2个函数,一个信号量加,一个减
  5. 子进程运行完信号量加
  6. 父进程先减然后运行在加
  7. 销毁锁
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
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 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 vPutBackKey(int id)
{
        struct sembuf set;
        set.sem_num = 0;
        set.sem_op = 1;
        set.sem_flg = SEM_UNDO;
        semop(id,&set,1);
        printf("put back the key\n");
}
int main()
{
        key_t key;
        int sem_id;
        key = ftok(".",'a');
        union semun initsem;
        initsem.val = 0;//锁初始没有钥匙
​
        sem_id = semget(key,1,IPC_CREAT|0666);
        semctl(sem_id,0,SETVAL,initsem);
​
        int pid = fork();
        if(pid > 0){
                pGetKey(sem_id);//父进程那不到,因为没有钥匙
                printf("this is father\n");
                vPutBackKey(sem_id);
        }else if(pid == 0){
                printf("this is child\n");
                vPutBackKey(sem_id);//子进程执行完,放钥匙进去
        }else
        {
                printf("fork error\n");
                exit(-1);
        }
​
        semctl(sem_id,0,IPC_RMID);
        return 0;
}