Linux进程间通信

2020-03-15  本文已影响0人  突击手平头哥

进程间通信方式比较

管道(pipe)

函数原型

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

代码示例

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

void server(int readfd, int writefd)
{
    char buffer[128] = { 0 };
    int len = read(readfd, buffer, 128);
    printf("server recv: %s\n", buffer);

    strcpy(buffer, "this is server!");
    write(writefd, buffer, strlen(buffer));
}

void client(int readfd, int writefd)
{
    char buffer[128] = { 0 };
    strcpy(buffer, "this is client!");
    write(writefd, buffer, strlen(buffer));

    int len = read(readfd, buffer, 128);
    printf("client recv: %s\n", buffer);
}


int main()
{
    int pipe1[2] = { 0 };
    int pipe2[2] = { 0 };

    pipe(pipe1);
    pipe(pipe2);

    pid_t pid = fork();

    if(pid == 0)
    {
        close(pipe1[1]);
        close(pipe2[0]);

        //持有第一个管道用于读取的, 和第二个管道用于写入的
        client(pipe1[0], pipe2[1]);
    }
    else
    {
        close(pipe1[0]);
        close(pipe2[1]);

        server(pipe2[0], pipe1[1]);
        waitpid(pid, NULL, 0);
    }
    return 0;
}

资源消耗

有名管道(FIFO)

接口

#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);

示例(在两个文件中分别运行客户端和服务器)

#include <sys/file.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstring>
#include <sys/stat.h>
 #include <fcntl.h>

#define FIFO_NAME "/tmp/test_fifo"

void server()
{
    mkfifo(FIFO_NAME, 0666);        //创建FIFO文件

    int fd = open(FIFO_NAME, O_WRONLY);

    printf("fd: %d\n", fd);

    write(fd, "this is server!", 15);

    close(fd);

    unlink(FIFO_NAME);
}

void client()
{
    int fd = open(FIFO_NAME, O_RDONLY);

    printf("fd: %d\n", fd);

    char buffer[128] = { 0 };
    read(fd, buffer, 128);
    printf("client recv: %s\n", buffer);
    close(fd);
    
    return;
}


int main()
{
    server();
    return 0;
}

信号

常用接口

int kill(pid_t pid, int sig);       //给指定进程发送信号

int raise(int sig);                 //向进程自己发送信号

unsigned int alarm(unsigned int seconds);       //设置定时器

int pause(void);            //挂起直到收到信号

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);  //指定信号处理方式

void abort(void);       //发送异常终止信号

简单示例

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


void handle(int sig)
{
    printf("recv sig: %d\n", sig);
    raise(SIGKILL);
}

int main()
{
    pid_t pid = fork();

    if(pid == 0)
    {
        printf("%s\n", "ss");
        signal(SIGTERM, handle);

        while(1) {};
        //无限循环
    }
    else
    {
        sleep(2);
        kill(pid, SIGTERM);

        waitpid(pid, NULL, 0);
    }
    return 0;
}

内存映射

接口说明

#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
+ addr: 将文件映射到内存空间指定地址, NULL即可
+ length: 映射到内存空间的内存块大小
+ prot: 访问权限, PROT_EXEC| PROT_READ | PROT_WRITE | PROT_NONE
+ flags: 程序对内存块的改变有什么影响
    + MAP_SHARED, 共享的, 内存块修改会保存到文件, 默认这个就可以
    + MAP_PRIVATE, 私有的, 修改只在局部范围有效
    + MAP_FIXED, 使用指定的映射起始地址
    + MAP_ANONYMOUS/MAP_ANON, 父子进程可以使用匿名映射, 文件描述符-1即可
+ fd: 文件描述符
+ offset: 从文件的哪里开始, 默认0即可

+ 返回映射的指针地址
int munmap(void *addr, size_t length);
+ addr: mmap的返回值
+ length: 长度

实例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
 
void server()
{
    int fd;
    char buffer[128] = { 0 };
    strcpy(buffer, "this is server!");
 
    fd = open("/tmp/mmap_temp_file", O_RDWR|O_CREAT|O_TRUNC, 0644);
    ftruncate(fd, 64);  //64的大小
 
    // 使用fd创建内存映射区
    void* addr = mmap(NULL, 64, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    close(fd); // 映射完后文件就可以关闭了
 
    memcpy(addr, buffer, strlen(buffer)); // 往映射区写数据

    munmap(addr, 64); // 释放映射区
}

void client()
{
    int fd;
 
    fd = open("/tmp/mmap_temp_file", O_RDONLY);
 
    // 使用fd创建内存映射区
    void* addr = mmap(NULL, 64, PROT_READ, MAP_SHARED, fd, 0);
    close(fd); 

    char *buffer = (char*)addr;
    printf("%s\n", buffer);

    munmap(addr, 64); // 释放映射区
}
 

 
int main() {
    server();
    return 0;
}

消息队列

接口介绍

创建和访问一个消息队列

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflag);

ftok

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);

消息格式

typedef struct _msg
{
    long mtype;      // 消息类型
    char mtext[100]; // 消息正文
    //…… ……          // 消息的正文可以有多个成员
}MSG;

