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

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 執行效果如下:
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 執行如下:
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
4-8 這一個小實驗主要是Linux中程序通過共享記憶體來通信,具體介紹在補充知識[4]。
Linux程序管理補充知識
[1]
[2]
[3]
[4]