目录
1)由于线程函数接口都是封装在一个线程库,所以我们是看不到源码的,查看线程的函数,都是在第3手册: man 3 xxxx2)所有线程函数的头文件:#include
1.如何创建一个子线程? -> pthread_create() -> man 3 pthread_create
2.如何接合一条线程? (阻塞等待子线程的退出) -> pthread_join() -> man 3 pthread_join
3.如何退出线程? -> pthread_exit() -> man 3 pthread_exit
一、守护进程
1.概念(简答题)
守护进程(Daemon)也被翻译为精灵进程、后台进程,是一种旨在运行于相对干净环境、不受终端影响的、
常驻内存的进程,就像神话中的精灵拥有不死的特性,长期稳定提供某种功能或服务。
在Unix/Linux系统中,使用 ps 命令可以看到许多以 -d 结尾的进程,它们大多都是守护进程,例如:
gec@ubuntu:/mnt/hgfs/codes/rockChip/dataStructure$ ps -ef | grep '.*d$'
root 407 1 0 6月09 ? 00:00:29 /lib/systemd/systemd-journald
root 436 1 0 6月09 ? 00:00:10 /lib/systemd/systemd-udevd
systemd+ 1049 1 0 6月09 ? 00:00:06 /lib/systemd/systemd-timesyncd
root 1150 1 0 6月09 ? 00:00:00 /usr/lib/bluetooth/bluetoothd
root 1151 1 0 6月09 ? 00:00:42 /usr/sbin/irqbalance --foreground
root 1192 1 0 6月09 ? 00:00:00 /usr/sbin/acpid
root 1193 1 0 6月09 ? 00:00:02 /usr/lib/udisks2/udisksd
root 1201 1 0 6月09 ? 00:00:03 /lib/systemd/systemd-logind
root 1583 1 0 6月09 ? 00:14:52 /usr/sbin/vmtoolsd
root 1832 1 0 6月09 ? 00:00:01 /usr/lib/upower/upowerd
gec 1860 1427 0 6月09 ? 00:00:00 /usr/lib/gvfs/gvfsd
root 1932 1 0 6月09 ? 00:00:00 /usr/lib/x86_64-linux-gnu/boltd
root 1970 1 0 6月09 ? 00:01:55 /usr/lib/packagekit/packagekitd
colord 2069 1 0 6月09 ? 00:00:02 /usr/lib/colord/colord
systemd+ 25748 1 0 6月15 ? 00:00:00 /lib/systemd/systemd-networkd
root 26322 1 0 00:08 ? 00:00:00 /usr/sbin/cups-browsed
root 26489 1 0 00:46 ? 00:00:13 /usr/lib/snapd/snapd
systemd+ 27264 1 0 18:58 ? 00:00:00 /lib/systemd/systemd-resolved
有许多程序或服务理应成为这种“不死”的守护进程,比如提供系统网络服务的核心程序systemd-networkd,
只要系统需要基于TCP/IP协议栈进行网络通信,它就应该一直常驻内存,永不退出。
1)怎样成为守护进程
成为守护进程的关键在于使进程运行在一个相对独立、干净的环境,尽量不受各种事件的影响(除非断电、关机),以下就是成为这种进程的详细步骤。
2.守护进程编写步骤
1) 忽略SIGHUP
由于终端的关闭会触发SIGHUP并发送给终端所关联的会话的所有进程,而一开始进程尚未脱离原会话,因此应尽早忽略该信号,避免被挂断信号误杀。
实现代码如下:
// 1,忽略挂断信号SIGHUP,防止被终端误杀
signal(SIGHUP, SIG_IGN);
2) 产生子进程
从终端(不管是远程登录窗口还是本地伪终端)启动的进程所在的会话都关联了控制终端,而控制终端会有各种数据或信号的输入干扰,为了避开这些干扰,需要脱离控制终端,而脱离控制终端的简单做法就是新建一个新的、没有控制终端的会话,但创建一个新会话的进程必须是非进程组组长,但Linux系统中,从终端启动的进程默认就是其所在进程组的组长,因此摆脱这一困境的简单做法就是让其产生一个子进程,退出原进程(即父进程)并让子进程继续下面的步骤即可。
实现代码如下:
// 2,退出父进程(原进程组组长),为能成功创建新会话做准备
if(fork() > 0)
exit(0);
3) 创建新会话
创建新会话,脱离原会话,脱离控制终端。
实现代码如下:
// 3,创建新会话,脱离原会话,脱离控制终端。
setsid();
4) 产生孙子进程
此时的进程是其所在的会话的创始进程,而创始进程拥有可以再次关联的控制终端的权限,为避免此种情况的发生,最简单的做法就是退出当前创始进程,改由其子进程(非创始进程)继续完成成为守护进程的使命。
实现代码如下:
// 4,断绝重新关联控制终端的可能性
if(fork() > 0)
exit(0);
5) 进入新进程组
虽然此时进程的父进程、祖父进程已经退出,但进程组是一直都在的,且处于新会话中的孙子进程一直都在其祖父进程的进程组之中,而进程组是可以传递信号的,因此为了与任何方面脱离关系,应“自立门户”创建新进程组,并将自身置入其中。
实现代码如下:
// 5,脱离原进程组,创建并进入只包含自身的进程组
setgpid(0, 0);
6) 关闭文件资源
文件资源是可以在父子进程之间代际相传的,这其中也包括了标准输入输出文件,而作为守护进程,是一种在后台运行的程序,运行过程中一般无需交互,若有消息需要输出一般会以系统日志的方式输出到指定日志文件中。因此,为了节约系统资源,也为了避免不必要的逻辑谬误,守护进程一般都需要将所有从父辈进程继承下来的文件全部关闭。
实现代码如下:
// 6,关闭父辈继承下来的所有文件
for(int i=0; i<sysconf(_SC_OPEN_MAX); i++)
close(i);
7) 关闭文件权限掩码
在Linux系统中创建一个新文件时,可以通过相关的函数参数指定文件的权限,比如:
// 试图在file.txt不存在的情况下,创建一个权限为0777的文件
int fd = open("file.txt", O_CREAT|O_RDWR, 0777);
但其实被创建出来的文件的权限并非代码中指定的权限,该权限与系统当前的文件权限掩码做位与操作之后的值才是文件真正的权限,我们可以通过命令 umask 来查看当前系统默认的文件权限掩码的值:
gec@ubuntu:~$ umask
0022
gec@ubuntu:~$
因此,上述创建的文件的权限最后不是0777,而是0755:
gec@ubuntu:~$ ls -l
-rwxr-xr-x 1 gec gec 0 6月 17 0