Linux 进程实验原理:探索操作系统的核心秘密

背景与目的
在现代计算机科学中,操作系统是系统软件的基础,它负责管理和协调计算机硬件与软件资源,Linux作为一种广泛应用的开源操作系统,因其稳定性和灵活性被广泛使用,理解Linux操作系统中的进程管理机制对于深入学习操作系统原理至关重要,本文将通过一系列实验,探讨Linux系统中的进程创建、通信及调度等核心概念,旨在加深对操作系统内部工作机制的理解,明确进程与程序的区别,并掌握相关系统调用的使用。
进程控制基础
fork()系统调用
fork() 是UNIX/Linux环境下用于创建新进程的主要系统调用,格式如下:
pid_t fork(void);
返回值:
对于父进程,返回子进程的PID。
对于子进程,返回0。
出错时,返回1。
fork()调用完成后,系统中存在两个几乎完全相同的进程,它们有着相同的用户级环境,但内核级环境有所不同,子进程会获得一个新的进程ID(PID)和父进程ID(PPID)。
示例代码:
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t pid = fork();
if (pid < 0) {
// Fork failed
perror("fork failed");
return 1;
} else if (pid == 0) {
// This is the child process
printf("This is the child process, my PID is %d
", getpid());
} else {
// This is the parent process
printf("This is the parent process, the child's PID is %d
", pid);
wait(NULL); // Wait for the child to complete
}
return 0;
}
运行结果展示了父进程和子进程分别输出的信息,验证了fork()的工作原理。
exec()系统调用
exec()系列函数用于执行一个新程序,替换当前进程的地址空间,常用的有execl(),execp(),execle(),execv(),execvp(), 和execve(),以execl()为例,其格式为:
int execl(const char *path, const char *arg, ... /*, (char *) NULL */);
参数:
path: 要执行的程序路径。
arg: 传递给新程序的参数列表。
最后一个参数必须用(char *) NULL
示例代码:
#include <unistd.h>
int main() {
execl("/bin/ls", "ls", "l", (char *) NULL);
return 0; // This will not be executed unless execl fails
}
该程序会调用/bin/ls命令列出当前目录的内容,如果execl()成功,当前进程的映像段将被新的程序替换,不会返回到原程序。
exit()系统调用
exit()用于终止进程执行,并返回一个状态码给其父进程,格式为:
void exit(int status);
参数:
status: 返回给父进程的状态码。
示例代码:
#include <stdio.h>
#include <unistd.h>
int main() {
printf("This is a process with PID %d
", getpid());
exit(0);
}
当调用exit()后,进程会终止,并返回状态码给其父进程。
wait()系统调用
wait()使父进程挂起,直到任意一个子进程结束,格式为:
pid_t wait(int *status);
参数:

status: 存储子进程的退出状态。
返回值:
成功时返回子进程的PID。
失败时返回1。
示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
pid_t pid = fork();
if (pid < 0) {
perror("fork failed");
exit(1);
} else if (pid == 0) {
printf("This is the child process, my PID is %d
", getpid());
exit(0);
} else {
int status;
wait(&status); // Wait for the child to complete
if (WIFEXITED(status)) {
printf("Child exited with status %d
", WEXITSTATUS(status));
}
}
return 0;
}
这段代码展示了父进程如何等待子进程完成并获取其退出状态。
与步骤
实验一:进程的创建与销毁
实验目的:
了解fork()系统调用的基本用法。
观察父进程和子进程之间的关系。
掌握进程的创建和终止操作。
实验步骤:
1、编写一个简单的C程序,使用fork()创建一个子进程。
2、在子进程中输出当前进程的PID和父进程的PID。
3、在父进程中等待子进程结束,并输出子进程的退出状态。
4、编译并运行程序,记录输出结果。
实验代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
pid_t pid = fork();
if (pid < 0) {
perror("fork failed");
exit(1);
} else if (pid == 0) {
// Child process
printf("Child process: PID = %d, PPID = %d
", getpid(), getppid());
exit(0); // Terminate child process normally
} else {
// Parent process
int status;
waitpid(pid, &status, 0); // Wait for child to finish
if (WIFEXITED(status)) {
printf("Parent process: Child exited with status %d
", WEXITSTATUS(status));
} else {
printf("Parent process: Child terminated abnormally
");
}
}
return 0;
}
预期结果:
父进程输出子进程的PID和退出状态。
子进程输出自身的PID和父进程的PID。
实验二:进程的管道通信
实验目的:
了解pipe()系统调用的基本用法。
掌握父子进程之间的管道通信机制。
熟悉管道的读写操作。

实验步骤:
1、创建一个管道,使用pipe()系统调用。
2、使用fork()创建一个子进程。
3、父进程关闭管道的读端,子进程关闭管道的写端。
4、父进程从管道写入数据,子进程从管道读取数据。
5、编译并运行程序,记录输出结果。
实验代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
int main() {
int pipefd[2]; // Array to hold the file descriptors for the pipe
pid_t pid;
char buffer[128]; // Buffer to store data read from the pipe
const char *msg = "Hello from your parent!";
// Create a pipe
if (pipe(pipefd) == 1) {
perror("pipe");
exit(1);
}
// Create a child process
pid = fork();
if (pid == 1) {
perror("fork");
exit(1);
} else if (pid == 0) {
// Child process: close the write end of the pipe, read from the pipe
close(pipefd[1]); // Close the write end of the pipe in the child process
read(pipefd[0], buffer, sizeof(buffer)); // Read data from the pipe
printf("Child process received: %s
", buffer); // Print the message received from the parent process
close(pipefd[0]); // Close the read end of the pipe after reading is done
exit(0); // Terminate child process normally
} else {
// Parent process: close the read end of the pipe, write to the pipe
close(pipefd[0]); // Close the read end of the pipe in the parent process
write(pipefd[1], msg, strlen(msg) + 1); // Write data to the pipe
close(pipefd[1]); // Close the write end of the pipe after writing is done
wait(NULL); // Wait for the child process to finish before terminating the parent process
}
return 0;
}
预期结果:
父进程向管道写入消息“Hello from your parent!”。
子进程从管道读取消息并打印出来。
父进程等待子进程结束后再终止。
实验三:进程的竞争与同步(选做)
实验目的:
了解进程间的竞争现象。
掌握使用信号量进行进程同步的方法。
熟悉POSIX信号量的基本操作。
实验步骤:
1、创建一个共享资源(如计数器)。
2、使用fork()创建多个子进程。
3、使用信号量确保每次只有一个子进程能访问共享资源。
4、编译并运行程序,记录输出结果。
实验代码:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/ipc.h> #include <sys/sem.h> #include <sys/types.h> #include <sys/wait.h> #include <errno.h> #include <semaphore.h> #include <fcntl.h> #include <sys/stat.h> #include <signal.h> #include <time.h> #include <string.h> #include <stdbool.h> #include <limits.h> #include <bits/signum.h> #include <bits/mman.h> #include <bits/mqueue.h> #include <bits/time.h> #include <bits/resource.h> #include <bits/select.h> #include <bits/socket.h> #include <bits/sendfile.h> #include <bits/sem.h> #include <bits/shared_coredump.h> #include <bits/timex.h> #include <bits/uio.h> #include <bits/uio_prnt.h> #include <bits/sockaddr.h> #include <bits/sockopt.h> #include <bits/termios.h> #include <bits/term_ioctl.h> #include <bits/ttycom.h> #include <bits/ucred.h> #include <bits/uio_prnt.h> #include <bits/utmp.h> #include <bits/utmp.h> #include <bits/utmpx.h> #include <bits/utmpx.h> #include <bits/wchar.h>
小伙伴们,上文介绍了“Linux 进程实验原理:探索操作系统的核心秘密”的内容,你了解清楚吗?希望对你有所帮助,任何问题可以给我留言,让我们下期再见吧。














