Linux进程与线程学习随笔

1. 进程线程区别
进程与程序:
程序是一堆指令的有序集合,本身没有任何执行的含义,是一个静态实体;
进程是具有一定独立功能的程序在某个数据集上的一次运行,系统资源调度与分配的单位,是一个动态实体,具有生命周期。反应了一个程序在一定数据集上运行的全部动态过程。
进程与程序并不一一对应,程序执行在不同数据集上就是不同的进程,进程可以唯一标识,程序无法做到,因为程序和数据集没有直接联系,程序可能有多个进程对应也可能没有进程(没有执行)。
进程具有交互性和并发性,程序具有封闭性。

进程是系统资源调度和分配的基本单位,有独立的地址空间,进程崩溃不会有影响,比较健壮;

线程是进程的一个实体,比进程小,处理器调度的基本单位,同进程中的线程没有独立的地址空间,只有自己的线程ID、一组寄存器值、栈、调度优先级和策略、信号屏蔽字、errorno变量,线程私有数据等,线程死掉会影响整个进程死掉,健壮性要求高。

创建进程以及切换进程,开销比较大,线程共享进程资源,开销很小;

线程属于同步编程模式,并发度比较高, 进程属于异步编程模式,代码也比较复杂,并发度小;

处理器数量并不影响程序结构以及线程使用。

如果是一个分布式系统,每一个机器可以看做是一个进程,而机器里跑的应用就是一个进程中的线程了。

2.
Linux的线程是通过clone()系统调用实现pthread_create(),clone()调用创建子进程,可以共享父进程一定数量的执行环境(文件描述符与内存),这个数量可配置,一般的主线程调用在新建线程前,但Linux并不是,不能做此种假设。

3.
在不终止进程的情况下,线程有三种方式退出:
(1)从启动例程返回,返回值是退出码;
(2)被其它线程取消pthread_cancle(pthread_t tid),但是其仅仅提出请求,目标线程可以选择是否响应。
(3)调用pthread_exit()。

4.线程退出时需要调用函数(线程清理函数pthread_cleanup_push)与进程的atexit相似。
调用pthread_exit;
响应取消请求pthread_cancle(取消点);
非零execute参数调用pthread_cleanup_pop(int execute);

5.线程                                                                      进程
pthread_create(&tid, NULL, func, arg)                           fork()
pthread_join(pthread_t tid, void **rval_ptr)                   waitpid()
pthread_exit(void* rval_ptr)                                         exit()
pthread_cancle(pthread_t tid)                                       abort()
pthread_cleanup_push(void (*rtn)(void *), void *arg)     atexit()
pthread_self()                                                              getpid()

6. exit()执行一些清理,_exit()与_Exit()直接进入内核

7. retrict 关键字, 只用于限定指针,表示该指针是访问一个对象唯一且初始的方式,可以让编译器进行相关优化。

8. pthread_mutex_lock
    pthread_mutex_unlock
    pthread_mutex_trylock为了避免阻塞,可以先测试,没有加锁就加锁,并返回0,否则返回EBUSY

9. 线程安全:一个函数可以被多个线程同时安全调用,也就是多个线程同时调用与单线程调用的结果是相同的,不存在二义性的结果输出,主要是因为全局变量和静态变量引起的。
大部分系统函数都是线程安全的,但是POSIX为对网络编程API的线程安全作出规定,使用静态缓冲区的函数,修改接口,调用者自己提供缓冲区,从而可以变为线程安全的。
Vector是线程安全的,ArrayList不是。 Vector存在同步机制。

9.线程私有数据:维护基于线程的数据,因为线程ID并不能保证小而连续,基于进程的接口适应多线程环境机制(errorno被重定义为线程私有,不会影响其它线程)

10.线程有自己的信号屏蔽字,但是共享信号的处理。

11. 线程调用fork,为子进程创建整个地址空间的副本,也从父进程那里继承了所有互斥量、读写锁和条件变量状态,如父进程包含多个线程,子进程在fork返回以后,不是紧接着调用exec(如此老的地址空间会被丢弃,锁状态无关紧要),就需要清理锁状态。

子进程就只有一个线程,是父进程中调用fork的线程的副本,子进程不包含所有占有锁的线程的副本,没有知道占有哪些锁,需要释放哪些锁。

