当前位置: 首页 > news >正文

2 如何写一份详细的网站开发方案北京网站建设哪家强

2 如何写一份详细的网站开发方案,北京网站建设哪家强,wordpress电脑手机插件,互联网设计一般是什么专业Linux使用C语言获取进程信息 Author: OnceDay Date: 2024年2月22日 漫漫长路#xff0c;才刚刚开始… 全系列文章可查看专栏: Linux实践记录_Once_day的博客-CSDN博客 参考文档: Linux proc目录详解_/proc/mounts-CSDN博客Linux下/proc目录介绍 - 知乎 (zhihu.com)Linux内…Linux使用C语言获取进程信息 Author: OnceDay Date: 2024年2月22日 漫漫长路才刚刚开始… 全系列文章可查看专栏: Linux实践记录_Once_day的博客-CSDN博客 参考文档: Linux proc目录详解_/proc/mounts-CSDN博客Linux下/proc目录介绍 - 知乎 (zhihu.com)Linux内核通信之—proc文件系统详解 - 知乎 (zhihu.com)Linux /proc目录详解 - 滴水瓦 - 博客园 (cnblogs.com)Linux之(16)程序管理-CSDN博客 文章目录 Linux使用C语言获取进程信息1. Linux进程概述1.1 进程介绍1.2 介绍/proc目录信息1.3 常用进程相关命令 2. C语言编码获取进程信息2.1 读取comm线程名字2.2 读取cmdline命令行文本2.3 读取exe可执行程序路径2.4 如何判断内核进程2.5 修改comm和cmdline信息2.6 实现一个简易的killall工具 附录附录1: opendir和readdir函数简介附录2: 介绍fread函数附录3: 介绍fgets函数附录4: 介绍readlink函数附录5: 介绍kill函数 1. Linux进程概述 1.1 进程介绍 在计算机的世界里Linux进程是一个非常基础而且关键的概念。它可以被理解为正在执行的一个程序的实例。每个进程都有自己独特的身份我们称之为进程IDPID就像每个人都有自己的身份证号码一样。Linux操作系统是一种多任务操作系统可以同时运行多个进程就像一个杂技团队能同时上演多个节目一样。 现在想象一下进程是厨房里的一个厨师而计算机资源如CPU、内存则是厨房里的炉子、锅碗瓢盆。每个厨师都需要这些资源来完成他们的烹饪任务。Linux系统就像一个细心的餐厅经理确保所有厨师轮流合理使用这些资源并监督他们的工作进度。 首先进程的创建通常通过fork()系统调用完成。这个调用会创建一个与当前进程几乎完全相同的副本包括代码、数据和堆栈但拥有新的进程标识符PID。这个新进程可以继续执行或者通过exec()系列函数来加载新的程序。 进程有不同的状态包括运行正在使用CPU、等待等待资源可用、停止被挂起和僵尸已完成执行但等待父进程读取状态的进程。就像厨师有时在炒菜有时在等待食材有时在休息有时则在等待经理来检查他们做的菜。 Linux提供了很多管理进程的工具如ps命令可以查看当前的进程top命令可以实时监控进程的状态而kill命令可以用来结束某个进程。这就好比厨房里有各种管理工具可以查看厨师的工作列表监控他们的工作状态或者让某个厨师停止当前的工作。 进程间通信IPC是Linux系统中进程协作的重要机制。常见的IPC方式有管道pipe、命名管道named pipe、信号signal、共享内存shared memory、消息队列message queue和套接字socket。这些机制允许进程间传递数据和同步操作。 进程的同步主要通过信号量semaphore和互斥锁mutex来实现。信号量用于控制对共享资源的访问而互斥锁则确保同一时间只有一个进程可以访问某个资源。 进程的终止可以通过多种方式如正常退出、被其他进程杀死通过发送信号、或系统强制终止。进程结束后其资源会被回收但进程的退出状态exit status会被保存这对于调试和错误处理非常重要。 在Linux中进程管理还涉及到进程的优先级nice value和实时调度。优先级决定了进程获取CPU时间片的优先程度而实时调度则允许进程以更高的优先级运行通常用于需要快速响应的任务。 最后Linux系统还支持进程的层级关系也就是父子进程关系。当一个进程创建一个新进程时原来的进程就是父进程新创建的就是子进程。这有点儿像家族企业父亲开了一家餐馆后孩子长大也可能在旁边开一家小吃店。 了解Linux进程是掌握操作系统和进行系统编程的基础。虽然这是一门复杂的技术但它对于运行和管理Linux系统至关重要。不同的进程就像社会中不同的个体它们相互独立却又相云协作共同构成了一个运作高效的系统。 1.2 介绍/proc目录信息 在Linux操作系统中/proc目录是一个非常特殊和有趣的存在它实际上并不是存储在硬盘上的真实文件系统而是一个虚拟文件系统通常被称为进程信息伪文件系统Process Information Pseudo-File System。/proc提供了一个窗口通过它可以窥见内核中的世界包括系统信息及正在运行的进程详情。 /proc目录中的文件和子目录大都以数字命名这些数字对应着系统中的进程IDPID。每一个这样的目录里面包含了与该PID相关的信息例如进程的内存映射(/proc/pid/maps)、环境变量(/proc/pid/environ)、可执行文件的链接(/proc/pid/exe)等等。这些文件为系统管理员和程序员提供了一种简便的方式来监控进程和系统的内部状态。 除了进程相关信息/proc目录还包含了许多全系统范围的信息。例如/proc/meminfo文件包含了内存使用情况的详细信息/proc/cpuinfo提供了CPU的相关信息/proc/net目录包含了网络协议和配置的信息等等。 值得一提的是/proc目录下的文件大多是可读的文本文件可以直接使用常用的文本工具查看如cat、less等。但也有一些文件是可写的通过对这些文件写入特定的值用户或程序员可以调整或配置内核参数这是实现内核动态调优的一种方式。 一个常见的应用场景是当系统管理员想要快速检查系统的运行状态或者程序员在开发过程中需要获取系统或进程的某些信息时他们就会查阅/proc目录下的文件。例如通过读取/proc/uptime文件可以获取系统已经运行了多长时间通过查看/proc/loadavg文件可以了解系统的平均负载情况。 以下是/proc目录下的一些常见子目录和文件的详细总结表格。请注意这个表格并不包含所有可能存在的文件和目录因为/proc目录的内容可能会根据Linux内核的版本和系统配置有所不同。 路径描述/proc/[pid]每个进程都有一个对应的目录包含该进程的详细信息/proc/[pid]/attr安全属性如SELinux上下文/proc/[pid]/auxvELF解释器传递给进程的信息/proc/[pid]/cmdline进程启动时的完整命令行/proc/[pid]/comm进程的命令名称/proc/[pid]/coredump_filter核心转储文件的过滤设置/proc/[pid]/cpusetCPU亲和性设置/proc/[pid]/cwd当前工作目录的符号链接/proc/[pid]/environ环境变量列表/proc/[pid]/exe可执行文件的符号链接/proc/[pid]/fd打开文件的文件描述符/proc/[pid]/fdinfo文件描述符信息/proc/[pid]/limits资源限制/proc/[pid]/maps内存映射信息/proc/[pid]/mem进程内存页访问/proc/[pid]/mountinfo挂载信息/proc/[pid]/mounts当前进程挂载的文件系统列表/proc/[pid]/mountstats挂载统计信息/proc/[pid]/ns命名空间信息/proc/[pid]/numa_mapsNUMA内存映射信息/proc/[pid]/oom_adjOOM内存不足调整值/proc/[pid]/oom_scoreOOM评分/proc/[pid]/oom_score_adjOOM评分调整/proc/[pid]/root根目录的符号链接/proc/[pid]/smaps内存映射的详细内存使用情况/proc/[pid]/stat进程状态信息/proc/[pid]/statm内存使用状态信息/proc/[pid]/status进程状态信息可读格式/proc/[pid]/task包含进程中每个线程的信息/proc/[pid]/thread-self当前线程的信息/proc/self当前进程的信息链接/proc/self/task当前进程的线程信息/proc/apm高级电源管理APM信息/proc/buddyinfo内存碎片信息/proc/cmdline启动时传递给内核的参数/proc/cpuinfoCPU信息/proc/crypto加密算法信息/proc/devices设备列表/proc/diskstats磁盘I/O统计信息/proc/dmaISA DMA通道信息/proc/execdomains执行域信息/proc/fb帧缓冲设备信息/proc/filesystems支持的文件系统类型/proc/interruptsIRQ中断信息/proc/iomem物理设备内存映射信息/proc/ioports输入输出端口范围信息/proc/kallsyms内核符号信息/proc/kcore物理内存映射核心文件格式/proc/kmsg内核消息记录/proc/loadavgCPU和I/O负载平均值/proc/locks内核锁定的文件信息/proc/mdstatRAID配置信息/proc/meminfo内存使用信息/proc/mounts系统挂载信息/proc/modules加载的内核模块列表/proc/partitions分区信息/proc/pciPCI设备信息/proc/slabinfoSlab缓存信息/proc/statCPU活动统计信息/proc/sysrq-trigger系统请求触发器/proc/swaps交换空间使用情况/proc/uptime系统运行时间/proc/version内核版本信息/proc/bus总线信息/proc/driver驱动信息/proc/fs文件系统信息/proc/ideIDE设备信息/proc/irq中断请求设备信息/proc/net网络设备信息/proc/scsiSCSI设备信息/proc/ttyTTY设备信息/proc/net/dev网络适配器及统计信息/proc/vmstat虚拟内存统计信息/proc/vmcore内核panic时的内存映像/proc/diskstats磁盘信息/proc/schedstat调度器统计信息/proc/zoneinfo内存空间统计信息 这个表格提供了/proc目录下一些关键文件和目录的简要描述。由于/proc目录的内容非常丰富这里只列出了一部分。在实际使用中可以通过ls -l /proc命令来查看当前系统/proc目录下的完整内容。 1.3 常用进程相关命令 可参考文档: Linux之(16)程序管理-CSDN博客 在Linux系统中有一系列强大的进程相关工具命令它们是系统管理员和开发者日常工作中不可或缺的助手。以下是一些常用的进程工具及其简要介绍和示例 psProcess Status这是最基本的进程查看工具用于列出系统中当前运行的进程。例如命令ps aux会列出系统中所有进程的详细信息其中a代表所有用户的进程u代表用户以及其他详细信息x代表没有控制终端的进程。 ubuntu-~:$ ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.1 167488 11228 ? Ss Jan14 2:04 /sbin/init root 2 0.0 0.0 0 0 ? S Jan14 0:01 [kthreadd] root 3 0.0 0.0 0 0 ? I Jan14 0:00 [rcu_gp] ......top这个命令提供了一个实时更新的进程状态动态视图。它显示了CPU和内存的使用情况以及进程的动态列表可以实时查看系统的性能状况。只需在终端输入top即可启动。 ubuntu-~:$ top top - 22:54:50 up 38 days, 23:36, 1 user, load average: 0.06, 0.05, 0.01 Tasks: 123 total, 1 running, 122 sleeping, 0 stopped, 0 zombie %Cpu(s): 0.0 us, 1.6 sy, 0.0 ni, 98.4 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st MiB Mem : 7437.5 total, 2603.7 free, 400.8 used, 4433.0 buff/cache MiB Swap: 0.0 total, 0.0 free, 0.0 used. 6719.4 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME COMMAND 1 root 20 0 167488 11228 6444 S 0.0 0.1 2:04.38 systemd 2 root 20 0 0 0 0 S 0.0 0.0 0:01.59 kthreadd 3 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 rcu_gp ...... htop相比tophtop提供了一个更为友好和可交互的界面它允许用户通过键盘操作进行进程管理如结束进程等。启动方法是在终端输入htop。 kill这是用于发送信号给进程的命令通常用于终止进程。例如kill -9 pid会发送SIGKILL信号来强制终止指定PID的进程。 pkill和killall这两个命令也用于发送信号给进程但它们可以根据进程名而不是PID来指定进程。例如pkill nginx将终止所有名为nginx的进程。 pstree该命令用于以树形结构显示进程的层次关系这对于理解进程之间的父子关系非常有帮助。只需输入pstree即可显示当前进程树。 ubuntu-~:$ pstree systemd─┬─ModemManager───2*[{ModemManager}]├─YDLive─┬─YDService─┬─sh───10*[{sh}]│ │ └─23*[{YDService}]│ └─10*[{YDLive}]├─acpid├─2*[agetty]├─auditd───{auditd}vmstat虽然不是直接用于进程管理但vmstat命令能提供关于系统内存、交换、IO、CPU活动等重要信息这对于分析进程性能有重要的参考价值。例如vmstat 1会每秒刷新显示系统状态。 ubuntu-~:$ vmstat procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----r b swpd free buff cache si so bi bo in cs us sy id wa st0 0 0 2665612 220868 4320632 0 0 41 25 0 3 0 0 99 0 0lsofList Open Files的缩写用于列出被进程打开的文件对于查找使用了哪些文件或设备非常有用。例如lsof -p pid将显示指定PID进程打开的文件。 strace这个工具用于跟踪进程执行时的系统调用对于程序调试和性能分析至关重要。例如strace -p pid将跟踪并显示指定进程的系统调用。 nice和renice这两个命令用于调整进程的优先级。nice用于启动一个进程时设置其优先级而renice则用于修改已经运行进程的优先级。例如renice 10 -p pid会将指定PID的进程优先级调整为10。 这些工具是Linux上进行进程查看和管理的基石通过它们可以实现对系统进程的精细化控制和监控。掌握它们的使用可以帮助您更好地理解和管理Linux系统的运行状态。 2. C语言编码获取进程信息 2.1 读取comm线程名字 在C语言中可以通过遍历/proc目录并读取每个进程目录下的comm文件来获取所有进程的名称。以下是一个简单的示例代码它将列出/proc下所有进程的comm信息 #include stdio.h #include dirent.h #include string.h #include stdlib.hint main(void) {DIR *dir;struct dirent *entry;char path[512];char comm[256];FILE *fp;// 打开/proc目录dir opendir(/proc);if (dir NULL) {perror(opendir failed);exit(EXIT_FAILURE);}// 遍历目录项while ((entry readdir(dir)) ! NULL) {// 检查目录项名称是否为数字进程IDif (entry-d_type DT_DIR strtol(entry-d_name, NULL, 10) 0) {// 构建comm文件的路径snprintf(path, sizeof(path), /proc/%s/comm, entry-d_name);// 打开comm文件fp fopen(path, r);if (fp NULL) {continue;}// 读取comm文件的内容进程名称if (fgets(comm, sizeof(comm), fp) ! NULL) {// 移除换行符并打印进程名称comm[strcspn(comm, \n)] 0;printf(PID: %s, Comm: %s\n, entry-d_name, comm);}// 关闭comm文件fclose(fp);}}// 关闭/proc目录closedir(dir);return 0; }此程序执行以下步骤 打开/proc目录。遍历目录项检查目录名称是否为数字因为进程目录总是以数字命名。对于每个进程目录构建指向其comm文件的路径。打开comm文件并读取进程名称。打印进程ID和名称。关闭已打开的文件和目录。 我们在ubuntu环境下编译和该程序输出如下(可以读出进程的名字): ubuntu-cs-test:$ gcc -o read-comm read-comm.c ubuntu-cs-test:$ ./read-comm PID: 1, Comm: systemd PID: 2, Comm: kthreadd PID: 3, Comm: rcu_gp PID: 4, Comm: rcu_par_gp PID: 5, Comm: netns ......2.2 读取cmdline命令行文本 在Linux系统中/proc目录是一个虚拟的文件系统它提供了一个接口来访问内核数据结构。它不仅包含系统信息还包含了每个进程信息的目录例如/proc/[pid]其中[pid]是进程ID。在每个进程的目录下有一个名为cmdline的文件它包含了启动该进程的命令行参数。 要用C语言编码获取指定进程的cmdline信息可以按照以下步骤编写代码 构造/proc/[pid]/cmdline文件的路径。打开这个文件。读取文件内容。关闭文件。 以下是一个例子演示了如何获取进程ID为1234的cmdline信息 #include stdio.h #include stdlib.h #include string.h #include limits.hint main(int argc, char **argv) {int pid, i;FILE *file;char path[256], cmdline[PATH_MAX 1];size_t size;// 读取命令行参数if (argc ! 2) {fprintf(stderr, Usage: %s pid\n, argv[0]);return 1;}pid atoi(argv[1]);// 构造文件路径snprintf(path, sizeof(path), /proc/%d/cmdline, pid);// 打开文件file fopen(path, r);if (file NULL) {perror(fopen);exit(EXIT_FAILURE);}// 读取文件内容size fread(cmdline, 1, PATH_MAX, file);if (size 0) {perror(fread);exit(EXIT_FAILURE);}// 将中间间隔的\0变成 for (i 0; i size; i) {if (cmdline[i] \0) {cmdline[i] ;}}// 添加字符串终止符cmdline[size] \0;// 输出cmdline信息printf(The command line for PID %d is: %s\n, pid, cmdline);// 清理资源fclose(file);return 0; }这段代码首先定义了进程ID并构建了相应的文件路径。使用fopen打开cmdline文件。之后使用fread读取文件内容并在末尾添加空字符以形成一个标准的字符串。最后输出命令行信息释放内存关闭文件。 请注意cmdline文件的内容使用空字符分隔命令行参数所以上面代码中进行了转换使用空格进行输出。下面是实际运行情况: ubuntu-cs-test:$ gcc -o read-cmdline read-cmdline.c ubuntu-cs-test:$ ./read-cmdline 1911678 The command line for PID 1911678 is: sshd: ubuntupts/0 ubuntu-cs-test:$ ./read-cmdline 1949309 The command line for PID 1949309 is: /home/ubuntu/.vscode-server/extensions/ms-vscode.cpptools-1.18.5-linux-x64/bin/cpptools-srv 1949199 {EDAFE4D2-4BEC-4A09-9466-66C5A7850042} 2.3 读取exe可执行程序路径 为了获取Linux系统中/proc目录下所有进程的可执行程序路径我们可以编写一个C程序来遍历/proc目录查找所有的数字命名子目录这些通常代表进程ID然后对每个进程读取其exe链接文件该链接指向进程的可执行文件路径。 在Linux系统中/proc/[pid]/exe是一个符号链接指向启动该进程的可执行文件。 以下是实现这一功能的C语言代码示例 #include stdio.h #include stdlib.h #include dirent.h #include string.h #include unistd.h #include limits.h #include libgen.hint main() {DIR *proc;struct dirent *entry;char exe_path[PATH_MAX];char filepath[PATH_MAX];ssize_t len;// 打开/proc目录if ((proc opendir(/proc)) NULL) {perror(opendir);exit(EXIT_FAILURE);}// 遍历/proc目录while ((entry readdir(proc)) ! NULL) {// 检查目录名是否为数字代表进程IDif (entry-d_type DT_DIR strspn(entry-d_name, 0123456789) strlen(entry-d_name)) {// 构造链接路径snprintf(filepath, sizeof(filepath), /proc/%s/exe, entry-d_name);// 读取符号链接即可执行文件的路径if ((len readlink(filepath, exe_path, sizeof(exe_path) - 1)) ! -1) {exe_path[len] \0; // 确保字符串以NULL结尾// 获取basename和dirnamechar *base basename(exe_path);char *dir dirname(exe_path);printf(PID: %s - EXE: %s, BASE: %s, DIR: %s\n, entry-d_name, exe_path, base, dir);} else {perror(readlink);}}}// 关闭/proc目录closedir(proc);return 0; }本段代码首先打开/proc目录然后使用readdir函数遍历每个条目。对于每个条目如果它是一个目录并且它的名字只包含数字那么它很可能代表一个进程ID。对于这样的目录代码构造了一个指向exe链接的路径然后使用readlink函数尝试读取该链接的目标路径即可执行文件的路径。成功读取后将路径打印到标准输出。 需要注意的是由于涉及到读取系统目录和链接运行这个程序可能需要相应的权限通常需要root权限才能读取所有进程的exe路径。此外由于进程可能在任何时候结束所以该程序可能无法捕获到系统中的瞬时进程。 下面是实际运行结果(可以看到很多权限报错这是因为内核线程和一些root进程需要权限可以用sudo运行): ubuntu-cs-test:$ gcc -o read-exe read-exe.c ubuntu-cs-test:$ ./read-exe ...... readlink: Permission denied readlink: Permission denied readlink: Permission denied PID: 1911597 - EXE: /usr/lib/systemd, BASE: systemd, DIR: /usr/lib/systemd readlink: Permission denied readlink: Permission denied PID: 1911679 - EXE: /usr/bin, BASE: bash, DIR: /usr/bin readlink: Permission denied readlink: Permission denied PID: 1948965 - EXE: /usr/bin, BASE: bash, DIR: /usr/bin PID: 1949010 - EXE: /usr/bin, BASE: dash, DIR: /usr/bin PID: 1949074 - EXE: /home/ubuntu/.vscode-server/bin/903b1e9d8990623e3d7da1df3d33db3e42d80eda, BASE: node, DIR: /home/ubuntu/.vscode-server/bin/903b1e9d8990623e3d7da1df3d33db3e42d80eda PID: 1949131 - EXE: /home/ubuntu/.vscode-server/bin/903b1e9d8990623e3d7da1df3d33db3e42d80eda, BASE: node, DIR: /home/ubuntu/.vscode-server/bin/903b1e9d8990623e3d7da1df3d33db3e42d80eda ...... ubuntu-cs-test:$ sudo ./read-exe ...... PID: 1961276 - EXE: /usr/bin, BASE: sleep, DIR: /usr/bin readlink: No such file or directory readlink: No such file or directory readlink: No such file or directory PID: 1962795 - EXE: /usr/bin, BASE: sudo, DIR: /usr/bin PID: 1962796 - EXE: /usr/bin, BASE: sudo, DIR: /usr/bin ......sudo运行很多报错No such file or directory 其实这些进程时内核进程所以符号链接文件指向是空的。 2.4 如何判断内核进程 在Linux系统中内核线程是一种特殊的进程它们在内核空间运行而不是在用户空间。内核线程可以通过检查它们的特征来区分这些特征在/proc文件系统中可见。以下是一些区分用户进程和内核线程的方法 命令行为空对于内核线程/proc/[pid]/cmdline文件通常为空因为它们不是由外部命令启动的。 没有关联的终端内核线程通常没有关联的终端。你可以通过查看/proc/[pid]/stat文件中的TTY终端字段来检查这一点。如果这个值是0则表示没有关联的终端。 父进程内核线程的父进程通常是kthreaddPID为2。 进程目录的task子目录对于用户进程/proc/[pid]/task目录包含该进程的所有线程。内核线程通常只有一个线程所以这个目录下通常只有一个目录其名字与PID相同。 /proc/[pid]/exe链接对于用户进程/proc/[pid]/exe是一个到可执行文件的符号链接。对于内核线程exe通常不存在或者链接无效。 名字的形式内核线程的名字通常在/proc/[pid]/comm文件中以方括号包围例如[kthreadd]。 2.5 修改comm和cmdline信息 在Linux系统中进程的名称可以通过两个属性来表示comm和cmdline。comm表示进程的简短名称通常与可执行文件的名称相同而cmdline则包含了启动该进程时传递给它的命令行参数。 comm这个名称对应于/proc/[pid]/comm文件包含了进程的名称。这个名称是进程的可执行文件名的最后16个字符在Linux内核版本2.6.33之后长度从15个字符扩展到了16个字符包括终止的null字符。 cmdline这个文件/proc/[pid]/cmdline包含了进程启动时的命令行参数参数之间由null字符分隔。 要修改进程的comm和cmdline可以编写C语言程序直接写入到相应的/proc/self/目录下的文件。对于comm可以调用prctl函数对于cmdline需要直接修改argv[]数组的数据。以下是一个示例 #define _GNU_SOURCE #include stdio.h #include string.h #include unistd.h #include sys/prctl.hint main(int argc, char **argv) {char old_comm[16], comm[16], cmdline[64];const char *new_comm, *new_cmdline;size_t read_bytes, cmdline_len;FILE *cmdline_file;// 获取旧的commif (prctl(PR_GET_NAME, old_comm) 0) {perror(prctl get name);return 1;}// 修改commnew_comm my-new-comm;if (prctl(PR_SET_NAME, new_comm) 0) {perror(prctl set name);return 1;}// 验证修改if (prctl(PR_GET_NAME, comm) 0) {perror(prctl get name);return 1;}printf(Comm change: %s %s\n, old_comm, comm);// 修改cmdlinenew_cmdline fake-name;cmdline_len strlen(new_cmdline) 1;// 直接写入到arv[0]的内存空间strncpy(argv[0], new_cmdline, cmdline_len);// 读取验证修改cmdline_file fopen(/proc/self/cmdline, r);if (!cmdline_file) {perror(fopen cmdline);return 1;}read_bytes fread(cmdline, 1, sizeof(cmdline) - 1, cmdline_file);fclose(cmdline_file);if (read_bytes 0) {for (size_t i 0; i read_bytes; i) {if (cmdline[i] \0) {cmdline[i] ; // 替换null字符以打印}}printf(New cmdline: %s\n, cmdline);}return 0; } 请注意以下几点 修改comm通过prctl系统调用完成参数PR_SET_NAME用于设置新的comm值。出于安全和稳定性的考虑不是所有的环境都允许进程修改自己的cmdline。另外cmdline的修改通常在进程启动初期完成一旦进程开始执行就不推荐修改因为这可能会迷惑正在监视进程状态的系统管理工具和用户。修改这些信息通常用于使进程在系统监视工具中更易于识别但应谨慎使用以避免误导或带来安全风险。进程可以通过修改其内存空间中的参数来影响显示在cmdline中的内容。这通常是通过修改argv参数实现的因为/proc/[pid]/cmdline是根据argv的内容生成的。但是这种做法并不常见也不推荐使用因为它可能会导致与其他程序的不兼容性甚至可能会违反安全最佳实践。 下面是实际编译运行的结果: ubuntu-cs-test:$ gcc -o change-self change-self.c ubuntu-cs-test:$ ./change-self Comm change: change-self my-new-comm New cmdline: fake-name elf 可以看到成功改变了comm和cmdline信息从ps和top等命令看到的进程信息也会有变化。 2.6 实现一个简易的killall工具 下面是一个简单的killall工具可以kill掉指定可执行文件的进程通过exe确定(类似pidof -x)进程是否属于目标进程。 /** SPDX-License-Identifier: BSD-3-Clause** Copyright (c) 2024 Once Day once_dayqq.com, All rights reserved.** FilePath: /cs-test/my-killall.c* Author: Once Day once_dayqq.com.* Date: 2024-02-22 10:16* info: Encoderutf-8,Tabsize4,Eol\n.** Description:* 简单的进程查找程序** History:***/#include stdio.h #include stdlib.h #include stdint.h #include string.h #include stdbool.h #include unistd.h #include signal.h #include limits.h #include dirent.h #include ctype.h #include errno.h #include libgen.h/* 判断一个目录名是否是纯数字 */ static bool is_pid_directory(const char *dir_name) {while (*dir_name) {if (!isdigit(*dir_name)) {return false;}dir_name;}return true; }/* 在检索进程对象时遇到错误, 输出对应的信息用于定位 */ static void print_proc_error(const char *dir_name, const char *msg, int32_t err_code) {FILE *comm_file;char *newline;char path[256], comm[256];snprintf(path, sizeof(path), /proc/%s/comm, dir_name);comm[0] \0;comm_file fopen(path, r);if (comm_file) {if (fgets(comm, sizeof(comm), comm_file) ! NULL) {/* Remove newline if present */newline strchr(comm, \n);if (newline) {*newline \0;}}fclose(comm_file);}/* 如果没有读到任何字符, 则提示未知进程名 */if (comm[0] \0) {snprintf(comm, sizeof(comm), (unknown process name));}/* 打印错误日志信息 */printf(%s [%s(%s)] [%s(%d)].\n, msg, comm, dir_name, strerror(err_code), err_code);return; }/* 读取exe可执行文件信息, 判断是否为目标进程名, 全匹配 */ bool check_proc_exe(const char *dir_name, const char *proc_name) {size_t bytes_read, i, seg;ssize_t link_len;FILE *cmdline_file;char *exe_name;char proc_path[256], exe_path[PATH_MAX 1];/* /proc/[pid]/exe是一个符号链接文件, 内核进程exe链接文件不存在 */snprintf(proc_path, sizeof(proc_path), /proc/%s/exe, dir_name);link_len readlink(proc_path, exe_path, sizeof(exe_path) - 1);if (link_len -1) {/* note: 内核进程无法读取exe link文件, 只考虑用户空间进程, 即排除ENOENT的错误 */if (errno ! ENOENT) {/* 如果非root用户, 可以排除权限错误警告EACCES */print_proc_error(dir_name, Error reading executable link file, errno);}return false;}/* 确保字符串以 null 字符结尾 */exe_path[link_len] \0;/* printf(Executable path: %s\n, exe_path); *//* 处理路径字符串, 获取basename */exe_name basename(exe_path);/* printf(Executable name: %s\n, exe_name); */if (strncmp(exe_name, proc_name, strlen(proc_name)) 0) {printf(Process found: %s(%s) %ld.\n, proc_name, dir_name, strlen(proc_name));kill(atoi(dir_name), SIGKILL);return true;}return false; }/* 通过/proc文件系统查询当前设备上的进程信息 */ bool find_process_by_name(const char *proc_name) {uint64_t pid, count;DIR *proc_dir;struct dirent *entry;/* 读取/proc内核信息虚拟目录信息 */proc_dir opendir(/proc);if (proc_dir NULL) {printf(Failed to open /proc directory, %s(%d).\n, strerror(errno), errno);return false;}/* 遍历整个/proc目录, 找到进程子目录, 逐个判断是否为目标进程 */count 0;while ((entry readdir(proc_dir)) ! NULL) {if (entry-d_type DT_DIR is_pid_directory(entry-d_name)) {if (check_proc_exe(entry-d_name, proc_name) true) {count;}}}closedir(proc_dir);printf(Total %lu process(es) found.\n, count); }int main(int argc, char *argv[]) {if (argc ! 2) {printf(Usage: %s process name\n, argv[0]);return EXIT_FAILURE;}find_process_by_name(argv[1]);return EXIT_SUCCESS; } 测试时将2.5节的程序change-self加一个sleep函数延迟1000schange-self会改变自身的cmdline和comm内容但是我们的killall仍然可以识别全部的change-self进程。 编译运行执行如下: ubuntu-cs-test:$ gcc -o change-self change-self.c ubuntu-cs-test:$ ./change-self # 重复运行5次 [1] 1971781 Comm change: change-self my-new-comm New cmdline: fake-name elf ubuntu-cs-test:$ gcc -o my-killall my-killall.c ubuntu-cs-test:$ ./my-killall Usage: ./my-killall process name ubuntu-cs-test:$ ./my-killall change-self Error reading executable link file [kworker/u8:0-events_unbound(1971317)] [Permission denied(13)]. Process found: change-self(1971781) 11. Process found: change-self(1971795) 11. Process found: change-self(1971803) 11. Process found: change-self(1971811) 11. Process found: change-self(1971814) 11. Error reading executable link file [sh(1973550)] [Permission denied(13)]. Error reading executable link file [tat_agent(2029982)] [Permission denied(13)]. Total 5 process(es) found. [1] Killed ./change-self [2] Killed ./change-self [3] Killed ./change-self [4]- Killed ./change-self [5] Killed ./change-self可以看到my-killall成功找到5个change-self并且kill了它们实现了我们想要的功能。 附录 附录1: opendir和readdir函数简介 在C语言中opendir和readdir是两个用于操作目录的函数它们定义在dirent.h头文件中。这两个函数通常用来遍历文件系统中的目录结构。 opendir函数用于打开一个目录使其成为读取目录项的候选项。它的原型如下 DIR *opendir(const char *name);这里DIR是一个表示目录流的类型opendir函数接受一个参数即要打开目录的路径字符串并返回一个指向DIR类型的指针。如果打开目录失败它会返回NULL并且errno变量会被设置为相应的错误代码以便可以进一步检查错误发生的原因。 readdir函数用于读取由opendir打开的目录流中的目录项。它的原型如下 struct dirent *readdir(DIR *dirp);readdir接受一个指向DIR类型的指针返回一个指向struct dirent结构的指针。这个结构包含了目录项的详细信息最重要的字段包括 d_name一个字符数组包含了目录项的名字。d_type一个字符代表目录项的类型如普通文件、目录等。 当readdir读取到目录流的末尾或发生错误时将返回NULL。在使用readdir遍历目录时它会记住当前的位置所以每次调用时都会获取下一个目录项。需要注意的是readdir返回的目录项顺序是不确定的且可能会包含特殊的.和..目录项分别表示当前目录和父目录。 使用opendir和readdir时一旦目录项读取完成应该使用closedir函数关闭目录流释放资源。例如 DIR *dir opendir(/path/to/directory); if (dir) {struct dirent *entry;while ((entry readdir(dir)) ! NULL) {printf(%s\n, entry-d_name);}closedir(dir); }在这个例子中我们打开了指定路径的目录然后循环读取每个目录项并打印其名称最后关闭了目录流。opendir和readdir函数是目录操作中非常基础且重要的工具它们使得在C语言中处理文件系统变得更加容易。 附录2: 介绍fread函数 fread函数用于从文件流中读取数据块定义在stdio.h头文件中它的原型如下 size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);参数解释 ptr指向一个缓冲区的指针用于存储读取的数据。size每个数据块的大小以字节为单位。nmemb要读取的数据块的数量。stream指向FILE对象的指针该FILE对象代表了一个打开的文件流。 fread函数从stream指定的文件流中读取nmemb项数据每项数据的大小为size字节存储在ptr指向的缓冲区中。函数返回成功读取的数据块数目如果此数目与nmemb不相符可能是因为发生了错误或达到了文件末尾。 与fgets不同fread是以二进制形式读取文件不会在读取的数据后添加空终止符也不会因为换行符或文件末尾而停止读取。它通常用于读取结构体数据或二进制文件。 一个简单的fread使用示例可能如下 FILE *file fopen(example.bin, rb); if (file) {char buffer[1024];size_t bytesRead fread(buffer, sizeof(char), sizeof(buffer), file);fclose(file);// 处理读取的数据... }在这个例子中我们打开了一个名为example.bin的二进制文件然后尝试读取到缓冲区buffer中并记录实际读取的字节数。 fread非常适合读取二进制数据和大块数据。 附录3: 介绍fgets函数 fgets函数用于从文件中读取字符串定义在stdio.h头文件中它的原型如下 char *fgets(char *s, int size, FILE *stream);参数解释 s指向一个字符数组的指针用于存储读取的字符串。size指定最多读取的字符数包括空终止字符\0。stream指向FILE对象的指针该FILE对象代表了一个打开的文件流。 fgets函数会从指定的文件流stream中读取字符直到发生以下三种情况之一 读取了size-1个字符遇到换行符\n到达了文件末尾EOF。 读取停止后fgets会在字符串的末尾添加一个空终止符\0。如果读取成功fgets返回s如果遇到文件末尾或发生错误fgets返回NULL。 fgets是一个安全的读取字符串的函数因为它允许指定缓冲区的大小防止缓冲区溢出。 附录4: 介绍readlink函数 readlink函数是一个在类UNIX操作系统中用于读取符号链接symbolic link内容的系统调用。符号链接是一种特殊类型的文件它包含的是指向另一个文件路径的引用。不同于硬链接符号链接可以跨文件系统并且链接的目标可以是任何类型的文件包括目录。 函数的原型定义在 unistd.h 头文件中其用途主要是获取符号链接所指向的原始路径。当你想要知道一个符号链接指向何处时就可以使用 readlink 函数。 函数原型如下 #include unistd.h ssize_t readlink(const char *restrict path, char *restrict buf, size_t bufsize);path 参数是指向符号链接的路径字符串。buf 参数是一个字符数组用来存储符号链接指向的路径。bufsize 参数指定了缓冲区 buf 的大小。 调用 readlink 时它会将 path 指向的符号链接所引用的路径读取到 buf 中并返回读取的字节数。如果成功返回值是写入 buf 的字节数不包含空终止字符如果失败返回 -1 并设置 errno 来表示错误。 readlink 函数不会在 buf 后追加空字符所以在处理返回的数据时你可能需要根据返回值在 buf 后手动添加空字符以确保它是一个标准的字符串。 以下是 readlink 函数可能遇到的一些错误情况 EACCES路径中的目录不可搜索。EINVALpath 不是一个符号链接。EIO输入输出错误无法读取链接。ELOOP解析 path 中的符号链接时遇到太多层的符号链接。ENAMETOOLONGpath 太长。ENOENTpath 指向的符号链接不存在。ENOTDIRpath 的前缀不是目录。EFAULTpath 或 buf 指向了不可访问的内存区域。 readlink 是处理符号链接的常用函数它可以帮助程序了解文件的真实路径尤其是在处理文件链接时它提供了一种有效的方法来避免进入符号链接引起的循环。在文件系统操作中readlink 函数是实现某些特定功能比如寻找程序的实际安装位置时不可或缺的工具。 附录5: 介绍kill函数 kill函数是一种在UNIX-like操作系统中用于向进程发送信号的系统调用。它的作用是向指定的进程或进程组发送一个信号信号可以用来中断、终止甚至改变进程的行为。kill函数的原型定义在头文件 signal.h 中它的使用在C语言编程中相当常见尤其是在需要进行进程控制和管理的场景下。 函数原型如下 #include signal.h int kill(pid_t pid, int sig);这里的pid_t是一个用于进程ID的数据类型而sig是希望发送的信号的编号。如果调用成功kill函数返回0如果失败返回-1并设置errno以指示错误原因。 kill函数的参数pid可以是以下几种情况之一 pid 0信号被发送到进程ID为pid的进程。pid 0信号被发送到与发送进程属于同一个进程组的所有进程。pid -1信号被发送到进程组ID为-pid的所有进程。pid -1信号被发送到所有发送进程有权限发信号的进程除了系统进程和调用进程。 sig参数则指定了要发送的信号类型这些信号包括但不限于 SIGINT终止进程可中断。SIGKILL立即终止进程不可中断。SIGTERM请求终止进程可中断和处理。SIGSTOP停止暂停进程的执行不可中断。SIGCONT如果进程被停止让它继续执行。 kill函数的强大之处在于它提供了一个简洁的方式来控制进程的行为。例如当用户在命令行中按下CtrlC时通常会发送SIGINT信号给前台进程组中的所有进程这通常会导致进程的终止。而在编写守护进程或者需要清理资源的场景中可能会捕捉SIGTERM信号来执行清理操作后再退出进程。 值得注意的是有些信号是不能被进程捕捉或忽略的比如SIGKILL和SIGSTOP这是为了确保系统管理员能够控制系统中的进程。而其他信号比如SIGTERM则可以被进程捕捉以便进行适当的清理工作。 使用kill函数时需要具有相应的权限通常是需要进程的拥有者或者超级用户权限才能向进程发送信号。如果权限不足kill函数会失败并且errno会被设置为EPERM。 总的来说kill函数是进程间通信和控制的重要手段它允许进程间以及操作系统与进程之间进行有效的信号传递。程序员在使用kill函数时应当小心谨慎以确保正确、合理地管理进程。
http://www.dnsts.com.cn/news/216004.html

