一、主要内容:
1.进程如何启动和退出的(main函数,初启函数,exit函数,atexit函数)。
(1).Linux创建进程的唯一方法就是利用shell的fork系统调用。
(2).编译C程序时,连接器会将初启函数和main函数一起装入内存形成可执行文件,指定该初起函数为该初启函数的开始执行点。
(3).初启函数负责从UNIX SHELL获得命令行参数和系统的环境变量,将它们组织成main函数需要的形式,并调用main函数。
(4).当C程序从main返回时,初启函数负责调用exit函数中止进程。
(5).初启函数也是由shell创建的。也是shell的子进程。
2.命令行参数
(1).命令行参数分为两种,一种是选项参数,如-l –u 。一种是非选项参数,大多是文件名或路径名。
(2).对于系统,程序得到命令行参数有两种办法。
通过初起函数得到。
由父进程传递给子进程。
(3).对于程序,程序得到命令行参数的唯一方法是通过argc,argv.
(4).当参数个数固定或所以的参数都可按相同的方式理解,可直接用argc,argv获得。
(5).当参数有选项参数和非选项参数混合时,可使用getopt函数对选项参数进行逐一扫描,实现区分。但必须对选项参数进行语法约定。
(6).选项参数的约定有:
(一) 选项参数必须以”-“开头。
(二) 当选项不需要值的时候可以连着写。
(三) 选项参数必须写在它的非选项参数之前。
二、命令行参数
1.
名称:: |
getopt |
功能: |
扫描选项参数 |
头文件: |
#include <unistd.h> |
函数原形: |
int getopt(int argc,char *const argv[],const char *optstring); |
参数: |
argc argv opestring 选项参数 |
返回值: |
|
getopt( )用来分析命令行参数。参数argc和argv是由main( )传递的参数个数和内容。参数optstring 则代表欲处理的选项字符串。
返回值:如果找到符合的参数,则返回此参数字母.如果参数不包含在参数optstring 的选项字母,则返回字符,分析结束则返回-1
getopt(argc,argv,”islf”);
./a.out –i –x –l –s file1
如果在一个选项后面有“:”,那么该选项后面必须带有值。
getopt(argc,argv,”is:lf”);
和getopt有关的4个全局变量
1. opterr
当opterr等于0的时候,遇到非法的选项字符时,或者选项要求有值而缺省时,它会打印“?”。缺省时opterr是等于0的。
当opterr不等于0的时候,遇到非法的选项字符时,或者选项要求有值而缺省时,它会打印一条出错信息到标准输出。
还有一种情况时,第三个参数的开头是一个“:”,可以通过opterr区分它是因为遇到非法的选项字符(返回”?”),还是选项要求有值而缺省出错(返回“:”)。 如:
getopt(argc,argv,”:ls:if”);
./a.out –i –f –s –l file1
2.optopt——一但出现两种异常的时候,optopt会把它出错信息存储起来。
3. optind——argv存放下一个要处理元素的索引,初值为1。
4.optarg——若选项要求有值需接收值,optarg指向选项的值。
/7_1.c #include <stdio.h> #include <unistd.h> #include <stdlib.h>
main(int argc,char *argv[]) { int c;
while((c=getopt(argc,argv,”:d:s:))!=-1) { switch(c) { case ‘d’: printf(“d\n”); printf(“%s\n”,optarg); break; case ‘s’: printf(“s\n”); printf(“%s\n”,optarg); break; case ‘?’: printf(“?\n”); break; case ’:’: printf(“:\n”); break; default: printf(“default”); } } exit(0); }
|
三、终止进程
1.
名称:: |
exit/_exit |
功能: |
终止一个进程 |
头文件: |
#include <stdlib.h> (exit) #include <unistd.h>(_exit) |
函数原形: |
void exit(int status); void _exit(int status); |
参数: |
status 终止状态 |
返回值: |
无 |
exit和_exit函数用于正常终止一个程序:_exit立即进入内核,exit则先执行一些清除处理,然后进入内核。
exit函数总是执行一个标准I/O库的清除关闭操作,对于所有打开流调用fclose函数,把所缓存中的所有数据都写到文件中。
exit和_exit函数都带一个参数,称为终止状态,大多数UNIX Shell都提供检查一个进程终止状态的方法。
2.
名称:: |
atexit |
功能: |
register a function to be called at normal program termination. |
头文件: |
#include <stdlib.h> |
函数原形: |
int atexit(void(*function)(void)); |
参数: |
function 函数的地址 |
返回值: |
若成功则为0,否则不为0。 |
一个函数可以最多登记32个函数,这些函数将由exit自动调用,我们称这些函数为终止处理函数,用stexit登记它们。
其中,atexit的参数是一个函数地址,当调用此函数时无需向它传递任何参数,也不期望它返回一个值。exit以登记这些函数的相反顺序调用它们。同一函数如若登记多次,则也被调用多次。
exit首先调用各终止处理函数,然后按需多次调用fclose关闭所有打开流。
下面是一个实例:
/*7_2.c*/ #include <stdlib.h> #include <stdio.h>
static void exit1(void); static void exit2(void);
int main() { if(atexit(exit1)!=0) printf(“can’t register exit
if(atexit(exit2)!=0) printf(“can’t register exit
pritnf(“main is done\n”); return(0); }
static void exit1(void) { pritnf(“first exit handler\n”); }
static void exit2(void) { pritnf(“second exit handler\n”); } |
四、进程空间
3.
名称:: |
malloc/calloc/realloc/free |
功能: |
allocate and free dynamic memory |
头文件: |
#include <stdlib.h> |
函数原形: |
void *calloc(size_t nmemb,size_t size); void *malloc(size_t size) void *realloc(void *ptr,size_t size); void free(void *ptr); |
参数: |
size 存储区大小 nmemb 指定的对象 ptr 指向存储区的指针 |
返回值: |
若成功则返回非空指针,否则返回NULL |
malloc分配制定字节的存储区,此存储区的初始值不能确定。
calloc为指定的对象分配能容纳其指定个数的存储空间。
realloc更改以前分配的存储区大小。
free释放分配的存储区。
realloc使我们可以增减以前分配区的长度。如果在该存储区后有主够的空间可供扩充,则可在原存储区位置上向高地址方向扩充,如果在原存储区没有足够的空间,则realloc分配另一个足够大的存储区。通常分配存储空间比实际的要大一些,额外的空间用来记录管理信息——分配块的长度。指向下一个分配块的指针等等。
4.
名称:: |
alloca |
功能: |
memory allocator |
头文件: |
#include <alloca.h> |
函数原形: |
void *alloca(size_t size); |
参数: |
size 存储区大小 |
返回值: |
若成功则返回非空指针,否则返回NULL |
alloca也是用来分配存储空间的,它和malloc的区别是它是在当前函数的栈上分配存储空间,而不是在堆中。其优点是:当函数返回时,自动释放它所使用的栈。
环境变量顾名思义就是存储进程运行环境的变量.每个程序都接收到一张环境表。环境表是一个字符数组,其中每个指针包含一个以NULL结束的字符串的地址。全局变量environ则包含了该指针数组的地址。
下面列出一些环境变量。
COLUMNS 终端宽度 DATEMSK getdate模板文件路径名
HOME 起始目录 LANG 本地名
LC_ALL 本地名 LC_COLLATE 本地排序名
LC_CTYPE 本地字符分配名 LC_MESSAGES 本地消息名
LC_NUMERIC 本地货币编辑名 LC_TIME 本地日期/时间格式名
LINES 终端高度 LOGNAME 登陆名
NLSPATH 消息类模板系列 PATH 搜索可执行文件的路径前缀列表
PWD 当前工作目录的绝对路径 SGELL 用户首先的shell名
TERM 终端类型 TMPDIR 在其中创建临时文件的目录名
TZ 时期信息
五、进程的环境变量
5.
名称:: |
getenv |
功能: |
获取环境变量 |
头文件: |
#include <stdlib.h> |
函数原形: |
char *getenv(const char *name); |
参数: |
name 变量名 |
返回值: |
若成功则返回与name相关的value指针,否则返回NULL |
此函数返回一个指针,它指向name=value字符串的value。我们应当使用getenv从环境中取一个特定的环境变量值,而不是直接访问environ.
下面是一个例子。
/*7_3.c*/ #include <stdlib.h>
main() { char *p; if((p=getenv(“USER”))) printf(“user=%s\n”,p); if((p=getenv(“PWD”))) printf(“pud=%s\n”,p); } |
运行结果为:
# .7_3 user=li pwd=/roor/peixun |
注意程序中的USER环境变量是系统用于用户自定义的环境变量。
6.
名称:: |
putenv |
功能: |
增加环境变量 |
头文件: |
#include <stdlib.h> |
函数原形: |
int putenv(const char *str); |
参数: |
str 变量名 |
返回值: |
若成功则返回0,否则返回非0 |
qutenv取形式为name=value的字符串,将其放到环境表中。如果name已存在则先删除其原来的定义。
*/7_4.c*/
#include<stdlib.h>
main()
{
char *p;
if((p = getenv(“USER”)))
printf(“USER =%s\n”,p);
putenv(“USER=test”);
printf(“USER=%s\n”, getenv(“USER”));
}
|
运行结果为:
#./7_4 USER=li USER=test |
程序先读用getenv取当前环境变量USER,然后用putenv增加USER=test的新环境变量,新的环境变量覆盖掉了原有的环境变量。
练习:向系统中添加一条环境变量USER1=root
7.
名称:: |
setenv |
功能: |
修改环境变量 |
头文件: |
#include <stdlib.h> |
函数原形: |
int setenv(const char *name,const char *value,int rewrite); |
参数: |
str 变量名 |
返回值: |
若成功则返回0,否则返回非0 |
setenv将name设置为value.如果在环境中name已经存在,那么
(1) 若rewrite非0,则首先删除其现存的定义;
(2) 若rewrite为0,则不删除其现存定义(name不设置为新的value,而且也不出错)。
下面是一个例子:
/*7_5.c*/ #include <stdlib.h>
main() { if((p=getenv(“USER”))) printf(“USER=%s\n”,p); setenv(“USER”,”liuning”,0); printf(“rewrite=0\n”); printf(“USER=%s\n”,getenv(“USER”)); setenv(“USER”,”liuning”,1); printf(“rewrite!=0\n”); printf(“USER=%s\n”,getenv(“USER”)); }
|
下面是运行结果:
# ./7_5 USER=li rewrite=0 USER=li rewrite!=0 USER=liuning |
四、执行新程序
9.
名称:: |
exec |
功能: |
执行一个新程序 |
头文件: |
#include <unistd.h> |
函数原形: |
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 *fath,char *const argv[]); int execvp(const char *file,char *const argv[]); int execve(const char *file,char *const argv[],char *const envp[]); |
参数: |
path 路径名 file 文件名 arg 单独的命令参数,以NULL结束。 argv 指向命令参数列表 envp 指向环境字符串指针数组的指针 |
返回值: |
若出错返回-1。若成功不返回值 |
用fork函数创建子进程后,子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程完全由新程序代换,而新程序则从其main函数开始执行。因为调用exec并不创建新进程,所以前后的进程ID并未改变。exec只是用另一个新程序替换了当前进程的正文,数据,堆和栈段。
有六种不同的exec函数可供使用,它们常常被统称为exec函数。在这些函数中字母p表示该函数取filename作为参数,并且用PATH环境变量寻找可执行文件。字母l表示该函数去一个参数表,它与字符v互斥。v表示该函数去一个argv[]矢量。最后,字母e表示该函数取envp[]数组,而不使用当前环境。
在执行exec前后实际用户ID和实际组ID保持不变,而有效ID是否改变则取决于所执行程序文件的设置用户ID位和设置组ID位是否设置。如果新程序的设置用户ID位已设置,则有效用户ID变成程序文件所以者的ID,否则有效用户ID不变。对组ID的处理方式与此相同。
/*8_4.c*/ #include <sys/types.h> #include <sys/wait.h> #include <unistd.h>
int main(int argc,char *argv[]) { pid_t pid; if((pid=fork())==0) execvp(argv[1],argv); else waitpid(pid,NULL,0); } |
下面是运行结果:
#./8_4 ./main ll dd ./8_4 ./main ll dd |
程序先利用argv[1]调用./main函数,然后把argv传给main函数,把参数打印到屏幕上。要注意main函数必须和本程序在同一目录下。
五、更改ID
10.
名称:: |
setuid/setgid |
功能: |
更改用户ID和组ID |
头文件: |
#include <unistd.h> #include <sys/types.h> |
函数原形: |
int setuid(uid_t uid); int setgid(uid_t gid); |
参数: |
uid 用户ID gid 用户组ID |
返回值: |
若成功返回进程0,若出错返回-1。 |
(1)
若进程具有超级用户特权,则setuid函数将实际用户ID,有效用户ID,以及保存的设置-用户-ID设置为uid.
(2) 若进程没有超级用户特权,但是uid等于实际用户ID或保存的设置-用户-ID,则setuid只有效用户ID设置为uid。不改变实际用户ID和保存的设置-用户-ID。
(3) 如果上面两个条件都不满足,则errno设置-用户-ID。
11.
名称:: |
setreuid/setregid |
功能: |
交换实际用户ID和有效用户ID. |
头文件: |
#include <unistd.h> |
函数原形: |
int setreuid(uid_t ruid,uid_t euid); int setregid(gid_t rgid,uid_t egid); |
参数: |
euid 有效用户id ruid 实际用户id egid 有效组id rgid实际组id |
返回值: |
若成功返回进程0,若出错返回-1。 |
可以用setuid函数设置实际用户ID和有效用户ID。与此类似,可以用setgid函数设置实际组ID和有效ID。
12.
名称:: |
seteuid/setegid |
功能: |
更改用户有效ID和有效组ID |
头文件: |
#include <unistd.h> |
函数原形: |
int setuid(uid_t uid); int setgid(gid_t gid); |
参数: |
uid 用户id gid 组id |
返回值: |
若成功返回进程0,若出错返回-1
|
一、进程组
1.
名称:: |
getpgrp |
功能: |
得到进程的进程组id |
头文件: |
#include <unistd.h> |
函数原形: |
pid_t getpgrp(void); |
参数: |
无 |
返回值: |
调用进程的进程组ID |
进程组是一个或多个进程的集合,通常,它们与同一作业相关联,可以接收来自同一终端的各种信号。每个进程除了一个进程ID之外,还属于一个进程组。每个进程组有一个唯一的进程组ID。
2.
名称:: |
getpgid |
功能: |
得到进程的进程组id |
头文件: |
#include <unistd.h> |
函数原形: |
pid_t getpgid(pid_t pid); |
参数: |
pid 进程组id |
返回值: |
若成功则返回进程组ID,若出错则返回-1。 |
getpid的功能与getpgrp的功能相似,getpgrp返回调用进程的进程组,而getpgid返回指定进程的进程组,getgpid(0);等价于getpgrp(); .
每个进程组都可以有一个组长进程。组长进程的进程ID等于其进程组ID。组长进程可以创建一个进程组,创建该进程组中的进程,然后终止。
3.
名称:: |
setpgid |
功能: |
加入或创建一个进程组 |
头文件: |
#include <unistd.h> |
函数原形: |
pid_t setpgid(pid_t pid,pid_t pgid); |
参数: |
pid 进程id pgid 进程组id |
返回值: |
若成功则返回进程组ID,若出错则返回-1。 |
setpgid函数将pid进程的进程组ID设置为pgid,如果这两个参数相等,则由pid指定的进程变成进程组组长。如果pid是0,则使用调用者的进程Id。如果pgid是0,则有pid指定的进程ID将用作进程组id。
#include <unistd.h> #include <sys/types.h>
main(); { pid_t cpid; pid_t fpid;
if((cpid=fork())==0) { sleep(1); printf(“I’m child,my id is %d, gpid is %d\n”,getpid(),getpgrp()); } else { fpid=getpid(); setpgid(cpid,fpid); printf(“I’m father,my id is %d,gpid is %d\n”,fpid,getpgrp()); waitpid(cpid,NULL,0); } } |
二、会话
4.
名称:: |
setsid |
功能: |
建立一个会话 |
头文件: |
#include <unistd.h> |
函数原形: |
pid_t setsid(void); |
参数: |
无 |
返回值: |
若成功则返回进程组ID,若出错则返回-1。 |
会话是一个或多个进程组的集合。进程调用setsid函数建立一个新会话。
如果调用此函数的进程不是一个进程组的组长,则此函数九会创建一个新会话,该进餐变成会话的首进程,然后该进程成为一个新进程组的组长进程,该进程没有控制终端。因为会话首进程是具有唯一进程ID的单个进程,所以可以将会话首进程的进程ID视为会话Id。
5.
名称:: |
getsid |
功能: |
获得会话首进程的进程id |
头文件: |
#include <unistd.h> |
函数原形: |
pid_t getsid(pid_t pid); |
参数: |
pid |
返回值: |
若成功则返回进程组ID,若出错则返回-1。 |