欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > IT业 > Linux系统调用编程

Linux系统调用编程

2025/4/10 19:34:08 来源:https://blog.csdn.net/Apple66666666666/article/details/146989336  浏览:    关键词:Linux系统调用编程

Linux系统调用编程

一、理解进程和线程的概念。并在Linux系统下:1) 用 ps -a 命令查看系统中各进程的编号pid ; 2) 用kill 命令终止一个进程pid。

进程

定义: 进程是操作系统进行资源分配和调度的基本单位。每个进程都有自己的内存空间、数据栈和其他用于跟踪执行状态的辅助数据。
特点:
独立性: 每个进程在内存中都有独立的地址空间,一个进程的崩溃不会影响其他进程。
并发性: 多个进程可以同时运行,操作系统通过时间片轮转等方式让它们看起来像是同时执行的。
资源分配: 进程是资源分配的基本单位,每个进程都有自己独立的资源,如内存、文件描述符等。

线程

定义: 线程是进程中的一个执行单元,是操作系统能够进行运算调度的最小单位。一个进程可以包含多个线程,这些线程共享进程的资源。
特点:
轻量级:线程比进程更轻量级,创建和切换线程的开销比进程小。
共享资源:同一进程中的线程共享进程的内存空间和其他资源,线程之间的通信更高效。
并发性:多个线程可以并发执行,提高程序的执行效率。
在Linux系统下操作

实践:
  1. 用 ps -a 命令查看系统中各进程的编号pid

在Linux系统中,ps命令用于显示当前运行的进程信息。ps -a命令会显示所有与终端会话关联的进程。
如果想要查看系统中所有的进程及其对应的PID(进程编号),可以使用以下命令:

ps aux

这里的参数解释如下:

  • a 显示所有与终端会话的进程。
  • u 以用户易读的格式显示进程信息。
  • x 显示没有控制终端的进程。

在这里插入图片描述
在这里插入图片描述

  1. 用kill命令禁止一个进程pid
    在这里插入图片描述

由于我不知道这些进程强制终止会不会对电脑上的程序或者应用有什么影响,所以就只展示一下kill的用法。

解释Linux的“虚拟内存管理”,它与stm32中的 真实物理内存(内存映射)有什么区别?

Linux的虚拟内存管理

Linux系统中的“虚拟内存管理”是一种内存管理技术,它允许操作系统为每个进程提供独立的虚拟地址空间。这种地址空间是连续的,并且对进程来说是私有的,即使多个进程可能映射到相同的物理内存区域。虚拟内存通过分页机制实现,将虚拟内存分割成固定大小的页,称为“页”(page),物理内存也同样划分为“页框”(page frame)。

Linux虚拟内存通过页表来完成虚拟地址到物理地址的映射,页表中的每一项包含了虚拟页到物理页框的映射信息。现代Linux内核使用多级页表来提高查找效率,如二级页表或四级页表,通过分级的方式减少查找开销。同时,还使用页表缓存(TLB,Translation Lookaside Buffer)来加速地址转换。TLB 是一块高速缓存,用于存储最近使用的页表项,减少对页表的访问次数。
在Linux系统中,内存被划分为多个区域,每个区域有不同的作用。其中,虚拟内存是一种计算机系统的内存管理技术,它扩展了系统可用内存的容量,并为每个进程提供了独立的地址空间。虚拟内存通过分页机制实现,将虚拟内存分割成固定大小的内存块,称为“页”(page),物理内存也同样划分为与页大小相同的“页框”(page frame)。当进程需要访问数据时,内核将虚拟地址的页映射到物理内存中的页框。例如,在32位系统中,页的大小通常为4KB,虚拟地址被分为高位部分(页号)和低位部分(页内偏移)。通过页表来完成虚拟地址到物理地址的映射,页表中的每一项包含了虚拟页到物理页框的映射信息。

虚拟内存的整体流程可分为4步:

  • CPU产生一个虚拟地址
  • MMU从TLB中获取页表,翻译成物理地址
  • MMU把物理地址发送给主存
  • 主存将地址对应的数据返回给CPU

虚拟内存的主要特点包括:

地址隔离:每个进程拥有独立的虚拟地址空间,避免了内存冲突。
内存保护:通过权限位保护内存区域,防止未授权访问。
共享内存:允许进程间共享代码或数据,如动态库。
交换:操作系统可以使用硬盘上的交换空间作为“扩展内存”。

与stm32中的 真实物理内存相比

与STM32中的物理内存(内存映射)相比,STM32通常没有MMU(内存管理单元)来实现虚拟地址到物理地址的映射。STM32是一种微控制器,其内存管理通常更直接,程序直接访问物理地址。

>STM32的内存管理通常包括:

直接物理映射:外设寄存器通过固定物理地址访问。
静态内存分配:无动态分页或交换机制。
资源受限:适用于资源受限的嵌入式系统。

理解 Linux系统调用函数 fork()、wait()、exec() 等的含义和调用方法 。

fork()函数

fork() 函数用于创建一个新的进程,称为子进程(child process),它是调用进程(parent process)的一个副本。

函数原型:

pid_t fork(void);

返回值:
返回新创建的子进程的PID给父进程。
返回0给子进程,表示它是一个新创建的进程。
如果出现错误,返回-1。

调用方法:

