环境变量操作函数
char*getenv(const char* name);获取环境变量值
intputenv(cahr* str);形式为name=value的字符串,将其放到环境表中,如果name已经存在,则先删除其原来的定义
intsetenv(name,value,rewrite);略
intunsetenv(name);删除name的定义
setjmp 功能:设置非局部跳转的跳转点
longjmp功能:进行非局部跳转
C程序缺乏异常处理的语法,可以用非局部跳转处理C的程序异常
一. 通过命令查看进程信息:
ps id 查看进程ID 号 Ps –u查看进程状态 Ps –x
Ps –aux 查看所有进程
USER 进程属主 PID进程ID PPID 父进程 %CPU 进程占的CPU百分比 %MEM 占用内存百分比 NI 进程的NICE值,数值大表示较少占用CPU时间 VSZ 进程虚拟大小 RSS 驻留中页的数量 TTY 终端ID WCHAN 正在等待的进程资源 START 启动进程的时间 TIME 进程消耗的CPU时间COMMAND 命令的名称和参数 STAT 进程状态:(运行状态 stat=R 等待状态 stat=S 停止状态 stat=T僵尸状态 stat=Z)
Ctrl+z 暂停进程 fg 恢复暂停的进程
二. 获取进程和进程组ID的函数设置进程和进程组ID
Pid_tgetpid(void) 获得当前进程 ID
Pid_tgetppid(void) 获得当前进程的父进程 ID
Pid_tgetuid(void) 获得当前进程的实际用户ID
uid_tgeteuid(void) 获得当前进程的有效用户ID
Pid_tgetgid(void) 获得当前进程的用户组ID
Pid_tgetpgid(void) 获得进程所在的进程组ID
Pid_tgetpgrp(void) 获得进程所在的进程组ID getpgid(0)=getpgrp()
Pstree命令 查看进程树
int setpgid(pid_t pid, pid_t pgid); 将目前进程的进程ID设为目前进程所属的组ID
功能:将参数pid指定进程所属的组ID设为参数pgid指定的组ID。如果参数pid 为0,则会用来设置目前进程的组ID,如果参数pgid为0,则由pid指定的进程ID将用作进程组ID。一个进程只能为它自己或它的子进程设置进程组ID。
返回值: 成功返回组识别码,错误返回-1,错误原因存于errno中。
EINVAL 参数pgid小于0。
EPERM 进程权限不足,无法完成调用。
ESRCH 找不到符合参数pid指定的进程
参数:
pid用于指定要修改的进程id。如果为0,则指当前进程。
pgid用于指定新的进程组id。如果为0,则指当前进程。
setpgid(0,0)等价于setpgrp(0,0) // 使当前进程为新进程组的组长
setpgrp()
参数:
例子1:获取进程和进程组ID的函数
5 int main()
{
6 pid_t pid; 7
8 if((pid=fork())<0)
{
9 printf("fork error!");
10 }
else if (pid==0)
{
11 printf("The child process PID is %d.\n",getpid());
12 printf("The Group ID is %d.\n",getpgrp());
13 printf("The Group ID is %d.\n",getpgid(0));
14 printf("The Group ID is %d.\n",getpgid(getpid()));
15 exit(0);
16 }
18 sleep(3);
19 printf("The parent process PID is %d.\n",getpid());
20 printf("The Group ID is %d.\n",getpgrp());
22 return 0;
23}
//进程组id = 父进程id,即父进程为组长进程
例2.设置进程和进程组ID
组长进程标识: 其进程组ID==其进程ID
组长进程可以创建一个进程组,创建该进程组中的进程,然后终止
只要进程组中有一个进程存在,进程组就存在,与组长进程是否终止无关
进程组生存期: 进程组创建到最后一个进程离开(终止或转移到另一个进程组)
一个进程可以为自己或子进程设置进程组ID
setpgid()加入一个现有的进程组或创建一个新进程组
eg:父进程改变自身和子进程的组id
5int main() {
6 pid_t pid;
8 if((pid=fork())<0) {
9 printf("forkerror!");
10 exit(1);
11 }else if (pid==0) {
12 printf("The child process PID is %d.\n",getpid());
13 printf("The Group ID of child is %d.\n",getpgid(0));// 返回组id
14 sleep(5);
15 printf("The Group ID of child is changed to %d.\n",getpgid(0));
16 exit(0);
17 }
19 sleep(1);
20 setpgid(pid,pid); // 改变子进程的组id为子进程本身21
22 sleep(5);
23 printf("The parent process PID is %d.\n",getpid());
24 printf("The parent of parent process PID is %d.\n",getppid());
25 printf("The Group ID of parent is %d.\n",getpgid(0));
26 setpgid(getpid(),getppid());// 改变父进程的组id为父进程的父进程
27 printf("The Group IDof parent is changed to %d.\n",getpgid(0));
29 return 0;
30}
三. 创建进程
Pidfork(void) 创建进程
Pidvfork(coid) 创建进程,不把父进程的地址空间完全复制到子进程中,不会复制页表。
子进程在vfork()返回后直接运行在父进程的栈空间,并使用父进程的内存和数据。这意味着子进程可能破坏父进程的数据结构或栈,造成失败。
vfork保证子进程先运行,在她调用exec或exit之后父进程才可能被调度运行。如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁
#definetypedef _STD_TPYE
四. 进程的状态
1. 可运行
2. 等待
3. 暂停
4. 僵尸 通过top命令查看进程状态
孤儿进程:父进程结束 子进程没结束,子进程由1号进程结束
僵尸进程:子进程结束但没完全释放内存(内存中的task_struct没有释放)。僵尸进程的父进程结束后就会被Init进程领养,最终被回收
避免僵尸进程:进程在终止前向父进程发送SIGCLD信号,父进程调用wait(int*status)或waitpid(pid_tpid, int*status,int opration);等待子进程的退出!如果子进程没有调用exit,则父进程wait进入阻塞!
五.进程的优先级 nice setpriority getpriority
优先级数值越低,则优先级越高!
优先级由优先级别(PR)+进程的谦让值(NI) 联合确定。
PR值是由父进程继承而来,是不可修改的。
Linux提供nice系统调用修改自身的NI值;setpriority系统调用可以修改其他进程以及进程组的NI值。
#include<unistd.h>
#include <errno.h>
#include <sys/resource.h>
#include <stdlib.h>
#include <stdio.h>
main()
{
int nPr;
if(nice(3) == -1)//进程的谦让值是3,优先级降低
{
perror("nice");
exit(0);
}
errno = 0;
nPr = getpriority(PRIO_PROCESS,getpid());//获取当前进程的谦让值
if(errno != 0)
{
perror("getpriority");
exit(0);
}
printf("priority is %d\n",nPr);
}
输出:
priority is 3
六.结束旧进程打开新进程
1. Exec族函数:一共有6个,
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);
其中只有execve是真正意义上的系统调用,其它都是在此基础上经过包装的库函数。
功能:在调用进程内部执行一个可执行文件。这里的可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。
通过exec函数执行其他的程序之后,新打开的进程覆盖当前程序进程(金蝉脱壳),没有返回值,只有调用失败了,它们才会返回一个-1,从原程序的调用点接着往下执行。
2.system(constchar* comment); system =fork+exec+waitpid
例子1---------------------------------
system("your_program");
printf("You can see me! ");在你的程序执行完毕以后,会执行printf语句
例子2---------------------------------
exec("your_program");
printf("You can't see me!");由于exec将程序your_program代替了本身,因此程序不再会执行printf语句
---------------------------------
在Linux下,exec通常会和fork语句一起用。看下面的这个例子
--------------------------------------------
pid_t pid = fork();
if (pid < 0) {
printf(“fork error!”);
exit(-1);
}
else if (pid == 0) {//这里是子进程
printf("I'm son! ");
printf(“子进程ID=%d,她的父进程ID=%d\n”, getpid(),getppid());
exec("your_program");//执行其它的程序
}
else {//这里是父进程
printf("i'm father!");
printf(“子进程ID=%d,她的父进程ID=%d\n”, getpid(),getppid());
//exitlp(“\bin\ls”,”mingling”,NULL); //如果有这句 以后的都不会执行
wait();//等待子进程结束后返回
exit(0);
}
exec与system的区别
(1) exec是直接用新的进程去代替原来的程序运行,运行完毕之后不回到原先的程序中去。
(2) system是调用shell执行你的命令,system=fork+exec+waitpid,执行完毕之后,回到原先的程序中去。继续执行下面的部分。
总之,如果你用exec调用,首先应该fork一个新的进程,然后exec. 而system不需要你fork新进程,已经封装好了。
七.会话:
1. 概念:一个或多个进程组的集合,开始于用户登录,终止于用户退出。此期间所有进程都属于这个会话期
2. pid_t setsid(void)函数
功能:在子进程中调用setsid()后,子进程成为新会话首进程,且成为一个组长进程,其进程组id等于会话id
用法:该调用进程不是组长进程,则创建一个新会话。
•该进程变成新会话首进程(sessionheader)
•该进程成为一个新进程组的组长进程。
•该进程没有控制终端,如果之前有,则会被中断
组长进程不能成为新会话首进程,新会话首进程必定会成为组长进程...
获取会话 pid_t getsid(pid_t pid)函数
返回值:成功返回会话首进程的进程组ID,出错返回-1
5 int main() {
6 pid_t pid;
7
8 if ((pid=fork())<0) {
9 printf("forkerror!");
10 exit(1);
11 }else if (pid==0) {
12 printf("The child process PID is %d.\n",getpid());
13 printf("The Group ID of child is %d.\n",getpgid(0));
14 printf("The Session ID of child is %d.\n",getsid(0));
15 sleep(10);
16 setsid(); // 子进程非组长进程,故其成为新会话首进程,且成为组长进程。该进程组id即为会话进程
17 printf("Changed:\n");
18 printf("The child process PID is %d.\n",getpid());
19 printf("The Group ID of child is %d.\n",getpgid(0));
20 printf("The Session ID of child is %d.\n",getsid(0));
21 sleep(20);
22 exit(0);
23 }
25 return 0;
26 }
八. 守护进程
1. 概念 Linux大多数服务都是通过守护进程实现的,完成许多系统任务
0: 调度进程,称为交换进程(swapper),内核一部分,系统进程
1: init进程, 内核调用,负责内核启动后启动Linux系统
让某个进程不因为用户、终端或者其他的变化而受到影响,那么就必须把这个进程变成一个守护进程
2. 守护进程编程步骤
1. 创建子进程,父进程退出
•所有工作在子进程中进行
•形式上脱离了控制终端
2. 在子进程中创建新会话
•setsid()函数
•使子进程完全独立出来,脱离控制
3. 改变当前目录为根目录
•chdir()函数
•防止占用可卸载的文件系统
•也可以换成其它路径
4. 重设文件权限掩码
•umask()函数
•防止继承的文件创建屏蔽字拒绝某些权限
•增加守护进程灵活性
5. 关闭文件描述符
•继承的打开文件不会用到,浪费系统资源,无法卸载
•getdtablesize()
•返回所在进程的文件描述符表的项数,即该进程打开的文件数目
9 int main() {
10 pid_t pid;
11 int i,fd;
12 char *buf="This is a daemon program.\n";
13
14 if ((pid=fork())<0) {
15 printf("forkerror!");
16 exit(1);
17 }else if (pid>0) // fork且退出父进程
18 exit(0);
19
20 setsid(); // 在子进程中创建新会话。
21 chdir("/"); // 设置工作目录为根
22 umask(0); // 设置权限掩码
23 for(i=0;i<getdtablesize();i++) //getdtablesize返回子进程文件描述符表的项数
24 close(i); // 关闭这些不将用到的文件描述符
25
26 while(1) {// 死循环表征它将一直运行
27 // 以读写方式打开"/tmp/daemon.log",返回的文件描述符赋给fd
28 if ((fd=open("/tmp/daemon.log",O_CREAT|O_WRONLY|O_APPEND,0600))<0) {
29 printf("Open file error!\n");
30 exit(1);
31 }
32 write(fd,buf,strlen(buf)+1); // 将buf写到fd中
34 close(fd);
35 sleep(10);
36 printf("Neveroutput!\n");
37 }
39 return 0;
40 }
因为stdout被关掉了,所以“Never ouput!”不会输出。
查看/tmp/daemon.log,说明该程序一直在运行
九.进程的终止
正常终止
1. 从main返回
2. 调用exit(标准c函数)
3. 调用_exit或_Exit(系统调用)
4. Int atexit(fun);2和3调用4实现,可以登记进程终止时收尾工作的函数,可以多次登记
5. 最后一个线程调用pthread_exit
异常终止
1. 调用abort
2. 接收信号终止
3. 最后一个线程对取消请求做处理响应
进程返回
1. 通常程序运行成功返回0,失败返回非0
2. 在shell中可以查看进程返回值(echo$?)