进程的概念:
进程是一个具有独立功能的程序关于某个数据集合的一次可以并发执行的运行活动,是关于活动状态的计算机程序。
Linux下一个进程在内存里有三部分的数据:代码段、堆栈段、数据段。
进程的状态:运行状态、等待状态、停止状态、僵尸状态。
进程的执行模式:用户模式、内核模式。
Linux中
进程具有独立的权限与职责。如果系统中某个进程崩溃,它不会影响到其余的进程。每个进程运行在其各自的虚拟地址空间中,进程之间可以通过由内核控制的机制相互通讯。
ps命令:
如有ps –A | grep filename
ps –ax | grep filename
pstree –p | grep filename 查看进程树
ps输出信息
USER 进程的属主;
PID 进程的ID;
PPID 父进程;
%CPU 进程占用的CPU百分比;
%MEM 占用内存的百分比;
NI 进程的NICE值,数值大,表示较少占用CPU时间;
VSZ 进程虚拟大小;
RSS 驻留中页的数量;
TTY 终端ID
WCHAN 正在等待的进程资源;
START 启动进程的时间;
TIME 进程消耗CPU的时间;
COMMAND 命令的名称和参
STAT 进程状态
D Uninterruptible sleep (usually IO)
R 正在运行可中在队列中可过行的;
S 处于休眠状态;
T 停止或被追踪;
Z 僵尸进程;
< 优先级高的进程
N 优先级较低的进程
L 有些页被锁进内存;
s 进程的领导者(在它之下有子进程);
l is multi-threaded (using CLONE_THREAD, like NPTL pthreads do)
+ 位于后台的进程组;
每个进程用一个tast_struct数据结构来表示
(1)进程状态 (2)进程调度信息 (3)标识符 (4)进程间通信 (5)链接
(6)时间和定时器 (7)文件系统 (8)虚拟内存(9)处理器的内容
其中链接用pstree –p 命令查看
进程控制
fork() 创建子进程
exec() 创建子进程后,引用其他程序
wait() 等待子进程退出,防止孤儿进程产生
及时释放子进程占用的系统资源
exit() 终止进程
进程调度
第一步:处理内核中的工作
第二步:处理当前进程
第三步:选择进程
实时进程
普通进程
第四步:进程交换
线程的概念:
进程是资源管理的最小单位,线程是程序执行的最小单位。
在操作系统设计上,从进程演化出线程,最主要的目的就是更好地支持多处理器,并且减小进程上下文切换的开销。
线程是进程的一条执行路径,它包含独立的堆栈和CPU寄存器状态,每个线程共享其所附属的进程的所有的资源,包括打开的文件、内存页面、信号标识及动态分配的内存等等。
进程与线程的关系:
线程和进程的关系是:线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一物理内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。一个进程至少需要一个线程作为它的指令执行体,进程管理着资源(比如cpu、内存、文件等等),而将线程分配到某个cpu上执行。
进程编程
(1) 获得与进程有关的ID
#include <unistd.h>
#include <sys/types.h>
uid_t getuid(void):获得进程的用户标识号。
gid_t getgid(void):获得进程的用户所属的用户组ID。
pid_t getpid(void):要获得当前进程的ID。
pid_t getppid(void):获得当前进程的父进程的ID。
pid_t getpgrp(void):获得当前进程所在的进程组的ID。
pid_t getpgid(pid_t pid):获得进程ID为pid的进程所在的进程组ID。
(2)派生编程
fork()
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
在父进程中,fork返回新创建子进程的进程PID。
在子进程中,fork返回0。
如果出现错误,fork返回-1。
(3)执行其他程序
exec()
可以用来执行一个可执行文件来代替当前进程的执行映像。
该调用并没有生成新的进程,而是在原有进程的基础上,替换原有进程的正文,调用前后是同一个进程,进程号PID不变,但执行的程序变了(执行的指令序列改变了)。
六种调用形式:
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 *filename, char *const argv [], char *const envp[]);-系统调用
exec()的六个函数实现的功能都是一样的,只是在传递参数和设置环境方面提供了不同的方式
(4)等待进程
系统调用wait()的功能是发出调用的进程只要有子进程,就睡眠直到它们中的一个终止为止:
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
waitpid()函数与调用wait()的区别是waitpid()等待由参数pid指定的子进程退出
pid_t wait(int *status);
pid_t waitpid(pid_t pid,int *status,int option);
waitpid()函数与wait()函数的区别是waitpid()等待由参数pid指定的子进程退出。
其中参数pid的含义与取值方法如下:
参数pid<-1时,等待进程组ID等于pid的绝对值的子进程退出
参数pid=0时,等待进程组ID等于当前进程的进程组ID的子进程退出(自己的子进程)
参数pid>0时,等待进程ID等于参数pid的子进程退出(特定的子进程)
参数pid=-1时,等待任何子进程退出,相对于调用wait().
宏定义 | 含义 |
WIFEXITED(status) | 如果进程通过系统调用_exit或函数调用exit正常退出,该宏的值为真。 |
WIFSIGNALED(status) | 如果子进程由于得到的信号(signal)没有被捕捉而导致退出时,该宏的值为真。 |
WIFSTOPPED(status) | 如果子进程没有终止,但停止了并可以重新执行时,该宏返回真。这种情况仅出现在waitpid调用中使用了WUNTRACED选项。 |
WEXITSTATUS(status) | 如果WIFEXITED(status)返回真,该宏返回由子进程调用_exit(status)或exit(status)时设置的调用参数status值。 |
WTERMSIG(status) | 如果WIFSIGNALED(status)返回为真,该宏返回导致子进程退出的信号(signal)的值。 |
WSTOPSIG(status) | 如果WIFSTOPPED(status)返回真,该宏返回导致子进程停止的信号(signal)值。 |
(5)终止进程进程返回值
exit()函数的功能是终止发出调用的进程
void exit(int status);
线程编程
(1) 线程的创建于使用
#include <pthread.h>
int pthread_create(pthread_t *restrict thread,const pthread_attr_t *restrict attr,
void *(*start_routine)(void*), void *restrict arg);
第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性(一般为NULL就可以了),
三个参数是线程运行函数其实地址,第四个参数是运行函数的参数。
当创建成功时返回0.
EAGAIN:前者表示系统限制创建新的线程,
EINVAL:后者表示第二个参数代表的线程属性值非法。
创建线程成功后,新的一个线程则运行参数三和参数四确定的函数。
#include <phread.h>
int pthread_join(pthread_t thread,void **value_ptr);
第一个参数为等待的线程标识符,第二个参数为用户定义的指针,可以用来存储被等待线程的返回值,这是一个线程阻塞函数,调用它的函数将一直等待到被等待线程结束为止,当函数返回时,被等待线程的资源被收回。
#include<pthread.h>
Void pthread_exit(void *value_ptr);
函数的参数是函数的返回代码,只要pthread_join中的第二个参数value_ptr不是NULL,这个值将被传给主线程
注意:一个线程不能被多个线程等待,否则第一个接收到的信号的线程成功返回,其余调用pthread_join()的线程则返回错误代码ESRCH.
(2)线程同步-互斥锁
对共享资源的存取,需要用锁的方式加以控制。
函数pthread_mutex_init()用来生成一个互斥锁:
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
第一个参数是互斥变量的地址,第二个参数设置互斥变量的属性,大多情况下,选择默认属性,则传入空指针。
上锁与解锁:
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
两个函数的参数都是互斥变量的地址,函数执行成功返回0,否则返回错误号。
死锁:两个线程试图同时占用两个资源,并按不同的次序锁定相应的互斥锁
解决办法:使用函数pthread_mutex_trylock(),它是函数pthread_mutex_lock()的非阻塞函数 :
#include <pthread.h>
int pthread_mutex_trylock(pthread_mutex_t *mutex);
当互斥锁不再使用的时候,应该释放互斥变量,函数pthread_mutex_destory(pthread_mutex_t *mutex)用来释放一个互斥变量。
(3)线程同步—条件变量
互斥锁的缺点是它只有两种状态:锁定和非锁定。
条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足。
条件变量通常和互斥锁一起使用。
条件变量被用来阻塞一个线程,当条件不满足时,线程通常先解开相应的互斥锁进入阻塞状态,等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。
条件变量只是起阻塞和唤醒线程的作用,具体的判断条件还需用户给出。
初始化条件变量
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
第一个参数cond是一个指向结构pthread_cond_t的指针,第二个参数cond_attr是一个指向结构体pthread-condattr_t的指针,
释放条件变量
#include <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond);
条件变量阻塞线程:
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
当我们需要用一个条件变量来阻塞线程的时候,调用函数pthread_cond_wait()。
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime);
唤醒一个或多个线程
Int pthread_cond_signal(pthread_cond_t *cond);
Int pthread_cong_broadcast(pthread_cond_t *cond);
(4)线程同步—信号量
信号量从本质上是一个非负整数计数器,通常被用来控制对公共资源的访问。
当可用的公共资源增加时,调用函数sem_post()增加信号量。
只有当信号量值大于0时,函数sem_wait()才能返回,并将信号量的值减1,当信号量等于0时,sem_wait()将被阻塞直到信号量的值大于0。
函数sem_trywait()是函数sem_wait()的非阻塞版本
。
初始化信号量:
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned value);
第一个参数sem为指向信号量结构的一个指针,第二个参数pshared不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享,第三个参数value给出了信号量的初始值。
增加信号量的值:
#include <semaphore.h>
int sem_post(sem_t *sem);
参数是要增加的信号量结构体的指针。
阻塞当前线程直到信号量sem的值大于0
#include <semaphore.h>
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
当信号量的值等于0时,sem_trywait()函数不会阻塞当前线程。
释放信号量
#include <semaphore.h>
int sem_destroy(sem_t *sem);