Linux进程解析

进程

进程标识

进程有很多的ID标识,如:

1.

进程ID和父进程ID
#include <unistd.h>
pid_t getpid(void); //获得当前进程ID
pid_t getppid(void); //获得父进程ID

2.

一个进程有真实用户ID和真实组ID,也有有效用户ID和有效组ID
有效ID的作用:比如,一个进程是root权限运行的,真实ID为root。这个进程却要用普通身份创建一个文件,通过将进程的有效ID设置为这个普通用户的ID,进程就能创建“好像是”那个用户创建的文件了。
#include <unistd.h>
gid_t getgid(void); //真实组ID
uid_t getuid(void); //真实用户ID
gid_t getegid(void); //有效组ID
uid_t geteuid(void); //有效用户ID

进程状态


一个 简单系统的状态

ps工具用来显示有关进程的信息。默认情况下,ps显示与用户有关的进程信息。-a显示与终端有关进程的信息,-A显示所有进程的信息,-o用来指定输出的格式。
ps [-aA] [-G grouplist] [-o format] ... [-p proclist]
[-t termlist] [-U userlist]


进程的创建

进程可以通过调用fork() 来创建进程。fork()可以拷贝父进程中的内存映像,子进程进行和父进程没有任何关系,只会在进程结束时,返回一个pid给父进程。

执行如下程序时,会发生什么情况?

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main()
{
    pid_t childpid;
    pid_t mypid;

    mypid = getpid();
    childpid = fork();
    if (childpid == -1)
    {
        prror("Failed to fork");
        return 1;
    }

    if(childpid == 0)
    {
        printf("I am child %ld, ID = %ld\n", (long)getpid(), (long)mypid);
    }
    else
    {
        printf("I am parent %ld, ID = %ld\n", (long)getpid(), (long)mypid);
    }
    return 0;
} 

进程的阻塞状态

一个进程创建子进程时,父进程和子进程都从fork后的那个点开始继续执行。父进程可以通过wait或waitpid一直阻塞到子进程结束。wait函数会使用调用者的执行挂起,直到子进程的状态成为可用的,或调用者收到一个信号为止。
#include <sys/wait.h>
pid_t wait(int *stat_loc); //获得pid
pid_t waitpid(pid_t pid, int *stat_loc, int options); //获得pid
wait或waitpid的参数stat_loc是一个指向整数变量的指针。如果stat_loc不为NULL,这些函数就将子进程的返回状态存储在这个单元中。子进程通过exit、_exit、_Exit或从main函数中return来返回它的状态。

进程exec函数

fork()函数创建了调用进程的一份拷贝,但很多应用程序都需要子进程与其父进程不同代码。
exec函数族提供了用新的映射来覆盖调用进程的进程映射的功能。fork-exec配合应用的传统方法是:子进程执行新程序,而父进程继续执行原来的代码。
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg0, .../* char * (0)*/ );
int execle(const char *path, const char *arg0,... /* char * (0) , char *const envp[] */ );
int execlp(const char *file, const char *arg0, ... /* char *(0) */);
int execv(const char *path,char *const argv[] );
int execve(const char *path,char *const argv[] , char *const envp[]);
int execvp(const char *path,char *const argv[] );
调用exec后保留的属性 and 与这些属性相关的一些库函数
进程ID getpid
父进程ID getppid
进程组ID getgid
serssionID getsid
真实组ID getgid
真实用户ID getuid
有效组ID getegid
有效用户ID geteuid
附加组ID getgroups
告警信号剩余时间 alarm
当前工作目录 getcwd
根目录
文件形式的创建掩码 umask
进程信号掩码 sigprocmask


后台进程和守护进程

