操作系统基础知识---进阶篇

 

 

 

  

互斥量(互斥锁):互斥量是最简单的线程同步的方法,处于两态之一的变量:解锁和加锁,两个状态可以保证资源访问的串行。

原子性:原子性指一系列操作不可被中断的特性,这一系列操作要么全部执行,要么全部没有执行,不存在部分执行部分未执行的情况。
//互斥量示例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <vector>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

// 临界资源
int num = 0;

// 生产者
void *producer(void*){
    int times = 100000000;
    while(times --){
    pthread_mutex_lock(&mutex);
    num += 1;
    pthread_mutex_unlock(&mutex);
    }
}

// 消费者
void *comsumer(void*){
    int times = 100000000;
    while(times --){
    pthread_mutex_lock(&mutex);
    num -= 1;
    pthread_mutex_unlock(&mutex);
    }
}


int main(){
    printf("Start in main function.");
    pthread_t thread1, thread2;
    pthread_create(&thread1, NULL, &producer, NULL);
    pthread_create(&thread2, NULL, &comsumer, NULL);
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    printf("Print in main function: num = %d\n", num);
    return 0;
}

自旋锁:自旋锁也是一种多线程同步的变量,使用自旋锁的线程会反复检查锁变量是否可用,自旋锁不会让出CPU,是一种忙等待状态(死循环等待锁被释放)。
相比于互斥量,自旋锁避免了进程或线程的上下文切换的开销,操作系统内部很多地方使用的都是自旋锁,但自旋锁不适合在单核CPU使用。

互斥锁和自旋锁的区别:https://www.cnblogs.com/lztkdr/p/8377853.html

//自旋锁示例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <vector>

pthread_spinlock_t spin_lock;

int num = 0;

void *producer(void*){
    int times = 10000000;
    while(times --){
        pthread_spin_lock(&spin_lock);
        num += 1;
        pthread_spin_unlock(&spin_lock);
    }
}

void *comsumer(void*){
    int times = 10000000;
    while(times --){
        pthread_spin_lock(&spin_lock);
        num -= 1;
        pthread_spin_unlock(&spin_lock);
    }
}


int main(){
    printf("Start in main function.\n");
    pthread_spin_init(&spin_lock, 0);
    pthread_t thread1, thread2;
    pthread_create(&thread1, NULL, &producer, NULL);
    pthread_create(&thread2, NULL, &comsumer, NULL);
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    printf("Print in main function: num = %d\n", num);
    return 0;
}

读写锁是一种特殊的自旋锁,它允许多个读者同时访问资源以提高读性能,对于写操作则是互斥的。

 

  

//读写锁示例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <vector>

int num = 0;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;

void *reader(void*){
    int times = 10000000;
    while(times --){
        // pthread_rwlock_rdlock(&rwlock);
        pthread_mutex_lock(&mutex);
        if (times % 1000 == 0){
            // printf("print num in reader: num = %d\n", num);
            // sleep(1);
            usleep(10);
            }
        pthread_mutex_unlock(&mutex);
        // pthread_rwlock_unlock(&rwlock);
    }
}

void *writer(void*){
    int times = 10000000;
    while(times --){
        pthread_mutex_lock(&mutex);
        // pthread_rwlock_wrlock(&rwlock);
        num += 1;
        // pthread_rwlock_unlock(&rwlock);
        pthread_mutex_unlock(&mutex);
    }
}


int main(){
    printf("Start in main function.\n");
    pthread_t thread1, thread2, thread3;
    pthread_create(&thread1, NULL, &reader, NULL);
    pthread_create(&thread2, NULL, &reader, NULL);
    pthread_create(&thread3, NULL, &writer, NULL);
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    pthread_join(thread3, NULL);
    printf("Print in main function: num = %d\n", num);
    return 0;
}

条件变量是一种相对复杂的线程同步方法,条件变量允许线程睡眠,直到满足某种条件,当满足条件时,可以向该线程发送信号,通知唤醒。
之前的生产者消费者模型有两个条件需要进行约束:
  ①当缓冲区小于等于0时,不允许消费者消费,消费者必须等待;
  ②缓冲区满时,不允许生产者往缓冲区生产,生产者必须等待;
当生产者生产一个产品时,唤醒可能等待的消费者,当消费者消费一个产品时,唤醒可能等待的生产者。
//条件变量示例
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <unistd.h>
#include <pthread.h>

