进程
进程环境
进程终止
环境表
C存储空间布局
atexit
atexit函数是一个特殊的函数,它是在正常程序退出时调用的函数,我们把他叫为登记函数
个进程可以登记若⼲个个函数,这些函数由exit⾃动调⽤,这些函数被称为终⽌处理函数, atexit函数可以登记这些函数。 exit调⽤终⽌处理函数的顺序和atexit登记的顺序相反
#include<stdlib.h>
int atexit(void (*func)(void));
传入的参数是一个函数地址,调用此参数时无需向它传递任何参数,也不期望它返回任何一个值
getenv
获取环境变量的值
#include <stdlib.h>
char *getenv(const char *name);
函数返回一个指针,它指向name=value字符串中的value,若未找到返回NULL
putenv setenv unsetenv
putenv取形式为name=value的字符串,将其放到环境表中。如果name已经存在,则先删除原来的定义(添加或者修改 环境变量)。
setenv将name设置为value,overwrite参数指示是否覆盖已存在的同名变量。如果overwrite参数为0,且已存在同名变量,则不会修改该变量;否则,将会覆盖原有的同名变量
unsetenv删除name的定义,即使name不存在也不算出错
#include <stdlib.h>
//str:指向环境变量的指针,其中环境变量必须以 "name=value" 的形式给出
int putenv(char *str);
//成功返回0,出错返回非0
int setenv(const char *name, const char *value, int overwrite);
int unsetenv(const char *name);
//成功返回0,出错返回非-1
setjmp longjmp
可跨函数的goto
setjmp函数用于记录当前位置,初次调用他的值等于0.然后再调用longjmp函数,此时longjmp会跳转到setjmp函数的地方,使得setjmp函数的返回值为非0的val值
int setjmp(jmp_buf buf);
void longjmp(jmp_buf buf,int val);
例子:
#include<stdio.h>
#include<setjmp.h>
jmp_buf buf;
void hello(){
printf("hello\n");
longjmp(buf,1);
printf("you will see this\n");
}
main(){
if(setjmp(buf)==0){
printf("first \n");
hello();
}else
printf("ok\n");
return 0;
}
getrlimit setlimit
每个进程都有一组资源限制,可用getrlimit获取和setlimit更改
#include <sys/resource.h>
int getrlimit(int resource, struct rlimit *rlim);
int setrlimit(int resource, const struct rlimit *rlim);
struct rlimit {
rlim_t rlim_cur; //软限制 :当前的限制
rlim_t rlim_max; //硬限制 :软限制的最大值
};
resource:可能的选择有
RLIMIT_AS //进程的最大虚内存空间,字节为单位。
RLIMIT_CORE //内核转存文件的最大长度。
RLIMIT_CPU //最大允许的CPU使用时间,秒为单位。当进程达到软限制,内核将给其发送SIGXCPU信号,这一信号的默认行为是终止进程的执行。然而,可以捕捉信号,处理句柄可将控制返回给主程序。如果进程继续耗费CPU时间,核心会以每秒一次的频率给其发送SIGXCPU信号,直到达到硬限制,那时将给进程发送 SIGKILL信号终止其执行。
RLIMIT_DATA //进程数据段的最大值。
RLIMIT_FSIZE //进程可建立的文件的最大长度。如果进程试图超出这一限制时,核心会给其发送SIGXFSZ信号,默认情况下将终止进程的执行。
RLIMIT_LOCKS //进程可建立的锁和租赁的最大值。
RLIMIT_MEMLOCK //进程可锁定在内存中的最大数据量,字节为单位。
RLIMIT_MSGQUEUE //进程可为POSIX消息队列分配的最大字节数。
RLIMIT_NICE //进程可通过setpriority() 或 nice()调用设置的最大完美值。
RLIMIT_NOFILE //指定比进程可打开的最大文件描述词大一的值,超出此值,将会产生EMFILE错误。
RLIMIT_NPROC //用户可拥有的最大进程数。
RLIMIT_RTPRIO //进程可通过sched_setscheduler 和 sched_setparam设置的最大实时优先级。
RLIMIT_SIGPENDING //用户可拥有的最大挂起信号数。
RLIMIT_STACK //最大的进程堆栈,以字节为单位。
成功执行时,返回0。失败返回-1
进程控制
每个进程都有一个非负整数表示的唯一进程ID---(pid)
用户ID---(uid)
组ID---(gid)
解释器文件
getpid
#include <unistd.h>
pid_t getpid(void); //return 调用进程的进程ID
pid_t getppid(void);//return 调用进程的父进程ID
pid_t getuid(void);//return 调用进程的实际用户ID
pid_t geteuid(void);//return 调用进程的有效用户ID
pid_t getgid(void);//return 调用进程的实际组ID
pid_t getegid(void);//return 调用进程的有效组ID
fork
创建子进程
pid_t fork (void);
pid_t vfork (void); //vfork可保证子进程先运行,在它调用exec或exit后父进程才继续运行
调用它一次返回两次,在调用进程(父进程)中返回一次,返回值是派生进程的进程ID号
fork的一个特性是父进程所有打开的文件描述符都被复制到子进程中,就好像执行了dup函数
在子进程中返回一次,返回值为0
wait waitpid
回收子进程的资源
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid , int *statloc ,int options);
statloc : 通过statloc指针返回子进程的终止状态 , pid : 指定想等待的进程id(值-1表示等待第一个终止的子进程) , optinos : 指定附加选项(WNOHANG 表示告知内核在没有已终止的子进程时不要阻塞)
输出 成功为已终止子进程id , 出错则为-1
exec
调用进程
存放在硬盘上的可执行文件能够被UNIX执行的唯一方法是: 由一个现有进程调用六个exec函数的某一个
#include <unistd.h>
int execl( const char *pathname, const char *arg0, ... /* (char *)0 */ );
int execv( const char *pathname, char *const argv[] );
int execle( const char *pathname, const char *arg0, ... /* (char *)0, char *const envp[] */ );
int execve( const char *pathname, char *const argv[], char *const envp[] );
int execlp( const char *filename, const char *arg0, ... /* (char *)0 */ );
int execvp( const char *filename, char *const argv[] );
6个函数返回值:若出错则返回-1,若成功则不返回值
参数:要执行的程序路径,参数
char *const ps_argv[] ={"ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL};
char *const ps_envp[] ={"PATH=/bin:/usr/bin", "TERM=console", NULL};
execl("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);
execv("/bin/ps", ps_argv);
execle("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL, ps_envp);
execve("/bin/ps", ps_argv, ps_envp);
execlp("ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);
execvp("ps", ps_argv);
setuid
设置实际用户ID和有效用户ID
#include <unistd.h>
int setuid(uid_t uid);
当root 使用setuid来变换成其他用户识别码时, root 权限会被抛弃, 完全转换成该用户身份, 也就是说, 该进程往后将不再具有可setuid()的权利, 如果只是向暂时抛弃root 权限, 稍后想重新取回权限, 则必须使用seteuid
seteuid
只更改有效用户ID
#include <unistd.h>
int seteuid(uid_t ruid , uid_t euid);
system
执行shell命令
int system(const char * command)
getlogin
获取用户登录名
char *getlogin(void);
times
获取时间
#include <sys/times.h>
clock_t times(struct tms *buf);
/* Structure describing CPU time used by a process and its children. */
struct tms
{
clock_t tms_utime ; /* User CPU time. 用户程序 CPU 时间*/
clock_t tms_stime ; /* System CPU time. 系统调用所耗费的 CPU 时间 */
clock_t tms_cutime ; /* User CPU time of dead children. 已死掉子进程的 CPU 时间*/
clock_t tms_cstime ; /* System CPU time of dead children. 已死掉子进程所耗费的系统调用 CPU 时间*/
};
成功返回墙上时钟时间,失败返回-1
进程调度
调度策略和调度优先级是由内核决定的。进程可以调整nice值选择以更低优先级运行(nice值越小,优先级越高)
nice
获取或更改它的nice值
int nice(int incr);
参数incr被增加到调用进程的nice值上!成功设置输出inc的值,错误返回-1
getpriority
获取进程、进程组和用户的进程执行优先级
int getpriority(int which, int who);
参数 which的不同时who代表的意思也不一样 :
PRIO_PROCESS who为进程的ID
PRIO_PGRP who为进程组ID
PRIO_USER who为用户ID
setpriority
设置进程的优先级
int setpriority(int which, int who, int prio);
参数 which的不同时who代表的意思也不一样 :
PRIO_PROCESS who为进程的ID
PRIO_PGRP who为进程组ID
PRIO_USER who为用户ID
进程关系、信号
alarm
1.alarm函数的作用是设置一个定时器,在seconds秒之后,将会发送SIGALRM信号给当前的进程,故而alarm函数也被称为闹钟函数。
如果不对SIGALRM信号进行忽略或者捕捉,默认情况下会退出进程。
2.如果second的值为0的话,那么定时器将会被取消。
3.如果在seconds秒内再次调用了alarm函数设置了新的闹钟,那么之前设置的秒数将会被新的闹钟时间所取代。
unsigned int alarm(unsigned int seconds)
1.如果seconds的值被设置为0,那么返回值将会是0
2.如果在当前的定时器之前设置过定时器,那么当前定时器返回的值将是之前定时器剩余的秒数
守护进程
syslog
syslogd守护进程用于解决守护进程的日志记录问题,而日志信息保存的位置和记录的信息级别是在syslogd守护进程的配置文件中设定的
Openlog函数用于打开系统日志服务的一个连接;
Syslog函数用于向日志文件中写入消息,在这里可以规定消息的优先级、消息的输出格式等;
Closelog函数用于关闭系统日志服务的连接。
调用openlog是可选择的。如果不调用openlog,则在第一次调用syslog时,自动调用openlog。调用closelog也是可选择的,它 只是关闭被用于与syslog守护进程通信的描述符
void syslog(int priority, const char *format, ...);
使用实例
int main(int argc, char **argv)
{
syslog(LOG_ERR|LOG_USER,"test - %m/n");
openlog("SyslogTest", LOG_CONS | LOG_PID, 0);
syslog(LOG_DEBUG,"This is a syslog test message generated by program '%s'/n",argv[0]);
closelog();
return 0;
}
daemon_init daemon_inted
daemon_init函数能把一个普通进程转变为守护进程
daemon_inted函数能把一个普通进程转变为inted守护进程
int daemon_init(const char *pname , int facility);
int daemon_inted(const char *pname , int facility);