我是靠谱客的博主 受伤盼望,最近开发中收集的这篇文章主要介绍Linux进程间通信(IPC),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

简单介绍几个进程间通信的API

分为以下几个方面

1.管道pipe 和popen

2.命名管道

3.信号量

4.共享内存

5.消息队列

没有介绍套接字


一.管道
1.进程管道
    FILE* popen(const char* command, const char* open_mode);
    int pclose(FILE* stream_to_close);
    
    popen允许一个程序将另一个程序作为新的进程来启动,并且可以给那个程序或者进程发送数据,或者接受那个程序或者进程的数据
    open_mode取“r”时,则可以接受数据
    open_mode取"w"时,则可以发送数据
    pclose负责关闭管道

//程序实例1:启动一个shell进程,读取shell的结果
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

int main()
{
        FILE* read_fp = NULL;
        char buf[255];
        int chars_read = 0;
        memset(buf, '', 255);
        read_fp = popen("ls", "r");
        if(read_fp == NULL)
        {
                printf("open failn");
                return 0;
        }

        chars_read = fread(buf, sizeof(char), 255, read_fp);
        if(chars_read > 0)
        {
                printf("Output was:-n%sn", buf);
        }
        pclose(read_fp);
        exit(0);
        return 0;
}
//这个程序会启动ls命令,shell进程开启后,输出ls的结果,程序则可以读取输出的结果,最后输出

实例2:一个进程向另一个进程发送数据

程序1

//1.进程1
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>

int main()
{
        char buf[255];
        memset(buf,'',255);
        fgets(buf,255,stdin);
        printf("发送的数据=%s",buf);

        return 0;
}

程序2

//2.进程2
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>

int main()
{
        FILE* fp = popen("./one","w");
        char buf[255];
        memset(buf,0,255);
        fgets(buf, 255, stdin);
        fwrite(buf, 1, strlen(buf)+1, fp);
        pclose(fp);
        return 0;
}

假设进程1的可执行文件是one
进程2则能够运行one,然后在程序2输入字符串,程序1会接受到字符串,并输出。


2.pipe
这是一个半双工管道,一端只能读,另一端只能写。
    int pipe(int file[2]);
    pipe函数创建了一个管道,数组file,file[0]是读取端,file[1]是写入端
    这个管道是阻塞管道,当管道中没有数据时,则读取端会一直阻塞等待,直到管道有了数据。

//父进程从管道写入数据,子进程读取数据
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
int main()
{
        int fp[2];
        if(pipe(fp) < 0)
        {
                printf("管道创建失败n");
                return 0;
        }
        int pid = fork();
        //父进程写入数据
        if(pid > 0)
        {
                close(fp[0]);
                char buf[] = "hello world";
                write(fp[1],buf,strlen(buf));
                wait(NULL);
        }
        else if(pid == 0)//子进程读取管道数据
        {
                char buf[100];
                memset(buf,0,100);
                read(fp[0],buf,100);
                printf("%sn",buf);
        }
        return 0;
}

二.命名管道FIFO
命名管道是一个特殊的文件,在创建这个管道之后,能够看见管道文件的存在,也可以和普通文件一样对它进行操作
(1).创建命名管道
    int mkfifo(const char* pathname, mode_t mode);
pathname代表创建的路径,mode代表权限,和创建文件类似,创建成功后,会在对应的路径下出现一个特殊文件。
(2).打开命名管道
    open(const char* name, mode_t mode)
    这个操作和操作普通文件差不多,只有mode有所不同
    a).O_RDONLY
    只读打开,会自动阻塞,直到管道有数据写入才返回
    b).O_WRONLY
    只写打开,阻塞,直到有其他操作读取才返回
    c).O_RDONLY | O_NONBLOCK
    只读打开,但是不阻塞
    d).O_WRONLY | O_NONBLOCK
    只读打开,不阻塞

/程序1.创建管道,写入数据
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main(){

        int res = mkfifo("./temp_fifo",0777);
        if(res < 0)
        {
                printf("文件创建失败n");
                return 0;
        }

        //打开
        int fp = open("./temp_fifo",O_WRONLY);
        //写入
        write(fp,"hello world",11);
        close(fp);
        return 0;
}

