《UNIX 环境高级编程》学习笔记——进程控制

《UNIX 环境高级编程》学习笔记——进程控制,第1张

《UNIX 环境高级编程》学习笔记——进程控制

UNIX环境高级编程——进程控制

进程标识函数 fork函数 exit函数 wait 和 waitpid函数 waitid竞争条件函数 exec更改用户ID和更改组ID函数 system进程调度进程时间

进程标识

ID为0的进程,通常是调度进程,常被称为交换进程。
该进程是内核的一部分,它并不执行任何磁盘上的程序,因此也被称为系统进程。

ID为1的进程,通常是init进程,在自举过程结束由内核调用。

init 通常读取与系统有关的初始化文件 (/etc/rc*或/etc/inittab及在/etc/init.d中的文件),并将系统引导到一个状态(如多用户)。

init 进程决不会终止。它是一个普通的用户进程(与交换进程不同,它不是内核中的系统进程),但是它以超级用户特权运行。

除了进程ID,每个进程还有一些其他标识符。下列函数返回这些标识符。

#include 
pid_t getpid(void);  
                                  返回值:调用进程的进程ID
                                  
pid_t getppid(void);
                                  返回值:调用进程的父进程ID
uid_t getuid(void);
                                   返回值:调用进程的实际用户ID

uid_t geteuid(void);
                                    返回值:调用进程的有效用户ID

gid_t getgid(void);
                                    返回值:调用进程的实际组ID

gid_t getegid(void);
                                     返回值:调用进程的有效组ID

函数 fork

一次调用,两次返回。

#include 
pid_t fork(void);
                     返回值:子进程返回0,父进程返回子进程ID;若出错,返回-1

在 fork 之后处理文件描述符有以下两种常见的情况。

(1)父进程等待子进程完成。
在这种情况下,父进程无需对其描述符做任何处理。当子进程终止后,它曾进行过读、写 *** 作的任一共享描述符的文件偏移量已做了相应更新。(2)父进程和子进程各自执行不同的程序段。
在这种情况下,在 fork 之后,父进程和子进程各自关闭它们不需使用的文件描述符,这样就不会干扰对方使用的文件描述符。这种方法是网络服务进程经常使用的。

除了打开文件外,父进程的很多其他属性也由子进程继承,包括:

实际用户ID、实际组ID、有效用户ID、有效组ID附属组ID进程组ID会话ID控制终端设置用户ID标志/设置组ID标志当前工作目录根目录文件模式创建屏蔽字信号屏蔽和安排文件描述符的执行时关闭标志环境连接的共享存储段存储映像资源限制

父进程和子进程之间的区别具体如下。

fork 的返回值不同进程ID不同这两个进程的父进程ID不同:子进程的父进程ID是创建它的进程的ID,而父进程的父进程ID则不变子进程的 tms_utime、tms_stime、tms_cutime 和 tms_ustime 的值设置为0子进程不继承父进程设置的文件锁子进程的未处理闹钟被清除子进程的未处理信号集设置为空集

使 fork 失败的两个主要原因是:

(a)系统中已经有了太多的进程(通常意味着某个方面出了问题)(b)该实际用户ID的进程总数超过了系统限制。

fork 有以下两种用法。

(1)一个父进程希望复制自己,使父进程和子进程同时指向不同的代码段。
这在网络服务进程中是最常见的——父进程等待客户端的服务请求。
当这种请求到达时,父进程调用 fork ,使子进程处理此请求。父进程则继续等待下一个服务请求。(2)一个进程要执行一个不同的程序。
在这种情况下,子进程从 fork 返回后立即调用 exec 。 函数 exit

5种正常终止方式具体如下。

(1)在 main 函数内执行 return 语句。
这等效于调用 exit 。(2)调用 exit 函数。
此函数由 ISO C 定义,其 *** 作包括调用个终止处理程序(终止处理程序在调用 atexit 函数时登记),然后关闭所有标准 I/O流等。(3)调用 _exit 或 _Exit 函数。
ISO C定义 _Exit ,其目的是为进程提供一种无需运行终止处理程序或信号处理程序而终止的方法。(4)进程的最后一个线程在其启动例程中指针 return 语句。
但是,该线程的返回值不用作进程的返回值。当最后一个线程从其启动例程返回时,该进程以终止状态0返回。(5)进程的最后一个线程调用 pthread_exit 函数。
在这种情况下,进程终止状态总是0,这与传送给 pthread_exit 的参数无关。

3种异常终止具体如下。

(1)调用 abort 。
它产生 SIGABRT 信号,这是下一种异常终止的一种特例。(2)当进程接收到某些信号时。
信号可由进程自身(如调用 abort 函数)、其他进程或内核产生。(3)最后一个线程对“取消”请求作出相应。
默认情况下,“取消”以延迟方式发生:一个线程要求取消另一个线程,若干时间之后,目标线程终止。

不管进程如何终止,最后都会执行内核中的同一段代码。这段代码为相应进程关闭所有打开描述符,释放它所使用的存储器等。

内核为每个终止的进程保存了一定量的信息,进程父进程调wait或waitpid时,可得到这些信息。

一个已经终止,但其父进程尚未对其进程善后处理(获取退出信息,释放相关资源)的进程称为僵尸进程。

函数 wait 和 waitpid

进程终止时,内核向其父进程发SIGCHLD信号。

