vim selec.c
开始播放涉及到多进程并发涉及到共享内存
当输出1的时候开始播放音乐
case 1:
start_play()
break;
在main.c中初始化共享内存
//初始化共享内存
ret = InitShm();
if (FAILURE == ret)
{
printf("共享内存初始化失败\n");
exit(1);
}
player.h
#ifndef PLAYER_H
#define PLAYER_H
#include <unistd.h>
#define MUSICPATH "/root/music_list/"
#define SHMKEY 1234
#define SHMSIZE 4096
#define SEQUENCEMODE 1
#define RANDOM 2
#define CIRCLE 3
//共享内存数据
struct shm
{
int play_mode;
char cur_name[64];
pid_t ppid;
pid_t child_pid;
pid_t grand_pid;
};
typedef struct shm shm;
int InitShm();
#endif
vim play.c
void *g_addr = NULL; //共享内存映射地址
int g_start_flag = 0; //表示没有开始播放音乐
int g_suspend_flag = 0; //表示没有暂停
int InitShm()
{
//创建共享内存
shmid = shmget(SHMKEY, SHMSIZE, IPC_CREAT | IPC_EXCL);
if (-1 == shmid)
{
return FAILURE;
}
//映射
g_addr = shmat(shmid, NULL, 0);
if (NULL == g_addr)
{
return FAILURE;
}
//初始化共享内存数据
shm s;
s.play_mode = SEQUENCEMODE;
s.ppid = getpid(); //父进程pid存入
memcpy(g_addr, &s, sizeof(s)); //把初始数据写入共享内存,播放模式和父进程id
return SUCCESS;
}
在pleyer.c中编写开始播放的主要程序
void start_play()
{
if (g_start_flag == 1) //已经开始播放
{
return;
}
//获取歌曲名称
if (head->next == NULL) //空链表
{
return;
}
开始播放音乐
play_music(head->next->music_name);
g_start_flag = 1;
}
开始播放需要有三个进程的
父进程创建子进程,子进程创建孙进程
父进程作用产生子进程,孙进程播放音乐
void play_music(const char *name)
{
pid_t child_pid = fork(); // 创建子进程
if (-1 == child_pid) //创建失败
{
perror("fork");
exit(1);
}
else if (0 == child_pid) //子进程
{
while (1) //子进程死循环
{
pid_t grand_pid = vfork(); //创建孙进程
if (-1 == grand_pid)
{
perror("fork");
exit(1);
}
else if (0 == grand_pid) //孙进程
{
shm s ;
char cur_name[64] = {0};
//获取共享内存
int shmid = shmget(SHMKEY, SHMSIZE, 0);
if (-1 == shmid)
{
perror("shmget");
exit(1);
}
//映射
void *addr = shmat(shmid, NULL, 0);
if (NULL == addr)
{
perror("shmat");
exit(1);
}
if (strlen(name) != 0) //直接开始播放
{
strcpy(cur_name, name);
}
else //遍历链表,找到一首歌播放
{
//判断播放模式,找到一首歌曲
memcpy(&s, addr, sizeof(s));
FindNextMusic(s.cur_name, s.play_mode, cur_name);
}
//把信息写入共享内存(父子孙进程号、当前歌曲名)
shm s;
strcpy(s.cur_name, name);
s.child_pid = getppid();
s.grand_pid = getpid();
memcpy(addr, &s, sizeof(s));
shmdt(addr); //解除映射
char music_path[128] = {0};
strcpy(music_path, MUSICPATH);
strcat(music_path, cur_name);
execl("/bin/madplay", "madplay", music_path, NULL);
}
else //子进程
{
memset((void *)name, 0, strlen(name)); //歌曲名长度变为0,方便下一次操作
int status;
waitpid(grand_pid, &status, 0); //回收孙进程
}
}
}
else
{
return;
}
}
因为涉及到上一首下一首,需要改变链表,上一首下一首,双向循环链表
link.h
#ifndef LINK_H
#define LINK_H
struct Node
{
char music_name[64];
struct Node *next;
struct Node *prior;
};
typedef struct Node Node;
int InitLink();
#endif
link.c
//初始化双向循环链表
int InitLink()
{
head = (Node *)malloc(sizeof(Node) * 1);
if (NULL == head)
{
return FAILURE;
}
head->next = head;
head->prior = head;
return SUCCESS;
}
int InsertLink(Node *h, const char *name)
{
if (NULL == h || NULL == name)
{
return FAILURE;
}
Node *end = h->prior;
Node *n = (Node *)malloc(sizeof(Node) * 1);
if (NULL == n)
{
return FAILURE;
}
n->next = h;
strcpy(n->music_name, name);
end->next = n;
n->prior = end;
h->prior = n;
return SUCCESS;
}
/*
函数描述:根据当前歌曲和播放模式,找到下一首歌
函数参数:cur:当前歌曲
mode:播放模式
next:存放下一首歌名
返回值:无
*/
void FindNextMusic(const char *cur, int mode, char *next)
{
if (mode == CIRCLE) //单曲循环,把当前音乐再赋值给下一首歌
{
strcpy(next, cur);
return;
}
else if (mode == SEQUENCEMODE)//循环播放
{
Node *p = head->next;//创建新节点指向头结点的下一个节点
while (strcmp(p->music_name, cur) != 0) //找到当前播放音乐
{
p = p->next;
}
if (p->next == head) //忽略头结点,指向头结点的下一个
{
strcpy(next, head->next->music_name);
}
else
{
strcpy(next, p->next->music_name);//将找到的下一首音乐的名字赋值给next
}
return;
}
else //上述模式都不是则是四级播放
{
Node *p = head->next;
srand(time(NULL)); //随机查找种子
int num = rand() % 100;
int i;
for (i = 0; i < num; i++)
{
p = p->next;
}
if (p == head)
{
strcpy(next, head->next->music_name);
}
else
{
strcpy(next, p->music_name);
}
return;
}
}
link.h
#ifndef LINK_H
#define LINK_H
struct Node
{
char music_name[64];
struct Node *next;
struct Node *prior;
};
typedef struct Node Node;
int InitLink();
void FindNextMusic(const char *cur, int mode, char *next);
int InsertLink(Node *h, const char *name);
#endif