Linux应用层编程
[TOC]
一、多线程
资源分配的最小单位是进程,任务调度的最小单位是线程。进程包含线程集和资源集,进程内的所有线程共享资源集,因此在进行线程间任务调度时开销小,多线程共享数据简单(因为本来就是共享的)。线程占用资源少。在编译时需要链接pthread库。同一个进程的不同线程可以在多核处理器的不同核心执行。
多线程中随进程创建的线程为主线程,主线程退出时会关闭所有线程
1. 线程创建
1 | /** |
通过pthread_create函数可创建一个线程,创建即可开始运行。arg参数会传递给start_routine。
2. 等待线程结束
1 | /** |
pthread_join对某个线程只允许调用一次,在调用后线程的全部资源全部释放。原使用的thread无效,重复调用返回错误码3,不修改retval中的值。
3. 线程自行退出
1 | /** |
线程执行函数执行结束也会使线程退出,也可以调用pthread_exit退出。两种方式略有区别,使用return退出时仅在线程执行函数完全执行结束时会自行退出,当出现嵌套调用时会逐层退出。使用pthread_exit可以在嵌套调用时直接退出线程。在C++中使用pthread_exit退出不会调用析构函数。一般情况下不适用exit退出,exit用于退出进程,退出进程时杀死当前进程的所有线程。
4. 杀死指定线程
1 | /** |
5. 线程分离
线程在执行结束后会释放大部分资源,但是仍会保留一部分信息(包括线程退出状态等),直到其他线程调用pthread_join获取线程的退出信息。如果不需要知道线程的退出信息希望线程执行结束后自行释放全部资源。则需要在创建时设置线程分离属性或者在创建完成后调用pthread_detach进行线程分离。
1 | /** |
使用attr配置线程分离状态
1 | pthread_attr_t attr;//定义attr结构体 |
对已分离的线程调用pthread_join出现错误码22
二、多进程
Linux中进程是资源分配的基本单位,不同进程间资源一般不共享(除非只用进程间通信的相关机制)。
如果一个进程A创建了一个新的进程B,则A是B的父进程,B是A的子进程。
每个可执行文件启动后会占用一个进程。
每个进程有全局唯一的进程号
1号进程(init进程)是所有进程的直接或间接父进程
如果子进程结束,父进程不等待子进程结束(等待函数会在子进程结束后释放子进程的全部资源,父进程不等待子进程结束则资源不释放)。在父进程未结束之前子进程结束后,子进程被称为僵尸进程。若父进程先与子进程结束,子进程由init托管称为孤儿进程。init托管孤儿进程后等孤儿进程结束释放孤儿进程资源。init托管到僵尸进程时会直接释放资源。
1. 子进程的创建
1 | /** |
具体可参看示例
2. 获取进程号
1 | /** |
3. 等待进程结束
假设有A、B两个进程。A进程等待B进程结束并不只是为了进行不同进程间的同步,A调用wait相关函数时,wait相关函数还会释放B结束后所占用的资源。
1 | /** |
4. 进程退出
进程退出可以使用return、exit、_exit等方式退出进程。不同方式退出操作不同return退出最安全。在C++环境下不使用retrun不会调用析构函数。exit会执行提前注册的退出处理函数,随后清空IO缓冲,即将缓冲区的数据写入磁盘。_exit函数则会直接退出,不调用atexit注册的函数,也不保证数据完整性。
1 | /** |
5. 进程分离
进程其实并没有分离的概念,这里说进程分离只是沿用线程分离的概念,实则是通过异步的方式为子进程释放资源,看起来不影响父进程的执行,也不会产生僵尸进程。子进程结束时会向父进程发送SIGCHLD信号,父进程可以在该信号的处理函数中执行wait操作为子进程释放资源。
方法一:自己处理
1 | void reap_zombie(int signo) |
方法二:系统处理
1 | int main() |
三、进程间通信
1. 管道
(1)管道(无名管道)
管道:正如其名,就像水管、燃气管一样进行传输,但是它们传输的是水和燃气。Linux中的管道传输的是数据。
允许在一端写入并在另一端读出。写入端不可读取,读取端不可写入。只能用于父子进程之间。
使用read和write读写即可。
多个线程读取同一个管道时,管道中的数据在哪个线程被读出是不确定的
同一个管道同时只允许读或者写
1 | /** |
(2)命名管道(fifo)
命名管道可以任何进程间实现通信。在创建命名管道时会在指定的路径创建管道文件,其他进程打开该文件即可进行读写。
由于管道文件是文件,因此也有读写权限控制。
调用函数创建管道文件时文件必须不存在,否则会产生错误
创建后使用open打开文件即可进行读写操作。
非阻塞写入打开、没人读:打开失败
阻塞写入打开、没人读:等有人读时打开
非阻塞读取打开、没人写:打开成功,read阻塞,有人写后反复读取管道,会读取到历史数据
阻塞读取打开、没人写:等待有人写时打开,正常读取数据、不会读取到历史数据
若在管道读写过程中,读取端先断开会导致写入端写入到无人读取的管道中,引起管道破裂,在写入端进程中产生SIGPIPE管道终止信号
1 | /** |