作業系統課程設計(三):Linux程序管理
一、設計內容
實現一個模擬shell:編寫三個不同的程式:cmd1.c, cmd2.c, cmd3.c,每個程式輸出一句話,分別編譯成可執行檔案cmd1, cmd2, cmd3。然後再編寫一個程式,模擬shell程式的功能,能根據使用者輸入的字串(表示相應的命令名),去為相應的命令建立子程序並讓它去執行相應的程式,而父程序則等待子程序的結束,然後再等待接收下一條命令。如果接收到的命令為exit,則父程序結束,如果接收到無效命令,則顯示”command not found”,繼續等待。
實現一個管道通訊程式:由父程序建立一個管道,然後再建立3個子程序,並由這三個子程序用管道與父程序之間進行通訊:子程序傳送資訊,父程序等三個子程序全部發完訊息後再接收資訊。通訊的具體內容可根據自己的需要隨意設計,要求能夠實驗阻塞型讀寫過程的各種情況,並要求實現程序間對管道的互斥訪問。執行程式,觀察各種情況下,程序實際讀寫的位元組數以及程序阻塞喚醒情況。
利用linux的訊息佇列通訊機制實現兩個執行緒間的通訊:編寫程式建立兩個執行緒:sender執行緒和receive執行緒,其中sender執行函式sender(),他建立一個訊息佇列,然後,迴圈等待使用者通過終端輸入一串字元,將這串字元通過訊息佇列傳送給receiver執行緒,直到使用者輸入”exit”為止;最後,它向receiver執行緒傳送訊息”end”,並且等待receiver的應答,直到應答訊息後,將接收到的應答訊息顯示在終端上,刪除相關訊息佇列,結束程式執行。receiver執行緒執行receive(),它通過訊息佇列接收來自sender的訊息,將訊息顯示在終端螢幕,直到接收到”end”的訊息後它向sender傳送一個應答訊息”over”,結束程式執行。使用無名訊號量實現兩個執行緒之間的同步與互斥。
利用linux的共享記憶體通訊機制實現兩個程序間的通訊:編寫程式sender,它建立一個共享記憶體,然後等待使用者通過終端輸入一串字元,並將這串字元通過共享記憶體傳送給receiver,最後,等待receiver應答,等到應答訊息後,它接收到的應答訊息顯示在終端螢幕上,刪除共享記憶體,結束程式執行。編寫receiver程式,它通過共享記憶體接收來自sender的訊息,將訊息顯示在終端螢幕上,然後再通過該共享記憶體向sender傳送一個應答訊息”over”,結束程式的執行。使用有名訊號量或System V訊號量實現兩個程序對共享記憶體的互斥使用。
二、程式碼實現
(1)實現一個模擬shell
1-1 編寫cmd1.c cmd2.c cmd3.c,可自己設計
//cmd1.c
#include<stdio.h>
int main()
{
printf("this is the cmd1111111\n");
return 0;
}
//cmd2.c
#include<stdio.h>
int main()
{
printf("this is the cmd222222\n");
return 0;
}
//cmd3.c
#include<stdio.h>
int main()
{
printf("this is the cmd333333\n");
return 0;
}
1-2 編寫Makefile(clear下面的三個rm的前面是tab,不是單純空行)
all: myshell cmd1 cmd2 cmd3
.PHONY : clean
myshell.o : myshell.c
clean :
rm cmd1 cmd2 cmd3
rm myshell
rm *.o
1-3 編寫myshell.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#define CMD_COLLECTION_LEN 4 //命令陣列的長度(有哪幾個命令)
//command index
#define INVALID_COMMAND -1 //無效命令返回-1
#define EXIT 0
#define CMD_1 1
#define CMD_2 2
#define CMD_3 3
//bool
#define TRUE 1
char *cmdStr [CMD_COLLECTION_LEN ]= {"exit","cmd1","cmd2","cmd3"};
//對比所有命令引數,如果有一樣的,就返回對應數字,用於後面執行
int getCmdIndex(char *cmd)
{
int i;
for(i=0;i<CMD_COLLECTION_LEN;i++)
{
if (strcmp(cmd,cmdStr[i])==0)
{
return i;
}
}
return -1;
}
/*
建立子程序,這裡使用了execl,後面的l表示list,即引數列表。
第一引數為path(要執行的檔案路徑),最後一個引數必須是NULL,
中間的為要傳送的引數
*/
void myFork(int cmdIndex)
{
pid_t pid;
if((pid = fork())<0)
{
printf("建立子程序錯誤\n");
exit(0);
}
else if (pid == 0)
{
int execl_status = -1;
printf("子程序正在執行\n");
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("無此命令!!!\n");
break;
}
if(execl_status<0)
{
printf("建立錯誤\n");
exit(0);
}
printf("執行完畢!\n");
exit(0);
}
else{
return;
}
}
//執行cmd
void runCMD(int cmdIndex)
{
switch(cmdIndex)
{
case INVALID_COMMAND:
printf("Command Not Found \n"); //沒有找到該命令
break;
case EXIT: //exit命令返回0
exit(0);
break;
default:
myFork(cmdIndex); //建立子程序執行
break;
}
}
int main()
{
pid_t pid;
char cmdStr[30]; //命令陣列(最長30)
int cmdIndex; //用於顯示執行哪個資料
while(TRUE)
{
printf("\n輸入命令\n>>:");
scanf("%s",cmdStr);
cmdIndex = getCmdIndex(cmdStr);
runCMD(cmdIndex); //根據數字執行不同的cmd
wait(0);
}
}
1-4 編譯:
make
- 1
1-5 執行截圖示例: ![在這裡插入圖片描述](https://img-blog.csdn.net/20181022103145242?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1ZpY1RyZWU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
(2)實現一個管道通訊程式
2-1 編寫pipe_communication.c檔案
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#define READ 0 //filedes[0]用於讀
#define WRITE 1 //filedes[1]用於寫
int main() {
/*
函式原型:pipe(int filedes[2])
引數含義:filedes[0]對應管道讀端,filedes[1]對應管道寫端
功能:pipe在記憶體緩衝區中建立一個管道,並將讀寫該管道的一對檔案描述符儲存在filedes所指陣列中
返回值:成功返回0,失敗返回-1
*/
int filedes[2];
pid_t pid1,pid2,pid3;//pid_t本質就是int
char buf[256]; //用作read的緩衝區,儲存讀取的字元
pipe(filedes); //建立無名管道
if((pid1 = fork()) == -1) { //建立子程序
printf("fork error(pid1)!\n");
exit(1);
}
if(pid1 == 0) {
sleep(1); //掛起一秒
printf("正在產生子程序pid1:%d\n",getpid());
//子程序向父程序寫資料,關閉管道的讀端
close(filedes[READ]);
write(filedes[WRITE], "pid111111\n", strlen("pid111111\n"));
exit(0);
}
if ((pid2 = fork()) == -1) {
printf("fork error(pid2)\n");
exit(1);
}
if (pid2 == 0) {
sleep(1);
printf("正在產生子程序pid2:%d\n",getpid());
close(filedes[READ]);
write(filedes[WRITE], "pid222222\n", strlen("pid222222\n"));
exit(0);
}
if ((pid3 = fork()) == -1) {
printf("fork error(pid3)\n");
exit(1);
}
if (pid3 == 0) {
sleep(1);
printf("正在產生子程序pid3:%d\n",getpid());
close(filedes[READ]);
write(filedes[WRITE], "pid333333\n", strlen("pid333333\n"));
exit(0);
}
else {
//waitpid()會暫時停止目前程序的執行,直到有訊號來或者子程序結束
pid1 = waitpid(pid1, NULL, WUNTRACED);
pid2 = waitpid(pid2, NULL, WUNTRACED);
pid3 = waitpid(pid3, NULL, WUNTRACED);
printf("main pid: %d\n",getpid());
printf("wait pid: %d %d %d 返回資訊\n",pid1,pid2,pid3);
/*父程序從管道讀取子程序寫的資料,關閉管道的寫端*/
close(filedes[WRITE]);
//read():讀取的資料儲存在緩衝區buf
read(filedes[READ], buf, sizeof(buf));
printf("3個子程序傳輸的資料為:\n%s\n", buf);
}
return 0;
}
2-2 Makefile
pipe_communication : pipe_communication.o
.PHONY : clean
clean:
rm *.o
rm pipe_communication
2-3 make編譯
make
2-4 執行
(3)利用linux的訊息佇列通訊機制實現兩個執行緒間的通訊
3.1 程式碼(message_queue.c)
#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 //緩衝大小
//S_IRUSR|S_IWUSR:允許檔案建立者讀取|寫入(感覺就是賦予許可權)
#define PERM S_IRUSR|S_IWUSR
#define KEY_NUM 1000
typedef struct msgbuf msgbuf;
//訊息緩衝區結構體
struct msgbuf
{
long mtype; //訊息型別
char mtext[BUF_SIZE + 1]; //256,訊息正文
};
//sem_t 訊號量的資料型別,本質是個長整型的數
sem_t full;
sem_t empty;
sem_t mutex;
//pthread_t 宣告執行緒,類似於pid_t
pthread_t write_pid;
pthread_t read_pid;
/*
IPC物件鍵值,每個IPC物件都關聯著一個唯一的長整型的鍵值,
不同程序通過相同相同的鍵值可訪問到同一個IPC物件。
若為0,建立一個新的訊息佇列,若大於0(通常通過ftok()生成的)
*/
key_t key;
//messageid
int msgid;
struct msgbuf msg;
//初始化
void Init()
{
/*
函式:sem_init(sem_t *sem,int pshared,unsigned int value)
引數:sem表示一個訊號量,pashared(0-訊號量被程序內的執行緒共享,非0-程序之間共享),value訊號量初始值
*/
sem_init(&full,0,0);
sem_init(&empty,0,1);
sem_init(&mutex,0,1);
key = KEY_NUM;//給鍵值賦值
//建立訊息佇列
/*
函式:msgget(key_t key,int smgflag)。新教材p107
引數:key(訊息佇列鍵值,具體看上面)。msgflg(對訊息佇列的訪問許可權和控制命令的組合)
功能:如果引數msgflag為IPC_CREATE,則semget()新建立一個訊息佇列並返回其識別符號,
或返回具有相同鍵值的已存在的訊息佇列的識別符號
返回值:成功返回訊息佇列的識別符號,失敗返回-1
*/
if((msgid = msgget(key,PERM|IPC_CREAT)) == -1)
{
fprintf(stderr, "Create Message Queue Error %s\n",strerror(errno) );
exit(EXIT_FAILURE);
}
}
//讀取資訊
void * ReadProcess(void *arg)
{
msgbuf msg;
//init msg
msg.mtype = 1;
while(TRUE)
{
//sem_wait阻塞程序,直到訊號量>=0,解除阻塞後sem值-1,表示公共資源使用後減少
sem_wait(&full);
sem_wait(&mutex);
//從訊息佇列獲取內容
/*
函式:ssize_t msgrcv(int msqid,struct msgbuf *msgp,size_t msgsz,long msgtyp,int msgflg)
引數:msqid(訊息佇列的識別符號),
msgp(用來存放接受到的訊息內容的緩衝區指標)
msgsz(訊息正文的長度),
msgtyp(接收的訊息型別,0-接受訊息佇列中第一個訊息,>0接收第一個型別為msgtyp的訊息,<0接收第一個型別小於等於msgtyp的絕對值的訊息)
msgflg(0-沒有可接收的訊息時,呼叫程序阻塞。其他略)
返回值:接收成功,返回實際接收到的訊息正文的位元組數,否則返回-1
*/
msgrcv(msgid,&msg,sizeof(msgbuf),1,0);//接收型別為1的訊息(即mtype=1)
//如果接受到"end"
if(strcmp(msg.mtext,"end") == 0)
{
msg.mtype = 2;
strncpy(msg.mtext,"over",BUF_SIZE);
//msgsnd用於向識別符號為msqid的訊息佇列傳送一個訊息(即傳送over)
msgsnd(msgid,&msg,sizeof(msgbuf),0);
sem_post(&empty);
sem_post(&mutex);
break;
}
//print message
printf("Receive: %s\n\n",msg.mtext);
//sem_post增加訊號量的值,當有執行緒阻塞在這個訊號量時,該函式會使其中一個執行緒不在阻塞
sem_post(&empty);
sem_post(&mutex);
}
exit(EXIT_SUCCESS);
}
void * WriteProcess(void *arg)
{
char input[50];
msgbuf msg;
msg.mtype = 1;
while (TRUE)
{
sem_wait(&empty);
sem_wait(&mutex);
sleep(0.1);
printf("Sent: Please Input the message you want to send.\n");
scanf("%s",input);
if(strcmp(input,"exit") == 0)
{
strncpy(msg.mtext,"end",BUF_SIZE);
msgsnd(msgid,&msg,sizeof(msgbuf),0);
sem_post(&full);
sem_post(&mutex);
break; //輸出exit後,轉化為end,然後跳出while迴圈
}
strncpy(msg.mtext,input,BUF_SIZE);
msgsnd(msgid,&msg,sizeof(msgbuf),0);
printf("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),2,0);
printf("Sent:%s\n",msg.mtext );
//Remove Message Queue
if( msgctl (msgid,IPC_RMID,0) == -1)
{
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 0;
}
3-2 Makefile
message_queue : message_queue.o
cc -pthread -o message_queue message_queue.o
.PHONY : clean
clean:
rm message_queue
rm *.o
3-3 執行
make
3-4 截圖
(4)利用linux的共享記憶體通訊機制實現兩個程序間的通訊
4-1 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-2 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
//返回共享記憶體的識別符號
int GetShmId(key_t key);
void SemInit();
void SemDestroy();
void P(sem_t *sem);
void V(sem_t *sem);
#endif
4-3 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|0666);
if(shmid < 0)
{
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,0644,1)) < 0)
{
perror("sem_open");
exit(EXIT_FAILURE);
}
if((sem_open(FULL_NAME,O_CREAT,0644,0)) < 0){
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-4 sender.c (傳送訊息到記憶體區域)
#include "common.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,0); // 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*/
scanf("%s",input); //input message from shell
// TODO input a whole line
SaveMessage();
printf("Sender: Process End\n");
return 0;
}
4-5 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,0); // 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 0;
}
4-6 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 0;
}
4-7 編譯測試
make