<acronym id="s8ci2"><small id="s8ci2"></small></acronym>
<rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
<acronym id="s8ci2"></acronym>
<acronym id="s8ci2"><center id="s8ci2"></center></acronym>
0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Linux驅動開發_視頻廣告機開發、Linux進程編程介紹

DS小龍哥-嵌入式技術 ? 2022-09-17 15:49 ? 次閱讀

【摘要】 介紹Linux下進程編程、進程的創建、進程通信、完成廣告機項目代碼。

昨天完成的任務: Mplayer播放器安裝成功,并且實現基本使用。

任務1: 學習Linux下進程編程

1.?進程簡介

進程是操作系統調度的最小單元,線程是進程內部的執行單元,一個進程默認有一個主線程。

進程在操作系統里使用PID號作為標識符號----查看當前終端運行的進程: ps 。

poYBAGMlOoiAfax4AAB43IUPk_w877.png

每個進程之間的資源都是獨立的。--------進程可以自己獨立的運行、帶main----------

??今天學習的主要任務:

1. 進程間通信: 管道(無名管道、命名管道)、消息隊列、共享內存、內存映射(mmap)、信號。

2. execl函數族: 用于啟動一個新的進程,新的進程開始執行之后,會覆蓋原來進程空間。

3. dup2函數: 復制文件表。------實現文件描述符重定向。 dup2(fds[1],1);

4. 編寫廣告機播放器程序

5. 編寫shell腳本,實現文件同步

1.1 進程創建

#include

pid_t fork(void);

功能: 在當前進程里再創建一個子進程。

函數返回值: ==0 表示子進程,>0表示父進程,<0表示出現錯誤

新創建的子進程特性: 在fork成功那一刻,會將父進程所有的資源全部拷貝一份,重新運行。

??僵尸進程: 子進程先退出,父進程沒有清理子進程的空間。如何清理子進程空間? wait();

??孤兒進程: 父進程比子進程先退出。避免,就是父進程要保證最后退出。

1.2 等待子進程退出,并且清理子進程空間

#include

#include

pid_t wait(int *status);

函數功能: 隨機等待一個子進程退出,并清理子進程資源。

返回值: 返回退出的子進程PID號。

函數的形參: int *status可以保存進程退出的狀態。 exit(-1); //結束當前進程。

pid_t waitpid(pid_t pid, int *status, int options);

函數: 可以指定特定的PID號。-1表示所有子進程。

#include

#include

#include

#include

#include

#include

#include

int main(int argc,char **argv)

{

int pid;


/*創建子進程*/

pid=fork();

if(pid==0) //子進程

{

printf("子進程正常運行!....\n");

sleep(1);


/*結束當前進程*/

exit(0);

}

else if(pid>0) //父進程

{

int state=0; //保存子進程退出狀態值

/*阻塞-等待子進程退出*/

wait(&state);

printf("父進程提示: 子進程已經安全退出! 子進程退出的狀態=%d\n",state);

}

else

{

printf("進程創建錯誤!");

}

return 0;

}

1.3 終止當前進程

#include

void _exit(int status);

#include

void _Exit(int status);

#include

void exit(int status);

1.4 管道通信

管道: FIFO文件,特性: 先入先出。

1.4.1 無名管道: 有親緣關系之間的進程才可以使用無名管道進程通信。

無名管道這個FIFO文件沒有實體。

如果創建無名管道?

#include

int pipe(int pipefd[2]);

函數形參: 傳入一個數組的首地址。

管道創建成功之后: [0]表示(FIFO)無名管道讀端。 [1]表示(FIFO)無名管道寫端。

9cc4.png

#include

#include

#include

#include

#include

#include

#include

#include

int main(int argc,char **argv)

{

int pid;

int pipefd[2];

/*創建無名管道*/

pipe(pipefd);


/*創建子進程*/

pid=fork();

if(pid==0) //子進程

{

printf("子進程正常運行!....\n");

sleep(1);


char *p="1234567";

write(pipefd[1],p,strlen(p)+1); //向管道的寫端寫入數據


/*結束當前進程*/

exit(0);

}

else if(pid>0) //父進程

{

int state=0; //保存子進程退出狀態值


char buff[100];

read(pipefd[0],buff,100); //從管道的讀端讀取數據

printf("父進程收到的數據=%s\n",buff);


/*阻塞-等待子進程退出*/

wait(&state);

printf("父進程提示: 子進程已經安全退出! 子進程退出的狀態=%d\n",state);

}

else

{

printf("進程創建錯誤!");

}

return 0;

}

