Bootstrap

基于linux多线程的MP3播放器小程序

一、简介

1.功能介绍

        利用对进程操作实现对音乐的歌曲搜索、暂停、继续、上一首、下一首等功能操作。

2.程序逻辑

        在执行程序是,对主进程创建子进程,其中主进程的作用是功能的选择,子进程的作用是歌曲播放。

二、主要功能程序

        fork函数

        是Unix/Linux操作系统中的一个系统调用,用于创建一个新进程。调用fork函数后,操作系统会复制当前进程的所有信息,包括进程代码、数据、堆栈等,创建一个新的进程,称为子进程,子进程的初始状态和父进程完全相同。

fork函数的用法如下:

pid_t fork(void);

        其中,返回值为pid_t类型,表示子进程的进程ID。在父进程中,返回子进程的进程ID;在子进程中,返回0。如果调用fork函数失败,返回-1。

        父进程和子进程在fork函数返回后,执行的代码是不同的。父进程会继续执行fork函数后面的代码,而子进程则从fork函数返回并开始执行自己的代码。

通过fork函数可以实现多进程编程,可以利用多核CPU并行处理任务,提高程序的性能。

        signal函数

是一个用于处理信号的函数,它可以指定当某个信号发生时,应该执行什么操作或者忽略该信号。signal函数的原型如下:

#include <signal.h>

void (*signal(int sig, void (*func)(int)))(int);

其中,sig参数指定信号的类型,而func参数则是一个指向信号处理函数的指针,它将被用来处理指定的信号。

常见的信号包括:

  • SIGINT:中断信号,通常由用户按下Ctrl+C触发。
  • SIGTERM:终止信号,通常用于请求程序结束。
  • SIGKILL:强制终止信号,该信号无法被阻塞或处理。
  • SIGSEGV:段错误信号,通常表示程序访问了未分配的内存或者越界访问了已分配的内存。

当信号发生时,操作系统会将该信号发送给程序。如果程序注册了信号处理函数,则该函数将被调用来处理信号。否则,操作系统将使用默认的信号处理方式来处理该信号,通常是结束程序或者忽略该信号。

在编写程序时,我们可以使用signal函数来注册自定义的信号处理函数,以便程序能够更好地处理各种异常情况。

        glob_t是一个结构类型

定义在头文件glob.h中。它用于在特定路径下搜索匹配某个模式的文件或目录。

glob_t结构包含以下成员:

  1. gl_pathc:匹配到的文件或目录的数量。
  2. gl_pathv:一个指向匹配到的文件或目录名的指针数组。
  3. gl_offs:指定gl_pathv数组中的偏移量,以便于您在数组中插入其他文件或目录名。
  4. gl_flags:指定glob()函数的行为标志。
  5. gl_closedir_func:指向一个可选的函数,用于关闭打开的目录流。
  6. gl_readdir_func:指向一个可选的函数,用于调用readdir()函数来读取目录项。

您可以使用glob()函数来填充glob_t结构。

以下是一个使用glob_t结构在特定路径下搜索匹配某个模式的文件或目录的示例:

#include <stdio.h>
#include <glob.h>

int main()
{
    glob_t paths;
    int i;

    glob("/path/to/dir/*", GLOB_NOSORT, NULL, &paths);

    printf("%d file(s) found:\n", paths.gl_pathc);

    for (i = 0; i < paths.gl_pathc; i++)
        printf("%s\n", paths.gl_pathv[i]);

    globfree(&paths);
    return 0;
}

在上面的示例中,glob()函数将匹配到的文件或目录名存储在paths结构中,然后我们遍历该结构,输出匹配到的文件或目录名。最后,我们使用globfree()函数释放paths结构中的内存。

        kill函数

是一个用于向指定进程发送信号的函数。该函数可以用于终止进程、停止进程、重新启动进程等操作。具体介绍如下:

函数原型:

#include <signal.h>

int kill(pid_t pid, int sig);

参数说明:

  • pid:指定要发送信号的进程ID,可以是自己或其他进程的ID。
  • sig:指定要发送的信号编号,常用的信号有SIGTERM、SIGKILL、SIGSTOP等,具体信号编号可参考signal.h头文件中的宏定义。

返回值:成功返回0,失败返回-1。

函数说明:

  • 进程不能向自己发送SIGKILL信号,因为该信号会立即终止进程。
  • 可以通过pid参数指定一个进程组ID(将pid值设为负数),这样就可以向该进程组中的所有进程发送信号。
  • 对于不同的信号,进程会有不同的处理方式,例如SIGTERM信号会让进程正常退出,而SIGKILL信号则会强制进程立即终止。
  • 可以通过signal函数为进程自定义处理指定信号的方式。
atexit函数

是C/C++标准库中的一个函数,用于在程序退出时自动执行一个函数。该函数可以注册多个退出处理程序,这些程序会按照注册的顺序倒序执行。atexit函数的原型如下:

int atexit(void (*function)(void));

其中function为要注册的退出处理程序的函数指针,返回值为0表示注册成功,-1表示失败。

当程序正常退出、调用exit函数、或main函数返回时,atexit函数注册的所有处理程序会被自动调用。该函数通常用于释放在程序运行期间动态分配的内存或者关闭打开的文件等资源。

 

chdir函数

是C语言中的一个库函数,用于改变当前工作目录。

其原型为:

int chdir(const char *path);