int MAX_BUF = 100;
int num = 0;


pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void* producer(void*){
    while(true){
        pthread_mutex_lock(&mutex);
        while (num >= MAX_BUF){
            // 等待
            printf("缓冲区满了, 等待消费者消费...\n");
            pthread_cond_wait(&cond, &mutex);
        }
        num += 1;
        printf("生产一个产品,当前产品数量为:%d\n", num);
        sleep(1);
        pthread_cond_signal(&cond);
        printf("通知消费者...\n");
        pthread_mutex_unlock(&mutex);
        sleep(1);
    }

}

void* consumer(void*){
    while(true){
        pthread_mutex_lock(&mutex);
        while (num <= 0){
            // 等待
            printf("缓冲区空了, 等待生产者生产...\n");
            pthread_cond_wait(&cond, &mutex);
        }
        num -= 1;
        printf("消费一个产品,当前产品数量为:%d\n", num);
        sleep(1);
        pthread_cond_signal(&cond);
        printf("通知生产者...\n");
        pthread_mutex_unlock(&mutex);
    }
}

int main(){
    pthread_t thread1, thread2;
    pthread_create(&thread1, NULL, &consumer, NULL);
    pthread_create(&thread2, NULL, &producer, NULL);
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    return 0;
}

 

 

 

 

①fork系统调用是用于创建进程的;
②fork创建的进程初始化与父进程一样;
③系统会为fork的进程分配新的资源;
④fork系统调用无参数;
⑤fork会返回两次,分别返回子进程id和0
⑥返回子进程id的是父进程,但会0的是子进程
#include <iostream>
#include <cstring>
#include <stdio.h>
#include <unistd.h>

using namespace std;

int main()
{
    pid_t pid;

    int num = 888;
    pid = fork();

    if(pid == 0){
        cout << "这是一个子进程." << endl;
        cout << "num in son process: " << num << endl;
        while(true){
            num += 1;
            cout << "num in son process: " << num << endl;
            sleep(1);
        }
    }
    else if(pid > 0){
        cout << "这是一个父进程." << endl;
        cout << "子进程id: " << pid << endl;
        cout << "num in father process: " << num << endl;
        while(true){
            num -= 1;
            cout << "num in father process: " << num << endl;
            sleep(1);
        }
    }
    else if (pid < 0){
        cout << "创建进程失败." << endl;
    }
    return 0;
}

在某种程度上,多进程是共同使用物理内存的;由于操作系统的进程管理,进程空间的内存空间是独立的;进程默认是不能访问进程空间之外的内存空间的。
共享存储允许不相关的进程访问同一片物理内存;
共享内存是两个进程之间共享和传递数据最快的方式;
共享内存未提供同步机制,需要借助其他机制管理访问。

 

 

 

//共享内存示例
//common.h
#ifndef __COMMON_H__
#define __COMMON_H__

#define TEXT_LEN 2048

// 共享内存的数据结构
struct ShmEntry{
    // 是否可以读取共享内存,用于进程间同步
    bool can_read;
    // 共享内存信息
    char msg[2048]; 
};

#endif

//server.cpp
#include "common.h"

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

#include <iostream>

int main()
{
    // 共享内存的结构体
    struct ShmEntry *entry;

    // 1. 申请共享内存
    int shmid = shmget((key_t)1111, sizeof(struct ShmEntry), 0666|IPC_CREAT);
    if (shmid == -1){
        std::cout << "Create share memory error!" << std::endl;
        return -1;
    }

    // 2. 连接到当前进程空间/使用共享内存
    entry = (ShmEntry*)shmat(shmid, 0, 0);
    entry->can_read = 0;
    while (true){
        if (entry->can_read == 1){
            std::cout << "Received message: " << entry->msg << std::endl;
            entry->can_read = 0;
        }else{
            std::cout << "Entry can not read. Sleep 1s." << std::endl;
            sleep(1);
        }
    }
    // 3. 脱离进程空间
    shmdt(entry);

    // 4. 删除共享内存 
    shmctl(shmid, IPC_RMID, 0);

    return 0;
}

//client.cpp
#include "common.h"

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

#include <iostream>