1.4.2 命名管道通信

命名管道可以在任何進程間通,因為命名管道是一個實體文件,在磁盤可用找到該FIFO文件。

如何在磁盤上創建管道文件:

#include

#include

int mkfifo(const char *pathname, mode_t mode);

管道文件不能在共享目錄下創建。(掛載的目錄)

1.5 dup2函數學習

#include

int dup2(int oldfd, int newfd);

示例: dup2(fds[1],1); //接下來對文件描述符1的操作都是相當于對管道fds[1]操作。

文件描述符在內核里對應的是一個文件結構體。

??示例1:

#include

#include

#include

#include

#include

#include

#include

#include

int main(int argc,char **argv)

{

int pid;

int pipefd[2];

/*創建無名管道*/

pipe(pipefd);


/*創建子進程*/

pid=fork();

if(pid==0) //子進程

{

printf("子進程正常運行!....\n");


dup2(pipefd[1],1);

//pipefd[1]管道寫端,1表示當前進程的標準輸出

sleep(1);

printf("---1234567---\n");


/*結束當前進程*/

exit(0);

}

else if(pid>0) //父進程

{

int state=0; //保存子進程退出狀態值


char buff[100];

read(pipefd[0],buff,100); //從管道的讀端讀取數據

printf("父進程收到的數據=%s\n",buff);


/*阻塞-等待子進程退出*/

wait(&state);

printf("父進程提示: 子進程已經安全退出! 子進程退出的狀態=%d\n",state);

}

else

{

printf("進程創建錯誤!");

}

return 0;

}

??示例2: 日志功能制作。

#include

#include

#include

#include

#include

#include

#include

#include

int main(int argc,char **argv)

{

/*1.創建存放日志的文件*/

int fd=open("/log.txt",O_RDWR|O_CREAT,S_IRUSR|S_IWUSR);


/*2. 重定向(將標準輸出重定向到fd)*/

dup2(fd,1);


/*3. 向日志文件寫入數據*/

printf("12345\n");

printf("abcd\n");

printf("日志文件測試!\n");


/*4. 關閉日志文件*/

close(fd);

return 0;

}

1.6 execl 函數族

#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功能介紹: 啟動新的子進程,當子進程啟動成功之后會覆蓋原來的進程空間。

Execl函數族介紹:

1.?帶p表示可執行文件可以從環境變量里獲取。

2.?不帶p表示,可執行文件需要填絕對路徑。

3.?帶e表示最后的參數,可以給新進程設置新的環境變量。

說明: 參數列表最后面都要加一個NULL。

#include

#include

#include

#include

#include

#include

#include

#include

int main(int argc,char **argv)

{

/*以ls命令為例子,講解*/

//execl(<可執行程序的路徑>,<執行的形參列表>,NULL);


//execl("/bin/ls","ls","-l",NULL);


//execlp("ls","ls","-l",NULL);


//char *envp[]={"PATH1=12345",NULL};

//execle("/bin/ls","ls","-l",NULL,envp);

//獲取環境變量的值: getenv("PATH1");


//char *argvs[]={"ls","-l",NULL};

//execv("/bin/ls",argvs);


char *argvs[]={"ls","-l",NULL};

execvp("ls",argvs);


printf("執行失敗!\n");

return 0;

}

//ls -l

1.7 mplayer播放器

Mplayer運行有兩個模式: 1. 主模式 2.從模式

pYYBAGMlOpOACkU0AAFMwIhnoNQ390.png

#include

#include

#include

#include

#include

#include

#include

#include

#include

/*

獲取標準輸入的數據、寫給FIFO文件

*/

void *pthread_func(void *argv)

