五、进程管理

进程:它由数据、资源、状态和一个虚拟计算机组成。

进程ID

每个进程都有一个唯一识别的进程ID,简称pid。
空闲进程:当没有其他进程运行时,内核所运行的进程它的pid为0。
在启动后内核第一个运行的进程为init进程,它的pid为1。
内核分配进程ID是以严格的线性函数方式进行的。如果上一个进程的ID为17则下一个分配就是18,若17进程以及不存在了,知道内核分配pid达到了/proc/sys/kernel/pid_max内核是不会重用以前已经分配过的值。

进程体系

创建进程的那个进程称为父进程,而新进程称为子进程。这种关系保存在每个进程的父进程ID号(ppid)中。每个进程都被一个用户和组所拥有。

#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void); //获取进程ID
pid_t getppid(void); //获取父进程ID

写时复制

在现代Unix系统中,如Linux子进程保存一个指向资源的指针,只有当需要修改资源的时候才将资源复制一份。

vfork()

vfork()成功调用产生的结果和fork()相同,vfork()会挂起父进程知道子进程终止或运行了一个新的可执行文件的映像。vfork()避免了地址空间的按页复制。父进程和子进程共享相同的地址空间和页表项。子进程不能修改地址空间中的任何内存。

pid_t vfork(void);

exit()

void exit(int status);

终止进程,用来标识进程退出的状态,status & 0377这个值返回给父进程。
当进程退出时,内核会清理进程所创建的、不再用到的任何资源。

waitid()等待子进程结束

#include <sys/wait.h>
int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);

和wait()和waitpid()一样,waitid()作用是等待子进程结束和了解子进程的状态。waitid()允许程序员指定所要等待的子进程。参数idtype和id来指定要等待的子进程。idtype的值是下面:

P_PID 等待pid值是id的子进程
P_GID 等待进程组ID是id的那些子进程
P_ALL 等待所有子进程,参数id被忽略

参数id是id_t类型,代表一种通用的ID号,id_t类型值足够大可以保存任何pid_t值。参数options是一下的选项

WEXITED 调用进程会等待结束的子进程(有id和idtype指定)
WSTOPPED 调用进程会等待收到了信号而停止了执行的子进程
WCONTINUED 调用进程会等待收到了信号而继续执行子进程
WNOHANG 调用进程不会阻塞,如果没有子进程结束,它就立即返回
WNOWAIT 调用进程不会移除满足条件的子进程的僵死状态

成功时会填充参数infop,它必须指向一个有效的siginfo_t类型。siginfo_t结构体的具体成员是与实现相关的。但是还是有一些成员是在waitid()调用之后才有效的。也就是成功的调用会保证下面的成员会被填充:

si_pid 子进程pid
si_uid 子进程uid
si_code 根据子进程的状态是被信号所终止、杀死、停止或者继续执行而分别设置CLD_EXITED、CLD_KILLED、CLD_STOPPED或者CLD_CONTINUED中的一个
si_signo 设置为SIGCHILD
si_status 如果si_code是CLD_EXITED,它是子进程的退出值。否则,它引起状态改变的那个信号的编码。

wait3()和wait4()

#include<sys/types.h>
#include<sys/time.h>
#include<sys/resource.h>
#include<sys/wait.h>

pid_t wait3(int *status, int options, struct rusage *rusage);
pid_t wait4(pid_t pid, int *status, int options, struct rusage *rusage);
pid = wait3(status, options, NULL);
//等价于
pid = waitpid(-1, status, options);

pid = wait4(pid, status, options, NULL);
//等价于
pid = waitpid(pid, status, optinos);

子进程返回的值可以用以下的宏

#include <sys/wait.h>
int WIFEXITED(status);
int WIFSIGNALED(status);
int WIFSTOPPED(status);
int WIFCONTINUED(status);
int WEXITSTATUS(status);
int WTETMSIG(status);
int WSTOPSIG(status);
int WCOREDUMP(status);

这里写图片描述

创建并等待一个新进程

创建一个进程然后就立刻开始等待它的结束