守护进程是一种可以无限期进行的后台进程。unix操作系统是由很多的守护进程来维持稳定的。
守护进程,也就是通常说的Daemon进程,是Linux中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程常常在系统引导装入时启动,在系统关闭时终止。Linux系统有很多守护进程,大多数服务都是通过守护进程实现的,同时,守护进程还能完成许多系统任务,例如,作业规划进程crond、打印进程lqd等(这里的结尾字母d就是Daemon的意思)。
创建子进程,父进程退出。这是编写守护进程的第一步。由于守护进程是脱离控制终端的,因此,完成第一步后就会在Shell终端里造成一程序已经运行完毕的假象。之后的所有工作都在子进程中完成,而用户在Shell终端里则可以执行其他命令,从而在形式上做到了与控制终端的脱离。

在Linux中父进程先于子进程退出会造成子进程成为孤儿进程,而每当系统发现一个孤儿进程时,就会自动由1号进程(init)收养它,这样,原先的子进程就会变成init进程的子进程。
在子进程中创建新会话这个步骤是创建守护进程中最重要的一步,虽然它的实现非常简单,但它的意义却非常重大。在这里使用的是系统函数setsid,在具体介绍setsid之前,首先要了解两个概念:进程组和会话周期
进程组:是一个或多个进程的集合。进程组有进程组ID来唯一标识。除了进程号(PID)之外,进程组ID也是一个进程的必备属性。每个进程组都有一个组长进程,其组长进程的进程号等于进程组ID。且该进程组ID不会因组长进程的退出而受到影响。
会话周期:会话期是一个或多个进程组的集合。通常,一个会话开始于用户登录,终止于用户退出,在此期间该用户运行的所有进程都属于这个会话期。
接下来就可以具体介绍setsid的相关内容:
setsid函数作用:
setsid函数用于创建一个新的会话,并担任该会话组的组长。调用setsid有下面的3个作用:
让进程摆脱原会话的控制
让进程摆脱原进程组的控制
让进程摆脱原控制终端的控制
那么,在创建守护进程时为什么要调用setsid函数呢?由于创建守护进程的第一步调用了fork函数来创建子进程,再将父进程退出。由于在调用了fork函数时,子进程全盘拷贝了父进程的会话期、进程组、控制终端等,虽然父进程退出了,但会话期、进程组、控制终端等并没有改变,因此,这还不是真正意义上的独立开来,而setsid函数能够使进程完全独立出来,从而摆脱其他进程的控制。
改变当前目录为根目录这一步也是必要的步骤

使用fork创建的子进程继承了父进程的当前工作目录。由于在进程运行中,当前目录所在的文件系统(如“/mnt/usb”)是不能卸载的,这对以后的使用会造成诸多的麻烦(比如系统由于某种原因要进入单用户模式)。因此,通常的做法是让"/"作为守护进程的当前工作目录,这样就可以避免上述的问题,当然,如有特殊需要,也可以把当前工作目录换成其他的路径,如/tmp。改变工作目录的常见函数是chdir。
重设文件权限掩码
文件权限掩码是指屏蔽掉文件权限中的对应位。比如,有个文件权限掩码是050,它就屏蔽了文件组拥有者的可读与可执行权限。由于使用fork函数新建的子进程继承了父进程的文件权限掩码,这就给该子进程使用文件带来了诸多的麻烦。因此,把文件权限掩码设置为0,可以大大增强该守护进程的灵活性。设置文件权限掩码的函数是umask。在这里,通常的使用方法为umask(0)。
关闭文件描述符
同文件权限码一样,用fork函数新建的子进程会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不会被守护进程读写,但它们一样消耗系统资源,而且可能导致所在的文件系统无法卸下。
在上面的第二步之后,守护进程已经与所属的控制终端失去了联系。因此从终端输入的字符不可能达到守护进程,守护进程中用常规方法(如printf)输出的字符也不可能在终端上显示出来。所以,文件描述符为0、1和2 的3个文件(常说的输入、输出和报错)已经失去了存在的价值,也应被关闭。通常按如下方式关闭文件描述符:
===============================
for(i=0;i<MAXFILE;i++)
close(i);
===============================
守护进程退出处理
当用户需要外部停止守护进程运行时,往往会使用 kill命令停止该守护进程。所以,守护进程中需要
编码来实现kill发出的signal信号处理,达到进程的正常退出。
===============================
signal(SIGTERM, sigterm_handler);
void sigterm_handler(int arg)
{
_running = 0;
}
===============================
这样,一个简单的守护进程就建立起来了。
实现守护进程的完整实例(每隔10ms在/tmp/dameon.log中写入一句话):
=====================================================================

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<fcntl.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/stat.h>