参数path为要改变到的目录的路径名。函数执行成功时返回0,执行失败时返回-1,并设置errno变量。

例如,以下代码将当前工作目录更改为"/home/user/Documents":

#include <unistd.h>
#include <stdio.h>
#include <errno.h>

int main() {
    if (chdir("/home/user/Documents") == -1) {
        printf("Failed to change directory: %s", strerror(errno));
        return 1;
    }
    printf("Current working directory: %s", getcwd(NULL, 0));
    return 0;
}

 三、代码如下

main.c
#include<stdio.h>
#include <sys/wait.h>
#include <stdlib.h>
#include"main.h"
#include<signal.h>
#include<unistd.h>
#include <glob.h>
#include <unistd.h>
#include <sys/types.h>	
#include <dirent.h>
pid_t pd;
int num=0;//用来指定播放的歌曲
char flag='0';//用来判断是否退出的标志
glob_t glob_result;
void my()
{                                     
	waitpid(pd,NULL,WNOHANG);//等待回收子进程,不阻塞
	globfree(&glob_result);                //释放
	system("stty echo");        //终端回显
	printf("\e[?25h");     //光标回显
}
void myfun(int sum)//提供给signal函数调用
{
	int status;
	if(waitpid(pd,&status,WNOHANG)==0)//等待回收子进程,如果子进程结束回收返回子进程pid,如果子进程未结束,则不能回收,返回0,子进程暂停和继续不会被回收返回的结果为0
	{
		return;
	}
	if(WIFEXITED(status))//判断回收的子进程是自然结束还是父进程迫使子进程结束自然结束为真,否则为假
	{
		num++;
	}
	if(flag=='1')
	{
		return;
	}
	if(num==glob_result.gl_pathc||num<0) //防止播放的歌曲数组越界
	{
		num=0;
	}
	pd=fork();//子进程被回收完后,重新创建
	if(pd==0)
	{
		play(glob_result,num);
	}
}

int main(int argv,char *argc[])
{
	chdir(argc[1]);//切换目录
    	int result = glob("*.mp3", 0, NULL, &glob_result);//对.mp3文件进行匹配存放
   	if (result == 0) {
		printf("歌曲列表\n");
        	for (unsigned int i = 0; i < glob_result.gl_pathc; ++i)
		{
        		printf("%s\n", glob_result.gl_pathv[i]);
       		}
	}
	else {
        	printf("No matching files found.\n");
		return -1;
       	}
	pd=fork();//创建子进程
	if(pd<0)
	{
		perror("fork");
		return -1;
	}
	if(pd==0)
	{
		play(glob_result,num);//播放功能
	}
	if(pd>0)
	{
		atexit(my);//钩子函数,父进程结束之前执行
		signal(SIGCHLD,myfun);//子进程在结束时会给父进程发送一个信号,父进程接收到进行函数调用
		while(1)
		{
			printf("请选择功能:\n");
			printf("1、查找歌曲 2、下一首 3、上一首 4、暂停 5、继续 0、退出\n");
			flag=getchar();
			getchar();
			switch(flag)
			{
				case '1':
					num=find(glob_result);//查找歌曲功能
					if(num!=-1)
					{
						kill(pd,SIGKILL);//给子进程传输结束信号,让子进程结束
					}
					break;
				case '2':
					num==(glob_result.gl_pathc-1)?(num=0):num++;
					kill(pd,SIGKILL);
					break;
				case '3':
					num==0?(num=(glob_result.gl_pathc-1)):(num--);
					kill(pd,SIGKILL);
					break;
				case '4':
					printf("暂停成功\n");
					kill(pd,SIGSTOP);//子进程暂停
					break;
				case '5':
					printf("继续播放\n");
					kill(pd,SIGCONT);
					break;
				case '0':
					flag='1';
					kill(pd,SIGKILL);
					return 0;
				default:
					break;
			}
		}
	}
	return 0;
}
play.c
#include<stdio.h>
#include <stdlib.h>
#include<unistd.h>
#include <glob.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
int play(glob_t glob_result,int x)
{
	printf("正在播放%s\n",glob_result.gl_pathv[x]);
  	execlp("mpg123","mpg123","-q",glob_result.gl_pathv[x],NULL);//
	return 0;
}
select.c
#include<stdio.h>
#include <stdlib.h>
#include<unistd.h>
#include <glob.h>
#include"main.h"
#include <unistd.h>
#include <sys/types.h>
int find(glob_t glob_result)
{
	char music[100];
	char flag[100];
	sprintf("请输入要查找的歌曲\n");
	scanf("%s",flag);
	sprintf(music,"%s.mp3",flag);
	for (unsigned int i = 0; i < glob_result.gl_pathc; ++i)
        {
		if(strcmp(music,glob_result.gl_pathv[i])==0)
		{
			printf("该歌曲存在\n");
			printf("%s\n", glob_result.gl_pathv[i]);
			printf("是否播放\n");
			printf("1、播放 0、退出\n");
			int y=0;
			scanf("%d\n",&y);
			switch(y)
			{
				case 1:
					play(glob_result,i);
					return 0;
				case 0:
					return 0;
			}
		}	
        }
	printf("无此歌曲\n");
	return 0;
}
main.h
#ifndef MAIN_H
#define MAIN_H
#include <glob.h>
int play(glob_t glob_result,int x);
int find(glob_t glob_result);
#endif

;