{

int fd=open("/mplayer_fifo",2);

if(fd<0)

{

printf("FIFO文件打開失敗!\n");

pthread_exit(NULL); //結束當前線程

}

char buff[100];

int len;

while(1)

{

printf("請輸入命令:");

fflush(stdin); //刷新緩沖區

fgets(buff,100,stdin); //從鍵盤上獲取數據 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. 創建無名管道*/

int fds[2];

pipe(fds);


/*2. 創建子進程*/

pid=fork();


/*子進程代碼: mplayer播放器*/

if(pid==0)

{

/*將子進程的標準輸出重定向到管道寫端*/

dup2(fds[1],1);


/*啟動子進程*/

execlp("mplayer","mplayer","-zoom","-x","800","-y","480","-slave","-quiet","-input","file=/mplayer_fifo","/work/video_file/Video_2018-12-11.wmv",NULL);

}

else /*父進程*/

{

char buff[100];

int cnt=0;


/*創建新的線程: 從鍵盤上獲取輸入的數據,寫給播放器的FIFO文件*/

pthread_t threadID;

pthread_create(&threadID,NULL,pthread_func,NULL);

pthread_detach(threadID); //設置分離屬性


while(1)

{

/*從管道的讀端讀取數據: 讀取就是mplayer播放器輸出的數據*/

cnt=read(fds[0],buff,100);

buff[cnt]='\0';

printf("播放器輸出的值=%s\n",buff);

}

}

return 0;

}

任務2: 廣告機項目

??廣告機項目要求:

廣告機應用場景: 公交站臺、地鐵車廂、銀行前臺大廳、高速公路、公園….

1.?有些廣告機只有視頻播放,沒有聲音。

2.?廣告機都支持網絡視頻文件更新---->文件更新使用現成的服務器: FTP服務器、NFS服務器。

(1)?如何判斷服務器上那些文件需要下載到本地? 通過shell腳本代碼或者使用C語言。

(2)?更新的時間一般是固定的: 20:00 23:00 …… 通過時間函數判斷時間是否到達。

(3)?在視頻文件更新的時候,視頻需要停止播放,可以在屏幕上顯示提示(正在更新…..)。

3.?廣告機需要支持自動播放,播放一個自動切換下一個、循環播放。

調用讀目錄、循環遍歷目錄、得到視頻文件、mplayer播放器需要使用子進程方式啟動。

廣告機: 音量調整、選擇視頻播放…….都不是廣告機的功能---是視頻播放器的功能。

poYBAGMlOpSACaywAADXbVHn7Jg884.png
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/*-----------全局變量區------------*/
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 //更新的時間: 小時
#define UPDATE_MIN 38  //更新的時間: 分鐘
#define UPDATE_SEC 00  //更新的時間: 秒

/*-------函數聲明---------*/
void DeleteListALLNode(void);
/*定義鏈表使用的結構體*/
struct VideoList
{
	char *file_path;
	struct VideoList *next;
};

/*鏈表頭*/
struct VideoList *ListHead=NULL;
/*函數: 信號處理函數*/
void exit_sighandler(int sig)
{
	/*1. 判斷是什么信號*/
	if(sig==2)
	{
		printf("用戶終止了進程!\n");
	}
	else if(sig==11)
	{
		printf("進程訪問了非法內存空間!\n");
	}
	
	/*2. 殺死父進程對應所有子進程*/
	char cmd_buff[100];
	//殺死父進程創建所有子進程,父進程本身不受影響
	sprintf(cmd_buff,"pkill -9 -P %d",getpid());
	system(cmd_buff);
	sleep(2);
	
	/*3. 關閉打開所有的文件或者目錄*/
	close(log_fd); 
	closedir(video_dir_p);
	DeleteListALLNode();
	
	/*4. 退出父進程*/
	exit(1);
}

/*
函數功能: 獲取本地時間,判斷是否是否到達預設的時間
*/
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); //獲取當前系統的時間秒單位
		if(time1!=time2)  //保證if每1秒進去一次
		{
			time2=time1;
			system_time=localtime(&time1); //將秒單位時間,轉成標準時間結構
			printf("%d-%d-%d\n",system_time->tm_hour,system_time->tm_min,system_time->tm_sec);
			/*更新的時間*/
			if(system_time->tm_hour==UPDATE_HOUR 
			&& system_time->tm_min==UPDATE_MIN
			&& system_time->tm_sec==UPDATE_SEC)
			{
				video_state=1; //表示進程需要終止
				
				//"pkill -9 -P <父進程的PID>" 
				//殺死父進程創建所有子進程,父進程本身不受影響
				sprintf(cmd_buff,"pkill -9 -P %d",getpid());
				
				/*執行命令*/
				system(cmd_buff);
				printf("正在結束子進程!\n");
				pthread_exit(NULL);
			}
		}
	}
}
/*
函數功能: 掃描目錄下的所有文件,加載到鏈表里
*/
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;
		}
		
		//創建新節點
		new_p=(struct VideoList*)malloc(sizeof(struct VideoList));
		if(new_p==NULL)
		{
			printf("創建新節點空間申請錯誤!\n");
			return;
		}
		
		//申請存放文件名稱的空間
		new_p->file_path=malloc(strlen(VIDEO_FILE_PATH)+strlen(dir_file->d_name)+1);
		if(new_p->file_path==NULL)
		{
			printf("申請存放文件名稱的空間錯誤!\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);
		
		//添加新的節點
		while(head_p->next!=NULL)
		{
			head_p=head_p->next;
		}
		
		head_p->next=new_p;
		new_p->next=NULL;
	}
}