#define _XOPEN_SOURCE
#include <stdlib.h>
int system(const char *command);

system()来运行一个简单的工具程序或者shell脚本,它的调用使command指定的程序得到执行

用户和用户组

进程相关的用户ID:

实际用户ID:运行进程的用户ID
有效用户ID:当前进程使用的用户ID
保存设置的用户ID:进程原先的有效用户ID
文件系统用户ID

更改用户ID和组ID

#include <sys/types.h>
#include <unistd.h>
int seteuid(uid_t euid); //设置有效用户ID
int setguid(gid_t egid);
BSD版本

设置实际用户ID和游侠用户ID

#include <sys/types.h>
#include <unistd.h>
int setreuid(uid_t ruid, uid_t euid);
int setregid(gid_t rgid, gid_t egid);

获得用户ID和组ID

//返回实际用户ID和组ID
uid_t getuid();
gid_t getgid();
//返回有效用户ID和组ID
uid_t geteuid();
gid_t getegid();

会话和进程组

每个进程组ID(pgid)唯一标识。会话首进程的pid就是会话ID,进程组ID就是组长进程pid。
守护进程会创建自己的会话。
这里写图片描述

创建会话

#include <unistd.h>
pid_t setsid(void);

如果调用进程不是某个进程的组长进程那么会创建一个新会话。调用进程就是会话进程的唯一进程也是新会话的首进程。setsid创建新会话,并在其中创建一个新的进程组,使得调用进程称为新会话的首进程和新进程组的组长进程。对于守护进程来说很有用。

守护进程

守护进程运行在后台,不与任何控制终端相关联。通常在系统启动时就运行,它们以root用户运行或者其他特殊用户,并处理一些系统级任务。
守护进程必须是init进程的子进程,并且不予任何控制终端相关联。
一般来说以下步骤会成为守护进程

(1)调用fork()创建新的进程,它会是将来的守护进程。
(2)在守护进程的父进程中调用exit()。
(3)调用setsid(),使得守护进程有一个新的进程组和新的会话。
(4)用chdir()将当前工作目录改为根目录。因为之前fork继承了父进程的工作目录。
(5)关闭所有的文件描述符。不需要继承任何打开的文件描述符。
(6)打开0、1、2号文件描述符重定向到/dev/null
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。在编写C程序时,需要注意变量的声明和定义、指针的使用、内存的分配与释放等问题。C语言中常用的数据结构包括: 1. 数组:一种存储同类型数据的结构,可以进行索引访问和修改。 2. 链表:一种存储不同类型数据的结构,每个节点包含数据和指向下一个节点的指针。 3. 栈:一种后进先出(LIFO)的数据结构,可以通过压入(push)和弹出(pop)操作进行数据的存储和取出。 4. 队列:一种先进先出(FIFO)的数据结构,可以通过入队(enqueue)和出队(dequeue)操作进行数据的存储和取出。 5. 树:一种存储具有父子关系的数据结构,可以通过中序遍历、前序遍历和后序遍历等方式进行数据的访问和修改。 6. 图:一种存储具有节点和边关系的数据结构,可以通过广度优先搜索、深度优先搜索等方式进行数据的访问和修改。 这些数据结构在C语言中都有相应的实现方式,可以应用于各种不同的场景。C语言中的各种数据结构都有其优缺点,下面列举一些常见的数据结构的优缺点: 数组: 优点:访问和修改元素的速度非常快,适用于需要频繁读取和修改数据的场合。 缺点:数组的长度是固定的,不适合存储大小不固定的动态数据,另外数组在内存中是连续分配的,当数组较大时可能会导致内存碎片化。 链表: 优点:可以方便地插入和删除元素,适用于需要频繁插入和删除数据的场合。 缺点:访问和修改元素的速度相对较慢,因为需要遍历链表找到指定的节点。 栈: 优点:后进先出(LIFO)的特性使得栈在处理递归和括号匹配等问题时非常方便。 缺点:栈的空间有限,当数据量较大时可能会导致栈溢出。 队列: 优点:先进先出(FIFO)的特性使得

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值