12. 进程终止方式:main函数返回,调用exit、_exit、_Exit函数,调用abort函数,最后一个线程结束(pthread_exit, 返回, pthread_cancle),接收信号终止进程

13. atexit(pthread_cleanup_push)注册函数之后,调用顺序与注册顺序相反。

14. malloc 分配指定字节数长度存储,不初始化;
      cmalloc 分配指定长度,并初始化为0;
      realloc(void *ptr, size_t newsize) 更改分区长度(增加减少),对增加内容不具有初始化功能。
      都是通过sbrt系统调用实现,其扩充或缩小进程的堆。
      大多数实现分配的存储空间比实际要求大一些,额外空间用来记录管理信息(快长度,下一个块指针)。不能越区写操作。

15. volatile:易失变量,编译器不能对访问该变量的代码优化。访问寄存器比较快,编译器一般会做减少访问内存的优化,可能存在脏数据,声明之后,系统总是从内存读取该变量的值。
auto声明为自动变量,只能在函数内声明,是变量的默认形式。

16. C程序进程地址空间布局:
低地址起, 正文段(text),初始化数据段(data,程序中声明初值),未初始化数据段(bss,代码为声明初值,但是一般会被默认初始化为0),堆;
高地址起,环境变量与命令行参数,栈;
栈和堆之间是程序运行时调用,共享库与内存映射也在同一位置。

17. Linux内核有三种调度策略:
 a) SCHED_OTHER 分时调度;
 b) SCHED_FIFO 实时调度,先到先服务,占用CPU一直运行,直到具有更高优先级的的任务到达或者自己放弃;
 c) SCHED_RR 实时调度,时间片轮转,进程时间片用完,系统重新分配时间片,然后放到就绪队列队尾,保证具有相同优先级的任务的调度公平性。

SHCED_OTHER不支持优先级, SCHED_RR与SCHED_FIFO支持,优先级从1-99,数值越高,优先级越高。

17. 父子进程区别:
fork返回值;
进程ID,父进程ID;
子进程tms_utime, tms_stime, tms_ctime均被置为0;
父进程文件锁不会被子进程继承;
子进程的未处理闹钟(alarm)会被清除;
子进程未处理信号被设置为空集。

18. 僵尸进程,一个已经终止但是其父进程没有对其进行善后处理(获取终止进程的相关信息,释放资源wait或waitpid)的进程。

19. 进程之间存在竞争条件,为避免竞争和轮询,可以使用信号机制,也可以使用进程间通信IPC。

20. exec执行另一个程序,用全新的程序替换了当前进程的正文、数据、堆和栈,但是执行新程序的进程保持原进程一下特征:、
      进程ID和父进程ID,实际用户ID与实际组ID,附加组,进程组,会话,控制终端,闹钟剩余时间,当前工作目录,根目录,文件模式创建屏蔽字,文件锁,进程信号屏蔽字,未处理信号,资源限制,tms_utime,tms_stime,tms_cutime,tms_cstime。

21. 进程通信方式
(1) 管道:相关进程使用,具有公共主线的进程间使用。通常一个管道由一个进程创建,然后该进程调用fork,父子进程就用该管道。
(2) FIFO:命名管道,不相关的进程也能够交换数据,FIFO是一种文件类型;一般的文件操作都可以用于FIFO(read, write, close, open, close, unlink等)。FIFO用于复制串行管道命令之间的数据流,避免中间磁盘文件。

三种IPC被称为XSI IPC,消息队列,信号量,共享存储器。
IPC结构内核中是非负整数的标识符,唯一标识,是循环递增,和文件描述符选最小值不同。
外部命名方案使用键key。ftok(const char* path, int id)由路径和项目id产生键值。
缺点:系统范围内起作用,没有访问计数。(最后一个访问管道的进程结束之后管道就完全删除了,FIFO最后一个引用进程终止之后,名字仍然保留在系统,直到显示删除,但是FIFO中的数据全部删除,徒有其名);IPC结构是文件系统中没有名字,不能使用文件操作函数,要单独的专用函数;不使用文件描述符,不能使用多路转接I/O函数(select与poll)。

(3)消息队列(msgget, msgsnd, msgrcv):不一定先进先出的取出消息,也可以按消息类型字段取消息。
(4)信号量(semaphore):计数器,多进程对共享数据的访问。
(5)共享存储:多进程共享存储区,不需要复制,是最快的IPC。需要处理同步访问的问题,一般是信号量与记录锁。创建新的共享区,内容会被初始化为0。存在引用计数。
mmap映射函数可以将一个文件的若干部分映射到进程地址空间,与shmat类似,但是mmap映射的存储段与文件关联。
共享存储可以由不相关进程使用。