添加信息

#include <sys/msg.h>
int msgsnd(  int msqid, const void *msgp, size_t msgsz, int msgflg);

获取信息

#include <sys/msg.h>
ssize_t msgrcv( int msqid, void *msgp,  size_t msgsz, long msgtyp, int msgflg );

消息队列的控制

#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

代码


#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
 
typedef struct _msg
{
    long mtype;
    char mtext[50];
}MSG;

void server()
{
    struct _msg m = {100, "hihihih"};

    key_t key;
    int msgqid;


    key = ftok("./", 2015);
    msgqid = msgget(key, IPC_CREAT|0666);       //额外指定权限


    msgsnd(msgqid, &m, sizeof(m) - sizeof(long), 0);
    //正文大小
}

void client()
{
    key_t key = ftok("./", 2015);
    int msgqid = msgqid = msgget(key, IPC_CREAT|0666); 

    struct _msg m;
    msgrcv(msgqid, &m, sizeof(m) - sizeof(long), 100, 0);

    msgctl(msgqid, IPC_RMID, NULL);
    printf("%s\n", m.mtext);
    return ;
}
 

 
int main() {
    client();
    return 0;
}

信号量

接口

头文件

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

创建信号量集

int semget(key_t key,int nsems,int flags)

删除信号量

int semctl(int semid, int semnum, int cmd, ...);
union semun
{ 
    int val;  //使用的值
    struct semid_ds *buf;  //IPC_STAT、IPC_SET 使用的缓存区
    unsigned short *arry;  //GETALL,、SETALL 使用的数组
    struct seminfo *__buf; // IPC_INFO(Linux特有) 使用的缓存区
};

改变信号量的值

int semop(int semid, struct sembuf *sops, size_t nops);
struct sembuf{ 
    short sem_num;   //除非使用一组信号量,否则它为0 
    short sem_op;   //信号量在一次操作中需要改变的数据,通常是两个数,                                        
                    //一个是-1,即P(等待)操作, 
                    //一个是+1,即V(发送信号)操作。 
    short sem_flg; //通常为SEM_UNDO,使操作系统跟踪信号量, 
                  //并在进程没有释放该信号量而终止时,操作系统释放信号量 
}; 

代码实例


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

void server()
{
    key_t key;
    int semid;
    struct sembuf sb = { 0 };
    sb.sem_op = -1;
    sb.sem_flg = SEM_UNDO;

    key = ftok("./", 2015);

    semid = semget(key, 1, IPC_CREAT | 0666);   //创建信号量

    printf("%s\n", "Get Sem!");
    semop(semid, &sb, 1);

    semctl(semid, 0, IPC_RMID);     //删除

}

void client()
{
    key_t key = ftok("./", 2015);
    int semid = semget(key, 1, IPC_CREAT|0666); 

    struct sembuf sb = { 0 };
    sb.sem_op = 1;
    sb.sem_flg = SEM_UNDO;

    semop(semid, &sb, 1);
    printf("%s\n", "Release Sem!");

    return ;
}
 

 
int main() {
    server();
    return 0;
}

共享内存

接口

创建共享内存

#include<sys/ipc.h>
#include<sys/shm.h>
int shmget(key_t key,size_t size,int shmflg);

将共享内存映射到虚拟地址空间

#include<sys/types.h>
#include<sys/shm.h>
void * shmat (int shmid, const void * shmaddr, int shmflg);

操作共享内存

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

分离操作

int shmdt(const void *shmaddr);

代码实例(sem+shm)


#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <string.h>

void server()
{
   key_t sem_key = ftok("./", 2016);        //信号量的key
   int sem_id = semget(sem_key, 1, IPC_CREAT | 0666);

    struct sembuf sb = { 0 };
    sb.sem_op = -1;
    sb.sem_flg = SEM_UNDO;

    semop(sem_id, &sb, 1);
    //如果获取了信号量, 表示另外以测已经写入了

    key_t shm_key = ftok("./", 2015);
    int shmid = shmget(shm_key, 64, IPC_CREAT | 0666);
    void *addr = shmat(shmid, NULL, 0);

    printf("Get Sem & Get [%s]\n", (char*)addr);
   
    semctl(sem_id, 0, IPC_RMID);     //删除信号量

    shmdt(addr);
    shmctl(shmid, IPC_RMID, NULL);      //删除共享内存

}

void client()
{
    key_t key = ftok("./", 2015);
    int shmid = shmget(key, 64, IPC_CREAT | 0666);

    key_t sem_key = ftok("./", 2016);
    int semid = semget(sem_key, 1, IPC_CREAT|0666);

    struct sembuf sb = { 0 };
    sb.sem_op = 1;
    sb.sem_flg = SEM_UNDO;

    void *addr = shmat(shmid, NULL, 0);

    strcpy((char*)addr, "this is client!");

    semop(semid, &sb, 1);
    printf("%s\n", "Write Data & Release Sem!");

    shmdt(addr);
    return ;
}
 

 
int main() {
    server();
    return 0;
}

几种通信方式比较

接口

很多接口都是类似的, 做一下总结

上一篇下一篇

猜你喜欢

热点阅读