Linux课程project----基于c/c++

Linux课程project----基于c/c++

写在前面:Linux课程大作业,实现生产者/面包师,售货员与消费者的业务逻辑的程序。
题目大概要求:面包师为守护进程并添加服务,且是多线程程序。售货员负责接收消费者的消息,并与生产者通信,且为多线程程序。消费者为单线程程序。
关键词:进程间通信;多线程同步;守护进程;文件IO
project的README和代码如下,提供目前水平有限,如有建议,欢迎指正!

README.md
作者:ArthurWang
文件说明:
include 头文件文件夹
consumer.cpp ---- 消费者
producter.cpp ---- 生产者/面包师
saler.cpp ---- 售货员
producter_num_etc.txt ---- 生产者/面包师配置文件

运行方式:
step1:make ---- 生成可执行文件
step2:将生成的三个可执行文件和producter_num_etc.txt移动到根目录($ mv XXX /)
step3:运行prod($ ./prod)
step4:编写shell脚本放到/etc/init.d文件夹,并更改权限,利用sysv-rc-conf添加prod服务并开启
step5:运行saler($ ./saler)
step6:运行consumer($./consumer),并跟踪查看日志
step7:重启测试,待服务启动后(自动启动prod服务需要一定时间,等待启动),执行step5 – step6
代码主要逻辑:
生产者多线程竞争资源采用互斥锁
生产者与售货员通信采用信号量
售货员多线程竞争资源采用互斥锁
售货员与生产者通信采用信号量
售货员与消费者通信采用消息队列
消费者为单线程进程
消费者与售货员通信采用消息队列
测试验证:
$ipcs ---- 查看进程间通信资源创建情况
$service prod status ---- 查看prod服务运行状态
$sysv-rc-conf ---- 查看各个服务自动启动的runlevel
$ps ajx | grep prod ---- 查看prod是否成功运行为守护进程
$tail -f producter.log ---- 查看生产者log
$tail -f saler.log ---- 查看售货员log
$pstree -p XXX ---- 查看某个进程的线程数

出于考虑该文章只贴出源文件代码,即只给出了处理问题的逻辑方法,头文件代码及makefile没有贴出。整体project布局如下图。(注意如消息队列的消息的struct以及线程互斥锁的宏定义初始化等等均在头文件)

Linux课程project----基于c/c++_第1张图片

producter.cpp

#include "producter.h"
using namespace std;

int full_semid;
int empty_semid;
int process_mutex;

time_t timel;

ofstream prodlog_ofs;

int sem_p(int semid){
    struct sembuf semb;
    semb.sem_num = 0;
    semb.sem_op = -1;
    semb.sem_flg = SEM_UNDO;
    if (semop(semid, &semb, 1) == -1){
        fprintf(stderr, "semphore_p failed\n");
        return 0;
    }
    return 1;
}

int sem_v(int semid){
    struct sembuf semb;
    semb.sem_num = 0;
    semb.sem_op = 1;
    semb.sem_flg = SEM_UNDO;
    if (semop(semid, &semb, 1) == -1){
        fprintf(stderr, "semphore_p failed\n");
        return 0;
    }
    return 1;
}


void * pthreadProduct(void *arg){
    while (1)
    {
        pthread_mutex_lock(&mutex);
        //------------------------------------------------

        sem_p(empty_semid);   // 进程通信逻辑
        sem_p(process_mutex);

        prodlog_ofs.open("producter.log",ios::app);   // 每次都打开文件关闭文件避免了缓存or丢失信息的问题
        time(&timel);
        prodlog_ofs<<"生产者线程号:"<<(unsigned int)pthread_self()<<" 产品编号:"<<semctl(full_semid, 0, GETVAL)+1<<" 时间:"<<asctime(localtime(&timel))<<endl;
        prodlog_ofs.close();

        sem_v(process_mutex); // 进程通信逻辑
        sem_v(full_semid);

        //------------------------------------------------
        pthread_mutex_unlock(&mutex);
        
        if (semctl(empty_semid, 0, GETVAL) == 199) { break; }  
    }
}


void producter::semphoreInit(){

    full_semid = semget(SEMKEYFULL, 1, IPC_CREAT|750); // set的num是1
    semctl(full_semid, 0, SETVAL, 0); // 设置set[0]初始值为0

    empty_semid = semget(SEMKEYEMPTY, 1, IPC_CREAT|750);
    semctl(empty_semid, 0, SETVAL, 200);

    process_mutex = semget(SEMKEYMUTEX, 1, IPC_CREAT|750);
    semctl(process_mutex, 0, SETVAL, 1);
}

