以下所用到的系统调用的详细解释可以都可以通过man来查,所有的代码调用详见GitHub:https://github.com/Beichen-Wang/HPCTest/tree/master/IPC
目录
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>。