IPC(Inter Process Communication)通信(C代码)

以下所用到的系统调用的详细解释可以都可以通过man来查,所有的代码调用详见GitHub:https://github.com/Beichen-Wang/HPCTest/tree/master/IPC

目录

1.shared files

2.shared memory

3.信号量

4.pipe

4.1 unamed pipe

4.2 named pipe-----fifo

5.消息队列

6.套接字

7.信号

1.shared files

主要利用fntcl和flock来对文件进行加锁,释放锁。其中flock是一个struct;

struct flock
  {
    short int l_type;   /* Type of lock: F_RDLCK, F_WRLCK, or F_UNLCK.  */
    short int l_whence; /* Where `l_start' is relative to (like `lseek').  */
#ifndef __USE_FILE_OFFSET64
    __off_t l_start;    /* Offset where the lock begins.  */
    __off_t l_len;  /* Size of the locked area; zero means until EOF.  */
#else
    __off64_t l_start;  /* Offset where the lock begins.  */
    __off64_t l_len;    /* Size of the locked area; zero means until EOF.  */
#endif
    __pid_t l_pid;  /* Process holding the lock.  */
  };

最重要的是要设置l_type和pid来进行绑定,绑定好之后利用fcntl来实现对文件lock的上锁和释放锁;这里分别写了两个class来实现文件读和写;

class ReadFile {
    private:
        std::string stringBuffer;
    public:
    ReadFile(const char *fileName = "data.txt") {
        int fd;
        fd = open(fileName, O_RDONLY);
        Flock lock(F_RDLCK, getpid());
        CHECK(fcntl(fd, F_SETLKW, &lock));
        
        off_t fileSize = lseek(fd, 0, SEEK_END);
        lseek(fd, 0, SEEK_SET);

        stringBuffer.resize(fileSize);
        read(fd, &stringBuffer[0], fileSize);

        lock.SetLockType(F_UNLCK);
        CHECK(fcntl(fd, F_SETLK, &lock));
        close(fd);
    };
    const std::string & GetBuffer() {return stringBuffer;};
};

class WriteFile {
public:
    WriteFile(const char * data, const char * fileName = "data.txt"){
        int fd;
        fd = open(fileName, O_RDWR | O_CREAT, 0666);
        
        Flock lock(F_WRLCK, getpid());
        CHECK(fcntl(fd, F_SETLK, &lock.GetLock()));

        CHECK(write(fd, data, strlen(data)));
        CHECK(truncate(fileName, strlen(data)));

        lock.SetLockType(F_UNLCK);
        CHECK(fcntl(fd, F_SETLK, &lock.GetLock()));
        close(fd);

        std::cout << "write success and the file is "<< fileName << std::endl;
    }
};

2.shared memory

shared memory主要用两种,一种是legency System V,主要是通过ftok,shmget,shmat和shmdt进行共享;另外一种是POSIX的接口,主要通过shm_open,ftruncate,和mmap组成;我在这里采用的是第二种方式,其中Writer和Reader的核心代码为:

	/* create the shared memory object */
    shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666);
 
    /* configure the size of the shared memory object */
    ftruncate(shm_fd, SIZE);
 
    /* memory map the shared memory object */
    ptr = static_cast<char *>(mmap(0, SIZE, PROT_WRITE, MAP_SHARED, shm_fd, 0));
 	/* open the shared memory object */
    shm_fd = shm_open(name, O_RDONLY, 0666);
 
    /* memory map the shared memory object */
    ptr = mmap(0, SIZE, PROT_READ, MAP_SHARED, shm_fd, 0);

可以发现,他们之间的identified ID应该主要靠name来进行匹配;

3.信号量

主要用sem_open,sem_post,sem_wait,sem_try_wait,同样也是基于name进行匹配,其中sem_post每次执行,信号量会加1,sem_wait每次执行,信号量会减1,这里同样写了一个简单的Producer和Consumer;

#include <iostream>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/stat.h>

int main(){
    sem_t* semptr = sem_open("/sem", O_CREAT, 0644, 0);
    sem_post(semptr);
    std::cout << "semaphre have been publish" << std::endl;
}
#include <iostream>
#include <semaphore.h>
#include <fcntl.h>