因为子进程终止是个异步事件(这可以在父进程运行的任何时候发生),所以这种信号也是内核向父进程发的异步通知。
父进程可以选择忽略该信号,或者提供一个该信号发生时即被调用执行的函数(信号处理程序)。
对于这种信号的系统默认动作是忽略它。

调用 wait 或 waitpid 的进程可能会发生如下情况:

如果其所有子进程都还在运行,则阻塞。如果一个子进程已终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回。如果它没有任何子进程,则立即出错返回。

#include 
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid, int *statloc, int options);
                                        两个函数返回值:若成功,返回进程ID;若出错,返回0或-1

这两个函数的区别如下。

在一个子进程终止前,wait 使其调用者阻塞,而 waitpid 有一选项,可使调用者不阻塞。waitpid 并不等待在其调用之后的第一个终止子进程,它有若干个选项,可以控制它所等待的进程。

对于 waitpid 函数中 pid 参数的作用解释如下。

pid == -1
等待任一子进程。此种情况下,waitpid 与 wait 等效。pid > 0
等待进程ID与 pid 相等的子进程。pid == 0
等待组ID等于调用进程组ID的任一子进程。pid < -1
等待组ID等于 pid 绝对值的任一子进程。


waitpid 函数提供了 wait 函数没有提供的3个功能。

(1)waitpid 可等待一个特定的进程,而 wait 则返回任一终止子进程的状态。(2)waitpid 提供了一个 wait 的非阻塞版本。有时希望获取一个子进程的状态,但不想阻塞。(3)waitpid 通过 WUNTRACED 和 WConTINUED 选项支持作业控制。 函数 waitid

#include 
int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
                               返回值:若成功,返回0;若出错,返回-1

竞争条件

多个进程/多个线程,相互间执行顺序没有保证的。

如果某些 *** 作有时序要求,则需要加上进程/线程同步机制来保证时序要求。

函数 exec
int execve(const char * pathname, const *const argv[], char *const envp[]);
                                  返回值:若出错,返回-1;若出错,不返回

PATH 变量包含了一张目录表(称为路径前缀),目录之间用冒号( : )分隔。

PATH=/bin:/usr/bin:/usr/local/bin:.

最后的路径前缀 . 表示当前目录。(零长前缀也表示当前目录。在 value 的开始处可用 : 表示,在行中间则要用 : : 表示,在行尾以 : 表示。)

执行exec后,进程ID不变,以下属性也不变:

进程ID,父进程ID实际用户ID,实际组ID附属组ID进程组ID会话ID控制终端闹钟剩余时间当前工作目录根目录文件模式创建屏蔽字文件锁进程信号屏蔽未处理信号资源限制友好值tms_utime, tms_stime, tms_cutime及tms_cstime 值

文件描述符是否保留依赖其FD_CLOEXEC标志是否设置。
有效用户ID、有效组ID是维持不变,还是变为可执行文件所有者ID、组所有者ID,依赖可执行文件的设置用户ID,设置组ID位是否设置。

更改用户ID和更改组ID
#include 
int setuid(uid_t);
int setgid(gid_t);
                              两个函数返回值:若成功,返回0;若出错,返回-1

进程的实际用户ID、实际组ID是登录是依据登录用户设置
如登录后,有更改需求,必须是超级用户来更改进程的有效ID、有效组ID。
默认下与进程的实际用户ID、实际组ID一致
特殊情况是,可执行文件模式有设置用户ID、设置组ID,则执行此可执行文件的进程有效ID、有效组ID采用此可执行文件的用户ID、组ID。
setuid、setgid对普通用户,只能用于将有效ID/有效组ID设为和实际ID、实际组ID一致。保存的用户ID、组ID为后续添加的特性,暂不考虑。

函数 system

相当于让shell执行命令字符串。

#include 
int system(const char *cmdstring);

因为 system 在其实现中调用了 fork 、exec 和 waitpid ,因此有3中返回值。

(1)fork 失败或者 waitpid 返回除 EINTR 之外的出错,则 system 返回-2,并且设置 errno 以指示错误类型。(2)如果 exec 失败(表示不能执行 shell),则其返回值如同 shell 执行了 exit(127) 一样。(3)否则所有3个函数 (fork、exec 和 waitpid )都成功,那么 system 的返回值是 shell 的终止状态,其格式已在 waitpid 中说明。 进程调度

nice 值越小,优先级越高。

#include 
int nice(int incr);
                            返回值:若成功,返回新的 nice 值 NZERO;若出错,返回-1

getpriority 函数可以像 nice 函数那样用于获取进程的 nice 值, 但是 getpriority 还可以获取一组相关进程的 nice 值。

#include 
int getpriority(int which, id_t who);
                                  返回值:若成功,返回 -NZERO~NZERO-1 之间的 nice 值;若出错,返回-1

setpriority 函数可用于为进程、进程组合属于特定用户ID的所有进程设置优先级。

#include 
int setpriority(int which, id_t who, int value);
                                             返回值:若成功,返回0;若出错,返回-1
进程时间

可以度量的3个时间:墙上时钟时间、用户CPU时间和系统CPU时间。
任一进程都可调用 times 函数获得它自己以及已终止子进程的上述值。

#include 
clock_t times(struct tms *buf);
                                返回值:若成功,返回流逝的墙上时钟时间(以时钟滴答数为单位);若出错,返回-1

学习参考资料:

《UNIX 环境高级编程》第3版

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5704561.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存