另外一个程序读取管道信息

#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>

int main(){
        int fd = open("./temp_fifo",O_RDONLY);
        if(fd < 0)
        {
                printf("open failn");
                return 0;
        }

        //读取
        char buf[255];
        memset(buf, 0, 255);
        read(fd, buf, 255);
        printf("%sn",buf);
        close(fd);
        return 0;
}

三.信号量
信号量是用来控制临界区资源的,当某个信号量的值大于0的时候,则可以访问,等于0的时候,则被挂起,无法访问一直等待信号量大于0的时候为止
信号量主要有两种操作,一种是p操作,一种是v操作
p用于等待信号,如果信号值大于0,则减去1
v用于发送信息,+1
int semctl(int sem_id, int sem_num, int command, ...);
int semget(key_t key, int num_sems, int sem_flags);
int semop*(int sem_id, struct sembuf *sem_ops, size_t num_sem_ops);

(1)int semget(key_t key, int num_sems, int sem_flags);
这个函数既可以产生新的信号,也可以获取存在的信号
key是一个特殊值,不同的进程通过它来访问同一个信号量,指定key值后
num_sems 指定信号量的数目
flags是权限,如果权限包含IPC_CREAT则会创建新的信息,如果指定的信号存在,也不会出错
函数成功则返回一个正整数,这个值是信号量标识符,失败则返回-1

(2).int semop*(int sem_id, struct sembuf *sem_ops, size_t num_sem_ops);
改变信号量的值
sem_id代表信号标识符
sem_ops是struct sembuf数组的指针
num_sem_ops代表数组的数量,比如只有1个,应该取1.
结构体定义如下:
struct sembuf{
    short sem_num;//代表信号量编号,如果信号量只有一个,这个数一般为0
    short sem_op;//一般会取-1或者+1,-1代表p操作,而+1代表v操作
    short sem_flg;//通常设定为SEM_UNDO,如果信号量没用被释放,而使用此信号量的进程终止了,操作系统会自动回收该信号量
}

(3).int semctl(int sem_id, int sem_num, int command, ...);
这个函数是用来直接控制控制信号量的信息的
sem_id是信号量标识符
sem_num是信号量编号
command代表将要采取的动作,常用的参数有两个SETVAL:把信号量初始化为一个已知的值,这个值由union_semun的成员val决定,IPC_RMID
如果有第四个参数,它将是union_semun结构,这个结构可以自己指定成员,但是最好是用系统提供的结构,这个联合体一定要自己按照规定定义
union_semun{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
};

下面几个函数是操作函数

//设置信号量的参数
int set_semvalue(int sem_id)
{
        union semun sem_union;
        sem_union.val = 1;
        if(semctl(sem_id, 0, SETVAL, sem_union) < 0)
                return 0;
        return 1;
}

//删除信号量
void del_semvalue(int sem_id)
{
        semctl(sem_id, 0, IPC_RMID);
}

//p操作
int semaphore_p(int sem_id)
{
        struct sembuf sem;
        sem.sem_num = 0;
        sem.sem_op = -1;
        sem.sem_flg = SEM_UNDO;
        if(semop(sem_id, &sem, 1) < 0)
                return 0;
        return 1;

}

//v操作
int semaphore_v(int sem_id){
        struct sembuf sem;
        sem.sem_num = 0;
        sem.sem_op = 1;
        sem.sem_flg = SEM_UNDO;
        if(semop(sem_id, &sem, 1) < 0)
                return 0;
        return 1;
}

四.共享内存

共享内存可以供多个不同的进程访问,它由下面一组函数控制。
void* shmat(int shm_id, const void* shm_addr, int shmflg);
int shmctl(int shm_id, int cmd, struct shmid_ds* buf);
int shmdt(const void* shm_addr);
int shmget(key_t key, size_t size, int shmflg);
这一组函数与信号量类似
(1).int shmget(key_t key, size_t size, int shmflg);
key:用来产生独特共享内存的键值
size:指定共享内存的大小
shmflg:权限,与创建文件的权限差不多,需要注意的是IPC_CREAT,这是用来产生一个新的共享内存的权限,如果已经存在,这个标志会被忽略
成功之后,则会返回一个共享内存的标识符,失败返回-1