(6)基于STREAMS的管道:双向管道,可以有命名的双向STREAMS管道。

(7)Unix域套接字:套接字与管道的混合物,本地服务,效率高。分为Unix域数据包套接字和字节流套接字,数据包套接字也可靠。

(8)网络套接字:网络通信socket。

22. 记录锁:当一个进程正在读或者修改文件的某个部分,可以阻止其它进程修改同一个文件区,更准确的应成为字节范围锁,只是锁定文件的一个区域。
读写锁规则是针对不同进程,如果同一个进程,则新锁将代替旧锁。

23. 信号也是属于进程间的一种通信方法。不存在进程为0的信号,kill发送为0的空信号用来错误检查,不发送信号,检查进程是否存在,不存在则kill返回-1。信号属于异步事件。

24. 线程同步:互斥量(pthread_mutex)、条件变量(pthread_cond_t)、信号灯sem_t
Linux 线程机制是通过内核的轻量级进程实现的,异步信号对于线程也是起作用,但是无法做到针对线程发送信号。
条件变量与互斥量一起使用,允许线程以无竞争的方式等待特定条件的发生。
信号灯,实际就是信号量,标识共享资源的可用数量,可以限定并发线程的数量。

25.多线程服务模型:
单线程+阻塞I/O
线程池+阻塞I/O

26. 有时候必须使用单线程:
(1). 可能调用fork的进程,fork一般不能在多线程中使用,fork只能克隆当前线程的thread of control,不克隆其它线程。不能一下克隆出和父进程一样多线程的子进程,linux也没有forkall这样的系统调用,其也很难完成,因为可能其它线程可能在等待条件互斥量condition variable,阻塞在系统调用,可能等待mutex进入临界区,密集计算中,都不好搬进子进程。
(2).限制程序的CPU占用率。在多核主机上,一个单线程即使发生busy-wait,也最多占用1/8的CPU,可以留给其它服务进程使用资源。例如辅助进程,最好是能够限制CPU使用率,防止过分抢占系统资源。

27. 单线程相比于多线程的优势:I/O bound或者CPU bound,多线程没有绝对意义上的优势,即很少的CPU就能够让I/O跑满,或者很少的I/O就让CPU跑满,即二者之一,很容易满负荷工作。
第一种情况:I/O容易跑满,比如静态web、ftp等,CPU负载轻,主要性能限制在磁盘I/O和网络I/O,单一线程容易让I/O满负荷,增加线程对性能提升有限。
第二种情况:CPU满负荷,比较少见,主要是计算复杂型的问题,如计算NP-Hard问题,一个小输入就能够让CPU满负荷运行,多线程没有太多优势。

多线程应当使用的场合:提高响应速度,I/O与计算相互重叠,降低latency。不能提高绝对性能,但是能够提高平均性能。

28. 一个程序要做成多线程的,大致要满足:

- 有多个 CPU 可用。单核机器上多线程的优势不明显。
- 线程间有共享数据。如果没有共享数据,用模型 3b 就行。虽然我们应该把线程间的共享数据降到最低,但不代表没有;
- 共享的数据是可以修改的,而不是静态的常量表。如果数据不能修改,那么可以在进程间用 shared memory,模式 3 就能胜任;
- 提供非均质的服务。即,事件的响应有优先级差异,我们可以用专门的线程来处理优先级高的事件。防止优先级反转;
- latency 和 throughput 同样重要,不是逻辑简单的 IO bound 或 CPU bound 程序;
- 利用异步操作。比如 logging。无论往磁盘写 log file,还是往 log server 发送消息都不应该阻塞 critical path;
- 能 scale up。一个好的多线程程序应该能享受增加 CPU 数目带来的好处,目前主流是 8 核,很快就会用到 16 核的机器了。
- 具有可预测的性能。随着负载增加,性能缓慢下降,超过某个临界点之后急速下降。线程数目一般不随负载变化。
- 多线程能有效地划分责任与功能,让每个线程的逻辑比较简单,任务单一,便于编码。而不是把所有逻辑都塞到一个 event loop 里,就像 Win32 SDK 程序那样。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值