void producter::pthreadCreate(pthread_t tid[], int thread_num){
    for (int i = 0; i < thread_num; i++)
    {
        pthread_create(&tid[i], NULL, pthreadProduct, NULL);
    }
}

void producter::pthreadDestory(pthread_t tid[], int thread_num){
    for (int i = 0; i < thread_num; i++)
    {
        pthread_join(tid[i], NULL);  //wait on the given thread to finsh and stores its return_data. 阻塞等待
    }
}

int producter::readEtcFile(){
    ifstream ifs;
    ifs.open("./producter_num_etc.txt",ios::in);
    if (!ifs.is_open()){
        perror("Could not get etc file");
        exit(1);
    }
    char num;
    num=ifs.get();
    ifs.close();
    int res = num-'0';
    return res;
}

void producter::writeProdLogInit(){
    prodlog_ofs.open("producter.log",ios::out|ios::trunc); // 初始化阶段,trunc文件存在先删除,再创建
    if (!prodlog_ofs.is_open()){
        perror("Could not get producter.log file");
        exit(1);
    }
    prodlog_ofs.close();
}

void producter::daemonCreate(){
    // int daemon(int nochdir, noclose);
    int fd;
    signal(SIGTTOU,SIG_IGN);
    signal(SIGTTIN,SIG_IGN);
    signal(SIGTSTP,SIG_IGN);
    signal(SIGHUP,SIG_IGN);
    // father process exit
    if (fork()!=0)
    {
        exit(1);
    }
    // session leader;
    setsid(); 
    // close open file descriptors
    for (fd=0; fd < OPEN_MAX; fd++)
    {
        close(fd);
    }
    // to keep we can delete our file. In this  
    // chdir("/home/mydaemon");
    umask(0);
    // 如果将信息发送给 syslogd 守护进程时发生错误,直接将相关信息输出到终端
    // 本地使用保留
    openlog("生产者/面包师启动",LOG_CONS,LOG_LOCAL0);
}


int main(){
    
    producter prod;
    
    prod.daemonCreate();
    prod.semphoreInit();
    prod.writeProdLogInit();
    while (1)
    {
        int etc = prod.readEtcFile();
        pthread_t tid[etc];
        int thread_num = etc;

        char temp = thread_num;
        syslog(LOG_NOTICE, &temp);
        
        prod.pthreadCreate(tid, thread_num);
        prod.pthreadDestory(tid, thread_num);
    }

    semctl(full_semid, 0, IPC_RMID,NULL);
    semctl(empty_semid, 0, IPC_RMID,NULL);
    semctl(process_mutex, 0, IPC_RMID,NULL);

    return 0;
}

saler.cpp

#include "saler.h"
using namespace std;

int full_semid;
int empty_semid;
int process_mutex;

struct msgin mymsgin;
struct msgout mymsgout;
int msgid;

time_t timel;

ofstream salerlog_ofs;

ofstream prodsalerlog_ofs;

int sem_p(int semid){
    struct sembuf semb;
    semb.sem_num = 0;
    semb.sem_op = -1;
    semb.sem_flg = SEM_UNDO;
    if (semop(semid, &semb, 1) == -1){
        fprintf(stderr, "semphore_p failed\n");
        return 0;
    }
    return 1;
}

int sem_v(int semid){
    struct sembuf semb;
    semb.sem_num = 0;
    semb.sem_op = 1;
    semb.sem_flg = SEM_UNDO;
    if (semop(semid, &semb, 1) == -1){
        fprintf(stderr, "semphore_p failed\n");
        return 0;
    }
    return 1;
}


void * pthreadSaler(void *arg){

    while (1)
    {
        // 阻塞式接收消息
        msgrcv(msgid, &mymsgin, sizeof(mymsgin), 1, 0);
        // 购买数量接收
        int buy_num = mymsgin.buy_num;
        // 发送消息的mytpe编号是接收消息的pid号
        mymsgout.mytype = mymsgin.mypid;
        // saler communicate producter.
        pthread_mutex_lock(&mutex);
        while ( buy_num !=0 )
        {
            //------------------------------------------------
            sem_p(full_semid);   // 进程通信逻辑
            sem_p(process_mutex);

            prodsalerlog_ofs.open("producter.log",ios::app);
            time(&timel);
            prodsalerlog_ofs<<"售货员线程号:"<<(unsigned int)pthread_self()<<" 产品编号:"<<semctl(full_semid, 0, GETVAL)+1<<" 时间:"<<asctime(localtime(&timel))<<endl;
            prodsalerlog_ofs.close();

            sem_v(process_mutex); // 进程通信逻辑
            sem_v(empty_semid);
            //------------------------------------------------
            buy_num--;
        }
        salerlog_ofs.open("saler.log",ios::app);
        time(&timel);
        salerlog_ofs<<"售货员线程号:"<<(unsigned int)pthread_self()<<" 卖面包数量:"<<mymsgin.buy_num<<" 时间:"<<asctime(localtime(&timel))<<endl;
        salerlog_ofs.close();

        pthread_mutex_unlock(&mutex);
        strcpy(mymsgout.answer,"success");
        msgsnd(msgid, &mymsgout, sizeof(mymsgout), IPC_NOWAIT);
    }
}


