天天看點

Linux程序管理Linux程序管理

Linux程序管理

這是Linux實踐課程系列第四篇

内容要求

  1. 編寫三個不同的程式:cmd1.c, cmd2.c, cmd3.c,每個程式輸出一句話,分别編譯成可執行檔案cmd1, cmd2, cmd3。然後再編寫一個程式,模拟shell程式的功能,能根據使用者輸入的字元串(表示相應的指令名),去為相應的指令建立子程序并讓它去執行相應的程式,而父程序則等待子程序的結束,然後再等待接收下一條指令。如果接收到的指令為exit,則父程序結束,如果接收到無效指令,則顯示”command not found”,繼續等待。
  2. 由父程序建立一個管道,然後再建立3個子程序,并由這三個子程序用管道與父程序之間進行通信:子程序發送資訊,父程序等三個子程序全部發完消息後再接收資訊。通信的具體内容可根據自己的需要随意設計,要求能夠實驗阻塞型讀寫過程的各種情況,并要求實作程序間對管道的互斥通路。運作程式,觀察各種情況下,程序實際讀寫的位元組數以及程序阻塞喚醒情況。
  3. 編寫程式建立兩個線程:sender線程和receive線程,其中sender運作函數sender(),他建立一個消息隊列,然後,循環等待使用者通過終端輸入一串字元,将這串字元通過消息隊列發送給receiver線程,直到使用者輸入”exit”為止;最後,它向receiver線程發送消息”end”,并且等待receiver的應答,直到應答消息後,将接收到的應答消息顯示在終端上,删除相關消息隊列,結束程式運作。receiver線程運作receive(),它通過消息隊列接收來自sender的消息,将消息顯示在終端螢幕,直到接收到”end”的消息後它向sender發送一個應答消息”over”,結束程式運作。使用無名信号量實作兩個線程之間的同步與互斥。
  4. 編寫程式sender,它建立一個共享記憶體,然後等待使用者通過終端輸入一串字元,并将這串字元通過共享記憶體發送給receiver,最後,等待receiver應答,等到應答消息後,它接收到的應答消息顯示在終端螢幕上,删除共享記憶體,結束程式運作。編寫receiver程式,它通過共享記憶體接收來自sender的消息,将消息顯示在終端螢幕上,然後再通過該共享記憶體向sender發送一個應答消息”over”,結束程式的運作。使用有名信号量或System V信号量實作兩個程序對共享記憶體的互斥使用。

開發平台

  • Linux環境 gcc vim

具體步驟

1-1 編寫cmd1.c cmd2.c cmd3.c代碼如下:

#include<stdio.h>
int main(){
    printf("This is cmd1\n");
    return ;
}
           
#include<stdio.h>
int main(){
    printf("This is cmd2\n");
    return ;
}
           
#include<stdio.h>
int main(){
    printf("This is cmd3\n");
    return ;
}
           

并編譯:

gcc -o cmd1 cmd1.c

gcc -o cmd2 cmd2.c

gcc -o cmd3 cmd3.c

1-2 編寫shell.c檔案,代碼如下:

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

#define MAX_CMD_LEN 20 //輸入指令最大長度
#define CMD_COLLECTION_LEN 4 //指令數組長度

#define INVALID_COMMAND -1 //未識别指令
#define EXIT 0 //正常退出
#define CMD_1 1
#define CMD_2 2
#define CMD_3 3

#define TRUE 1

char *cmdStr [CMD_COLLECTION_LEN] = {"exit","cmd1","cmd2","cmd3"};

int getCmdIndex(char *cmd)
{
    int i;
    for(i=;i<CMD_COLLECTION_LEN;i++)
    {
        if(strcmp(cmd,cmdStr[i])==)
        {
            return i;
        }
    }
    return -;
}

