APUE(8)---进程控制(1)
一、进程标识
每个进程都有一个非负整型标识的唯一进程ID。因为进程ID标识符总是唯一的,常将其用做其他标识符的一部分以保证其唯一性。进程ID虽然是唯一的, 但是却是可以复用的。ID为0的进程通常是调度进程,常常被称为交换进程(swapper)。该进程是内核的一部分,它并不执行任何磁盘上的程序,因此也被称为系统进程。进程ID为1通常是init进程,在自举过程结束时由内核调用。此进程负责在自举内核后启动一个UNIX系统,init通常读取与系统有关的初始化文件,并将系统引导一个状态。init进程绝不会终止。它是一个普通的用户进程,但是它以超级用户特权运行。进程ID为2是页守护进程,此进程负责支持虚拟存储器系统的分页操作。
#include <unistd.h>
pid_t getpid(void); //返回进程的进程ID
pid_t getppid(void); //返回进程的父进程ID
pid_t getuid(void); //返回进程的实际用户ID
pid_t geteuid(void); //返回调用进程的有效用户ID
pid_t getgid(void); //返回进程的实际组ID
pid_t getepid(void); //返回进程的有效组ID
二、函数fork
#include <unistd.h>
pid_t fork(void);
由fork创建的新进程被称为子进程(child process)。fork函数被调用一次,但返回两次。两次返回的区别是子进程的返回值是0,而父进程的返回值则是新建子进程的进程ID。原因:因为一个进程的子进程可以有很多个,并且没有一个函数使一个进程可以获得其所有子进程的进程ID;一个进程只会有一个父进程,所以子进程总是可以调用getppid以获得其父进程的进程ID。子进程是父进程的副本,子进程获得父进程数据空间、堆和栈的副本,父进程和子进程不共享这些存储空间部分。父进程和子进程只共享正文段。由于在fork之后经常跟随着exec,所以现在的很多实现并不执行一个父进程数据段和栈和堆的完全副本,作为替代,使用了写时复制(Copy-On-Write)技术。
#include "apue.h"
int globvar = ;
char buff[] = "a write to stdout\n"; int main(void)
{
int var;
pid_t pid; var = ;
if(write(STDOUT_FILENO, buf, sizeof(buf) - ) != sizeof(buf) - )
{
err_sys("write error");
} printf("before fork\n"); if((pid = fork()) < )
{
err_sys(" fork error");
}
else if(pid == )
{
globvar ++;
var ++;
}
else
{
sleep();
} printf("pid = %ld, glob = %d, var = %d\n", (long)getpid(), globvar, var);
exit();
}
8-1:fork函数实例
fork的一个特性是父进程的所有打开文件描述符都被复制到子进程中。我们说赋值是因为对每个文件描述符来说,就好像执行了dup函数,父进程和子进程每个相同的打开描述符共享一个文件表项。重要的一点是:父进程和子进程共享同一个文件偏移量。
8-2: fork之后父进程和子进程之间对打开文件的共享
除了打开文件之外,父进程的很多其他属性也由子进程继承:实际用户ID、实际组ID、有效用户ID、有效组ID;附属组ID;进程组ID;会话ID;控制终端;设置用户ID标志和设置组ID标志;当前工作目录;根目录;文件模式创建屏蔽字;信号屏蔽和安排;对任一打开文件描述符的执行时关闭标志;环境;连接的共享存储段;存储映像;资源限制。父进程和子进程之间的区别在于:fork的返回值不同;进程ID不同;两个进程的父进程ID不同;自己称tms_utime、tms_stime、tms_ustime的值设置为0;子进程不继承父进程设置的文件所;子进程的未处理闹钟被清除;子进程的未处理信号集设置为空集。
fork失败的两个主要原因:系统中已经有了太多的进程;该实际用户ID的进程总数超过了系统限制。fork有以下两个用法:一个父进程希望复制自己,使父进程和子进程执行不同的代码段;一个进程要执行一个不同的程序。
三、函数vfork
vfork与fork一样都创建一个子进程,但是它并不将父进程的地址空间完全复制到子进程中,因为子进程会立即调用exec,预示也就不会引用该地址空间;vfork和fork之间的另一个区别是:vfork保证子进程先运行,在它调用exec或exit之后父进程才可能被调度运行,当子进程调用这两个函数中的任意一个时,父进程会恢复运行。
#include "apue.h" int globvar = ;
int main(void)
{
int var;
pid_t pid; var = ;
pirntf("before vfork\n");
if((pid = vfork()) < )
{
err_sys("vfork error");
}
else if(pid == )
{
globvar ++;
var ++;
_exit();
} pirntf("pid = %ld, glob = %d, var = %d\n",(long)getpid(), globvar, var);
exit();
}
8-3: vfork函数实例
四、函数exit
在大多数UNIX系统实现中,exit是标准C库中的一个函数,而_exit则是一个系统调用。不管进程如何终止,最后都会执行内核中的同一段代码。这段代码为相应进程关闭所有打开描述符,释放它所使用的存储器等。在任意一种情况下,该终止进程的父进程都能用wait或waipid函数取得其终止状态。对于父进程已经终止的所有进程,它们的父进程都改变为init进程。我们称这些进程由init进程手痒。在UNIX术语中,一个已经终止、但是父进程尚未对其进行善后处理(获取终止子进程的有关信息、释放它仍占用的资源)的进程被称为僵死进程。
五、函数wait和waitpid
当一个进程正常或异常终止时,内核就向父进程发送SIGCHLD信息。因为子进程终止是个异步事件,所以这种信号也是内核向父进程发的异步通知。父进程可以选择忽略该信号,或者提供一个该信号发生时即被调用执行的函数,对这种信号的系统默认动作是忽略它。
#include <sys/wait.h>
pid_t wait(int *statloc);
pid_t waitpidd(pid_t pid, int *statloc, int options);
如果其所有子进程都还在运行,则阻塞;如果一个子进程已终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回;如果它没有任何子进程,则立即出错返回。
#include "apue.h"
#include <sys/wait.h> void pr_exit(int status); int main(void)
{
pid_t pid;
int status; if((pid = fork) < )
{
err_sys("fork error");
}
else if(pid == )
{
exit();
} if(wait(&status) != pid)
{
err_sys("wait error");
}
pr_exit(status); if((pid = fork) < )
{
err_sys("fork error");
}
else if(pid == )
{
abort();
} if(wait(&status) != pid)
{
err_sys("wait error");
}
pr_exit(status); if((pid = fork) < )
{
err_sys("fork error");
}
else if(pid == )
{
status /= ;
} if(wait(&status) != pid)
{
err_sys("wait error");
}
pr_exit(status); exit();
} void pr_exit(int status)
{
if(WIFEXITED(status))
{
printf("normal termination, exit status = %d\n", WEXITSTATUS(status));
}
else if(WIFSIGNALED(status))
{
printf("abnormal termination, signal number = %d\n", WTERMSIG(status),
#ifdef WCORDUMP
WCORDUMP(status)? "core file generated":"");
#else
"");
#endif
}
else if(WIFSTOPPED(status))
{
printf("child stopped, signal number = &d\n", WSTOPSIG(status));
}
}
8-6:演示不同的exit值
如果一个进程有几个子进程,那么只要有一个子进程终止,wait就返回。waitpid函数提供了wait函数没有提供的3个功能:1、waitpid可等待一个特定的进程,而wait则返回任一终止子进程的状态;2、waitpid提供了一个wait的非阻塞版本;3、waitpid通过WUNTRACED和WCONTINUED选项支持作业控制。
#include "apue.h"
#include <sys/wait.h> int main(void)
{
pid_t pid;
if((pid = fork()) < )
{
err_sys("fork error");
}
else if(pid == )
{
if((pid = fork()) < )
{
err_sys("fork error");
}
else if(pid > )
{
exit();
} sleep();
printf("second child, parent pid = %ld\n",(long) getppid());
exit();
} if(waitpid(pid, NULL, ) != pid)
{
err_sys("waitpid error");
} exit();
}
8-8:fork两次以避免僵死进程
六、函数exec
当进程调用一种exec函数时,该进程执行的程序完全替换为新程序,而新程序则从其main函数开始执行。因为调用exec并不创建新进程,所以前后的进程ID并未改变。exec只是用磁盘上的一个新程序替换了当前进程的正文段、数据段、堆段和栈段。用fork可以创建新进程,用exec可以初始化执行新的程序。exit函数和wait函数处理终止和等待终止。这些是我们需要的基本的进程控制原语。
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *file, char *const argv[], char *const envp[]);

