Unix环境编程-进程

 

一、主要内容:

1.进程如何启动和退出的(main函数,初启函数,exit函数,atexit函数)。

1).Linux创建进程的唯一方法就是利用shellfork系统调用。

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( )用来分析命令行参数。参数argcargv是由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 1” );

if(atexit(exit2)!=0)

    printf(“can’t register exit 2” );

pritnf(“main is done\n”);

return(0);

}

 

static void exit1(void)

{

pritnf(“first exit handler\n”);

}

 

static void exit2void

{

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 *getenvconst 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 putenvconst 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

 

 

 

 

 

 

 

setenvname设置为value.如果在环境中name已经存在,那么

(1)    rewrite0,则首先删除其现存的定义;

(2)    rewrite0,则不删除其现存定义(name不设置为新的value,而且也不出错)。

下面是一个例子:

/*7_5.c*/

#include <stdlib.h>

 

main()

{
    char *p;

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 execlpconst 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指定的进程变成进程组组长。如果pid0,则使用调用者的进程Id。如果pgid0,则有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

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(unix)