void myFork(int cmdIndex)
{
    pid_t pid;
    if((pid = fork())<)
    {
        printf("fork error");
        exit();
    }
    else if(pid == )
    {
        int execl_status = -;
        printf("child is running");
        switch(cmdIndex)
        {
            case CMD_1:
                execl_status = execl("./cmd1","cmd1",NULL);
                break;
            case CMD_2:
                execl_status = execl("./cmd2","cmd2",NULL);
                break;
            case CMD_3:
                execl_status = execl("./cmd3","cmd3",NULL);
                break;
            default:
                printf("Invalid Command\n");
                break;
        }
        if(execl_status<)
        {
            printf("fork error");
            exit();
        }
        printf("fork success\n");
        exit();
    }
    else
    {
        return;
    }
}
void runCMD(int cmdIndex)
{
    switch(cmdIndex)
    {
        case INVALID_COMMAND:
            printf("COMMAND NOT FOUND \n");
            break;
        case EXIT:
            exit();
            break;
        default:
            myFork(cmdIndex);
            break;
    }
}
int main()
{
    pid_t pid;
    char cmdStr[MAX_CMD_LEN];
    int cmdIndex;
    while(TRUE)
    {
        printf("\n---Input your command > ");
        scanf("%s",cmdStr);
        cmdIndex = getCmdIndex(cmdStr);
        runCMD(cmdIndex);
        wait();
        printf("waiting for next command");
    }
}
           

編譯:

gcc -o myshell shell.c

執行:

Linux程式管理Linux程式管理

1-3 這個小實驗主要用到了Linux中的Fork子程序知識,具體介紹在下邊補充知識[1]。

2-1 代碼如下:

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

#define READ 0

#define WRITE 1

int main() {

    int file_descriptors[];

    pid_t pid1,pid2,pid3;

    char buf[];

    int returned_count;

    pipe(file_descriptors);  /*建立無名管道*/

    if((pid1 = fork()) == -) { /*建立子程序*/
        printf("Error in fork\n");
        exit();
    }
    if(pid1 == ) {
        sleep();
        printf("pid1:%d in the spawned (child) process...\n",getpid());
        //        子程序向父程序寫資料,關閉管道的讀端
        close(file_descriptors[READ]);
        write(file_descriptors[WRITE], "pid1 send string111\n", strlen("pid1 send string111\n"));
        exit();
    }



    if ((pid2 = fork()) == -) {
        printf("ERROR in fork pid2\n");
        exit();
    }
    if (pid2 == ) {
        sleep();
        printf("pid2:%d in the spawned (child) process...\n",getpid());
        close(file_descriptors[READ]);
        write(file_descriptors[WRITE], "pid2 send string222\n", strlen("pid2 send string222\n"));
        exit();
    }


    if ((pid3 = fork()) == -) {
        printf("ERROR in fork pid3\n");
        exit();
    }
    if (pid3 == ) {
        sleep();
        printf("pid3:%d in the spawned (child) process...\n",getpid());

        close(file_descriptors[READ]);
        write(file_descriptors[WRITE], "pid3 send string333\n", strlen("pid3 send string333\n"));

        exit();
    }
    else {
        //        pid = wait(NULL);
        pid1 = waitpid(pid1, NULL, WUNTRACED);
        pid2 = waitpid(pid2, NULL, WUNTRACED);
        pid3 = waitpid(pid3, NULL, WUNTRACED);
        printf("main process pid: %d\n",getpid());
        printf("wait pid: %d %d %d in the spawning (parent) process...\n",pid1,pid2,pid3);
        /*父程序從管道讀取子程序寫的資料,關閉管道的寫端*/
        close(file_descriptors[WRITE]);
        returned_count = (int)read(file_descriptors[READ], buf, sizeof(buf));
        printf("%d bytes of data received from spawned process: \n%s\n",returned_count, buf);
    }
    return ;
}
           

2-2 執行效果如下:

Linux程式管理Linux程式管理

2-3 這個實驗主要涉及Linux中父程序和子程序間管道通信,具體介紹在補充知識[2]。

2-1 代碼如下

#include <pthread.h>
#include <semaphore.h>
#include <sys/types.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>

#define TRUE 1

#define BUF_SIZE 255
#define PERM S_IRUSR|S_IWUSR
#define KEY_NUM 1000


typedef struct msgbuf msgbuf;

struct msgbuf
{
    long mtype;
    char mtext[BUF_SIZE + ];
};


//semaphore and mutex
sem_t full;
sem_t empty;
sem_t mutex;

//pthread
pthread_t write_pid;
pthread_t read_pid;


key_t key;
//message
int msgid;

struct msgbuf msg;

void Init()
{
    /// init semaphore
    sem_init(&full,,);
    sem_init(&empty,,);
    sem_init(&mutex,,);

    key = KEY_NUM;

    // Create Message Queue
    if((msgid = msgget(key,PERM|IPC_CREAT)) == -)
    {
        fprintf(stderr, "Create Message Queue Error %s\n",strerror(errno) );
        exit(EXIT_FAILURE);
    }   
}