int main(){
    sem_t* semptr = sem_open("/sem", O_CREAT, 0644, 0);
    sem_wait(semptr);
    std::cout << "semaphre have been recevie" << std::endl;
    sem_close(semptr);
}

4.pipe

4.1 unamed pipe

pipe()会构造一个pipefd的数组,pipefd[0]代表读,pipefd[1]代表写,数据写到pipe中会被buffer住,知道被读出来,下面是一个简单的例子:

#include <sys/wait.h> /* wait */
#include <stdio.h>
#include <stdlib.h> /* exit functions */
#include <unistd.h> /* read, write, pipe, _exit */
#include <string.h>
#define ReadEnd 0
#define WriteEnd 1
void report_and_exit(const char* msg) {
 perror(msg);
 exit(-1); /** failure **/
}
int main() {
 int pipeFDs[2]; /* two file descriptors */
 char buf; /* 1-byte buffer */
 const char* msg = "Nature's first green is gold\n"; /* bytes to write */
 if (pipe(pipeFDs) < 0) report_and_exit("pipeFD");
    pid_t cpid = fork(); /* fork a child process */
 if (cpid < 0) report_and_exit("fork"); /* check for failure */
 if (0 == cpid) { /*** child ***/ /* child process */
    close(pipeFDs[WriteEnd]); /* child reads, doesn't write */
    while (read(pipeFDs[ReadEnd], &buf, 1) > 0) /* read until end of byte stream */
        write(STDOUT_FILENO, &buf, sizeof(buf)); /* echo to the standard output */
    close(pipeFDs[ReadEnd]); /* close the ReadEnd: all done */
    _exit(0); /* exit and notify parent at once */
 }
 else { /*** parent ***/
    close(pipeFDs[ReadEnd]); /* parent writes, doesn't read */
    write(pipeFDs[WriteEnd], msg, strlen(msg)); /* write the bytes to the pipe */
    close(pipeFDs[WriteEnd]); /* done writing: generate eof */
    wait(NULL); /* wait for child to exit */
    exit(0); /* exit normally */
 }
 return 0;
}

4.2 named pipe-----fifo

unamed pipe必须在具有亲缘关系的管道中才能使用,fifo管道,将能够避免这个问题,fifo的核心API是mkfifo,具体使用方式类似于文件,下面是一个fifo调用的Writer和Reader;

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#define MaxLoops 12000 /* outer loop */
#define ChunkSize 16 /* how many written at a time */
#define IntsPerChunk 4 /* four 4-byte ints per chunk */
#define MaxZs 250 /* max microseconds to sleep */

