wait()
#include<sys/wait.h>
pid_t wait(int *status);
复制代码
- 阻塞直到一个子进程终止
- status非空则子进程终止信息经过status指向的int变量返回
- 内核为父进程下全部子进程的运行总量追加进程CPU时间和资源使用数据
- 终止子进程ID做为返回
- 若是
wait()
返回-1,可能的缘由是无等待子进程,此时errno会被置位ECHILD
局限性linux
- 没法等待某个特定子进程完成
- 没法非阻塞等待
- 只能检测终止,对于中止或其余情况没法检测
waitpid()
#include<sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
复制代码
- status 和 返回值和
wait()
一致- pid>0,为等待进程的pid
- pid=0,为父进程同一进程组的全部子进程
- pid<-1,取绝对值
- pid=-1,为任意子进程,
wait(&status) eq waitpid(-1, &status, 0)
- option = WUNTRACED:除了子进程终止信息外,还返回因信号而中止的进程信息
- option = WCONTINUED:返回收到SIGCONT信号而恢复执行的已中止进程的状态信息
- option = WNOHANG:指定子进程未发生改变马上返回不会阻塞,若是无匹配pid的子进程,
waitpid()
报错,错误号设置为ECHILD- 克隆子进程会有option扩展,见28章
<sys/wait.h>
定义了一组宏对status处理shell
- WIFEXITED(status):子进程正常退出返回true
- WIFSIGNALED(status):经过信号杀掉子进程为true,WTERMSIG(status)返回信号编号,WCOREDUMP(status)true代表生成核心转储文件
- WIFSTOPPED(status):因信号中止返回true, WSTOPSIG(status)返回信号编号
- WIFCONTINUED(status):因受到SIGCONT恢复执行返回true
若是在函数中调用_exit(EXIT_SUCCESS)父进程会认为子进程正常终止编程
// 函数应当以下,wait才能正常捕捉是信号致使终止
void handler(int sig){
/*func body*/
signal(sig, SIG_DFL);
raise(sig);
}
复制代码
waittid()
,wait3()
,wait4()
waittid()
更精准的控制,略
孤儿进程: 父进程终止后,init进程接管子进程,
getppid()
返回1 僵尸进程: 在父进程调用wait()
以前,子进程就已经终止bash
- 系统容许父进程在以后某一时刻执行
wait()
,肯定子进程为什么- 内核经过将子进程转化为僵尸进程处理此状况,内核将释放子进程大部分资源,仅保留内核进程表中的一条记录(包含子进程ID,终止状态,资源使用数据)
- 没法经过信号杀死僵尸进程,即便SIGKILL也不行
- 父进程调用
wait()
,或者父进程未调用后退出,init接管后自动调用wait()
,从系统移除僵尸进程
在一个进程终止或者中止时,将SIGCHLD信号发送给其父进程函数
若是每次收到SIGCHLD的时调用
wait()
由于在调用时对同一信号(SIGCHLD)要么忽略要么阻塞,阻塞的话标准信号之会保留一次,可能会遗漏SIGCHLD信号。工具
一个比较丑的解决僵尸进程的方法(调用前有僵尸进程产生不能解决)性能
while(waitpid(-1, NULL, WNOHANG) > 0)
continue;
复制代码
一些特殊spa
- 一般子进程中止也会向父进程发送SIGCHLD
- 经过
sigaction()
调用,传入SA_NOCLDSTOP,可取消这个行为- 虽然默认SIGCHLD是忽略
- 若是显示设置SIGCHLD为SIG_IGN,以后不会把符合条件的子进程其转化为僵尸进程,而是直接删除(有些UNIX实现可能会把以前僵尸进程给删除)
- 经过
sigaction()
调用,传入SA_NOCLDWAIT,也可终止僵尸进程的产生,可是不保证是否发送SIGCHLD信号
#include<unistd.h>
int execve(const char *pathname, char *const argv[], char *const envp[]) // envp 成员字符串格式为name=value 复制代码
- 若是pathname文件设置了set-user-id权限位,会将有效用户ID设为文件属主ID
- 不论有没set-user-id,均会将当前进程的有效用户id覆盖已保存的set-user-id
默认由
exec()
调用程序所打开的全部文件描述符在exec()
执行过程当中都会保持打开线程
$ ls /tmp > dit.txt
复制代码
execlp()
复制代码
- 调用fork()建立子进程
- 子shell进程以描述符1打开dir.txt用于输出,打开的方式可能以下:
- 子shell关闭描述符1,随机打开文件dir.txt,由于open老是取最小值,标准输入0此时已经打开
- 打开dir.txt 若是获取的描述符不是标准输出,shell会调用dup2强制将标准输出复制为新描述符的副本
dup2(fd, STDOUT_FILENO)
, 以后再close(fd)
- 子进程执行ls, 将结果输出到标准输出
shell有内建命令如
cd
,不会调用fork()
或者exec()
unix
执行时关闭标志(FD_CLOEXEC)
- 库函数应该老是为其打开文件设置执行时关闭标志
- 若是设置了执行时关闭
- 成功执行
exec()
时, 会自动关闭此描述符- 若是调用
exec()
失败,文件描述符会保持打开
exec()
略
system()
#include<stdlib.h>
int system(const char *command);
system(cmd);
//等同于
execl("/bin/sh", "sh", "-c", cmd, (char *) NULL);
复制代码
#define _BSD_SOURCE
#include<unistd.h>
int acct(const char *acctfile) // 特权进程能够调用, acctfile 一般是路径字符串/var/log/pacct 或 /usr/account/pacct的指针,若取消记帐功能,accfile为NULL便可 复制代码
clone()
#define _GNU_SOURCE
#include<sched.h>
int clone(int (*func)(void *), void *child_stack, int flags, void *func_arg, ...)
复制代码
- flags 由高位和低位组成
- 低位保存子进程终止时发个父进程的信号, 为0则不发送,若是子进程是被信号终止,则仍是发SIGCHLD给父进程
- 高位
![]()
进程和线程都是KSE
略略略结合线程
- 共享数据
- 建立线程比建立进程快10倍多(经过
clone()
)
类型 | 描述 |
---|---|
pthread_t | 线程ID |
pthread_mutex_t | 互斥对象 |
pthread_mutexattr_t | 互斥属性对象 |
pthread_cond_t | 条件变量 |
pthread_condattr_t | 条件变量的属性对象 |
pthread_key_t | 线程持有数据的键 |
pthread_once_t | 一次性初始化控制上下文 |
pthread_attr_t | 线程的属性对象 |
在线程中
errno
被标记成宏,展开后是返回一个可修改左值的函数
编译Pthread API程序
设置
cc -pthread
#include<pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start) (void *), void *arg);
复制代码
- 新线程经过调用
start(arg)
开始start
的返回值不该等于PTHREAD_CANCELED,会被pthread_join
捕获?,不该在线程栈中分配start
的返回值pthread
参数保存线程的惟一标识attr
指定了新线程的各类属性
start
函数执行return并返回返回值- 线程调用
pthread_exit()
- 调用
pthread_cancel()
取消线程- 任意线程调用了
exit()
,或主线程执行了return 语句,致使进程中全部线程当即终止
#include<pthread.h>
void pthread_exit(void *retval);
复制代码
retval
指定了线程的返回值,同start
返回值同样不该再线程栈中分配- 若主线程调用
pthread_exit
则其余线程继续运行
# include<pthread.h>
pthread_t pthread_self(void);
int pthread_equal(pthread_t t1, pthread_t t2);
复制代码
linux中线程ID全部进程中都惟一,其余不必定
#include<pthread.h>
int pthread_join(pthread_t thread, void **retval);
// 等待thread标志的线程终止,若是已经终止马上返回
// 传入以前链接过的thread 会致使未定义结果
复制代码
pthread_join
接waitpid
比较
- 线程之间没有层级关系,ex:线程A 建立B,B建立C,A能够
pthread_join
C, C能够pthread_join
A,fork
以后只有父进程能wait子进程- 没法链接任意进程,已不能以非阻塞方式链接
若是线程既没有分离也没有链接,会产生僵尸线程
- 不关心线程的返回,但愿在线程终止时自动清理而且移除
- 一旦线程detach,不能再join
- 其余线程调用了
exit()
或者主线程执行return,遭分离的线程仍是会受到影响
#inlcude<pthread.h>
int pthread_detach(pthread_t thread);
复制代码
静态分配互斥量
#include<pthread.h>
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
复制代码
- 若是发起
pthread_mutex_lock()
的进程已然将目标互斥量锁定
- 线程死锁(linux默认)
- 返回EDEADLK错误
pthread_mutex_trylock()
不会阻塞,失败会返回EBUSYpthread_mutex_timedlock()
能够传参数abstime, 超时后返回ETIMEOUT
- 加锁减锁的操做比之间代码逻辑操做小的多时,性能影响较小,比文件锁,信号量加减锁性能要高
- 互斥量实现采用了机器语言级原子操做,内存中执行
- 相同顺序对互斥量加锁
- 使用
pthread_mutex_lock()
锁定第一个,以后用pthread_mutex_trylock()
锁定以后,若是锁定失败,释放全部互斥量在重来,效率较低
动态初始化互斥量
#include<pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t * attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
void pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);
复制代码
必须动态初始化的case
- 互斥量动态分配于堆中
- 互斥量在栈中自动分配
- 初始化由静态分配,不使用默认属性的互斥量
- 动态分配的必须destory,在堆free或是函数栈消失前释放
互斥量的类型(pthread_mutexattr_t)
- PTHREAD_MUTEX_NORMAL 不具备死锁检测,对本身锁定互斥量加锁则死锁,对未锁定或其余线程锁定的互斥量解锁会致使不肯定结果(Liunx上都会成功)
- PTHREAD_MUTEX_ERRORCHECK 以上三种状况都会犯回错误,但运行比前者慢,可做为调试工具
- PTHREAD_MUTEX_RECURSIVE 同一线程每次加锁锁计数器会加一,解锁到0其余线程才能加锁,对于后两种异常,解锁都会失败
容许一个线程就某个共享资源的状态变化通知其余线程,并让其余线程等待这一通知
静态分配条件变量
#include<pthread.h>
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
//唤醒任意单个线程
int pthread_cond_signal(pthread_cond_t *cond);
//唤醒全部线程
int pthread_cond_broadcast(pthread_cond_t *cond);
//解锁互斥量mutex -> 阻塞调用线程,直到另外一线程cond发出型号->从新锁定mutex (若是在2->3中发现mutex被锁了,会再次休眠)
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
复制代码
pthread_cond_wait
的操做
- 解锁互斥量mutex
- 阻塞调用线程,直到另外一线程cond发出信号
- 从新锁定mutex
若是在2->3中发现mutex被锁了,会再次休眠
阻塞时其余线程得到CPU使用权,而不会盲轮训之类的浪费资源
#include<pthread.h>
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
s = pthread_mutex_lock(&mtx);
while (...){
s = pthread_cond_wait(&cond, &mtx);
}
s = pthread_mutex_unlock(&mtx);
复制代码
之因此while
- 其余线程抢先
- 宽松原则
- 虚假唤醒
动态分配条件变量
#include<pthread.h>
int pthread_cond_init(pthread_cond_t *mutex, const pthread_condattr_t * attr);
int pthread_cond_destroy(pthread_cond_t *mutex);
复制代码