【摘要】 介紹Linux下進(jìn)程編程、進(jìn)程的創(chuàng)建、進(jìn)程通信、完成廣告機(jī)項(xiàng)目代碼。
昨天完成的任務(wù): Mplayer播放器安裝成功,并且實(shí)現(xiàn)基本使用。
任務(wù)1: 學(xué)習(xí)Linux下進(jìn)程編程
1.?進(jìn)程簡介
進(jìn)程是操作系統(tǒng)調(diào)度的最小單元,線程是進(jìn)程內(nèi)部的執(zhí)行單元,一個(gè)進(jìn)程默認(rèn)有一個(gè)主線程。
進(jìn)程在操作系統(tǒng)里使用PID號(hào)作為標(biāo)識(shí)符號(hào)----查看當(dāng)前終端運(yùn)行的進(jìn)程: ps 。
每個(gè)進(jìn)程之間的資源都是獨(dú)立的。--------進(jìn)程可以自己獨(dú)立的運(yùn)行、帶main----------
??今天學(xué)習(xí)的主要任務(wù):
1. 進(jìn)程間通信: 管道(無名管道、命名管道)、消息隊(duì)列、共享內(nèi)存、內(nèi)存映射(mmap)、信號(hào)。
2. execl函數(shù)族: 用于啟動(dòng)一個(gè)新的進(jìn)程,新的進(jìn)程開始執(zhí)行之后,會(huì)覆蓋原來進(jìn)程空間。
3. dup2函數(shù): 復(fù)制文件表。------實(shí)現(xiàn)文件描述符重定向。 dup2(fds[1],1);
4. 編寫廣告機(jī)播放器程序
5. 編寫shell腳本,實(shí)現(xiàn)文件同步
1.1 進(jìn)程創(chuàng)建
#include pid_t fork(void); |
功能: 在當(dāng)前進(jìn)程里再創(chuàng)建一個(gè)子進(jìn)程。
函數(shù)返回值: ==0 表示子進(jìn)程,>0表示父進(jìn)程,<0表示出現(xiàn)錯(cuò)誤
新創(chuàng)建的子進(jìn)程特性: 在fork成功那一刻,會(huì)將父進(jìn)程所有的資源全部拷貝一份,重新運(yùn)行。
??僵尸進(jìn)程: 子進(jìn)程先退出,父進(jìn)程沒有清理子進(jìn)程的空間。如何清理子進(jìn)程空間? wait();
??孤兒進(jìn)程: 父進(jìn)程比子進(jìn)程先退出。避免,就是父進(jìn)程要保證最后退出。
1.2 等待子進(jìn)程退出,并且清理子進(jìn)程空間
#include #include pid_t wait(int *status); 函數(shù)功能: 隨機(jī)等待一個(gè)子進(jìn)程退出,并清理子進(jìn)程資源。 返回值: 返回退出的子進(jìn)程PID號(hào)。 函數(shù)的形參: int *status可以保存進(jìn)程退出的狀態(tài)。 exit(-1); //結(jié)束當(dāng)前進(jìn)程。 pid_t waitpid(pid_t pid, int *status, int options); 函數(shù): 可以指定特定的PID號(hào)。-1表示所有子進(jìn)程。 |
#include #include #include #include #include #include #include int main(int argc,char **argv) { int pid;
/*創(chuàng)建子進(jìn)程*/ pid=fork(); if(pid==0) //子進(jìn)程 { printf("子進(jìn)程正常運(yùn)行!....\n"); sleep(1);
/*結(jié)束當(dāng)前進(jìn)程*/ exit(0); } else if(pid>0) //父進(jìn)程 { int state=0; //保存子進(jìn)程退出狀態(tài)值 /*阻塞-等待子進(jìn)程退出*/ wait(&state); printf("父進(jìn)程提示: 子進(jìn)程已經(jīng)安全退出! 子進(jìn)程退出的狀態(tài)=%d\n",state); } else { printf("進(jìn)程創(chuàng)建錯(cuò)誤!"); } return 0; } |
1.3 終止當(dāng)前進(jìn)程
#include void _exit(int status); #include void _Exit(int status); #include void exit(int status); |
1.4 管道通信
管道: FIFO文件,特性: 先入先出。
1.4.1 無名管道: 有親緣關(guān)系之間的進(jìn)程才可以使用無名管道進(jìn)程通信。
無名管道這個(gè)FIFO文件沒有實(shí)體。
如果創(chuàng)建無名管道?
#include int pipe(int pipefd[2]); 函數(shù)形參: 傳入一個(gè)數(shù)組的首地址。 管道創(chuàng)建成功之后: [0]表示(FIFO)無名管道讀端。 [1]表示(FIFO)無名管道寫端。 |
#include #include #include #include #include #include #include #include int main(int argc,char **argv) { int pid; int pipefd[2]; /*創(chuàng)建無名管道*/ pipe(pipefd);
/*創(chuàng)建子進(jìn)程*/ pid=fork(); if(pid==0) //子進(jìn)程 { printf("子進(jìn)程正常運(yùn)行!....\n"); sleep(1);
char *p="1234567"; write(pipefd[1],p,strlen(p)+1); //向管道的寫端寫入數(shù)據(jù)
/*結(jié)束當(dāng)前進(jìn)程*/ exit(0); } else if(pid>0) //父進(jìn)程 { int state=0; //保存子進(jìn)程退出狀態(tài)值
char buff[100]; read(pipefd[0],buff,100); //從管道的讀端讀取數(shù)據(jù) printf("父進(jìn)程收到的數(shù)據(jù)=%s\n",buff);
/*阻塞-等待子進(jìn)程退出*/ wait(&state); printf("父進(jìn)程提示: 子進(jìn)程已經(jīng)安全退出! 子進(jìn)程退出的狀態(tài)=%d\n",state); } else { printf("進(jìn)程創(chuàng)建錯(cuò)誤!"); } return 0; } |
1.4.2 命名管道通信
命名管道可以在任何進(jìn)程間通,因?yàn)槊艿朗且粋€(gè)實(shí)體文件,在磁盤可用找到該FIFO文件。
如何在磁盤上創(chuàng)建管道文件:
#include #include int mkfifo(const char *pathname, mode_t mode); |
管道文件不能在共享目錄下創(chuàng)建。(掛載的目錄)
1.5 dup2函數(shù)學(xué)習(xí)
#include int dup2(int oldfd, int newfd); 示例: dup2(fds[1],1); //接下來對(duì)文件描述符1的操作都是相當(dāng)于對(duì)管道fds[1]操作。 文件描述符在內(nèi)核里對(duì)應(yīng)的是一個(gè)文件結(jié)構(gòu)體。 |
??示例1:
#include #include #include #include #include #include #include #include int main(int argc,char **argv) { int pid; int pipefd[2]; /*創(chuàng)建無名管道*/ pipe(pipefd);
/*創(chuàng)建子進(jìn)程*/ pid=fork(); if(pid==0) //子進(jìn)程 { printf("子進(jìn)程正常運(yùn)行!....\n");
dup2(pipefd[1],1); //pipefd[1]管道寫端,1表示當(dāng)前進(jìn)程的標(biāo)準(zhǔn)輸出 sleep(1); printf("---1234567---\n");
/*結(jié)束當(dāng)前進(jìn)程*/ exit(0); } else if(pid>0) //父進(jìn)程 { int state=0; //保存子進(jìn)程退出狀態(tài)值
char buff[100]; read(pipefd[0],buff,100); //從管道的讀端讀取數(shù)據(jù) printf("父進(jìn)程收到的數(shù)據(jù)=%s\n",buff);
/*阻塞-等待子進(jìn)程退出*/ wait(&state); printf("父進(jìn)程提示: 子進(jìn)程已經(jīng)安全退出! 子進(jìn)程退出的狀態(tài)=%d\n",state); } else { printf("進(jìn)程創(chuàng)建錯(cuò)誤!"); } return 0; } |
??示例2: 日志功能制作。
#include #include #include #include #include #include #include #include int main(int argc,char **argv) { /*1.創(chuàng)建存放日志的文件*/ int fd=open("/log.txt",O_RDWR|O_CREAT,S_IRUSR|S_IWUSR);
/*2. 重定向(將標(biāo)準(zhǔn)輸出重定向到fd)*/ dup2(fd,1);
/*3. 向日志文件寫入數(shù)據(jù)*/ printf("12345\n"); printf("abcd\n"); printf("日志文件測試!\n");
/*4. 關(guān)閉日志文件*/ close(fd); return 0; } |
1.6 execl 函數(shù)族
#include extern char **environ; int execl(const char *path, const char *arg, ...); int execlp(const char *file, const char *arg, ...); int execle(const char *path, const char *arg,..., char * const envp[]); int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]); |
execl功能介紹: 啟動(dòng)新的子進(jìn)程,當(dāng)子進(jìn)程啟動(dòng)成功之后會(huì)覆蓋原來的進(jìn)程空間。
Execl函數(shù)族介紹:
1.?帶p表示可執(zhí)行文件可以從環(huán)境變量里獲取。
2.?不帶p表示,可執(zhí)行文件需要填絕對(duì)路徑。
3.?帶e表示最后的參數(shù),可以給新進(jìn)程設(shè)置新的環(huán)境變量。
說明: 參數(shù)列表最后面都要加一個(gè)NULL。
#include #include #include #include #include #include #include #include int main(int argc,char **argv) { /*以ls命令為例子,講解*/ //execl(<可執(zhí)行程序的路徑>,<執(zhí)行的形參列表>,NULL);
//execl("/bin/ls","ls","-l",NULL);
//execlp("ls","ls","-l",NULL);
//char *envp[]={"PATH1=12345",NULL}; //execle("/bin/ls","ls","-l",NULL,envp); //獲取環(huán)境變量的值: getenv("PATH1");
//char *argvs[]={"ls","-l",NULL}; //execv("/bin/ls",argvs);
char *argvs[]={"ls","-l",NULL}; execvp("ls",argvs);
printf("執(zhí)行失敗!\n"); return 0; } //ls -l |
1.7 mplayer播放器
Mplayer運(yùn)行有兩個(gè)模式: 1. 主模式 2.從模式
#include #include #include #include #include #include #include #include #include /* 獲取標(biāo)準(zhǔn)輸入的數(shù)據(jù)、寫給FIFO文件 */ void *pthread_func(void *argv) { int fd=open("/mplayer_fifo",2); if(fd<0) { printf("FIFO文件打開失敗!\n"); pthread_exit(NULL); //結(jié)束當(dāng)前線程 } char buff[100]; int len; while(1) { printf("請(qǐng)輸入命令:"); fflush(stdin); //刷新緩沖區(qū) fgets(buff,100,stdin); //從鍵盤上獲取數(shù)據(jù) get_percent_pos get_file_name len=strlen(buff); // get_file_name [0~12] [13]='\n' write(fd,buff,len); // get_file_name '\n' memset(buff,0,100); } } int main(int argc,char **argv) { int pid;
/*1. 創(chuàng)建無名管道*/ int fds[2]; pipe(fds);
/*2. 創(chuàng)建子進(jìn)程*/ pid=fork();
/*子進(jìn)程代碼: mplayer播放器*/ if(pid==0) { /*將子進(jìn)程的標(biāo)準(zhǔn)輸出重定向到管道寫端*/ dup2(fds[1],1);
/*啟動(dòng)子進(jìn)程*/ execlp("mplayer","mplayer","-zoom","-x","800","-y","480","-slave","-quiet","-input","file=/mplayer_fifo","/work/video_file/Video_2018-12-11.wmv",NULL); } else /*父進(jìn)程*/ { char buff[100]; int cnt=0;
/*創(chuàng)建新的線程: 從鍵盤上獲取輸入的數(shù)據(jù),寫給播放器的FIFO文件*/ pthread_t threadID; pthread_create(&threadID,NULL,pthread_func,NULL); pthread_detach(threadID); //設(shè)置分離屬性
while(1) { /*從管道的讀端讀取數(shù)據(jù): 讀取就是mplayer播放器輸出的數(shù)據(jù)*/ cnt=read(fds[0],buff,100); buff[cnt]='\0'; printf("播放器輸出的值=%s\n",buff); } } return 0; } |
任務(wù)2: 廣告機(jī)項(xiàng)目
??廣告機(jī)項(xiàng)目要求:
廣告機(jī)應(yīng)用場景: 公交站臺(tái)、地鐵車廂、銀行前臺(tái)大廳、高速公路、公園….
1.?有些廣告機(jī)只有視頻播放,沒有聲音。
2.?廣告機(jī)都支持網(wǎng)絡(luò)視頻文件更新---->文件更新使用現(xiàn)成的服務(wù)器: FTP服務(wù)器、NFS服務(wù)器。
(1)?如何判斷服務(wù)器上那些文件需要下載到本地? 通過shell腳本代碼或者使用C語言。
(2)?更新的時(shí)間一般是固定的: 20:00 23:00 …… 通過時(shí)間函數(shù)判斷時(shí)間是否到達(dá)。
(3)?在視頻文件更新的時(shí)候,視頻需要停止播放,可以在屏幕上顯示提示(正在更新…..)。
3.?廣告機(jī)需要支持自動(dòng)播放,播放一個(gè)自動(dòng)切換下一個(gè)、循環(huán)播放。
調(diào)用讀目錄、循環(huán)遍歷目錄、得到視頻文件、mplayer播放器需要使用子進(jìn)程方式啟動(dòng)。
廣告機(jī): 音量調(diào)整、選擇視頻播放…….都不是廣告機(jī)的功能---是視頻播放器的功能。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*-----------全局變量區(qū)------------*/
int log_fd; /*保存日志文件的文件描述符*/
#define LOG_FILE "/log.txt" /*日志文件存放的路徑*/
DIR *video_dir_p=NULL; /*存放視頻的目錄: 打開的目錄指針*/
int video_state=0;
#define VIDEO_FILE_PATH "/work/video_file/" //存放視頻文件的目錄
#define UPDATE_HOUR 16 //更新的時(shí)間: 小時(shí)
#define UPDATE_MIN 38 //更新的時(shí)間: 分鐘
#define UPDATE_SEC 00 //更新的時(shí)間: 秒
/*-------函數(shù)聲明---------*/
void DeleteListALLNode(void);
/*定義鏈表使用的結(jié)構(gòu)體*/
struct VideoList
{
char *file_path;
struct VideoList *next;
};
/*鏈表頭*/
struct VideoList *ListHead=NULL;
/*函數(shù): 信號(hào)處理函數(shù)*/
void exit_sighandler(int sig)
{
/*1. 判斷是什么信號(hào)*/
if(sig==2)
{
printf("用戶終止了進(jìn)程!\n");
}
else if(sig==11)
{
printf("進(jìn)程訪問了非法內(nèi)存空間!\n");
}
/*2. 殺死父進(jìn)程對(duì)應(yīng)所有子進(jìn)程*/
char cmd_buff[100];
//殺死父進(jìn)程創(chuàng)建所有子進(jìn)程,父進(jìn)程本身不受影響
sprintf(cmd_buff,"pkill -9 -P %d",getpid());
system(cmd_buff);
sleep(2);
/*3. 關(guān)閉打開所有的文件或者目錄*/
close(log_fd);
closedir(video_dir_p);
DeleteListALLNode();
/*4. 退出父進(jìn)程*/
exit(1);
}
/*
函數(shù)功能: 獲取本地時(shí)間,判斷是否是否到達(dá)預(yù)設(shè)的時(shí)間
*/
void *Time_pthread_func(void *argv)
{
time_t time1;
time_t time2;
struct tm *system_time;
char cmd_buff[100];
while(1)
{
time1=time(NULL); //獲取當(dāng)前系統(tǒng)的時(shí)間秒單位
if(time1!=time2) //保證if每1秒進(jìn)去一次
{
time2=time1;
system_time=localtime(&time1); //將秒單位時(shí)間,轉(zhuǎn)成標(biāo)準(zhǔn)時(shí)間結(jié)構(gòu)
printf("%d-%d-%d\n",system_time->tm_hour,system_time->tm_min,system_time->tm_sec);
/*更新的時(shí)間*/
if(system_time->tm_hour==UPDATE_HOUR
&& system_time->tm_min==UPDATE_MIN
&& system_time->tm_sec==UPDATE_SEC)
{
video_state=1; //表示進(jìn)程需要終止
//"pkill -9 -P <父進(jìn)程的PID>"
//殺死父進(jìn)程創(chuàng)建所有子進(jìn)程,父進(jìn)程本身不受影響
sprintf(cmd_buff,"pkill -9 -P %d",getpid());
/*執(zhí)行命令*/
system(cmd_buff);
printf("正在結(jié)束子進(jìn)程!\n");
pthread_exit(NULL);
}
}
}
}
/*
函數(shù)功能: 掃描目錄下的所有文件,加載到鏈表里
*/
void ScanDirFile()
{
struct dirent *dir_file;
struct VideoList *head_p=ListHead; //保存鏈表頭的地址
struct VideoList *new_p=NULL;
while(dir_file=readdir(video_dir_p))
{
//過濾掉.和..
if(strcmp(dir_file->d_name,".")==0 || strcmp(dir_file->d_name,"..")==0)
{
continue;
}
//創(chuàng)建新節(jié)點(diǎn)
new_p=(struct VideoList*)malloc(sizeof(struct VideoList));
if(new_p==NULL)
{
printf("創(chuàng)建新節(jié)點(diǎn)空間申請(qǐng)錯(cuò)誤!\n");
return;
}
//申請(qǐng)存放文件名稱的空間
new_p->file_path=malloc(strlen(VIDEO_FILE_PATH)+strlen(dir_file->d_name)+1);
if(new_p->file_path==NULL)
{
printf("申請(qǐng)存放文件名稱的空間錯(cuò)誤!\n");
return;
}
//拼接路徑
strcpy(new_p->file_path,VIDEO_FILE_PATH);
strcat(new_p->file_path,dir_file->d_name);
printf("播放的列表:%s\n",new_p->file_path);
//添加新的節(jié)點(diǎn)
while(head_p->next!=NULL)
{
head_p=head_p->next;
}
head_p->next=new_p;
new_p->next=NULL;
}
}
/*
函數(shù)功能: 刪除鏈表節(jié)點(diǎn)
*/
void DeleteListALLNode(void)
{
struct VideoList *head_p=ListHead; //保存鏈表頭的地址
struct VideoList *tmp_p;
struct VideoList *delete_p;
if(head_p!=NULL && head_p->next==NULL)
{
free(head_p); //釋放鏈表頭
}
else if(head_p->next!=NULL)
{
tmp_p=head_p->next;
free(head_p); //釋放鏈表頭
while(tmp_p->next!=NULL)
{
delete_p=tmp_p;
tmp_p=tmp_p->next;
free(delete_p->file_path);
free(delete_p);
}
free(tmp_p->file_path);
free(tmp_p);
}
}
int main(int argc,char **argv)
{
int pid;
int state=0;
struct VideoList *next_p=ListHead;
/*1. 注冊(cè)將要捕獲的信號(hào)*/
signal(SIGINT,exit_sighandler); /*進(jìn)程終止信號(hào):Ctrl+C*/
signal(SIGSEGV,exit_sighandler); /*進(jìn)程訪問了非法內(nèi)存*/
/*2. 創(chuàng)建日志文件: 保存mplayer播放器的輸出*/
log_fd=open(LOG_FILE,O_RDWR|O_CREAT,S_IRUSR|S_IWUSR);
/*3. 打開目錄*/
video_dir_p=opendir(VIDEO_FILE_PATH);
if(video_dir_p==NULL)
{
printf("%s 存放視頻的目錄打開失敗!\n",VIDEO_FILE_PATH);
exit(1);
}
/*4. 創(chuàng)建新的線程:判斷更新時(shí)間*/
pthread_t threadID;
pthread_create(&threadID,NULL,Time_pthread_func,NULL);
pthread_detach(threadID); //設(shè)置分離屬性
/*5. 遍歷目錄,更新鏈表*/
//創(chuàng)建鏈表頭
ListHead=(struct VideoList*)malloc(sizeof(struct VideoList));
if(ListHead==NULL)
{
printf("鏈表頭創(chuàng)建失敗!\n");
exit(1);
}
ListHead->next=NULL; //下個(gè)節(jié)點(diǎn)為NULL
//掃描目錄,并將目錄下的文件添加到鏈表
ScanDirFile();
next_p=ListHead; //鏈表頭
next_p=next_p->next; //取出數(shù)據(jù)節(jié)點(diǎn)
LOOP: //該標(biāo)簽表示繼續(xù)播放下一個(gè)視頻時(shí),重復(fù)創(chuàng)建子進(jìn)程
printf("正在播放視頻名稱:%s\n",next_p->file_path);
/*創(chuàng)建子進(jìn)程*/
pid=fork();
/*子進(jìn)程代碼: mplayer播放器*/
if(pid==0)
{
/*將子進(jìn)程的標(biāo)準(zhǔn)輸出重定向到日志文件*/
dup2(log_fd,1);
/*啟動(dòng)子進(jìn)程*/
execlp("mplayer","mplayer","-zoom","-x","800","-y","480","-slave","-quiet","-input","file=/mplayer_fifo",next_p->file_path,NULL);
}
/*父進(jìn)程代碼: 控制播放*/
else
{
/*等待子進(jìn)程退出*/
wait(&state);
//判斷是否需要結(jié)束當(dāng)前進(jìn)程
if(video_state==1)
{
/*執(zhí)行外部腳本: 啟動(dòng)視頻文件更新*/
system("./update_video.sh");
/*退出父進(jìn)程*/
DeleteListALLNode();
close(log_fd);
closedir(video_dir_p);
exit(0);
}
/*遍歷鏈表的下一個(gè)節(jié)點(diǎn),取出文件名稱,傳遞給子進(jìn)程*/
if(next_p->next==NULL) //表示視頻播放完畢
{
printf("視頻播放完畢---->鏈表歸位!\n");
next_p=ListHead; //鏈表頭
next_p=next_p->next; //取出數(shù)據(jù)節(jié)點(diǎn)
}
next_p=next_p->next; //取出數(shù)據(jù)節(jié)點(diǎn)
/*再次啟動(dòng)子進(jìn)程,播放下一個(gè)視頻*/
goto LOOP;
}
return 0;
}
-
Linux
+關(guān)注
關(guān)注
87文章
11292瀏覽量
209322 -
編程
+關(guān)注
關(guān)注
88文章
3614瀏覽量
93685 -
廣告機(jī)
+關(guān)注
關(guān)注
0文章
157瀏覽量
9768 -
進(jìn)程
+關(guān)注
關(guān)注
0文章
203瀏覽量
13960
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論