UNIX高级环境编程: 终端登录过程-远程登录-进程组-Session-Linux启动过程-dup与重定向-守护进程

简介:

1 终端登录(Terminal Logins)

BSD Terminal Logins

 BSD终端的登录程序在过去35年都没有改变。

  1. 系统管理员(the system adminstrator)创建一个文件 /etc/ttys,每一个登录终端都在该文件占一行,改行包含登录终端名,其他的参数则会传递给getty函数。

  2. 其中一个参数是终端的传输波特率(baud rate)。

  3. 当系统引导完成,内核创建init进程,进程ID为1。init进程负责引导系统启动。

  4. init进程读取文件/etc/ttys,并为每一个登录设备fork一个进程,然后执行exec运行getty程序。

 上面的流程如下图所示:

NewImage

由init进程fork创建的进程的real user ID为0,effective user ID为0,并且他们都有超级用户权限。

程序getty的职责:为终端设备调用open函数,一旦设备被打开,文件描述符0,1,2被设置给该设备。然后getty输出一些提示符,等待我们输入用户名。当我们输入用户名后,getty的工作就完成了,然后通过调用exec函数执行登录函数,如下。

execle(“/bin/login”, “login”, “-p”, username, (char *)0, envp);

增加了login程序后,流程如下图所示:

NewImage

上图中fork出来的进程都有超级用户权限,因为他们都是从init进程fork出来,而init进程有超级用户权限。

下面的进程的进程ID都是相同的,因为exec函数不改变进程的ID,并且他们的父进程的ID都是1。

现在登录程序转到login程序执行,login程序会做下面的事情:

  1. 根据我们输入的用户名,调用函数getpwnam获取用户名对应的密码;

  2. 调用函数getpass打印提示符 Password: ,等待读取我们输入的密码;

  3. 对我们输入的密码进行加密,将加密后的密码和从系统密码文件获取的密码进行对比,如果密码不同,则login程序调用exit函数退出,并返回退出状态1。init进程得到1的进程终止状态,则会再次执行fork进行登录重试。

如果我们正确登录,则login程序会继续做下面的事情:

  1. 当前工作目录切换到我们的主目录(chdir);

  2. 改变我们的终端设备的所有权为我们自己所有(chown);

  3. 改变我们的终端设备的权限,使得我们可以从该终端设备读取和输入;

  4. 设置我们的组ID(setgid和initgroups);

  5. 初始化我们的环境变量;

  6. 改变我们的用户ID(setuid),激活我们的登录shell ,如execl(“/bin/sh”, “-sh”, (char *)0);

 过程如下图所示:

NewImage

我们的shell已经启动后,会去读取启动文件(.profile或.bash_profile或.bash_login或.profile,不同的系统启动文件的命名不同)。这些启动文件的作用是增加系统的环境变量,设置一些全局变量,链接等。

 

2 网络登录(Network Logins)

物理登录和网络登录的区别在于:登录终端到主机的连接是否是点对点的。

网络登录情况下,登录是一种可用服务,就像其他的服务,如FTP或SMTP。

网络登录服务特点是不知道会有多少登录请求会来。所以内核不是在等待每一个可能的登录,而是通过网络接口驱动(network interface drivers)在等待一个网络连接登录请求。

为了统一处理物理登录和网络登录,一个软件驱动,叫做虚拟终端(pseudo terminal)被用来用将网络登录后的行为请求映射为真实终端的行为。

BSD Network Logins

进程inetd等待处理大部分的网络连接。

下面我们将了解网络登录的过程。

  1. 系统启动时,init进程创建一个shell执行脚本/etc/rc,其中一个后台进程就是inetd。一旦该脚本终止,inetd进程的父进程就成为了init进程;

  2. inetd的职责是等待TCP/IP连接请求,一旦有新的连接请求到来,inetd会执行fork and exec执行相应的处理程序;

  3. telnetd程序会启动一个TELNET服务器,等待用户远程登录,用户通过TCP协议链接服务器,并通过合法的用户密码进行登录。

启动telnetd程序的过程如下图所示:

 

telnetd进程启动后的动作为:

  1. 打开一个虚拟终端(pseudo teminal),然后调用fork创建出两个进程;

  2. 父进程处理来自网络的连接请求;

  3. 子进程执行exec函数调用login程序;

  4. 父进程和子进程通过虚拟终端链接;

  5. 如果子进程正确登录,则后面的过程和物理登录相同。

过程如下图所示:

NewImage

 

 3 进程组(Process Groups)

每一个进程都属于一个进程组。

进程组是一些进程的集合,这些进程常常关联于同一个job,并且从同一个终端接收信号。

每一个进程组都有一个唯一的进程组ID。

函数getpgrp返回调用进程的进程组ID。

