linux学习总结(四)

来源:互联网 发布:mysql select unique 编辑:程序博客网 时间:2024/05/17 21:55

linux学习总结(四)

回收内核空间资源

进程退出时释放了用户空间的资源,但是,进程的PCB并没有释放,这一工作显然不是自己完成的,而是由当前进程的父进程完成的,父进程可以显示的调用wait()和waitpid()函数来完成
wait()函数将会使父进程阻塞式等待该进程的任意一个子进程结束后,回收该子进程的内核进程资源

_pid_t wait(_WAIT_STATUS _stat_loc);

如果等待到任意一个子进程结束,将返回当前结束的子进程的PID,同时将子进程退出时的状态存储在_stat_loc变量中,如果执行失败则返回-1.
waitpid()等待指定子进程结束

_pid_t waitpid(_pid_t _pid, int *_stat_loc, int _options);

第一个参数为进程PID值i,该值的设置范围如下
PID>0 表示等待进程PID为该PID值的进程结束
PID=-1,表示等待任意子进程结束,相当于调用wait()
PID=0,表示等待与当前进程的进程组PGID一致的进程结束
PID<-1.表示等待进程组PGID是此值的绝对值的进程结束
第二个参数为调用它的函数中某个变量地址,如果执行成功,则用来存储结束进程的结束状态
第三个参数为等待选项,可以设置为0,也可以为WNOHANG(1)不阻塞等待,WUNTRACED(2)报告状态信息

孤儿进程与僵尸进程

孤儿进程:因父亲进程先退出而导致一个子进程被init进程收养的进程
僵尸进程:进程已经退出,但是他的父亲进程还没有回收内核资源的进程为僵尸进程。

修改进程用户相关信息

access用来检查当前进程是否拥有对某文件的相应访问权限

int access(_const char *_name, int _type);

第一个参数我i要访问的文件(需要包含路径)
第二个参数为相应的访问权限,文件权限定义如下
R_OK 4//读权限
W_OK 2//写权限
X_OK 1//执行权限
F_OK 0//文件是否存在

设置进程真实用户RUID

调用setuid函数可以修改真实用户RUID

int setuid(_uid_t _uid);

如果当前用户是超级用户,则将设置真实用户号(UID),有效用户号EUID为指定ID,并以返回0一标识成功
如果当前用户是普通用户,且要设置的UID值为自己的UID,则可以修改成功,否则无权修改,函数将返回-1.

设置进程有效用户EUID

seteuid()用来设置有效用户号,

int seteuid(_uid_t _uid);需要是超级用户权限

守护进程及其创建过程

守护进程的特点

守护进程是在后台运行的一种特殊进程,他脱离于终端,从而可以避免进程被任何终端所产生的信号所打断,它在执行过程中的产生信息也不在任何终端上显示,守护进程周期性地执行某种任务或等待处理某些发生的事件。
一般情况下,守护进程可以通过以下方式启动:
在系统启动时由启动脚本启动,这些启动脚本通常放在/etc/rc.d目录下
利用inetd超级服务器启动,如telnet等
由cron命令定时启动以及终端用nohup命令启动进程也是守护进程

守护进程编程要点

编写守护进程的基本过程

  • 1、屏蔽一些有关控制终端操作的信号:这是为了防止在守护进程没有正常启动起来前,控制终端受到干扰退出或挂起。忽略所有可以忽略的信号,SIGSTOP和SIGKILL不能忽略
  • 2、在后台运行:这是为了避免挂起终端将其放入后台执行。方法是在进程中创建子进程,并使父进程终止,让其在子进程在后台执行
  • 3、脱离控制终端和进程组:这是因为:
    (1)一个进程属于一个进程组,进程组号(PGID)就是进程组长的进程号
    (2)同进程组中的进程共享一个控制终端,这个控制终端默认是创建进程的终端
    (3)一个进程关联的控制终端和进程组通常是从父进程继承下来,因此,这个子进程仍然受到父亲进程终端的影响,因为终端产生的信号会发送给前台进程组的所有进程
    我们需要调用setsid()使子进程成为新的会话组长,彻底摆脱该终端的影响
    setsid()调用成功后,调用此函数的进程成为新的会话组长和新的进程组长,并与原来的进程组脱离关系,由于会话过程对控制终端的独占性,进程同时与控制终端脱离
  • 4、禁止进程重新打开控制终端
  • 5、关闭打开的文件描述符
  • 6、改变当前工作目录
  • 7、重设文件创建掩码:进程从创建它的父进程那里继承了文件创建掩码。它可能修改守护进程所创建的文件的存取权限。umask(0)
  • 8、处理SIGCHLD信号(子进程退出信号),在守护进程得到请求生成子进程处理新的请求后,我们使用一种简单的方式来处理子进程的退出,将子进程退出SIGCHLD信号的操作设为SIG_IGN处理方式,放系统帮助回收僵尸进程资源

    signal(SIGCHLD, SIG_IGN);

日志信息及其管理

日志信息基本概念

为了告诉系统管理员守护进程的运行情况,特别是出现异常时,守护进程需要输出特定信息,而守护进程又不能吧信息输出到某个终端,因此,守护进程一般采用日志信息的方式输出。在linux系统下,守护进程又两种写日志信息的方式
* 1、进程直接与日志文件建立联系(或者自己创建一个独立的日志文件),即open该文件,然后调用write函数写日志
* 2、使用日志守护进程:为了便于管理日志文件,系统创建了日志守护进程syslogd专门负责管理日志文件,因此,要向日志文件中写日志信息,只需要将日志发送给日志守护进程
建立与日志守护进程联系
在进程中,调用函数openlog()将与日志守护进程建立联系

