子进程调用mpg123播放歌曲


程序要求:父进程通过管道将mp3文件传送给子进程,子进程调用 mpg123 播放器来播放该歌曲。

mpg123 安装过程(Ubuntu 20.04):

1
$ sudo apt-get install mpg123

程序编写思路:

  1. 父进程打开指定路径的歌曲;
  2. 创建一个管道;
  3. 创建一个子进程;
  4. 子进程关闭管道的写端,将其标准输入重定向为管道的读端,然后调用 mpg123 播放器从标准输入播放音乐
  5. 父进程关闭管道的读端,从文件中读取歌曲数据并写入到管道的写端;
  6. 当所有数据都写入后,父进程关闭管道的写端和文件,并等待子进程完成。

程序源文件 player.c 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>

#define BUFSIZE 4096

int main(int argc, char *argv[]) {
int fd, pipefd[2];
pid_t pid;
char buf[BUFSIZE];
ssize_t ret;

if(argc != 2)
{
fprintf(stderr, "Usage: %s <path_to_mp3_file>\n", argv[0]);
exit(1);
}

fd = open(argv[1], O_RDONLY);
if(fd == -1)
{
perror("open()");
exit(1);
}

if(pipe(pipefd) == -1)
{
perror("pipe()");
exit(1);
}

pid = fork();
if(pid < 0)
{
perror("fork()");
exit(1);
} else if(pid == 0) { // child process
close(pipefd[1]); // close the write end of the pipe
dup2(pipefd[0], STDIN_FILENO); // redirect stdin to read end of the pipe
execlp("mpg123", "mpg123", "-", NULL); // play from stdin ("-")
perror("execlp()"); // only gets here if there's an error
exit(1);
} else { // parent process
close(pipefd[0]); // close the read end of the pipe
while((ret = read(fd, buf, BUFSIZE)) > 0) {
write(pipefd[1], buf, ret);
}
close(pipefd[1]);
close(fd);
wait(NULL); // wait for the child to finish
}

exit(0);
}

Makefile 文件如下:

1
2
3
4
player:player.o
gcc $^ -o $@
clean:
rm -rf *.o player

编译运行结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
$ make
cc -c -o player.o player.c
gcc player.o -o player
$ ./player apologize.mp3
High Performance MPEG 1.0/2.0/2.5 Audio Player for Layers 1, 2 and 3
version 1.25.13; written and copyright by Michael Hipp and others
free software (LGPL) without any warranty but with best wishes

Playing MPEG stream 1 of 1: - ...

MPEG 1.0 L III vbr 48000 j-s

可以正常播放该歌曲。