void * ReadProcess(void *arg)
{
    msgbuf msg;
    //init msg
    msg.mtype = ;
    while(TRUE)
    {
        sem_wait(&full);
        sem_wait(&mutex);

        //receive message from  message queue
        msgrcv(msgid,&msg,sizeof(msgbuf),,);

        //detect for end
        if(strcmp(msg.mtext,"end") == )
        {
            msg.mtype = ;
            strncpy(msg.mtext,"over",BUF_SIZE);
            msgsnd(msgid,&msg,sizeof(msgbuf),);
            break;
        }

        //print message
        printf("RP: [message received] %s\n\n",msg.mtext);

        sem_post(&empty);
        sem_post(&mutex);
    }

    exit(EXIT_SUCCESS);
}

void * WriteProcess(void *arg)
{   
    char input[];
    msgbuf msg;
    msg.mtype = ;

    while (TRUE)
    {
        //semaphore
        sem_wait(&empty);
        sem_wait(&mutex);
        sleep();
        printf("WP: Please Input the message you want to send.\n");
        scanf("%s",input);

        if(strcmp(input,"exit") == )
        {
            strncpy(msg.mtext,"end",BUF_SIZE);
            msgsnd(msgid,&msg,sizeof(msgbuf),);
            break;
        }
        strncpy(msg.mtext,input,BUF_SIZE);
        msgsnd(msgid,&msg,sizeof(msgbuf),);

        printf("WP: [message sent] %s\n",msg.mtext );

        //semaphore
        sem_post(&full);
        sem_post(&mutex);
    }



    // Clear Node
    memset(&msg,'\0',sizeof(msgbuf));
    // Block ,waiting for msg with type = 2
    msgrcv(msgid,&msg,sizeof(msgbuf),,);
    printf("WP: [message sent]%s\n",msg.mtext );

    //Remove Message Queue
    if( msgctl (msgid,IPC_RMID,) == -)
    {
        fprintf(stderr, "Remove Message Queue Error%s\n", strerror(errno));
        exit(EXIT_FAILURE);
    }
    exit(EXIT_SUCCESS);
}

int main()
{


    Init();

    pthread_create(&write_pid,NULL,WriteProcess,NULL);
    pthread_create(&read_pid,NULL,ReadProcess,NULL);

    //waiting for the thread end
    pthread_join(write_pid,NULL);
    pthread_join(read_pid,NULL);


    printf("Main Function End...\n");

    return ;
}
           

3-2 執行如下:

Linux程式管理Linux程式管理

3-3 這一部分主要是Linux中消息隊列的使用以及無名信号量的相關知識,具體介紹在補充知識的[3]。

4-1 先是sender的編寫

//sender.c
#include "common.h"
#include <stdio.h>

//key
key_t key;

//shared memory
int shmid;
char * shmptr;
char input[SHM_SIZE];

//semaphore 
sem_t * full;
sem_t * mutex;
                            //semaphore


void Init()
{
    key = KEY_NUM;                  //init key
    shmid  = GetShmId(key);         // init shared memory
    shmptr = shmat(shmid,NULL,);       // attach segement to vitural ...?
    //semaphore init
    full = sem_open(FULL_NAME,O_CREAT);
    mutex = sem_open(MUTEX_NAME,O_CREAT);
}

void SaveMessage()
{

    P(mutex);                       
    strcpy(shmptr,input);
    V(mutex);

    V(full);
}

int main(int argc, char const *argv[])
{


    Init();

    /*waiting for user to input message*/

    fgets(input, , stdin);              //input message from shell 

    SaveMessage();

    printf("Sender:  Process End\n");
    return ;
}
           

4-2 編寫receiver

//receiver.c
#include "common.h"
//key
key_t key;

//shared memory
int shmid;
char * shmptr;
char result[SHM_SIZE];

//semaphore 
sem_t * full;
sem_t * mutex;
                            //semaphore


void Init()
{
    key = KEY_NUM;                  //init key
    shmid  = GetShmId(key);         // init shared memory
    shmptr = shmat(shmid,NULL,);       // attach segement to vitural ...?
    //semaphore init
    full = sem_open(FULL_NAME,O_CREAT);
    mutex = sem_open(MUTEX_NAME,O_CREAT);
}

void ReadMessage()
{
    P(full);
    P(mutex);                       
    strcpy(result,shmptr);
    V(mutex);
}