#define MAXFILE 65535

void sigterm_handler(int arg);
volatile sig_atomic_t _running = 1;

int main()
{
    pid_t pc,pid;
    int i,fd,len,flag = 1;
    char *buf="this is a Dameon\n";
    len = strlen(buf);
    pc = fork(); //第一步
    if(pc<0)
    {
        printf("error fork\n");
        exit(1);
    }
    else if(pc>0)
    exit(0);
    pid = setsid(); //第二步
    if (pid < 0)
    perror("setsid error");
    chdir("/"); //第三步
    umask(0); //第四步
    for(i=0;i<MAXFILE;i++) //第五步
    close(i);
    signal(SIGTERM, sigterm_handler);
    while( _running )
    {
        if( flag ==1 &&(fd=open("/tmp/daemon.log",O_CREAT|O_WRONLY|O_APPEND,0600))<0)
        {
            perror("open");
            flag=0;
            exit(1);
        }
        write(fd,buf,len);
        close(fd);
        usleep(10*1000); //10毫秒
  }
}
void sigterm_handler(int arg)
{
    _running = 0;
}


linux守护进程列表

amd:自动安装NFS(网络文件系统)守侯进程
apmd:高级电源治理
Arpwatch:记录日志并构建一个在LAN接口上看到的以太网地址和ip地址对数据库
Autofs:自动安装治理进程automount,与NFS相关,依靠于NIS
crond:linux下的计划任务
Dhcpd:启动一个DHCP(动态IP地址分配)服务器

Gated:网关路由守候进程,使用动态的OSPF路由选择协议

Httpd:WEB服务器
Inetd:支持多种 网络服务 的核心守候程序
Innd:Usenet新闻服务器
Linuxconf:答应使用本地WEB服务器作为 用户接口 来配置机器
Lpd: 打印服务器
Mars-nwe:mars-nwe文件和用于Novell的打印服务器
Mcserv:Midnight命令 文件服务器
named:DNS服务器
netfs:安装NFS、Samba和NetWare 网络文件系统
network:激活已配置网络接口的 脚本程序
nfs:打开NFS服务
nscd:nscd(Name Switch Cache daemon)服务器,用于NIS的一个支持服务,它高速缓存用户口令和组成成员关系
portmap:RPC portmap治理器,与inetd类似,它治理基于RPC服务的连接
postgresql:一种SQL 数据库服务器
routed:路由守候进程,使用动态RIP 路由选择协议
rstatd:一个为LAN上的其它机器收集和提供系统信息的守候程序
ruserd: 远程用户 定位服务,这是一个基于RPC的服务,它提供关于当前记录到LAN上一个机器日志中的用户信息
rwalld:激活rpc.rwall服务进程,这是一项基于RPC的服务,答应用户给每个注册到LAN机器上的其他终端写消息
rwho d:激活rwhod服务进程,它支持LAN的rwho和ruptime服务
sendmail: 邮件服务器 sendmail
smb:Samba文件共享/打印服务
snmpd:本地简单网络治理候进程
squid:激活代理服务器squid
syslog:一个让系统引导时起动syslog和klogd 系统日志 守候进程的脚本
xfs:X Window字型服务器,为本地和远程X服务器提供字型集
xntpd:网络时间服务器
ypbind:为NIS(网络信息系统)客户机激活ypbind服务进程
yppasswdd:NIS口令服务器
ypserv:NIS主服务器
gpm:管鼠标的
identd:AUTH服务,在提供用户信息方面与finger类似

Bootparamd:引导参数服务器,为LAN上的无盘工作站提供引导所需的相关信息





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值