西安网站建设企业,wordpress自定义用户信息,网站模板展示,百度站长之家工具进程 PID
进程的PID#xff08;Process ID#xff09;是操作系统中用于唯一标识一个进程的整数值。每个进程在创建时#xff0c;操作系统都会分配一个唯一的PID#xff0c;用来区分不同的进程。
PID的特点
唯一性#xff1a;
在操作系统运行的某一时刻#xff0c;每个…进程 PID
进程的PIDProcess ID是操作系统中用于唯一标识一个进程的整数值。每个进程在创建时操作系统都会分配一个唯一的PID用来区分不同的进程。
PID的特点
唯一性
在操作系统运行的某一时刻每个进程的PID都是唯一的。不同进程不会共享同一个PID。即使一个进程终止后该PID可以被回收并分配给新创建的进程但在同一时刻不会有两个进程拥有相同的PID。
进程生命周期
PID 的生命周期与进程的生命周期相对应。当一个进程被创建时操作系统为它分配一个PID当进程终止时该PID被释放并可能被分配给后续的新进程。
系统管理和调试
PID 在进程管理和调试中起着重要作用。系统管理员和开发者可以通过PID来监视、控制和调试进程。例如使用 ps 命令可以查看系统中所有进程的PID以及它们的状态、资源使用等信息。使用 kill 命令可以通过PID来终止指定的进程。
PID的分配
PID 是由操作系统内核管理和分配的通常是一个非负整数。PID 通常从一个最小值通常是1开始逐渐递增。当系统运行的进程数达到最大PID值时PID 会回绕到最小值并重新开始分配。
特殊的PID
PID 1在大多数类UNIX操作系统中PID 1 通常分配给初始化进程init 或 systemd这是系统启动时创建的第一个进程。init 是所有其他进程的祖先它负责启动系统的其余部分并在系统运行期间维持各种系统服务。PID 0PID 0 通常被保留给调度进程或空闲进程这个进程在大多数情况下不会被普通用户或程序直接操作。5
日志
Linux系统日志
Linux提供一个守护进程后台进程来处理系统日志rsyslogd。rsyslogd 守护进程既能接收用户进程输出的日志又能接收内核日志。
syslog 函数
应用程序使用syslog函数和守护进程rsyslog通信。
#include syslog.h
void syslog(int priority, const char* message, ...);
openlog用来改变syslog的默认输出方式进一步结构化日志内容。
#include syslog.h
void openlog(const char* ident, int logopt, int facility);
setlogmask 用于设置日志掩码使得日志级别大于日志掩码的信息被系统忽略。用于在程序发布之后将程序的调试信息关闭。
#include syslog.h
int setlogmask(int maskpri);
closelog函数用于关闭日志功能
#include syslog.h
void closelog();
用户信息
UID、EUID、GID、EGID
在操作系统中尤其是类 UNIX 系统中用户和组的标识符用于控制对系统资源的访问权限。以下是对 UIDUser ID、EUIDEffective User ID、GIDGroup ID和 EGIDEffective Group ID的解释
1. UIDUser ID
UID 是用于唯一标识系统中每个用户的整数值。当用户在系统中创建时系统会为该用户分配一个唯一的 UID。UID 用于控制用户对系统资源的访问权限。
UID 0通常保留给 root 用户超级用户它拥有系统的所有权限能够执行任何操作。普通用户的 UID通常从 1000 或 500 开始取决于操作系统的配置用于普通用户。
2. EUIDEffective User ID
EUID 是用于实际控制用户对文件和系统资源访问权限的用户标识符。它可能与 UID 相同但在某些情况下可以不同。例如通过 setuid 程序普通用户可以临时获得文件所有者的权限。EUID 通常用于判断用户是否有权执行某些操作。
典型用法当一个用户执行 setuid 程序时该程序的 EUID 会被设置为程序文件的所有者的 UID而不是当前用户的 UID从而赋予执行该程序的用户临时的更高权限。
3. GIDGroup ID
GID 是用于标识系统中每个用户组的整数值。与 UID 类似GID 用于控制用户组对系统资源的访问权限。每个用户在系统中都有一个与之关联的主组primary group该组由 GID 表示。
GID 0通常属于 root 组具有最高权限。普通用户的 GID通常与其主组的 GID 相同。
4. EGIDEffective Group ID
EGID 是用于实际控制用户对文件和系统资源访问权限的组标识符。类似于 EUIDEGID 可以通过 setgid 程序来修改以便临时提升执行程序的用户的组权限。
典型用法如果一个文件设置了 setgid 位那么当任何用户执行该文件时该进程的 EGID 将被设置为文件所属组的 GID从而赋予该用户临时的组权限。
总结
UID标识用户身份决定用户本身的所有权限。EUID用于实际判断用户的权限可能与 UID 不同。GID标识用户所属的组决定用户组的权限。EGID用于实际判断用户的组权限可能与 GID 不同。
在权限管理中UID 和 GID 决定了用户和组的基本身份和权限而 EUID 和 EGID 决定了用户在特定情境下如执行带有 setuid 或 setgid 标志的程序时所拥有的实际权限。这些标识符是 UNIX 权限模型的重要组成部分用于确保系统资源的安全访问和管理。
有效用户为 root 的进程称为特权进程privileged processes
下边一组函数可以获取和设置当前进程的UID、EUID、GID、EGID
#include sys/types.h
#include unistd.h
uid_t getuid(); //获取真实用户ID
uid_t geteuid(); //获取有效用户ID
gid_t getgid(); //获取真实组ID
gid_t getegid(); //获取有效组IDint setuid(uid_t uid); //设置真实用户ID
int seteuid(uid_t uid); //设置有效用户ID
int setgid(gid_t gid); //设置真实组ID
int setegid(gid_t gid); //设置有效组ID
测试 UID 和 EUID 的区别
新建一个 test_uid.cpp 文件并写入下面的代码
#include unistd.h // unistd.hUnix Standard Definitions头文件提供对POSIX操作系统API的访问主要用于提供对POSIX操作系统API的函数原型、符号常量等。
#include stdio.h // stdio.hStandard Input Output Header头文件提供了进行输入和输出操作的函数。int main(){uid_t uid getuid();uid_t euid geteuid();printf(userid :%d , euid: %d \n, uid, euid);return 0;
}
编译该文件
gcc test_uid.cpp -o test_uid
运行可执行文件
./test_uid 修改目标文件的所有者为 root
sudo chown root:root test_uid
设置目标文件的 set-user-id 标志
sudo chmod s test_uid
重新执行该文件无需再次编译 修改后进程的用户 ID 是启动程序的用户 ID1000而有效用户 ID 是文件所有者的 ID0这里为 root 账户。
进程间关系
进程组
Linux下的每一个进程都隶属于一个进程组因此他们除了 PID 信息外还有进程组IDPGID。
获取指定进程的 PGID
#include unistd.h
pid_t getpgid(pid_t pid);
设置 PGID
int setpgid(pid_t pid, pid_t pgid);
每个进程组都有一个首领进程其 PGID 和 PID 相同。一个进程只能设置自己或其子进程的 PGID。
会话
一些有关联的进程组将形成一个会话session。
创建一个会话
#include unistd.h
pid_t setsid(void);
注意该函数不能由进程组的首领进程调用否则会出错。对于非组首领的进程来说调用该函数会生成一个新的会话并且
调用进程成为会话的首领此时进程是新会话的唯一成员新建一个进程组其 PGID 就是调用该函数的进程PID调用进程成为首领调用进程甩开终端若有。
新的会话不会有控制终端controlling terminal。如果调用进程原本有控制终端那么它会被与控制终端分离。
Linux并没有提供会话IDSID的概念但 Linux 系统认为会话ID等同于会话首领所在进程组的 PGID并提供如下函数来读取 SID
#include unistd.h
pid_t getsid(pid_t pid);
使用 setsid() 创建一个新的会话和进程组
#include stdio.h
#include unistd.h
#include stdlib.hint main() {pid_t pid;// 创建一个子进程pid fork();printf(Child Process ID: %d\n, pid);if (pid 0) {perror(fork failed);exit(EXIT_FAILURE);} else if (pid 0) {// 父进程退出使子进程成为孤儿进程exit(EXIT_SUCCESS);}// 子进程开始执行创建新的会话pid_t sid setsid();if (sid 0) {perror(setsid failed);exit(EXIT_FAILURE);}// 此时进程已经成为新的会话和进程组的首领printf(New session ID: %d\n, sid);// 继续执行其他代码...return 0;
}编译运行
gcc create_sid.cpp -o create_sid
./create_sid fork() 是一个在 UNIX 和类 UNIX 操作系统中用于创建新进程的系统调用。调用 fork() 后操作系统会创建一个新的进程称为子进程这个子进程是调用进程父进程的副本除了一些特定的区别外子进程几乎完全继承了父进程的上下文。
create_sid.cpp 的执行逻辑
在调用 fork() 之前只有一个进程在运行这个进程是你的程序的父进程。当 fork() 被调用时操作系统会复制当前进程的全部内容从而创建一个几乎完全相同的子进程。fork() 成功时父进程会收到子进程的 PID而子进程会收到 0。此时printf(Child Process ID: %d\n, pid); 会在父进程和子进程中都执行打印不同的 PID 值。在代码中父进程收到这个返回值后执行 exit(EXIT_SUCCESS);表示父进程正常退出。父进程退出后子进程就成为了一个孤儿进程因为它的父进程不再存在但操作系统会将孤儿进程重新分配给 init 或 systemd 进程来管理。子进程在父进程退出后继续运行并调用 setsid() 来创建一个新的会话。调用 setsid() 后子进程将成为新会话的会话首领session leader并创建一个新的进程组其中该子进程是进程组的组首领group leader。
用ps命令查看进程关系
用 ps 命令可以查看进程、进程组和会话之间的关系
ps -o pid,ppid,pgid,sid,comm | less 我们在 bash 下执行 ps 和 less 命令所以 ps 和 less 的父进程是 bashbash 的 PID 为 264971而 ps 和 less 的 PPID 也为 264971。
这三条命令创建了一个会话SID 264971两个进程组PGID 264971, 266761。
bash 既是会话首领也是进程组 264971 的首领。
进程组 266761 的首领是 ps。
| less: 这是一个分页工具允许你逐页查看命令输出的内容。使用 less 可以方便地查看长输出内容而不会直接在终端上滚动过去。
系统资源限制
Linux上运行的程序会受到资源限制的影响。如物理设备限制CPU数量内存数量系统策略限制CPU时间具体实现限制文件名的最大长度等。
Linux 系统资源限制可以通过如下一对函数读取和设置
#include sys/resource.h
//成功返回0失败返回-1并设置errno
int getrlimit(int resurce, struct rlimit *rlim);
int setrlimit(int resurce, const struct rlimit *rlim);
rlimit 结构体
struct rlimit {//rlim_t 描述资源级别rlim_t rlim_ur; //软限制rlim_t rlim_max; //硬限制
}
改变工作目录和根目录
工作目录和根目录概念
在操作系统中尤其是 UNIX 和类 UNIX 系统中工作目录和根目录是两个非常重要的概念它们与文件系统的组织和进程的操作密切相关。
工作目录Working Directory
工作目录有时也称为当前目录是指一个进程当前所在的目录。所有相对路径的文件操作如打开文件、读取文件等都是相对于工作目录进行的。
特性: 每个进程都有一个工作目录。进程可以通过系统调用 chdir() 或命令 cd 来改变其工作目录。当你在终端中打开一个 shell 时通常 shell 的工作目录最初是用户的主目录如 /home/username。在编写程序时如果使用相对路径如 ./file.txt系统会从当前工作目录开始查找文件。
查看和更改: 使用 pwd 命令可以查看当前工作目录。使用 cd 命令可以更改当前工作目录。例如cd /var/log 会将工作目录更改为 /var/log。
示例: 根目录Root Directory
根目录是文件系统的最顶层目录用 / 表示。在 UNIX 和类 UNIX 系统中根目录是文件系统的起点所有文件和目录都位于根目录之下。
特性: 根目录是文件系统层级结构的起点没有父目录。所有其他目录如 /home、/etc、/usr都是从根目录派生出来的。在系统启动时操作系统会挂载根文件系统根目录是整个文件系统的基础。
根目录与工作目录的区别: 根目录是文件系统的最顶层是绝对路径的起点。工作目录是进程当前正在操作的目录可以在文件系统的任何位置。
示例:
cd /
ls
在这个示例中使用 cd / 切换到根目录并使用 ls 列出了根目录下的文件和子目录。 总结
工作目录是当前进程正在操作的目录所有相对路径的操作都是基于工作目录。根目录是文件系统的最顶层目录用 / 表示是绝对路径的起点。
有些服务器程序还需要改变工作目录和根目录比如web服务器的逻辑根目录不是文件系统的根目录/而是站点的根目录对于 Linux 上的 Web 服务来说该目录一般是/var/www/。
获取进程当前工作目录改变进程工作目录的函数分别是
#include unistd.h
char* getcwd(char* buf, size_t size);
int chdir(const char* path);
改变进程根目录的函数
#include unistd.h//成功返回0失败返回-1并设置errno
int chroot(const char* path);
只有特权进程才能改变根目录。
服务器程序后台化
下面代码实现了如何让一个进程以守护进程的方式运行
bool daemonize() {// 创建子进程关闭父进程这样子进程就不是进程组首进程就可以调用setsid了pid_t pid fork();if (pid 0) {return false;} else if (pid 0) {exit(0);}/* 设置文件权限掩码。当进程创建新文件使用open()系统调用文件的权限将是mode 0777 */umask(0);// 创建新会话本进程将成为进程组的首领pid_t sid setsid();if (sid 0) {return false;}// 切换工作目录防止当前工作目录所在文件系统不能卸载if ((chdir(/)) 0) {return false;}// 关闭所有文件描述符close(STDIN_FILENO);close(STDOUT_FILENO);close(STDERR_FILENO);// 此处省略了关闭其他已打开的文件描述符的代码// 将标准输入、标准输出、标准错误重定向到/dev/null文件open(/dev/null, O_RDONLY);open(/dev/null, O_RDWR);open(/dev/null, O_RDWR);return true;
}
Linux 提供了完成同样功能的库函数
#include unistd.h
int daemon(int nochdir, int noclose);
这个 daemonize() 函数执行了创建守护进程的标准步骤
通过 fork() 创建一个子进程并让父进程退出。通过 setsid() 创建一个新的会话脱离控制终端并成为会话首领。设置文件权限掩码和切换工作目录。关闭标准文件描述符并将它们重定向到 /dev/null。
如果所有步骤成功则返回 true 表示守护进程创建成功。否则返回 false。这段代码展示了将一个普通进程转化为守护进程的标准方法是在后台运行长时间任务的基础方法之一。
接下来对上面代码进行解释
代码解释
创建子进程并关闭父进程
pid_t pid fork();
if (pid 0) {return false;
} else if (pid 0) {exit(0);
}
fork() 创建一个子进程。如果 fork() 失败返回值为负则返回 false 表示守护进程创建失败。
如果 fork() 成功父进程收到子进程的 PID 并退出 (exit(0))子进程继续执行。
这样做的目的是让子进程成为孤儿进程由 init 或 systemd 进程接管从而保证守护进程在父进程结束后仍然继续运行。
设置文件权限掩码
umask(0) 清除文件模式创建掩码确保进程创建的文件权限不受父进程的文件权限掩码影响。这样进程创建的文件将具有最大的权限取决于创建文件时指定的权限。
创建新会话
pid_t sid setsid();
if (sid 0) {return false;
}
setsid() 创建一个新的会话使当前进程成为会话的首领并与原来的控制终端分离。该进程成为新会话的会话首领和进程组的首领并且没有控制终端。
切换工作目录
if ((chdir(/)) 0) {return false;
}
chdir(/) 将工作目录切换到根目录 /。这样做的目的是避免当前工作目录所在的文件系统不能卸载。
关闭所有文件描述符
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
关闭标准输入STDIN_FILENO、标准输出STDOUT_FILENO和标准错误STDERR_FILENO的文件描述符以避免守护进程不小心使用这些文件描述符。
重定向标准输入、输出和错误
open(/dev/null, O_RDONLY);
open(/dev/null, O_RDWR);
open(/dev/null, O_RDWR);
将标准输入重定向到 /dev/null并将标准输出和标准错误也重定向到 /dev/null。/dev/null 是一个特殊的文件读取它会返回 EOF写入它的数据将被丢弃。这样可以确保守护进程不向任何终端输出信息也不会从任何终端读取输入。
参考文章
Linux高性能服务器编程-游双——第七章 Linux服务器程序规范_linux高性能服务器编程 pdf-CSDN博客Linux高性能服务器编程 学习笔记 第七章 Linux服务器程序规范-CSDN博客