请通过目录查找相关内容
基础知识
main函数
c程序总是在main函数开始执行的,main的原型为
int main(int argc, char **argv)
其中 argc 是命令行参数的个数,argv是各参数按序组成的指针数字
argv[0] 通常是程序名,argv[1]是第一个参数,argv[argc -1]最后一个参数
argv[ argc] 将是一个空指针
进程的终止
进程的终止有八种方式
正常的终止有:main返回、调用exit、调用_exit ( _Exit )、最后一个线程返回、最后一个线程调用pthread_exit
异常终止有:调用abort、接到一个信号、最后一个进程对取消请求做出响应
以上还有一些目前尚未涉及到的,以后更新再说。
其中exit函数,会清理关闭标志io库对所有打开流调用fclose,输出缓冲所有的数据都被冲洗(就是被写到文件上)
在main中调用return时,return会去调用exit,但是在其他函数内的return不会去调用exit,就是说其他函数内的return不会导致进程退出
如果main定义时,不声明为返回值int,进程终止状态可能会是随机值
终止处理程序 和 atexit函数
在我们调用exit去终止一个进程时,exit会先调用登记好的终止处理程序,最后通过fclose关闭所有打开流
我们可以使用函数atexit来登记终止处理函数,一个进程最多登记32个函数
原型
int atexit(void (*func)(void));
/* 成功返回0,失败返回 非0 */
atexit的参数是一个函数指针,该函数是一个没有参数也没有返回值的函数
exit调用这些函数的顺序和登记的顺序相反,一个函数登记多次,也会被调用多次
使用实例
void aaa(void)
{
.....;
}
atexit(aaa);
程序的终止过程:
环境表
每个程序都会接收一个环境表,和命令行参数表一样,也是字符指针数组
其中每个指针指向一个以 ‘\0’ 结束的字符串,且最后一个指针值是NULL
全局变量environ包含了这个指针数组的首地址
每个字符串都是 name=value 的形式
我们来写一个程序,打印一下命令行参数列表和环境表
1 #include <stdio.h>
2
3 extern char **environ;
4
5 int main(int argc , char **argv)
6 {
7 int i = -1;
8 for(i = 0; i < argc; i++)
9 {
10 printf(" argv[%d] %s \n", i, argv[i]);
11 }
12
13 for(i = 0; environ[i] != NULL ; i++)
14 {
15 printf("environ %d : %s \n",i, environ[i]);
16 }
17
18 return 0;
19 }
20
21
运行结果为
环境变量函数 getenv putenv setenv等
如上,我们查看到的环境表的每一项都是 name=value的形式
实际上我们不必直接访问enveron,可以通过一些函数来取得所需环境变量的值
注意: 我们只能修改当前进程和子进程的环境,而不能改变父进程的环境
char *getenv(const char *name);
/* 返回name对应的 value的指针,未找到则返回NULL */
int putenv(char *str);
/* 成功返回0,失败返回非0 */
int setenv(const char *name, const char *value, int rewite);
/* 成功返回0,失败返回-1 */
int unsetenv(const char *name);
/* 成功返回0,失败返回-1 */
putenv :
参数str 是 name=value形式的字符串,该函数将其加入环境表中,如果name已经存在先删除原来的定义,再添加
setenv:
将name的值设置为value,如果环境中已经存在name,rewite为0时则不删除原定义、不设置新值、也不出错,rewite为非0时,先删除原定义再添加
unsetenv:
删除name的定义,不存在也不报错
进程标识
进程ID:
每个进程都有一个非负整数表示的进程ID,且这个ID是唯一的,但是也可被复用
进程ID 0 :
0号ID是调度进程,也叫交换进程,是内核的一部分,是系统进程
进程ID 1 :
1号ID是init进程,在自检完成后由内核调用,是一个有超级用户权限的用户进程,在系统运行过程中init进程永远不会退出。当其他进程的父进程死了后,会被init进程 ‘领养’。
查看进程ID
//返回调用进程的进程ID
pid_t getpid(void);
// 返回调用进程的父进程ID
pid_t getppid(void);
// 返回调用进程的实际用户id
uid_t getuid(void);
// 返回调用进程的有效用户ID
uid_t geteuid(void);
// 返回调用进程的实际组id
gid_t getgid(void);
// 返回调用进程的有效组id
gid_t getegid(void);
有效用户和实际用户
有效用户ID和有效用户组ID:
进程用来决定我们对资源的访问权限。一般情况下,有效用户ID等于实际用户ID,有效用户组ID等于实际用户组ID。当设置-用户-ID(SUID)位设置,则有效用户ID等于文件的所有者的uid,而不是实际用户ID;同样,如果设置了设置-用户组-ID(SGID)位,则有效用户组ID等于文件所有者的gid,而不是实际用户组ID。
实际用户:表示一开始执行程序的用户,比如用账号iceup登录shell,然后执行程序ls,那么实际用户就是iceup。
有效用户:有效用户是指在程序运行时,计算权限的用户
关于SUID和SGID 请点击查看文章:Linux系统学习—用户管理及权限管理(三)
进程资源限制的查询和修改 getrlimit setrlimit
每个进程都有一组资源限制,我们可以使用getrlimit setrlimit 进行查询和修改
原型
struct rilimit{
rlim_t rlim_cur; // 软件限制
rlim_t rlim_max; // 硬件限制
};
int getrlimit(int resource, struct rilimit *rlptr);
int setrlimit(int resource, const struct rilimit *rlptr);
/* 成功返回0,失败返回非0*/
在修改资源限制时,应当遵守下列规则
-
软件限制必须小于或对应硬件限制
-
普通进程只能降低硬件限制,但是硬件限制要保持大于等于软件限制,对于普通用户来说,这种减低是不可逆的
-
超级用户程序可以提高硬件限制
常量RLIM_INFINITY 代表无限量
参数resource是对应的资源,下面将列出
- RLIMIT_AS:进程可用存储区的最大总长度(字节),这会影响sbrk函数和mmap函数
- RLIMIT_CORE:core文件的最大字节数,若其值为0则阻止创建core文件
- RLIMIT_CPU:CPU时间的最大量值数(秒),当超过此软限制,向该进程发送SIGXCPU信号
- RLIMIT_DATA:数据段的最大字节长度,就是初始化数据、非初始以及堆的总和
- RLIMIT_FSIZE:可以创建的文件的最大字节长度。当超过此软限制,则向该进程发送SIGXFSZ信号
- RLIMIT_LOCKS:一个进程可持有的文件锁的最大数
- RLIMIT_MEMLOCK:一个进程使用mlock能够锁定在存储器中的最大字节数
- RLIMIT_NOFILE:每个进程能打开的最大文件数。更改此限制将影响到sysconf函数在参数_SC_OPEN_MAX返回的值
- RLIMIT_NPROC:每个实际用户ID可拥有的最大子进程数。更改此限制将影响到sysconf函数在参数_SC_CHILD_MAX返回的值
- RLIMIT_RSS:最大驻内存集的字节长度。如果物理存储器供不应求,则内核将从进程处取回超过RSS的部分
- RLIMIT_SBSIZE:用户在任一给定时刻可以占用的套接字缓冲区的最大长度
- RLIMIT_STACK:栈的最大字节长度
- RLIMIT_VMEM:这个是RLIMIT_AS的同义词