相关文章:

  • 成都专业网站制作建设优秀行业网站
  • 盐都区城乡建设局网站郑州计算机培训机构哪个最好
  • 北京的p2p网站建设wordpress去掉category
  • 响应式网站模板htmlwordpress支持论坛
  • 深圳市门户网站建设多少钱上海网络科技有限公司排名
  • 西峡微网站开发北京免费网站建设
  • 一件代发48个货源网站服务二级公司网站建设
  • 安平网站建设优化好用的h5网站模板
  • 电商商城网站建设方案安卓开发工具箱
  • wordpress建两个网站吗触屏版网站制作
  • 第一次做网站没头绪交换链接的其它叫法是
  • wordpress栏目置顶廊坊关键词优化
  • 商洛做网站的公司电话景区网站建设的意义
  • 广西网站建设银行生产制造erp
  • 网站评估 源码网络课程系统网站建设费用
  • 青岛君哲网站建设公司怎么样购物平台网站建设
  • 北京网站搭建方案怀柔做网站的公司
  • 设计logo网站免费国外农村网站平台建设方案
  • 制造业外贸营销网站建设商务网站建设规划流程
  • 怎么样备份网站数据wordpress固定衔接出错
  • 深圳找工作哪个网站好做网站php和asp哪个好
  • 南雄市建设局网站分级会员管理系统网站开发
  • 深圳旅游网站开发平面设计素材网站知乎
  • 微信彩票网站网站建设衡水企业做网站多少钱
  • 昆明建设网站公司计算机软件开发规范1988作废
  • 网站建设模板推广全网营销推广哪家正规
  • 购房者网站网站运营成本明细
  • 做网站找毛叶子歌网站改版设计方案
  • 想学ui设计从哪里入手搜索引擎优化的报告
  • 下载网站的服务器文件郑州企业型网站建设