【摘要】 介绍linux下进程编程、进程的创建、进程通信、完成广告机项目代码。
昨天完成的任务: mplayer播放器安装成功,并且实现基本使用。
任务1: 学习linux下进程编程 1. 进程简介
进程是操作系统调度的最小单元,线程是进程内部的执行单元,一个进程默认有一个主线程。
进程在操作系统里使用pid号作为标识符号----查看当前终端运行的进程: ps 。
每个进程之间的资源都是独立的。--------进程可以自己独立的运行、带main----------
今天学习的主要任务:
1. 进程间通信: 管道(无名管道、命名管道)、消息队列、共享内存、内存映射(mmap)、信号。
2. execl函数族: 用于启动一个新的进程,新的进程开始执行之后,会覆盖原来进程空间。
3. dup2函数: 复制文件表。------实现文件描述符重定向。 dup2(fds[1],1);
4. 编写广告机播放器程序
5. 编写shell脚本,实现文件同步
1.1 进程创建
#include
pid_t fork(void);
功能: 在当前进程里再创建一个子进程。
函数返回值: ==0 表示子进程,>0表示父进程,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)无名管道写端。
#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.从模式
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*
获取标准输入的数据、写给fifo文件
*/
void *pthread_func(void *argv)
{
int fd=open(/mplayer_fifo,2);
if(fd文件更新使用现成的服务器: ftp服务器、nfs服务器。
(1) 如何判断服务器上那些文件需要下载到本地? 通过shell脚本代码或者使用c语言。
(2) 更新的时间一般是固定的: 20:00 23:00 …… 通过时间函数判断时间是否到达。
(3) 在视频文件更新的时候,视频需要停止播放,可以在屏幕上显示提示(正在更新…..)。
3. 广告机需要支持自动播放,播放一个自动切换下一个、循环播放。
调用读目录、循环遍历目录、得到视频文件、mplayer播放器需要使用子进程方式启动。
广告机: 音量调整、选择视频播放…….都不是广告机的功能---是视频播放器的功能。
#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 //杀死父进程创建所有子进程,父进程本身不受影响 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;}
网络安全隔离设备StoneWall-2000原理是什么?有哪些应用?
如何在Ubuntu桌面环境上安装Google Chrome ?
如何在车辆行驶时准确的测量加速度
未来10年闪存将发展到尽头 3D内存接班
三星有望实现卷曲屏 启动更薄面板开发
Linux驱动开发_视频广告机开发、Linux进程编程介绍
2019年数字货币区块链系统开发把握方向大有可为
BMS应该怎样选用电池模拟器
中国自产研制的计算机诞生!意味彻底打破国外的技术垄断
折叠屏突围无望,Galaxy Fold难产,三星帝国该走向何方
长沙网约车管理细则正式发布 设有6个月过渡期
全球半导体芯片供应紧张蔓延到汽车行业
电瓶修复——锂离子电池充电与放电的工作原理
Tech Talk:解读闪存原理与颗粒类型
福禄克284信号源维修故障分析及维修问题解决【图文详解】
泰国监管机构已下令于2019年10月前终止所有2G服务
传感器控制板BGA芯片防止脱焊用底部填充胶应用案例
谐波与不平衡电流的危害
音箱的红外被阻隔后怎么解决
内存市场报价不确定 存储器原厂严控出货!