void openlog(_const char *_ident, int _option, int _facility)

第一个参数:要想每个消息加入的字符串,一般设置为进程名
第二个参数:用来描述已打开选项
三个参数:消息的类型,决定将消息写入到哪个日志文件中
如果不调用此函数,在调用syslog()时会隐式调用此函数
如果需要关闭与日志守护进程的联系,可以调用closelog函数

写日志信息

syslog()将产生一条日志信息,然后由日志守护进程将其发布到各日志文件中:

void syslog(int _pri, _const *_fmt, ...);

第一个参数决定日志级别
第二个参数为日志输出格式。类似于printf函数的参数
如果要设置当前进程的sysylog()函数输出消息的默认优先级,可以调用setlogmask()函数

int setlogmask(int _mask)

进程间通信——PIPE

无名管道:是一种临时的管道,命令执行完成后将自动消失,可以使用管道将一个的命令的输出作为另一个命令的输入,无名管道的内核资源在通信两进程退出后会自动释放。
无名管道的特殊性:
创建无名管道:在编程中,使用无名管道前要先创建无名管道。

int pipe(int _pipedes[2])

此参数为一个整型数组。如果执行成功,pipe将存储两个整型文件描述符于_pipedes[0]和_pipedes[1]中,它们分别指向管道的两端。如果系统调用失败,将返回-1。
无名管道是单工的,一个管道只能实现从一个进程向另一个进程发送消息_pipedes[0]用来完成读操作,也只能执行读操作,_pipedes[1]用来完成写操作,即输入到管道的数据将从此文件描述符写入
如果需要双工的,则需要两个管道。一个负责从进程A向进程B发送消息,一个管道负责从进程B向进程A发送消息。
读写无名管道:任何进程读/写无名管道时必须确认还存在一个进程(这个进程可以是自己),该进程以写/读的方式(即读对应写,写对应读)访问管道。读写管道使用的系统调用就是read()和write(),两者都默认以阻塞方式读写管道,如果要修改这两个函数的行为,可以使用fcntl函数实现

  • 1、以阻塞方式读无名管道,如果当前没有一个进程可以访问写段,读操作可以立即返回。
    (1)如果管道现有数据无数据,立即返回0
    (2)如果管道现有数据大于要读出数据,立即读取期望大小的数据
    (3)如果现有数据小于要读出数据,立即读取现有所有所有的数据
  • 2、如果以阻塞的方式读无名管道,有某个进程可以访问写段
    (1)管道中无任何数据,读操作阻塞
    (2)管道中有数据,现有数据大小小于期望读出值,读出现有数据并返回
    (3)管道中有数据,现有数据大小大于期望读出值,读出期望大小的数据返回
  • 3、如果以阻塞的方式写无名管道,如果当前没有一个进程可以访问到读端,写操作将受到SIGPIPE信号,write函数返回-1.如果当前有某进程可以访问到读端,且管道中有空间,则写入成功
  • 4、如果以阻塞的方式写无名管道,如果当前管道已满,则阻塞当前进程,如果有多个进程试图写管道操作,当有进程读管道唤醒写入操作时,唤醒那个进程未知
  • 5、如果以O_NDELAY或O_NONBLOCK设置管道的读段,如果管道中有数据,将读取数据,如果管道中没有数据,将立即返回-1
  • 6、如果以O_NELAY或O_NONBLOCK设置了管道的写端,如果管道中有空间,将写入数据,如果管道中没有足够的空间,将立即返回-1.

文件描述符重定向

shell重定向基本操作

  • (1)cat

重定向编程

输入重定向:关闭标准输入设备,打开(或复制)某普通文件,使其文件描述符为0
输出重定向:关闭标准输出设备,打开某普通文件,使其文件描述符为1
错误输出重定向:关闭标准错误输入设备,打开某普通文件按,使其文件描述符为2
使用dup()和dup2()函数可以实现文件描述符的复制操作

int dup(int _fd);

dup()会复制某打开的文件描述符fd,新的描述符值为下一个可用的最小非负文件描述符值,它将与原来的文件描述符共享同一个文件表项

dup2()函数:int dup2(int _fd, int _fd2);

fd和fd2,fd2为一个非负整数。如果fd2是一个已打开的文件描述符,则首先关闭文件,然后再复制,dup2()返回的文件描述符fd2与fd具有系列共同点

  • (1)相同的打开文件
  • (2)相同的文件指针
  • (3)相同的访问模式
  • (4)相同的文件状态标志

成功完成后,dup2()将返回新的文件描述符fd2,其值为非负整数,否则,将换回-1

流重定向

函数popen()和pclose()可以实现流的重定向,使用popen()函数可以将一个程序的输入或者输出重定向

FILE *open(_const char *_command, _const char *_modes);

popen函数创建一个子进程,并在子进程执行第一个参数所指程序,同时返回一个文件指针,第2个参数表示I/O方式
使用完后重定向后,需要使用pclose()关闭相应的流对象,

int pclose(FILE *_stream);
原创粉丝点击