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


     

     

      

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

    原子性:原子性指一系列操作不可被中断的特性,这一系列操作要么全部执行,要么全部没有执行,不存在部分执行部分未执行的情况。
    //互斥量示例
    #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
    ", 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.
    ");
        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
    ", 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
    ", 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.
    ");
        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
    ", 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("缓冲区满了, 等待消费者消费...
    ");
                pthread_cond_wait(&cond, &mutex);
            }
            num += 1;
            printf("生产一个产品,当前产品数量为:%d
    ", num);
            sleep(1);
            pthread_cond_signal(&cond);
            printf("通知消费者...
    ");
            pthread_mutex_unlock(&mutex);
            sleep(1);
        }
    
    }
    
    void* consumer(void*){
        while(true){
            pthread_mutex_lock(&mutex);
            while (num <= 0){
                // 等待
                printf("缓冲区空了, 等待生产者生产...
    ");
                pthread_cond_wait(&cond, &mutex);
            }
            num -= 1;
            printf("消费一个产品,当前产品数量为:%d
    ", num);
            sleep(1);
            pthread_cond_signal(&cond);
            printf("通知生产者...
    ");
            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;
    }
    域套接字只提供了单机简单的可靠的进程通信同步服务,只能在单机使用,不同跨机器使用。
    做一枚奔跑的老少年!
  • 相关阅读:
    sqlserver中递归写法
    keytools命令生成证书
    java中sql语句快速处理
    select * 替换写法
    oracle行转列
    oracle中查看当前用户的表结构、主键、索引
    Servlet三种实现方式
    【python之旅】python的面向对象
    【python之旅】python的模块
    【python之旅】python的基础三
  • 原文地址:https://www.cnblogs.com/xiaoshayu520ly/p/13913317.html
Copyright © 2020-2023  润新知