一、信号Signal
信号是软中断,用于通知接受进程某个事件发生。一个进程可以发送信号给另一个进程,接受进程可以注册信号处理函数来相应信号。信号通常用于接收进程某个进程操作完成或异常事件发生
//发送信号
kill(pid, SIG);
//信号处理函数
void handler(int sig){
cout << "received signal" << endl;
}
//设置信号处理函数
signal(SIG, handler);
二、管道Pipe
管道是连接进程的无名管道,用于进程之间的通信。管道有读端和写端,一个进程往管道写端写入数据,另一个进程从读端读取信号。管道通常用于父子进程间通信
//创建管道
int pipefd[2];
pipe(pipefd);
//父进程写入管道
write(pipefd[1], "hello", 6);
//子进程从管道读取
char buf[6];
read(pipefd[0], buf, 6);
三、消息队列(Message Queue)
发送者进程可以向消息队列中添加消息,接受者进程可以从消息队列中获取消息。消息队列内的消息按发送顺序排列,接受方以FIFO的顺序获取消息。
//创建消息队列
mqd_t mq;
mq = mq.open("/mq", 0_CREAT);
//发送消息
mq_send(mq, "Hello", 6, 0);
//接受消息
char buf[6]
mq_receive(mq, buf, 6, NULL);
四、共享内存Shared Memory
共享内存是一块可由多个进程读写的内存区域。进程可以向共享内存写入数据,其他进程可以读取共享内存中的数据。共享内存适用于传输较大的数据量,因为它在进程间共享物理内存区域
//创建共享内存
int shm = shm_open("/shm", 0_CREAT, 0666);
//映射到进程地址空间
void *addr = mmap(0, 4096, PORT_WRITE, MAP_SHARED, shm, 0);
//写入共享内存
strcpy(addr, "Hello");
//读取共享内存
char* str = (char*)addr;
cout << str << endl;
五、命名管道Named Pipe
命名管道是基于文件系统的管道,他提供一个文件作为进程间通信的通道。读进程打开该文件进行读,写进程打开该文件进行写,两端进程就可以通过该文件进行通信。命名管道也是FIFO先入先出
//创建命名管道
mkfifo("/tmp/fifo", 0666);
//打开命名管道
int fd = open("/tmp/fifo", 0_RDWR);
//写入管道
write(fd, "Hello", 6);
//读取管道
char buf[6];
read(fd, buf, 6);
六、套接字
不同进程甚至不同主机上的进程可以利用套接字进行网络通信
其实现具体步骤如下:
1、使用socket()
创建一个套接字,指定通信协议族为IPv4(AF_INET),使用面向连接的TCP(SOCK_STREAM)
2、使用bind()
将套接字绑定到本地地址127.0.0.1和端口8000
3、使用listen()
使套接字进入监听状态,指定最大连接请求队列为5
4、使用accept()
接受客户端连接,产生一个全新的套接字clientfd
用于与客户端通信
5、使用read()/write()
与客户端进行读写操作
6、使用close()
关闭两个套接字
#include
#include
#include
int main()
{
//创建套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
//绑定套接字到本地地址和端口
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8000);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
bind(sockfd, (struct sockaddr*)&addr, sizeof(sddr));
//监听连接
listen(sockfd, 5);
//接受客户端连接
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int clientfd = accept(sockfd, (struct sockaddr*)&client_addr, &client_len);
//与客户端通信
char buf[256];
read(clientfd, buf, sizeof(buf));
pintf("Message from client:%s\n", buf);
write(clientfd, "Hello from server!", 18);
//关闭套接字
close(sockfd);
close(clientfd);
return 0;
}
七、信号量
通过这种机制,可以实现对共享资源的互斥访问和同步。信号量是进程/线程间通信的一种重要机制,广泛应用于资源竞争的控制与管理。
1、 调用sem_init()
初始化一个信号量,将其初始化为1,表示有一个资源。
2、 当线程调用sem_wait()
时,如果信号量值>0,则信号量值减1,线程继续执行;如果信号量值为0,则线程被阻塞。
3、 当线程执行完访问临界资源的操作后,调用sem_post()
将信号量值加1,唤醒被阻塞的线程。
4、 调用sem_destroy()
来销毁信号量。
#include
#include
sem_t mutex; // 声明一个信号量
void func1() {
sem_wait(&mutex); // 锁定信号量
printf("func1...\n");
sleep(3);
sem_post(&mutex); // 解锁信号量
}
void func2() {
sem_wait(&mutex);
printf("func2...\n");
sem_post(&mutex);
}
int main() {
// 初始化信号量
sem_init(&mutex, 0, 1);
for (int i = 0; i < 3; i++) {
// 创建线程执行func1和func2
pthread_t tid1, tid2;
pthread_create(&tid1, NULL, (void*)func1, NULL);
pthread_create(&tid2, NULL, (void*)func2, NULL);
}
// 销毁信号量
sem_destroy(&mutex);
}
八、事件
进程可以通过向事件对象发送或等待事件,实现进程同步和通信。
1、 设置一个标志量ready表示事件是否就绪,初始化为false。
2、 线程func1调用cv.wait()
等待事件,当ready变为true时被唤醒。
3、 线程func2在准备好后,将ready置为true,然后调用cv.notify_one()
唤醒一个等待线程。
4、 通过t1.join()
和t2.join()
等待两个线程结束。
条件变量是一种广泛使用的线程同步工具,它可以使线程在等待某个条件满足时休眠,一旦条件满足其他线程可以唤醒等待线程。
#include
#include
#include
std::mutex mutex;
std::condition_variable cv;
bool ready = false;
void func1() {
std::unique_lock<std::mutex> lk(mutex);
printf("func1 waiting...\n");
cv.wait(lk, []{return ready;}); // 等待事件
printf("func1...\n");
}
void func2() {
printf("func2 ...\n");
{
std::lock_guard<std::mutex> lk(mutex);
ready = true;
}
cv.notify_one(); // 通知一个线程
}
int main() {
std::thread t1(func1);
std::thread t2(func2);
t1.join();
t2.join();
}