函数声明:

 #include <unistd.h>

pid_t getpgrp(void);

        // Returns: process group ID of calling process

 

pid_t getpgid(pid_t pid);

        // Returns: process group ID if OK, -1 on error

函数调用getpgid(0); 和函数调用getpgrp(); 作用相同,都返回调用进程的进程组ID。

每个进程组都有个头进程,该进程的进程ID和进程组ID相同。

进程组的生命周期:从一个进程创建一个组开始,只到最有一个组内进程终止或者成为另外一个组的进程为止。

一个进程可以调用函数setpgid加入到另一个进程组或者创建一个进程组。

函数声明:

 #include <unistd.h>

int setpgid(pid_t pid, pid_t pgid);

函数设置进程ID为pid的进程的进程组ID为pgid。

如果pid和pgid相同,都为某个进程的进程ID,则进程pid成为一个进程组的头进程。

如果pid为0,则表示待设置的进程为当前进程。

 

4 Sessions

一个session是一个或几个进程组的集合。

例如下图所示:


一个进程通过调用函数setsid创建一个新的session。

函数声明:

 #include <unistd.h>

pid_t setsid(void);

        // Returns: process group ID if OK, -1 on error

如果调用进程不是组头进程,则会发生三件事:

  1. 该进程成为创建的新session的session leader;

  2. 该进程成为一个新进程组的头进程,新进程组ID为该调用进程的进程号;

  3. 该进程不关联终端。

函数getsid返回一个session leader进程的进程组ID。

函数声明:

#include <unistd.h>

pid_t getsid(pid_t pid);

        // Returns: session leader’s process group ID if OK, -1 on error

如果pid为0,函数getsid返回调用进程所在的session leader进程的进程组号。

 

5、Linux启动及用户登录过程

用户登录时,Bash 首先执行全局登录脚本(由 root 建立) /etc/profile,

然后在用户起始目录下依次寻找 .bash_profile、.bash_login、.profile 三个文件,

执行最先找到的一个。可以用这种办法像 Netware 一样为不同的用户定制运行环境。 

此外,用户退出登录时还可以运行 .bash_logout 脚本。 


Linux的开机的整个过程如下:

1.打开电脑电源,开始读取 BIOS 并进行主机的自我测试; 

2.透过 BIOS 取得第一个可开机装置,读取主要开机区 (MBR) 取得开机管理程式; 

3.透过开机管理程式的设定,取得 kernel 并载入记忆体且侦测系统硬体; 

4.核心主动呼叫 init 程式; 

5.init 程式开始执行系统初始化 (/etc/rc.d/rc.sysinit) 

执行/etc/init.d/functions ,设置环境变量等。