if (pid == -1) {// 出错处理perror("fork");exit(EXIT_FAILURE);
} else if (pid == 0) {// 子进程代码printf("This is the child process\n");
} else {// 父进程代码printf("This is the parent process, PID is %d\n", pid);wait(NULL); // 等待子进程结束
}
wait()函数

wait() 函数用于使父进程暂停执行,直到一个子进程结束。

函数原型

int wait(int *status);**返回值**:
- 成功时返回被等待的子进程的PID。
- 如果`status`不为空指针,返回0。
- 如果出错,返回-1。**调用方法**:
int status;
pid_t pid = wait(&status);
if (pid == -1) {// 出错处理perror("wait");exit(EXIT_FAILURE);
}
printf("Child process PID: %d has terminated\n", pid);
exec() 系列函数

exec() 系列函数用于替换当前进程映像为一个新的程序

函数原型(以execl()为例):

int execl(const char *path, char *const argv[], char *const envp[]);

参数:
path:要执行的程序的路径。
argv[]:程序参数数组。
envp[]:环境变量数组。
返回值:

  • 成功时返回0,新程序开始执行。
  • 如果出错,返回-1。

调用方法:

char *argv[] = {"ls", "-l", NULL};
char *envp[] = {"HOME=/usr", NULL};if (execl("/bin/ls", argv, envp) == -1) {perror("execl");exit(EXIT_FAILURE);
}
  • 使用fork()创建子进程后,通常需要使用wait()等待子进程结束,以避免产生僵尸进程。
  • exec()系列函数替换当前进程映像,原进程的PID不变,因此父进程可以等待并获取子进程的退出状态。
  • fork()和exec()通常结合使用,先创建子进程,然后在子进程中调用exec()执行新程序。
僵尸进程

僵尸进程(Zombie Process)是类Unix和类Unix操作系统中的一种进程状态。僵尸进程是一个已经完成执行,但其父进程尚未对其执行wait()系统调用,以读取它的退出状态并回收其占用的资源(如进程ID)。
僵尸进程的特征包括:
已完成:僵尸进程已经运行完毕,其任务已经结束。
等待父进程:僵尸进程的父进程尚未调用wait()或waitpid()函数来获取僵尸进程的退出状态并回收资源。
资源占用:僵尸进程不再占用CPU时间,但它仍然占用着一些系统资源,如进程描述符(PID)等,直到其父进程调用wait()或waitpid()。
僵尸进程产生的原因通常是由于父进程没有适当地处理子进程的退出状态。如果父进程不调用wait()或waitpid(),僵尸进程将一直存在,占用系统资源。
处理僵尸进程的方法:
父进程调用wait():父进程应调用wait()或waitpid()来获取子进程的退出状态,并回收其占用的资源。
使用waitpid():父进程可以调用waitpid()并传递一个负的PID值,以等待任意子进程结束。
使用nohup选项:在创建子进程时,父进程可以设置子进程的SIGCHLD信号处理程序为nohup(忽略),这样子进程结束后会自动被系统回收。
使用init进程:init进程(PID为1)会自动回收所有孤儿进程(没有父进程的进程)。
僵尸进程虽然不占用CPU资源,但会占用系统资源,如进程描述符等。在系统资源有限的情况下,过多的僵尸进程可能会影响系统性能。因此,正确管理子进程的生命周期,及时回收资源是非常重要的。

实践

用putty或xterminal等工具软件,远程登录自己被分配的阿里云服务器Ubuntu系统的账号,在自己的home目录下,创建一个作业子目录,然后学习使用vi 编辑一个c代码,gcc编译,实现一个系统调用函数的例子

创建diaoyongfork.c


#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>
int main(){
pid_t pid;
int ret = 1;
int status;
pid = fork();
if (pid == -1){// pid == -1 表示发生了错误printf("can't fork, error occured\n");exit(EXIT_FAILURE);
}
else if (pid == 0){// pid == 0 表示创建了子进程// getpid() 返回调用进程的进程id// 返回子进程的进程idprintf("child process, pid = %u\n",getpid());// 返回子进程的父进程,即父进程本身printf("parent of child process, pid = %u\n",getppid());// argv list第一个参数应该指向// 与正在执行的文件关联的文件名// 数组指针必须以NULL结尾// 指针char * argv_list[] = {"ls","-lart","/home",NULL};// execv()仅在发生错误时返回。// 返回值为-1execv("ls",argv_list);exit(0);
}
else{// 为pid返回一个正数// 父进程// getppid()返回进程的父进程id// 调用进程// 返回父进程ID的父进程printf("Parent Of parent process, pid = %u\n",getppid());printf("parent process, pid = %u\n",getpid());// 父进程对子进程调用waitpid()// waitpid()系统调用暂停// 调用进程直到pid指定的子进程// 状态改变// 有关所有标志或选项,请参见wait()手册if (waitpid(pid, &status, 0) > 0) {if (WIFEXITED(status) && !WEXITSTATUS(status))printf("program execution successful\n");else if (WIFEXITED(status) && WEXITSTATUS(status)) {if (WEXITSTATUS(status) == 127) {// execv failedprintf("execv failed\n");}elseprintf("program terminated normally,"" but returned a non-zero status\n");          }elseprintf("program didn't terminate normally\n");      }else {// waitpid() failedprintf("waitpid() failed\n");}exit(0);
}
return 0;
}

在这里插入图片描述

*在各小组的树莓派中,也手动创建组员的账号,组员在各自的账号+home目录下,完成同样的Linux系统调用函数练习。
*
在这里插入图片描述

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com