/*
函數功能: 刪除鏈表節點
*/
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. 注冊將要捕獲的信號*/
	signal(SIGINT,exit_sighandler);   /*進程終止信號:Ctrl+C*/
	signal(SIGSEGV,exit_sighandler);  /*進程訪問了非法內存*/
	
	/*2. 創建日志文件: 保存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. 創建新的線程:判斷更新時間*/
	pthread_t threadID;
	pthread_create(&threadID,NULL,Time_pthread_func,NULL);
	pthread_detach(threadID); //設置分離屬性
		
	/*5. 遍歷目錄,更新鏈表*/
	//創建鏈表頭
	ListHead=(struct VideoList*)malloc(sizeof(struct VideoList));
	if(ListHead==NULL)
	{
		printf("鏈表頭創建失敗!\n");
		exit(1);
	}
	ListHead->next=NULL; //下個節點為NULL
	
	//掃描目錄,并將目錄下的文件添加到鏈表
	ScanDirFile();
	
	next_p=ListHead; 	 //鏈表頭
	next_p=next_p->next; //取出數據節點
	
LOOP: //該標簽表示繼續播放下一個視頻時,重復創建子進程

	printf("正在播放視頻名稱:%s\n",next_p->file_path);
	/*創建子進程*/
	pid=fork();

	/*子進程代碼: mplayer播放器*/
	if(pid==0)
	{
		/*將子進程的標準輸出重定向到日志文件*/
		dup2(log_fd,1);
		
		/*啟動子進程*/
		execlp("mplayer","mplayer","-zoom","-x","800","-y","480","-slave","-quiet","-input","file=/mplayer_fifo",next_p->file_path,NULL);
	}
	 /*父進程代碼: 控制播放*/
	else
	{
		/*等待子進程退出*/
		wait(&state);
		
		//判斷是否需要結束當前進程
		if(video_state==1)
		{
			/*執行外部腳本: 啟動視頻文件更新*/
			system("./update_video.sh");
			
			/*退出父進程*/
			DeleteListALLNode();
			close(log_fd); 
			closedir(video_dir_p);
			exit(0);
		}
		
		/*遍歷鏈表的下一個節點,取出文件名稱,傳遞給子進程*/
		if(next_p->next==NULL) //表示視頻播放完畢
		{
			printf("視頻播放完畢---->鏈表歸位!\n");
			next_p=ListHead; 	 //鏈表頭
			next_p=next_p->next; //取出數據節點
		}
		next_p=next_p->next;   //取出數據節點
		
		/*再次啟動子進程,播放下一個視頻*/
		goto LOOP; 
	}
	return 0;
}
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • Linux
    +關注

    關注

    87

    文章

    11025

    瀏覽量

    207141
  • 編程
    +關注

    關注

    88

    文章

    3454

    瀏覽量

    92758
  • 廣告機
    +關注

    關注

    0

    文章

    124

    瀏覽量

    9675
  • 進程
    +關注

    關注

    0

    文章

    196

    瀏覽量

    13905
