LinuxC—进程通信

进程间通讯

1 管道通信

1.1 匿名管道

定义

匿名管道用来实现具有亲缘关系的进程之间的通信,匿名管道是由内核创建的一块内存区域,返回这块区域对应的文件描述符后,进程间可以通过这个文件描述符的操作来进行通信。

特点

  • 由内核提供
  • 单工通信
  • 自同步机制,读端没有数据的时候会阻塞直到有数据写入后接着读,写端写满后会阻塞直到有数据被读取后再接着写入

相关函数

  • pipe() 会生成两个文件描述符回填到pipefd中去,其中pipefd[0]为读端,pipefd[1]为写端
int pipe(int pipefd[2]);
  • 成功返回0,失败返回-1并设置errno
  • 示例
#define BUFSIZE 1024
//利用管道实现父写自读的一个程序
int main(int argc, char **argv) {

    pid_t pid;
    int ps[2];
    size_t len;
    char buf[BUFSIZE];
    if (pipe(ps) == -1) {
        perror("pipe()");
        exit(1);
    }
    pid = fork();
    if (pid < 0) {
        perror("fork()");
        exit(1);
    }
    if (pid == 0) { //子进程
        close(ps[1]);
        len = read(ps[0], buf, BUFSIZE);
        write(1, buf, len);
        close(ps[0]);
        exit(0);
    } else {
        close(ps[0]);
        write(ps[1], "Hello world!", 12);
        close(ps[1]);
        wait(NULL);
        exit(0);
    }
    
}

1.2 命名管道

定义

命名管道能够实现任意进程之间的通信,命名管道是由内核在磁盘上创建的一个文件,进程间通过操作这个文件来实现通信

特点

  • 由内核提供
  • 单工通信
  • 自同步机制,读端没有数据的时候会阻塞直到有数据写入后接着读,写端写满后会阻塞直到有数据被读取后再接着写入

相关函数

  • mkfifo() 创建一个名称为path的管道,mode指定权限
/* Create a new FIFO named PATH, with permission bits MODE.  */
extern int mkfifo (const char *__path, __mode_t __mode);
  • 成功返回0,失败返回-1并设置errno

2 IPC

2.1 ftok()

  • 定义

    系统建立IPC通信时需要一个key,这个key就通过ftok()函数给出,pathname是一个已存在的文件名,proj_id就是子序号,虽然是int型,但是只是用8bits。在一般的unix实现中,key的值就是子序号加上pathname文件的额inode号

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

2.2 消息队列

msgget() 获取消息队列

  • 返回一个消息队列的id,成功返回消息队列id,失败返回-1并设置errno
int msgget(key_t key, int msgflg);

msgop() 操作消息队列

//发送消息
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
//接收消息,msgid为消息队列id,msgp为收取的消息存放位置,msgsz为收取消息的大小,msgtype表示收取的第几个包
//msgflg为特殊要求
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
               int msgflg);

msgctl() 其他控制

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

实例:传输结构体信息

  • 协议头文件
#ifndef MSG_PROTO_H
#define MSG_PROTO_H

#define KEYPATH "/etc/services"
#define KEYPROJ 'g'
#define NAMESIZE 1024

//要传递的消息体
struct msg_st {
    long mtype;
    char name[NAMESIZE];
    int chinese;
    int math;
};

#endif //MSG_PROTO_H
  • 接收方(被动端)
static int msgid;

static void handler(int s) {
    //销毁消息队列
    msgctl(msgid, IPC_RMID, NULL);
    exit(0);
}

int main() {
    key_t k;
    struct msg_st rbuf;
    struct sigaction sa;

    sa.sa_handler = handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sa.sa_restorer = NULL;
    sigaction(SIGINT, &sa, NULL);
    //获取key
    k = ftok(KEYPATH, KEYPROJ);
    if (k < 0) {
        perror("ftok()");
        exit(1);
    }
    //获取消息队列id,被动方需要有IPC_CREAT特殊要求来创建消息队列,0600为消息队列的使用权限
    msgid = msgget(k, IPC_CREAT | 0600);
    if (msgid < 0) {
        perror("msgget()");
        exit(1);
    }
    //收取信息
    while (1) {
        if (msgrcv(msgid, &rbuf, sizeof(rbuf) - sizeof(long), 0, 0) < 0) {
            perror("msgrcv()");
            exit(1);
        }
        printf("name = %s, cn = %d, math = %d\n", rbuf.name, rbuf.chinese, rbuf.math);
    }

}int main() {
    key_t k;
    int msgid;
    struct msg_st rbuf;
    //获取key
    k = ftok(KEYPATH, KEYPROJ);
    if (k < 0) {
        perror("ftok()");
        exit(1);
    }
    //获取消息队列id,被动方需要有IPC_CREAT特殊要求来创建消息队列,0600为消息队列的使用权限
    msgid = msgget(k, IPC_CREAT | 0600);
    if (msgid < 0) {
        perror("msgget()");
        exit(1);
    }
    //收取信息
    while (1) {
        if (msgrcv(msgid, &rbuf, sizeof(rbuf) - sizeof(long), 0, 0) < 0) {
            perror("msgrcv()");
            exit(1);
        }
        printf("name = %s, cn = %d, math = %d\n", rbuf.name, rbuf.chinese, rbuf.math);
    }
    //销毁消息队列
    msgctl(msgid, IPC_RMID, NULL);
    exit(0);
}
  • 发送端(主动端)