int main()
{
    struct ShmEntry *entry;

    // 1. 申请共享内存
    int shmid = shmget((key_t)1111, sizeof(struct ShmEntry), 0666|IPC_CREAT);
    if (shmid == -1){
        std::cout << "Create share memory error!" << std::endl;
        return -1;
    }

    // 2. 连接到当前进程空间/使用共享内存
    entry = (ShmEntry*)shmat(shmid, 0, 0);
    entry->can_read = 0;
    char buffer[TEXT_LEN];
    while (true){
        if (entry->can_read == 0){
            std::cout << "Input message>>> ";
            fgets(buffer, TEXT_LEN, stdin);
            strncpy(entry->msg, buffer, TEXT_LEN);
            std::cout << "Send message: " << entry->msg << std::endl;
            entry->can_read = 1;
        }
    }
    // 3. 脱离进程空间
    shmdt(entry);

    // 4. 删除共享内存 
    shmctl(shmid, IPC_RMID, 0);

    return 0;
}

 

域套接字是一种高级的进程间通信的方法;
Unix域套接字可以用于同一机器进程间通信。
套接字(socket)原是网络通信中使用的术语;
Unix系统提供的域套接字提供了网络套接字类似的功能。

 

//套接字示例
//server.cpp
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <strings.h>
#include <string.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>

#include <iostream>

// 域套接字
#define SOCKET_PATH "./domainsocket"
#define MSG_SIZE 2048

int main()
{
    int socket_fd, accept_fd;
    int ret = 0;
    socklen_t addr_len;
    char msg[MSG_SIZE];
    struct sockaddr_un server_addr;

    // 1. 创建域套接字
    socket_fd = socket(PF_UNIX,SOCK_STREAM,0);
    if(-1 == socket_fd){
        std::cout << "Socket create failed!" << std::endl;
        return -1;
    }
    // 移除已有域套接字路径
    remove(SOCKET_PATH);
    // 内存区域置0
    bzero(&server_addr,sizeof(server_addr));
    server_addr.sun_family = PF_UNIX;
    strcpy(server_addr.sun_path, SOCKET_PATH);

    // 2. 绑定域套接字
    std::cout << "Binding socket..." << std::endl;
    ret = bind(socket_fd,(sockaddr *)&server_addr,sizeof(server_addr));

    if(0 > ret){
        std::cout << "Bind socket failed." << std::endl;
        return -1;
    }
    
    // 3. 监听套接字
    std::cout << "Listening socket..." << std::endl;
    ret = listen(socket_fd, 10);
    if(-1 == ret){
        std::cout << "Listen failed" << std::endl;
        return -1;
    }
    std::cout << "Waiting for new requests." << std::endl;
    accept_fd = accept(socket_fd, NULL, NULL);
    
    bzero(msg,MSG_SIZE);

    while(true){
        // 4. 接收&处理信息
        recv(accept_fd, msg, MSG_SIZE, 0);
        std::cout << "Received message from remote: " << msg <<std::endl;
    }

    close(accept_fd);
    close(socket_fd);
    return 0;
}


//client.cpp
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <strings.h>
#include <string.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>

#include <iostream>

#define SOCKET_PATH "./domainsocket"
#define MSG_SIZE 2048

int main()
{
    int socket_fd;
    int ret = 0;
    char msg[MSG_SIZE];
    struct sockaddr_un server_addr;

    // 1. 创建域套接字
    socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
    if(-1 == socket_fd){
        std::cout << "Socket create failed!" << std::endl;
        return -1;
    }
    
    // 内存区域置0
    bzero(&server_addr,sizeof(server_addr));
    server_addr.sun_family = PF_UNIX;
    strcpy(server_addr.sun_path, SOCKET_PATH);

    // 2. 连接域套接字
    ret = connect(socket_fd, (sockaddr *)&server_addr, sizeof(server_addr));

    if(-1 == ret){
        std::cout << "Connect socket failed" << std::endl;
        return -1;
    }

    while(true){
        std::cout << "Input message>>> ";
        fgets(msg, MSG_SIZE, stdin);
        // 3. 发送信息
        ret = send(socket_fd, msg, MSG_SIZE, 0);
    }

    close(socket_fd);
    return 0;
}
域套接字只提供了单机简单的可靠的进程通信同步服务,只能在单机使用,不同跨机器使用。
posted @ 2020-11-02 10:47  小鲨鱼~  阅读(163)  评论(1编辑  收藏  举报