8-15:7个exec函数之间的关系
#include "apue.h"
#include <sys/wait.h> char *env_init[] = {"USER=unknow", "PATH=/tmp", NULL}; int main(void)
{
pid_t pid;
if((pid = fork()) < )
{
err_sys("fork error");
}
else if(pid == )
{
if(execle("/home/sar/bin/echoall", "echoall", "myarg1", "MY ARG2", (char *), env_init) < )
{
err_sys("execle error");
}
} if(waitpid(pid, NULL, ) < )
{
err_sys("waitpid error");
} if((pid == fork()) < )
{
err_sys("fork error");
}
else if(pid == )
{
if(execlp("echoall", "echoall", "only 1arg", (char *)) < )
{
err_sys("execle error");
}
} exit();
}
8-16:exec函数实例
APUE(8)---进程控制(1)的更多相关文章
- (六) 一起学 Unix 环境高级编程 (APUE) 之 进程控制
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- [08]APUE:进程控制
[a] getpid / getppid / getuid / geteuid / getgid / getegid #include <unistd.h> pid_t getpid(vo ...
- (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- (七) 一起学 Unix 环境高级编程(APUE) 之 进程关系 和 守护进程
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- (十) 一起学 Unix 环境高级编程 (APUE) 之 线程控制
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- 进程控制(Note for apue and csapp)
1. Introduction We now turn to the process control provided by the UNIX System. This includes the cr ...
- apue学习笔记(第八章 进程控制)
本章介绍UNIX系统的进程控制,包括创建新进程.执行程序和进程终止. 进程标识 每一个进程都有一个非负整数表示的唯一进程ID,除了进程ID,每个进程还有一些其他标识符.下列函数返回这些标识符 #inc ...
- 《UNIX环境高级编程》(APUE) 笔记第八章 - 进程控制
8 - 进程控制 Github 地址 1. 进程标识 每个进程都有一个非负整型表示的 唯一进程 ID .进程 ID 是可复用的(延迟复用算法). ID 为 \(0\) 的进程通常是调度进程,常常被称为 ...
- 进程控制之exec函数
用fork函数创建子进程后,子进程往往要调用一种exec函数以执行另一个程序.当进程调用一种exec函数时,该进程执行的程序完全替换为新程序,而新程序则从其main函数开始执行.因为调用exec并不创 ...
随机推荐
- Redis在.NET环境下实践篇(一)
redis简介 Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,和Memcached类似,它支持存储的value类型相对更多,包括st ...
- 移动终端App测试点归纳
以下所有测试最后必须在真机上完整的执行. 1 安装.卸载测试 1.1 在真机上.第三方软件(xy苹果助手.91.安卓助手)的安装与卸载 1.2 安装在手机卡上 或 SD卡上 (不同的IOS和安卓版本) ...
- 一,我的Android Studio 3.0.1 安装过程
安装成功于20171231的0:46分. 简要记录我的安装过程如下: 一,安装JDK1.8.X 二,安装ANDROID STUDIO.ZIP 三,运行AS,后按提示下载SDK,NDK,必要时设置一下J ...
- django之设置分页
分页 Django提供了一些类实现管理数据分页,这些类位于django/core/paginator.py中 Paginator对象 Paginator(列表,int):返回分页对象,参数为列表数据, ...
- Android 4 学习(21):对话框
对话框 创建Dialog的两种方式: 1. 使用Dialog类或其子类,包括DialogFragment 2. 在Activity中使用Dialog主题(theme) 下面是使用Dialog类的一个例 ...
- Rhythmk 一步一步学 JAVA (19) JAVA IO 文件常用操作
package com.rhythmk.filedemo; import java.io.BufferedReader; import java.io.File; import java.io.Fil ...
- ArcGIS 二次开发增加工具栏按钮时,给按钮图标设置失败
在类的构造函数中通过下面的代码获得的bitmap为null: string bitmapResourceName = GetType().Name + ".bmp"; base.m ...
- 针对android&ios yuv旋转、镜像、格式转换、裁剪 算法实现<转>
http://blog.csdn.net/dangxw_/article/details/50903693
- Python基础补充(二) 多核CPU上python多线程并行的一个假象【转】
在python上开启多个线程,由于GIL的存在,每个单独线程都会在竞争到GIL后才运行,这样就干预OS内部的进程(线程)调度,结果在多核CPU上: python的多线程实际是串行执行的,并不会同一时间 ...
- Spring 学习记录6 BeanFactory(2)
主题 除了Spring 学习记录5 BeanFactory 里写的几个接口外,BeanFactory的实现类还实现了一些其他接口,这篇文章主要介绍这些接口和实现类. 结构 DefaultListabl ...