收藏 人收藏

    評論

    相關推薦

    fpga開發板與linux開發板區別

    FPGA開發板與Linux開發板是兩種不同的硬件開發平臺,各自具有不同的特點和應用場景。在以下的文章中,我將詳細介紹FPGA
    的頭像 發表于 02-01 17:09 ?934次閱讀

    TLT507-Linux-RT應用開發案例

    TLT507-Linux-RT應用開發案例
    的頭像 發表于 01-26 09:46 ?425次閱讀
    TLT507-<b class='flag-5'>Linux</b>-RT應用<b class='flag-5'>開發</b>案例

    TL3588-Linux應用開發手冊

    TL3588-Linux應用開發手冊
    的頭像 發表于 01-24 10:58 ?239次閱讀
    TL3588-<b class='flag-5'>Linux</b>應用<b class='flag-5'>開發</b>手冊

    嵌入式Linux開發的三種方式

    嵌入式Linux開發主要有三種方式:裸機開發、SDK開發驅動開發。
    的頭像 發表于 01-22 14:22 ?401次閱讀

    DIY個人的Linux開發板教程

    作者簡介:大佬已在硬創社開源了近50款開發板,動手能力極強,于去年年底開始接觸學習Linux,并由全志V3s、F1C200S等芯片開始上手DIY個人的Linux開發板。
    的頭像 發表于 01-07 09:52 ?831次閱讀
    DIY個人的<b class='flag-5'>Linux</b><b class='flag-5'>開發</b>板教程

    一文總結linux的platform驅動

    linux設備驅動中,有許多沒有特定總線的外設驅動,在實際開發中,又需要使用到總線、驅動和設備模型這三個概念,故而
    的頭像 發表于 10-16 16:45 ?473次閱讀
    一文總結<b class='flag-5'>linux</b>的platform<b class='flag-5'>驅動</b>

    嵌入式Linux應用開發的完全手冊

    。對于有志于從事底層系統開發(比如改造 Bootloader、鉆研內核、為新硬件編寫驅動程序)的人,對于想從上層軟件開發轉到底層軟件開發的人,應該看得懂電路原理圖,看得懂芯片數據手冊,
    發表于 09-25 07:12

    基于樹莓派的嵌入式Linux開發教學

    + 虛擬環境 + 交叉編譯 + 驅動編程 + 應用層編程開發鏈可以深入理解和控制嵌入式 Linu
    發表于 09-21 07:09

    Ai-WB2系列模組linux開發環境搭建方案

    Linux下編譯速度比windows快很多,一般推薦優先使用linux環境開發。Ubuntu是非常常見Linux操作系統。這里介紹下如何快
    發表于 09-09 09:55 ?367次閱讀
    Ai-WB2系列模組<b class='flag-5'>linux</b><b class='flag-5'>開發</b>環境搭建方案

    ARM-Linux開發和 MCU開發的不同點 ARM-Linux的基本開發環境

    針對 ARM-Linux 程序的開發,主要分為三類:應用程序開發、驅動程序開發、系統內核開發,針
    的頭像 發表于 08-07 16:23 ?583次閱讀

    怎么才算熟悉linux,Linux系統開發有何難點

    對于有一定 Linux 開發基礎希望進階學習 Linux 系統編程開發人員來說,其難點在于,Linux
    發表于 07-28 11:45 ?498次閱讀

    嵌入式Linux系統開發介紹

    Linux 系統開發 基于 linux 操作系統來開發我們的產品叫 linux系統開發。此
    的頭像 發表于 07-27 17:00 ?920次閱讀
    嵌入式<b class='flag-5'>Linux</b>系統<b class='flag-5'>開發</b><b class='flag-5'>介紹</b>

    看看ARM-Linux開發和MCU開發的不同點

    針對ARM-Linux程序的開發,主要分為三類:應用程序開發、驅動程序開發、系統內核開發。針對不
    發表于 07-25 09:25 ?866次閱讀

    Linux 系統編程的難點是什么

    對于有一定 Linux 開發基礎希望進階學習 Linux 系統編程開發人員來說,其難點在于,Linux
    發表于 06-28 12:54 ?445次閱讀

    ARM-Linux開發和MCU開發的不同點

    針對ARM-Linux程序的開發,主要分為三類:應用程序開發、驅動程序開發、系統內核開發。針對不
    的頭像 發表于 06-22 11:46 ?727次閱讀
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看
    <acronym id="s8ci2"><small id="s8ci2"></small></acronym>
    <rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
    <acronym id="s8ci2"></acronym>
    <acronym id="s8ci2"><center id="s8ci2"></center></acronym>