(2).void* shmat(int shm_id, const void* shm_addr, int shmflg);
创建了共享内存后,需要把它连接到一个进程的地址空间,而这个工作则是由shmat完成
shm_id:共享内存的标识符
shm_addr: 共享内存连接到当前进程中的地址位置,它通常是一个空指针,表示这个地址由操作系统自己决定。
shmflg是一中标志位,一般取SHM_RND或者SHM_RDONLY
成功则返回共享内存第一个字节的指针,失败则返回-1

(3)int shmdt(const void* shm_addr);
将共享内存从当前进程分离,使用这个函数,不是删除了共享内存,只是共享内存对当前进程不再可见

(4).int shmctl(int shm_id, int cmd, struct shmid_ds* buf);
shm_id:共享内存的标识符
cmd: 表示将要采取的命令
buf:结构体struct shmid_ds指针
struct shmid_ds{
    uid_t shm_perm.uid;
    uid_t shm_perm.gid;
    uid_t shm_perm.mode;
}

cmd取值如下:
IPC_STAT: 将shmid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID 删除共享内存段

//实验-生产者-消费者模式
//1.生产者:
#include"shmaddr.h"

int main()
{
        //1.创建共享内存
        int shm_id = shmget((key_t)1234,sizeof(struct share_use_st),0777|IPC_CREAT);
        if(shm_id < 0)
        {
                fprintf(stderr,"创建共享内存失败n");
                return 0;
        }
        void* ptr = NULL;
        //连接到当前进程
        //ptr获取了共享内存的地址
        if((ptr = shmat(shm_id,NULL,0)) == (void*)-1 )
        {
                fprintf(stderr,"连接失败n");
                return 0;
        }


        struct share_use_st* ptr_share = (struct share_use_st*)ptr;
        ptr_share->written_by_you = 0;
        int running = 1;
        char buf[1024];
        //对如数据到共享内存
        while(running)
        {
                //等待消费者读取
                while(ptr_share->written_by_you == 1)
                {
                        sleep(1);
                }
                printf("输入写入的数据:");
                fgets(buf,1024,stdin);
                strncpy(ptr_share->some_text, buf, 1024);
                ptr_share->written_by_you = 1;
                if(strncmp("end", buf, 3) == 0)
                        running = 0;
        }

        //分离内存
        if(shmdt(ptr_share) < 0)
        {
                fprintf(stderr,"分离失败n");
                return 0;
        }
        return 0;
}
//消费者
#include"shmaddr.h"

int main()
{
        //1.创建共享内存
        int shm_id = shmget((key_t)1234,sizeof(struct share_use_st),0777|IPC_CREAT);
        if(shm_id < 0)
        {
                fprintf(stderr,"创建共享内存失败n");
                return 0;
        }
        void* ptr = NULL;
        //连接到当前进程
        //ptr获取了共享内存的地址
        if((ptr = shmat(shm_id,NULL,0)) == (void*)-1 )
        {
                fprintf(stderr,"连接失败n");
                return 0;
        }


        struct share_use_st* ptr_share = (struct share_use_st*)ptr;
        ptr_share->written_by_you = 0;
        int running = 1;

        //读取共享内存,一直读取到end结束
        while(running)
        {
                if(ptr_share->written_by_you)
                {
                        printf("%sn",ptr_share->some_text);
                        sleep(1);//等待另一个写入的进程写入数据
                        ptr_share->written_by_you = 0;
                        if(strncmp("end",ptr_share->some_text, 3)==0)
                                running = 0;
                }
        }

        //分离内存
        if(shmdt(ptr_share) < 0)
        {
                fprintf(stderr,"分离失败n");
                return 0;
        }

        //删除内存
        if(shmctl(shm_id, IPC_RMID, 0) < 0)
        {
                fprintf(stderr, "删除失败n");
                return 0;
        }
        return 0;
}