int main() {
    const char* pipeName = "./fifoChannel";
    mkfifo(pipeName, 0666); /* read/write for user/group/others */
    int fd = open(pipeName, O_CREAT | O_WRONLY); /* open as write-only */
    if (fd < 0) return -1; /* can't go on */
    int i;
    for (i = 0; i < MaxLoops; i++) { /* write MaxWrites times */
        int j;
        for (j = 0; j < ChunkSize; j++) { /* each time, write ChunkSize bytes */
            int k;
            int chunk[IntsPerChunk];
            for (k = 0; k < IntsPerChunk; k++)
                chunk[k] = rand();
            write(fd, chunk, sizeof(chunk));
        }
        usleep((rand() % MaxZs) + 1); /* pause a bit for realism */
    }
    close(fd); /* close pipe: generates an end-of-stream marker */
    unlink(pipeName); /* unlink from the implementing file */
    printf("%i ints sent to the pipe.\n", MaxLoops * ChunkSize * IntsPerChunk);
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
unsigned is_prime(unsigned n) { /* not pretty, but efficient */
    if (n <= 3) return n > 1;
    if (0 == (n % 2) || 0 == (n % 3)) return 0;
    unsigned i;
    for (i = 5; (i * i) <= n; i += 6)
        if (0 == (n % i) || 0 == (n % (i + 2))) return 0;
    return 1; /* found a prime! */
}
int main() {
    const char* file = "./fifoChannel";
    int fd = open(file, O_RDONLY);
    if (fd < 0) return -1; /* no point in continuing */
    unsigned count = 0, total = 0, primes_count = 0;
    while (1) {
        int next;
        int i;
        ssize_t count = read(fd, &next, sizeof(int));
        if (0 == count) break; /* end of stream */
        else if (count == sizeof(int)) { /* read a 4-byte int value */
            total++;
            if (is_prime(next)) primes_count++;
        }
    }
    close(fd); /* close pipe from read end */
    unlink(file); /* unlink from the underlying file */
    printf("Received ints: %u, primes: %u\n", total, primes_count);
    return 0;
}

5.消息队列

其有一个固定的消息模式:

struct msgbuf {
	long mtype;       /* message type, must be > 0 */
	char mtext[1];    /* message data */
};

其可以实现按照type来进行接收,不用严格按照先入先出的设计模式,具体使用的API主要为:msgget,msgrcv,msgsnd,具体的一个Sender一个Receiver的调用例子为:

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>
#include "Queue.h"
void report_and_exit(const char* msg) {
    perror(msg);
    exit(-1); /* EXIT_FAILURE */
}
int main() {
    key_t key = ftok(PathName, ProjectId);
    if (key < 0) report_and_exit("couldn't get key...");
    int qid = msgget(key, 0666 | IPC_CREAT);
    if (qid < 0) report_and_exit("couldn't get queue id...");
    char* payloads[] = {"msg1", "msg2", "msg3", "msg4", "msg5", "msg6"};
    int types[] = {1, 1, 2, 2, 3, 3}; /* each must be > 0 */
    int i;
    for (i = 0; i < MsgCount; i++) {
        /* build the message */
        queuedMessage msg;
        msg.type = types[i];
        strcpy(msg.payload, payloads[i]);
        /* send the message */
        msgsnd(qid, &msg, sizeof(msg), IPC_NOWAIT); /* don't block */
        printf("%s sent as type %i\n", msg.payload, (int) msg.type);
    }
    return 0;
}
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include "Queue.h"
void report_and_exit(const char* msg) {
    perror(msg);
    exit(-1); /* EXIT_FAILURE */
}
int main() {
    key_t key= ftok(PathName, ProjectId); /* key to identify the queue */
    if (key < 0) report_and_exit("key not gotten...");
    int qid = msgget(key, 0666 | IPC_CREAT); /* access if created already */
    if (qid < 0) report_and_exit("no access to queue...");
    int types[] = {3, 1, 2, 1, 3, 2}; /* different than in sender */
    int i;
    for (i = 0; i < MsgCount; i++) {
        queuedMessage msg; /* defined in queue.h */
        if (msgrcv(qid, &msg, sizeof(msg), types[i], MSG_NOERROR | IPC_NOWAIT) < 0)
            puts("msgrcv trouble...");
        printf("%s received as type %i\n", msg.payload, (int) msg.type);
    }
    /** remove the queue **/
    if (msgctl(qid, IPC_RMID, NULL) < 0) /* NULL = 'no flags' */
        report_and_exit("trouble removing queue...");
    return 0;
}

6.套接字

这里采用的是基于TCP网络进行通信,其调用流程为:

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include "Sock.h"
void report(const char* msg, int terminate) {
    perror(msg);
    if (terminate) exit(-1); /* failure */
}
int main() {
    int fd = socket(AF_INET, /* network versus AF_LOCAL */
        SOCK_STREAM, /* reliable, bidirectional, arbitrary payload size */
        0); /* system picks underlying protocol (TCP) */
    if (fd < 0) report("socket", 1); /* terminate */
    /* bind the server's local address in memory */
    struct sockaddr_in saddr;
    memset(&saddr, 0, sizeof(saddr)); /* clear the bytes */
    saddr.sin_family = AF_INET; /* versus AF_LOCAL */
    saddr.sin_addr.s_addr = htonl(INADDR_ANY); /* host-to-network endian */
    saddr.sin_port = htons(PortNumber); /* for listening */
    if (bind(fd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0)
        report("bind", 1); /* terminate */
    /* listen to the socket */
    if (listen(fd, MaxConnects) < 0) /* listen for clients, up to MaxConnects */
        report("listen", 1); /* terminate */
    fprintf(stderr, "Listening on port %i for clients...\n", PortNumber);
    /* a server traditionally listens indefinitely */
    while (1) {
        struct sockaddr_in caddr; /* client address */
        unsigned int len = sizeof(caddr); /* address length could change */
        int client_fd = accept(fd, (struct sockaddr*) &caddr, &len); /* accept blocks */
        if (client_fd < 0) {
            report("accept", 0); /* don't terminate, though there's a problem */
            continue;
        }
        /* read from client */
        int i;
        for (i = 0; i < ConversationLen; i++) {
            char buffer[BuffSize + 1];
            memset(buffer, '\0', sizeof(buffer));
            int count = read(client_fd, buffer, sizeof(buffer));
            if (count > 0) {
                puts(buffer);
                write(client_fd, buffer, sizeof(buffer)); /* echo as confirmation */
            }
        }
        close(client_fd); /* break connection */
    } /* while(1) */
    return 0;
}
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include "Sock.h"
const char* books[] = {"War and Peace",
    "Pride and Prejudice",
    "The Sound and the Fury"};
void report(const char* msg, int terminate) {
    perror(msg);
    if (terminate) exit(-1); /* failure */
}

int main() {
    /* fd for the socket */
    int sockfd = socket(AF_INET, /* versus AF_LOCAL */
        SOCK_STREAM, /* reliable, bidirectional */
        0); /* system picks protocol (TCP) */
    if (sockfd < 0) report("socket", 1); /* terminate */
    /* get the address of the host */
    struct hostent* hptr = gethostbyname(Host); /* localhost: 127.0.0.1 */
    if (!hptr) report("gethostbyname", 1); /* is hptr NULL? */
    if (hptr->h_addrtype != AF_INET) /* versus AF_LOCAL */
        report("bad address family", 1);
    /* connect to the server: configure server's address 1st */
    struct sockaddr_in saddr;
    memset(&saddr, 0, sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = ((struct in_addr*) hptr->h_addr_list[0])->s_addr;
    saddr.sin_port = htons(PortNumber); /* port number in big-endian */
    if (connect(sockfd, (struct sockaddr*) &saddr, sizeof(saddr)) < 0)
    report("connect", 1);
    /* Write some stuff and read the echoes. */
    puts("Connect to server, about to write some stuff...");
    int i;
    for (i = 0; i < ConversationLen; i++) {
        if (write(sockfd, books[i], strlen(books[i])) > 0) {
            /* get confirmation echoed from server and print */
            char buffer[BuffSize + 1];
            memset(buffer, '\0', sizeof(buffer));
            if (read(sockfd, buffer, sizeof(buffer)) > 0)
                puts(buffer);
        }
    }
    puts("Client done, about to exit...");
    close(sockfd); /* close the connection */
    return 0;
}

7.信号

信号处理函数,最简单的方式就是使用signal(int signum, sighandler_t handler);我这里调用了一个SIGUSR1(留给用户的signal);

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <iostream>
#include <atomic>

std::atomic<bool> sigusr1_received(false);

// 信号处理函数
void sigusr1_handler(int signo) {
    sigusr1_received.store(true, std::memory_order_relaxed);
}

int main() {
    int count = 0;

    // 将 SIGUSR1 信号与信号处理函数关联
    if (signal(SIGUSR1, sigusr1_handler) == SIG_ERR) {
        perror("Unable to set up signal handler");
        sleep(2);
        exit(EXIT_FAILURE);
    }

    printf("Waiting for SIGUSR1 signal...\n");

    // 进入一个无限循环,等待 SIGUSR1 信号
    while (1) {
        if (sigusr1_received.load(std::memory_order_relaxed)) {
            printf("Received SIGUSR1 signal\n");
            sigusr1_received.store(false, std::memory_order_relaxed);
            sleep(5);
            // 这里可以添加处理 SIGUSR1 信号的代码
        

        std::cout << " count : " << count++ << std::endl;
        sleep(1);
    }

    return 0;
}

其调用方式为:pkill -SIGUSR1 <process name>。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值