6.依据 init 的设定进行 daemon start (/etc/rc.d/rc[0-6].d/*) 

依次执行脚本,启动服务。关机时依次关闭服务。

7.载入本机设定 (/etc/rc.d/rc.local) 

  /etc/profile文件。这个文件是系统启动时,任何用户登录时执行的文件。任何用户登录前,root用户也会执行一遍。


  ~/.bash_profile文件。这个文件每个用户都有。它在用户登录时自动执行,拥有用户的权限。它export的环境变量对该用户随后启动的进程都有效。


因此,如果用户需要开机自动以root权限执行一些脚本,那么最好的方法是编辑/etc/rc.d/rc.local文件。

如果每一个用户登录时都应该执行的脚本,如设置一些环境变量。那么应该修改/etc/profile文件。

如果某一个特定用户登录时应该执行特定的脚本,如设置该用户特定的环境变量,应该修改~/.bash_profile文件。

6、Linux重定向与复制dup

重定向是针对文件描述符而言的:将某个文件描述符重新定向到某个文件设备。(一个文件描述符只能指向一个设备文件)


重定向相当于为文件设备重新分配文件描述符。

重定向的作用:将一个文件描述符定向到一个文件,以后对该描述符的操作将作用与该文件(相当于该描述符为该文件的描述符)

标准输入0默认为键盘文件的描述符/dev/tty

标准输出1默认为终端的文件描述符/dev/tty

exec 0<file.txt 将文件描述符0重定向到file.txt

0表示文件描述符,&0表示文件描述符0指向的文件设备

exec 5<file.txt

cat <&5

exec 0<&- 关闭用文件描述符0作为输入的文件

exec 1>&- 关闭用文件描述符1作为输出的文件

一般重定向都只会对0,1,2三个描述符进行重定向,因为系统调用和标准库的许多函数都是默认将0描述符使用/dev/tty作为标准输入,将1描述符使用/dev/tty作为标准输出,


dup复制是针对文件设备而言的:为某个文件设备新增加一个文件描述符。(一个设备文件可以同时有多个文件描述符)

重定向和复制都可以实现将标准输入输出描述符0,1指向指定的文件设备。

 

7.守护进程

  1. 各个硬件、缓存、网卡等一般都有一个守护进程。cron也是守护进程,负责在指定的日期和时间执行指定的命令。
    2.守护进程一般以root权限运行,不可能有控制终端
    3.守护进程的创建步骤:
      1)umask(0)取消创建文件屏蔽字。因为由继承来的文件创建模式可能屏蔽了某些位。
      2)fork()使父进程退出。保证该进程不是组长进程
      3)setsid()创建一个新的会话。使得该进程成为a.新会话的首进程b.新进程的组长进程c.没有控制终端。
      4)fork()将该进程变为子进程
      5)chdir('\')将进程的工作目录改为root目录。以免造成原目录不可卸载。
      6)关闭不再需要的文件描述符。open_max获取打开的最大文件描述符
      7)守护进程的出错信息可以使用syslog()输出到/dev/log文件中

8.fork和exec继承父进程环境

fork产生的子进程继承父进程的环境
 用户ID、组ID、附加组ID、进程组ID、会话ID ->有效和实际
 控制终端、文件模式创建屏蔽字、打开的文件描述符(与close-on-exec标志有关)
 信号屏蔽和安排、连接的共享内存块、存储映射、资源限制

 父子进程的区别:
 进程ID及父进程ID
 父进程的文件锁不会被子进程继承
 子进程未处理的闹钟被清除
 子进程未处理的信号集被清除

exec执行程序空间替换,进程了父进程的以下东西:
 用户ID、组ID、附加组ID、进程组ID、会话ID 进程ID、父进程ID->有效和实际
 控制终端、文件模式创建屏蔽字、闹钟尙余留的时间、工作目录
 文件锁、进程信号屏蔽、未处理信号、资源限制
 打开的文件描述符(与close-on-exec标志有关)




本文转自 a_liujin 51CTO博客,原文链接:http://blog.51cto.com/a1liujin/1659052,如需转载请自行联系原作者

相关文章
|
18天前
|
Oracle Java 关系型数据库
Linux环境安装配置JDK11
Linux环境安装配置JDK11
49 0
|
6天前
|
Unix Linux Shell
【探索Linux】P.12(文件描述符 | 重定向 | 基础IO)
【探索Linux】P.12(文件描述符 | 重定向 | 基础IO)
11 0
|
2天前
|
Linux 数据库
linux守护进程介绍 | Linux的热拔插UDEV机制
linux守护进程介绍 | Linux的热拔插UDEV机制
linux守护进程介绍 | Linux的热拔插UDEV机制
|
5天前
|
Linux C语言 UED
【Linux】开始了解重定向
上一篇文章我们复习了C文件IO相关操作,了解了linux下的文件系统调用(open write read ),认识了文件描述符fd值,今天我们来学习重定向和缓冲区,这个缓冲区之前遇到过很多次,比如进度条项目的刷新缓冲区操作。然后我们可以来尝试封装一下系统调用,模拟C语言的文件库。
13 2
|
5天前
|
关系型数据库 MySQL Linux
在Linux系统上实现高效安装与部署环境的全方位指南
在Linux系统上实现高效安装与部署环境的全方位指南
|
6天前
|
算法 Linux C++
【探索Linux】P.31(守护进程)
【探索Linux】P.31(守护进程)
19 7
|
7天前
|
运维 NoSQL Linux
linux环境收集core文件步骤
请注意,生成core文件可能会占用磁盘空间,因此应谨慎使用。一旦完成故障排查,建议将相关的core文件删除以释放磁盘空间。
34 5
|
缓存 Linux C语言
[Linux打怪升级之路]-重定向
[Linux打怪升级之路]-重定向
|
8天前
|
运维 NoSQL Linux
linux环境收集core文件步骤
请注意,生成core文件可能会占用磁盘空间,因此应谨慎使用。一旦完成故障排查,建议将相关的core文件删除以释放磁盘空间。
21 4
|
13天前
|
Linux 开发者
【亮剑】Linux系统中的四种文件锁定机制:flock、fcntl、lockfile和flockfile,用于多进程环境下协调共享资源访问,防止数据损坏和竞争条件
【4月更文挑战第30天】本文介绍了Linux系统中的四种文件锁定机制:flock、fcntl、lockfile和flockfile,用于多进程环境下协调共享资源访问,防止数据损坏和竞争条件。flock适合脚本,fcntl提供底层灵活性,lockfile用于管理锁定文件,flockfile则结合两者功能。选择锁定策略时需考虑应用场景,如脚本可选flock,复杂需求则用fcntl。理解并正确使用这些工具对保证系统稳定性和数据一致性至关重要。