int main(int argc, char const *argv[])
{


    Init();

    /*waiting for user to input message*/
    ReadMessage();

    printf("Receiver : message is %s\n",result);
    SemDestroy();
    printf("Receiver :  Process End \n");
    return ;
}
           

4-3 用到的頭檔案

//common.h
#ifndef   _COMMON_H_
#define   _COMMON_H_


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>  
#include <semaphore.h>

#include <sys/types.h>


#include <unistd.h>

#include <sys/ipc.h>
#include <sys/shm.h>


static const char * MUTEX_NAME = "mutex_shm";
static const char * FULL_NAME  = "full_shm";
//static const char * PATH_NAME = "tmp/shmtemp";


//constant define
#define SHM_SIZE 1024

#define KEY_NUM 1000

/*
key_t GetKey(const char * pathname);
*/

int GetShmId(key_t key);
/*
* create mutex + semaphore
* init those value
*/
void SemInit();

void SemDestroy();

void  P(sem_t *sem);

void  V(sem_t *sem);

#endif
           

4-4 一些公用的函數,如信号量的處理

//common.c
#include "common.h"

/*
key_t GetKey(const char * pathname)
{
    //int fd = open(pathname,O_CREAT,0666);
    int fd = open("log.txt",O_RDWR|O_CREAT,S_IRWXU);
    if(fd < 0)
    {
        perror("open file error");
        return -1;
    }
    close(fd);
    return ftok(pathname,0);
}
*/

int GetShmId(key_t key)
{
    int shmid;

    shmid = shmget(key,SHM_SIZE,IPC_CREAT|);
    if(shmid < )
    {
        perror("Receiver: Shmget Error");
        exit(EXIT_FAILURE);
    }

    return shmid;
}

/*
* create mutex + semaphore
* init those value
*/
void SemInit()
{
     /*
     * Funtion Prototype: 
     *
     *  sem_t *sem_open(const char *name, int oflag,
     *                  mode_t mode, unsigned int value);
     *                 
     * name     : MUTEX_NAME    "mutex_shm"
     * oflag    : O_CREAT       Create and initialize it if not exist
     * mode_t   : file perssion -rw-r--r--
     * value    : 1
     */
     if((sem_open(MUTEX_NAME,O_CREAT,,)) < )
     {
        perror("sem_open");
        exit(EXIT_FAILURE);
     }

     if((sem_open(FULL_NAME,O_CREAT,,)) < ){
        perror("sem_open");
        exit(EXIT_FAILURE);
     }
}


/*
* close and unlink semaphore that we crated
*/
void SemDestroy()
{
    sem_t * mutexPtr = sem_open(MUTEX_NAME,O_CREAT); 
    sem_t * fullPtr= sem_open(FULL_NAME,O_CREAT);

    /* Destroy mutex */
    sem_close(mutexPtr);                // int sem_close(sem_t *sem);
    sem_unlink(MUTEX_NAME);         // int sem_unlink(const char *name);

    /* Destory full*/
    sem_close(fullPtr);
    sem_unlink(FULL_NAME);
}


void  P(sem_t *semPtr)
{
    sem_wait(semPtr);                   //int sem_wait(sem_t *sem);
}

void  V(sem_t *semPtr)
{
    sem_post(semPtr);                   //int sem_post(sem_t *sem);
}
           

4-5 初始化

//init.c
#include "common.h"

int main(int argc, char const *argv[])
{
    key_t key;
    int semid;          //semaphore id
    int shmid;          //shared memory id


    /* Create key*/
    key = KEY_NUM;

    /* Initialize Semaphore*/
    SemInit();

    /* TODO Initialize Shared Memory*/ 
    GetShmId(key);

    printf("End of initialize\n");
    return ;
}
           

4-6 Makefile編寫

all : init sender receiver
.PHONY : clean


init : init.o common.o
    cc -pthread -o init init.o common.o

sender : sender.o common.o
    cc -pthread -o sender sender.o common.o

receiver : receiver.o common.o
    cc -pthread -o receiver receiver.o common.o


init.o : common.h           

sender.o : common.h

receiver.o : common.h

clean : 
    rm  init 
    rm  receiver
    rm  sender 
    rm  *.o
           

4-7編譯、測試

make

Linux程式管理Linux程式管理

4-8 這一個小實驗主要是Linux中程序通過共享記憶體來通信,具體介紹在補充知識[4]。

Linux程序管理補充知識

[1]

[2]

[3]

[4]

繼續閱讀