void saler::messageQueueInit(){
    if (msgid = msgget(SEMKEYQUEUE, IPC_CREAT|750) < 0)
    {
        perror("Could not create queue");
    }
}


int saler::randomNumCreate(int min, int max){
    srand((unsigned int)time(NULL));  // really random number
    this->salerNum = rand() % (max - min + 1) + min;
    return this->salerNum;
}

void saler::semphoreInit(){
    // 捕获信号量(总是生产者先,所以不用设置初值。)
    full_semid = semget(SEMKEYFULL, 1, IPC_CREAT);
    empty_semid = semget(SEMKEYEMPTY, 1, IPC_CREAT);
    process_mutex = semget(SEMKEYMUTEX, 1, IPC_CREAT);
}

void saler::pthreadCreate(pthread_t tid[], int thread_num){
    for (int i = 0; i < thread_num; i++)
    {
        pthread_create(&tid[i], NULL, pthreadSaler, NULL);
    }
}

void saler::pthreadDestory(pthread_t tid[], int thread_num){
    for (int i = 0; i < thread_num; i++)
    {
        pthread_join(tid[i], NULL);
    }
}


void saler::writeSalerLogInit(){
    salerlog_ofs.open("saler.log",ios::out|ios::trunc); // 初始化阶段,trunc文件存在先删除,再创建
    if (!salerlog_ofs.is_open()){
        perror("Could not get saler.log file");
        exit(1);
    }
    salerlog_ofs.close();

    prodsalerlog_ofs.open("producter.log",ios::app); // producter创建的文件,只需要app打开检测一下是否存在,若用out会把文件内容删光
    if (!prodsalerlog_ofs.is_open()){
        perror("Could not get producter.log file");
        exit(1);
    }
    prodsalerlog_ofs.close();
}


int main(){
    
    saler sale;
    sale.semphoreInit();
    sale.messageQueueInit();
    sale.writeSalerLogInit();

    int salerNum = sale.randomNumCreate(2,10);
    pthread_t tid[salerNum];

    sale.pthreadCreate(tid, salerNum);
    sale.pthreadDestory(tid, salerNum);
    
    semctl(full_semid, 0, IPC_RMID,NULL);
    semctl(empty_semid, 0, IPC_RMID,NULL);
    semctl(process_mutex, 0, IPC_RMID,NULL);
    msgctl(msgid, IPC_RMID, NULL);

    return 0;
}

consumer.cpp

#include "consumer.h"
using namespace std;

struct msgout mymsgout;
struct msgin mymsgin;
int msgid;

void consumer::messageQueueInit(){
    if (msgid = msgget(SEMKEYQUEUE, 0) < 0)
    {
        perror("Could not get queue");
        exit(1);
    }
}
int consumer::randomNumCreate(int min, int max){
    srand((unsigned int)time(NULL));  // really random number
    this->buy_num = rand() % (max - min + 1) + min;
    return this->buy_num;
}

int main(){
    
    consumer consum;    
    consum.messageQueueInit();
    mymsgout.mypid=getpid();
    int buy_num = consum.randomNumCreate(1,5);
    mymsgout.buy_num = buy_num;
    mymsgout.mytype = 1;
    msgsnd(msgid,&mymsgout,sizeof(mymsgout),0);
    printf("售货员工作,请等待\n");    // saler work for this consume or work for others. And /n to clear buffer.
    msgrcv(msgid,&mymsgin,sizeof(mymsgin),getpid(),0); 
    printf("成功拿货物!\n"); // consume successful!
    return 0;
}

不足总结:
1.循环检测etc文件改变线程数量可以利用信号机制,更加合理。
2.信号量用来当“开关”进行保护更加合理。

你可能感兴趣的:(linux,多线程)