int main() {
    key_t k;
    struct msg_st sbuf;
    int msgid;
    //获取key
    k = ftok(KEYPATH, KEYPROJ);
    if (k < 0) {
        perror("ftok()");
        exit(1);
    }
    //获取消息队列,主动方不需要特殊的msgflg
    msgid = msgget(k, 0);
    if (msgid < 0) {
        perror("msgget()");
        exit(1);
    }
    //发送消息
    sbuf.mtype = 1;
    strcpy(sbuf.name, "Alan");
    sbuf.math = rand() % 100;
    sbuf.chinese = rand() % 100;
    if (msgsnd(msgid, &sbuf, sizeof (sbuf) - sizeof (long ), 0) < 0) {
        perror("msgsnd()");
        exit(1);
    }
    exit(0);
}

2.3 信号量

semget() 创建信号量

  • 获取一个为信号量数组,数组中的信号量个数为nsems,semflg为特殊要求
int semget(key_t key, int nsems, int semflg)

semop() 操作信号量

  • 对semid信号量数组中信号量执行nsops个sops操作
int semop(int semid, struct sembuf *sops, size_t nsops);

int semtimedop(int semid, struct sembuf *sops, size_t nsops,
               const struct timespec *timeout);

semctl() 信号量其他控制操作

  • 对semid信号量数组中的下标为semnum的信号量进行cmd操作
int semctl(int semid, int semnum, int cmd, ...);

实例 多个进程给文件中的数+1

#define PROCNUM 20
#define FNAME "out"
#define LINESIZE 1024

static int semid;

static void P() {
    struct sembuf op;
    op.sem_num = 0;
    op.sem_op = -1; //表示在下标为sem_num的信号量上加上sem_op
    op.sem_flg = 0;
    while (semop(semid, &op, 1) < 0) {
        if (errno == EINTR || errno == EAGAIN) continue;
        perror("semop()");
        exit(1);
    }
}

static void V() {
    struct sembuf op;
    op.sem_num = 0;
    op.sem_op = 1; //表示在下标为sem_num的信号量上加上sem_op
    op.sem_flg = 0;
    while (semop(semid, &op, 1) < 0) {
        if (errno == EINTR || errno == EAGAIN) continue;
        perror("semop()");
        exit(1);
    }
}

static void func_add() {
    FILE *fp;
    char linebuf[LINESIZE];
    fp = fopen(FNAME, "r+");
    if (fp == NULL) {
        perror("fopen()");
        exit(1);
    }

    P();
    fgets(linebuf, LINESIZE, fp);
    fseek(fp, 0, SEEK_SET);
    fprintf(fp, "%d\n", atoi(linebuf) + 1);
    fflush(fp);
    V();

    fclose(fp);
}

int main(int argc, char **argv) {

    pid_t pid;
    int i;
    //获取信号量
    semid = semget(IPC_PRIVATE, 1, 0600);
    if (semid < 0) {
        perror("semget()");
        exit(1);
    }
    //设置信号量的值
    if (semctl(semid, 0, SETVAL, 1) < 0) {
        perror("semctl()");
        exit(1);
    }
    for (i = 0; i < PROCNUM; i++) {
        pid = fork();
        if (pid < 0) {
            perror("fork()");
            exit(1);
        }
        if (pid == 0) {
            func_add();
            exit(1);
        }
    }

    for (i = 0; i < PROCNUM; i++) {
        wait(NULL);
    }
    //销毁信号量数组
    semctl(semid, 0, IPC_RMID);

    exit(0);
}

2.4 共享内存

shmget() 获取共享内存

  • size为共享内存的到小
int shmget(key_t key, size_t size, int shmflg);

shmop() 操作共享内存

//将自己的shmaddr内存和共享内存做映射后返回一个指针用于操作共享内存
void *shmat(int shmid, const void *shmaddr, int shmflg);
//删除共享内存
int shmdt(const void *shmaddr);

shmctl() 共享内存其他控制操作

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

===

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值