五.消息队列
一个进程向另一个进程发送数据块,这个数据块的长度是有限制的,而且整个消息队列的总长度也是有限制的
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
int msgget(key_t key, int msgflg);
int msgrcv(int msqid, void*msg_ptr, size_t size, long msgtype, int msgflg);
int msgsnd(int msqid, const void* msg_ptr, size_t msg_sz, int msgflg);

(1).int msgget(key_t key, int msgflg);
用来创建或者访问一个消息队列
key:创建消息队列时候的键值
msgflg:权限,IPC_CREAT代表创建一个新的消息队列

(2)int msgsnd(int msqid, const void* msg_ptr, size_t msg_sz, int msgflg);
这个函数是把消息添加到消息队列中。
msqid:消息队列的标识符
msg_ptr:代表要发送的消息
msg_sz代表消息的长度
msg_flg:代表权限-当消息队列达到限制的时候,会按照这个参数的设置处理。比如IPC_NOWAIT,在队列到达限制的时候,会返回一个错误。

需要发送的消息,必须以一个长整型开始,就像下面这个结构一样
struct my_message{
    long message_type;
    ......
}
消息的长度,不包含这个长整型

(3).int msgrcv(int msqid, void*msg_ptr, size_t size, long msgtype, int msgflg);
这个是接受消息的函数
这几个参数和msgsnd含义是一样的
msgtype是一个长整型,它可以实现一种简单形式的优先接收级,
如果msgtype等于0,就获取消息队列第一个可用的消息
如果msgtype大于0,则获取相同类型消息的第一个可用消息
如果msgtype小于0,则获取消息类型小于等于这个数绝对值的第一个消息

(4).int msgctl(int msqid, int cmd, struct msqid_ds *buf);
这个函数是处理消息队列的函数

struct msqid_ds结构体至少包含下面几个字段
struct msqid_ds{
    uid_t shm_perm.uid;
    uid_t shm_perm.gid;
    uid_t shm_perm.mode;
}

cmd取值如下:
IPC_STAT: 将msqid_ds结构中的数据设置为消息队列的当前关联值
IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为msqid_ds结构中给出的值
IPC_RMID 删除消息队列

//头文件msg.h 定义了消息结构体
#include<stdio.h>
#include<stdlib.h>
#include<sys/msg.h>
#include<string.h>
#include<unistd.h>
#include<assert.h>

struct my_msg_st{
        long int my_msg_type;
        char some_text[BUFSIZ];
};



//1.创建信号队列,等待读取信号
#include"msg.h"

int main()
{
        //1.建立消息队列
        struct my_msg_st data;
        int msgid = msgget((key_t)1234, 0777 | IPC_CREAT);
        assert(msgid != -1);

        int running = 1;
        //接受信号
        while(running)
        {
                if(msgrcv(msgid, (void*)&data, BUFSIZ, 0, 0) < 0)
                {
                        fprintf(stderr, "接受错误n");
                        return 0;
                }
                printf("%sn",data.some_text);
                if(strncmp("end",data.some_text, 3)==0)
                {
                        printf("接受结束n");
                        running = 0;
                }
        }

        //删除消息队列
        if(msgctl(msgid, IPC_RMID, 0) < 0)
        {
                fprintf(stderr, "删除失败n");
                return 0;
        }

        return 0;
//2.发送信号
#include"msg.h"

int main()
{
        //1.建立消息队列
        struct my_msg_st data;
        int msgid = msgget((key_t)1234, 0777 | IPC_CREAT);
        assert(msgid != -1);

        int running = 1;
        char buf[1024];
        //加入消息队列
        while(running)
        {
                printf("输入信息:");
                fgets(buf, 1024, stdin);
                data.my_msg_type = 1;
                strcpy(data.some_text, buf);
                if(msgsnd(msgid, (void*)&data, 1024, 0) < 0)
                {
                        fprintf(stderr, "发送错误n");
                        return 0;
                }
                if(strncmp("end",data.some_text, 3)==0)
                {
                        printf("接受结束n");
                        running = 0;
                }
        }
        return 0;
}

最后

以上就是受伤盼望为你收集整理的Linux进程间通信(IPC)的全部内容,希望文章能够帮你解决Linux进程间通信(IPC)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(56)

评论列表共有 0 条评论

立即
投稿
返回
顶部