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 进程实验原理:探索操作系统的核心秘密”的内容,你了解清楚吗?希望对你有所帮助,任何问题可以给我留言,让我们下期再见吧。