【操作系统】课堂问题记录(PCB进程控制块,syscall,getpid)
文章目录
1.【PCB进程控制块】设计实验验证把同一个程序同时运行两次,拥有两个进程号。
结论:当一个程序同时运行两次时,系统会分配两个不同的进程号(PID)
实验步骤:
(1)编写程序。
编写一个简单的程序,并让其保持运行一段时间。
import time
print("HELLO!I AM STILL RUNNING!")
time.sleep(120)
#设置运行120秒
(2)程序启动两次:在终端/命令行中,分别启动两次该程序。打开两个终端窗口,分别输入以下命令:
python your-filename.py
(3)查看进程号(PID)
可用以下命令查看正在运行的程序的PID。
在Linux或MacOS中使用ps命令
ps aux | grep your-filename.py
在Windows中可以使用Task Manger 或者tasklist命令
tasklist | findstr your-filename.py
(4)观察输出
【因为我使用的是Windows,所以使用的是tasklist】
在输出中可以看到,同一个程序的多个实例,每个实例有不同的进程号。例如:
在这个例子中,PID分别为21592和24324,验证了同一个程序在同时运行时被分配了不同的PID。
2.syscall 可调用的参数有哪些?
在操作系统中,系统调用(syscall)是用户程序与操作系统内核交互的主要方式。每个系统调用都有自己的编号和对应的一组参数,参数的类型和数量取决于具体的系统调用。通常来说,系统调用的参数主要包括以下几种类型:
-
文件描述符(File Descriptor):
用于标识一个打开的文件。很多与文件操作相关的系统调用都需要传递文件描述符作为参数,例如 read, write, open, close 等。 -
内存地址(Memory Address):
通常是指向数据缓冲区的指针。比如 read 和 write 系统调用需要传递一个指向缓冲区的指针,用来读入或写出数据。 -
大小(Size):
表示操作的数据大小。比如 read 和 write 需要传递一个整数参数来指明要读/写的数据大小。 -
标志(Flags):
用于控制系统调用的行为。比如 open 系统调用中传递的标志参数可以决定文件是以读、写还是追加的方式打开。 -
路径名(Pathname):
文件操作系统调用通常需要传递文件路径名作为参数,例如 open, unlink, rename 等。 -
模式(Mode):
指定文件权限或者操作模式。比如在 open 系统调用中,可以传递一个模式参数来指定新文件的权限。 -
结构体(Structs):
某些复杂系统调用可能需要传递结构体参数,如 stat 需要一个结构体来存储文件属性信息。 -
整数或标识符(Integer or Identifier):
例如进程 ID、信号编号、用户或组 ID、网络套接字的端口号等。
[!NOTE]
示例
以 read 系统调用为例:
ssize_t read(int fd, void *buf, size_t count);
fd: 文件描述符,标识要读取的文件。
buf: 缓冲区指针,用于存储读取的数据。
count: 读取的字节数。
注意哦,不同操作系统(如 Linux, Windows, macOS)的系统调用接口会有一些差异,因此要具体查看相应系统的文档来了解每个系统调用的参数细节。
在 Linux 中,syscall 的典型用法如下:
#include <unistd.h> // 包含 syscall 函数的声明。
#include <sys/syscall.h> //定义了系统调用号(如 SYS_write)
#include <stdio.h>
int main() {
long result;
// 使用 syscall 直接调用 write 系统调用
result = syscall(SYS_write, STDOUT_FILENO, "Hello, syscall!\n", 16);
if (result == -1) {
perror("syscall");
return 1;
}
return 0;
}
1.返回值:
syscall 的返回值通常是系统调用的返回值。对于大多数系统调用,返回值为 -1 表示失败,成功时返回依赖于具体的系统调用(如读取的字节数、写入的字节数等)。
2.错误处理:
使用 perror 打印错误信息,如果系统调用返回 -1,表示调用失败。
[!IMPORTANT]
syscall 使用的注意事项
参数顺序和类型: 系统调用的参数顺序必须严格按照系统调用的定义传递,并且类型必须匹配。错误的参数会导致系统调用失败,甚至引发程序崩溃。
系统调用号: 每个系统调用都有一个唯一的系统调用号(在 sys/syscall.h 中定义)。不同的 Linux 内核版本可能会有不同的系统调用号定义,确保使用的系统调用号是当前内核版本支持的。
系统调用限制: 并不是所有系统调用都可以通过 syscall 来调用。一些特定的系统调用可能需要特殊的权限(如 root 权限)或者有特定的限制。
性能考虑: 直接使用 syscall 可以绕过标准库层的包装,可能带来一定的性能提升,但代价是代码的可读性和可维护性下降。通常只有在标准库不能满足需求时才直接使用 syscall。
syscall 的典型应用场景
-
标准库函数不可用:某些系统调用(如某些特定的 ioctl 调用)没有被封装在标准库中,可以通过 syscall 直接调用。
-
性能优化:避免标准库函数的额外开销,直接调用系统调用来减少不必要的封装层。
-
系统级别开发:如编写操作系统、驱动程序、系统监控工具等,需要精确控制系统调用的使用场景。
3.syscall—— getpid()的使用
getpid 是一个系统调用函数,用于获取当前进程的进程ID(PID)。进程ID是操作系统用来唯一标识每个进程的整数值。它在多进程编程中非常有用,可以用来区分和管理不同的进程。
getpid 的使用方法
- 使用标准库函数 getpid
#include <unistd.h> //include <unistd.h>: 头文件包含了 getpid 函数的声明。
#include <stdio.h>
int main() {
pid_t pid; //pid_t pid: pid_t 是一个数据类型,用于表示进程ID。
// 获取当前进程的 PID
pid = getpid(); //getpid(): 调用 getpid 函数,返回当前进程的进程ID。
// 输出当前进程的 PID
printf("Current process ID: %d\n", pid); //printf("Current process ID: %d\n", pid): 输出进程ID。
return 0;
}
- 使用 syscall 调用 getpid
在一些情况下,可以通过 syscall 函数来调用 getpid,特别是当你想要避免使用标准库时。
#include <sys/syscall.h> // 包含 SYS_getpid 的定义,这是 getpid 的系统调用号。
#include <unistd.h>
#include <stdio.h>
int main() {
pid_t pid;
// 使用 syscall 直接调用 getpid 系统调用
pid = syscall(SYS_getpid); // 通过 syscall 函数直接调用 getpid 系统调用。
// 输出当前进程的 PID
printf("Current process ID using syscall: %d\n", pid);
return 0;
}
getpid 的应用场景
- 多进程编程:
在多进程环境中使用 fork 创建子进程时,父进程和子进程的 PID 不同。可以使用 getpid 来区分它们。
#include <unistd.h>
#include <stdio.h>
int main() {
pid_t pid = fork();
if (pid == 0) {
// 子进程
printf("Child process ID: %d\n", getpid());
} else {
// 父进程
printf("Parent process ID: %d\n", getpid());
}
return 0;
}
以上代码中,fork 创建一个子进程,子进程和父进程都会执行 getpid。getpid 会返回当前进程的PID,父进程和子进程的PID不同,因此可以通过它来区分父进程和子进程。
-
进程管理:
在进程管理工具或监控工具中,通过 getpid 可以获取当前进程的 PID,然后将其用于进程管理(如 kill 某个进程、设置某个进程的优先级等)。 -
日志记录:
通过 getpid 记录当前进程的 PID,可以帮助开发者在日志中跟踪是哪一个进程在执行某个操作,特别是在多进程或多线程程序中。
总结来说,getpid 是一个简单而有效的函数,在多进程编程、进程管理和调试中非常有用。它的使用方法也非常直观,通常直接调用即可。
课堂上的疑问解决!!(◍´ಲ`◍)
希望可以帮到大家。 ( ͡◉ ͜ʖ ͡◉)