进程环境-

文件系统的第三部分

进程环境

讲单进程,因为每一个进程我们要搞清楚它里面到底是怎么样组成的,有什么状态,和系统的变量和资源相关的一些内容,为后面的多进程并发做铺垫

1. main函数

int main(int argc,char * argv[])

历史早期的main函数有第三个变量:环境变量,但现在已经成为一个分支了

int main(int argc,char * argv[],char * envp[])

2. 进程的终止(正常,异常)

正常终止:

  • 从main函数返回:return 0
  • 调用exit()
  • 调用_exit()或_Exit()
  • 最后一个线程从其启动例程返回
  • 最后一个线程调用pthread_exit(相当于进程阶段的exit)

异常终止:

  • 调用abort函数(杀掉当前进程,得到一个coredump文件)
  • 接到一个信号并终止
  • 最后一个线程对其取消请求作出响应(线程被迫接收取消请求)

这几点要牢记于心,要背过

main函数本身并不特殊,只是我们约定程序的入口是从main函数开始的
面试:
问:return 0是给谁看的?
答:给当前这个进程的父进程看的

void exit(int status)

带回int类型的数,范围就一定在-2G~2G之间吗?
看exit函数的描述:
The exit() function causes normal process termination and the least significant byte of status (i.e., status & 0xFF) is returned to the parent (see wait(2)).

status & 0xFF,相当于保留这个整数的低八位,所以这个返回值范围在[-128,127],共256种状态

最重要的两句话:
All functions registered with atexit(3) and on_exit(3) are called, in the reverse order of their registration. (It is possible for one of these functions to use atexit(3) or on_exit(3) to register an additional function to be executed during exit processing; the new registration is added to the front of the list offunctions that remain to be called.)

钩子函数:
register a function to be called at normal process termination(注册一个函数,当进程正常终止的时候这个函数将被调用,特别像C++中的析构函数)

int atexit(void (*function)(void));

使用示例

#include <stdio.h>
#include <stdlib.h>

static void f1(void)
{
        puts("f1() is working!");
}

static void f2(void)
{
        puts("f2() is working!");
}

static void f3(void)
{
        puts("f3() is working!");
}

int main(void)
{
        puts("Begin!");

        atexit(f1); //不是函数调用,只是把函数挂在钩子上,以逆序的方式调用(我猜测内部是使用了栈来保存函数入口)
        atexit(f2);
        atexit(f3);

        puts("End!");

        //exit(EXIT_SUCCESS);
        exit(EXIT_FAILURE);    //这也是正常终止
}

执行顺序

➜  process_environment ./atexit 
Begin!
End!
f3() is working!
f2() is working!
f1() is working!

和atexit函数差不多,大同小异

int on_exit(void (*function)(int, void *), void *arg);

_exit 和 _Exit 都是系统调用:

noreturn void _exit(int status);

noreturn void _Exit(int status);

_exit,_Exit和 atexit 的区别:
一个C程序如何开始和终止

exit()函数首先调用完终止处理程序(exit handler),然后调用标准IO清理程序(standard I/O cleanup),最后再调用 _exit 或 _Exit;

单纯调用_exit 和 _Exit 是不执行钩子函数和IO清理的

伪码解释:

int func()
{
	return 0/1/2;        //这个函数返回值的可能只有0,1,2三种
}
int main()
{
	int f;
	f = func();
	....         //极有可能是此处的代码出了问题,多半情况是写越界,把func函数的空间写了
	switch(f)
	{
		case 0:
            ......
		case 1:
            ......
		case 2:
            ......
		default:
			_exit(1);  //func函数返回值只有可能是0、1、2,如果出现其他值,很有可能程序出了大问题,如果这里使用exit()函数,其实是在掩盖问题(会调用钩子函数,IO清理),所以需要使用_exit函数使问题暴露出来,或者使用abort函数发送信号把当前进程杀死,对core文件进行分析.
	}
}

3. 命令行参数的分析(不仅仅是argc,argv)

和人打交道是最麻烦的,比如下面ls命令可能遇到的选项和参数情况:
ls -l -i -n -a /etc/ /tmp
ls -l /etc -a /tmp -i /home
ls /etc /tmp -lai
./myplayer -H xxx -W xxx a.avi(项目做的播放器也要传参)

几乎shell下面所有的命令行分析都是由这两个函数完成的:

int getopt(int argc, char *const argv[],const char *optstring);

关联的全局变量:
extern char *optarg;
extern int optind;
The variable optind is the index of the next element to be processed in argv.(变量optind是argv中要处理的==下一个==(不是当前)元素的索引)

@optstring:不包含选项中的’-’,

If the first character (following any optional ‘+’ or ‘-’ described above) of optstring is a colon (’:’), then getopt() likewise does not print an error message. In addition, it returns ‘:’ instead of ‘?’ to indicate a missing option argument. This allows the caller to distinguish the two different types of errors.

If the first character of optstring is ‘-’, then each nonoption argv-element is handled as if it were the argument of an option with character code 1.
如果optstring的第一个字符是’-’,那么遇到任何非选项的参数,getopt()都将返回值1

man手册上getopt的例子

/*The following trivial example program uses getopt() to handle two program options: -n, with no associated value; and -t val, which expects an associated value.*/
//使用示例:
//./man_getopt -n  hello 
//./man_getopt -t 22 hello

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
	int flags, opt;
	int nsecs, tfnd;

	nsecs = 0;
	tfnd = 0;
	flags = 0;
	while ((opt = getopt(argc, argv, "nt:")) != -1)
	{
		switch (opt)
		{
		case 'n':
			flags = 1;
			break;
		case 't':
			nsecs = atoi(optarg);
			tfnd = 1;
			break;
		default: /* '?' */
			fprintf(stderr, "Usage: %s [-t nsecs] [-n] name\n",
				argv[0]);
			exit(EXIT_FAILURE);
		}
	}

	printf("flags=%d; tfnd=%d; nsecs=%d; optind=%d\n",
	       flags, tfnd, nsecs, optind);

	if (optind >= argc)
	{
		fprintf(stderr, "Expected argument after options\n");
		exit(EXIT_FAILURE);
	}

	printf("name argument = %s\n", argv[optind]);

	/* Other code omitted */

	exit(EXIT_SUCCESS);
}

int getopt_long(int argc, char *const argv[],const char *optstring,const struct option *longopts, int *longindex);

longopts是一个结构体指针,指向结构体数组中第一个元素

struct option
{
        const char *name;
        int has_arg;   //no_argument(0),require_argument(1),optional_argument(2)
        int *flag;
        int val;
};
@flag:指定长选项如何返回结果,如果flag = NULL,getopt_long函数返回val值,通常val值设置为与长选项功能相同的短选项字符。否则getopt_long返回0

man手册上getopt_long的例子

//The following example program illustrates the use of getopt_long() with most of its features.

#include <stdio.h>  /* for printf */
#include <stdlib.h> /* for exit */
#include <getopt.h>

int main(int argc, char *argv[])
{
        int c;
        int digit_optind = 0;

        while (1)
        {
                int this_option_optind = optind ? optind : 1;
                int option_index = 0;
                static struct option long_options[] = {
                        {"add", required_argument, 0, 0},
                        {"append", no_argument, 0, 0},
                        {"delete", required_argument, 0, 0},
                        {"verbose", no_argument, 0, 0},
                        {"create", required_argument, 0, 'c'},
                        {"file", required_argument, 0, 0},
                        {0, 0, 0, 0}};

                c = getopt_long(argc, argv, "abc:d:012", long_options, &option_index);
                if (c == -1)
                        break;

                switch (c)
                {
                        case 0:
                                printf("option %s", long_options[option_index].name);
                                if (optarg)
                                        printf(" with arg %s", optarg);
                                printf("\n");
                                break;

                        case '0':
                        case '1':
                        case '2':
                                if (digit_optind != 0 && digit_optind != this_option_optind)
                                        printf("digits occur in two different argv-elements.\n");
                                digit_optind = this_option_optind;
                                printf("option %c\n", c);
                                break;

                        case 'a':
                                printf("option a\n");
                                break;

                        case 'b':
                                printf("option b\n");
                                break;

                        case 'c':
                                printf("option c with value '%s'\n", optarg);
                                break;

                        case 'd':
                                printf("option d with value '%s'\n", optarg);
                                break;

                        case '?':
                                break;

                        default:
                                printf("?? getopt returned character code 0%o ??\n", c);
                }
        }

        if (optind < argc)
        {
                printf("non-option ARGV-elements: ");
                while (optind < argc)
                        printf("%s ", argv[optind++]);
                printf("\n");
        }

        exit(EXIT_SUCCESS);
}

示例
./mydate -y 4 -m -d 打印年(4位形式) 月 日
./mydate -y 打印年份
./mydate -m 打印月份
./mydate -y 4 out 输出内容到out文件而不是终端
./mydate -y 4 out -H 4 -M out1 -S out2 所有结果输出到out,其他文件忽略(这里采用的是先入为主的方式;类似的还有后入为主的方式:所有结果输出到out2;或者三个文件分别输出的方式)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>

/*
 * 选项:
 * -y: year
 * -m: mouth
 * -d: day
 * -H: hout
 * -M: minute
 * -S: secend
 **/

#define TIMESTRSIZE 1024
#define FMTSTRSIZE 1024  //format string size

int main(int argc, char *argv[])
{
        time_t stamp;
        struct tm *tm;
        char timestr[TIMESTRSIZE];
        int c;
        char fmtstr[FMTSTRSIZE];
        fmtstr[0] = '\0'; //初始化为空串
        FILE *fp = stdout;

        stamp = time(NULL);
        tm = localtime(&stamp);
        while (1)
        {
                c = getopt(argc, argv, "-hH:MSy:md"); // H可以选择12/24小时制,y可以选择4位的年份(2021)和2位的年份(21),要让选项具有选择功能,需要在选项后加上":",这样如果运行时不提供选择将失败:./mydate -mdy 4 (y后面必须提供2/4)
                //全局变量optarg(option argument),当getopt()在分析optstring这个串时,如果看到这个串某一个选项加了":",那么当getopt拿到这个选项之后,这个字符串指针(optarg)就默认指向了选项后的参数(例:"-MSmH 12",指向"12")
                if (c < 0)
                        break;
                switch (c)
                {
                case 1:    //识别非选项类型的参数,getopt()返回1,这里是设置为输出到什么文件中,ls命令可设置为打开的目录
                        if (fp == stdout) //这里设置成最多打开一个文件写入
                        {
                                fp = fopen(argv[optind - 1], "w"); //下一个元素的索引是 optind ,那么当前元素的索引就是 optind-1
                                if (fp == NULL)
                                {
                                        perror("fopen()");
                                        fp = stdout; //如果文件打开失败,那么就往终端上写
                                }
                        }
                        break;
                case 'h':
                        printf("Usage:%s [OPTION]...[FILE]...\n", argv[0]);
                        printf("-H arg:show hour\n");
                        printf("-M:show minute\n");
                        printf("-S:show secend\n");
                        printf("-y arg:show year\n");
                        printf("-m :show mouth\n");
                        printf("-d:show day\n");
                        break;
                case 'H': //时,需指定12时制还是24时制
                        if (strcmp(optarg, "12") == 0)
                                strncat(fmtstr, "%I(%P) ", FMTSTRSIZE - 1); //%P用于打印am/pm,这在strftime的man手册中有
                        else if (strcmp(optarg, "24") == 0)
                                strncat(fmtstr, "%H ", FMTSTRSIZE - 1);
                        else
                                fprintf(stderr, "Invalid argument of -H\n");
                        break;
                case 'M': //分
                        strncat(fmtstr, "%M ", FMTSTRSIZE - 1);
                        break;
                case 'S': //秒
                        strncat(fmtstr, "%S ", FMTSTRSIZE - 1);
                        break;
                case 'y': //年,需指定2位还是4位
                        if (strcmp(optarg, "2") == 0)
                                strncat(fmtstr, "%y ", FMTSTRSIZE - 1);
                        else if (strcmp(optarg, "4") == 0)
                                strncat(fmtstr, "%Y ", FMTSTRSIZE - 1);
                        else
                                fprintf(stderr, "Invalid argument of -y\n");
                        break;
                case 'm': //月
                        strncat(fmtstr, "%m ", FMTSTRSIZE - 1);
                        break;
                case 'd': //日
                        strncat(fmtstr, "%d ", FMTSTRSIZE - 1);
                        break;
                default:
                        break;
                }
        }

        strcat(fmtstr, "\n");
        strftime(timestr, TIMESTRSIZE, fmtstr, tm);
        fputs(timestr, fp);

        if (fp != stdout)
                fclose(fp);

        exit(EXIT_SUCCESS);
}

练习,使用getopt_long()处理长格式(非老师编写)

#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <time.h>
#include <string.h>

#define FMTSTRSIZE 1024
#define TIMESTRSIZE 1024

int main(int argc, char *argv[])
{
        int c;
        char fmtstr[FMTSTRSIZE];
        fmtstr[0] = '\0';
        char timestr[TIMESTRSIZE];
        struct tm *tm;
        time_t stamp;
        FILE *fp = stdout;

        stamp = time(NULL);
        tm = localtime(&stamp);

        static struct option long_options[] = {
            {"year", required_argument, NULL, 'y'},
            {"mouth", no_argument, NULL, 'm'},
            {"day", no_argument, NULL, 'd'},
            {"hour", required_argument, NULL, 'H'},
            {"minute", no_argument, NULL, 'M'},
            {"secend", no_argument, NULL, 'S'},
            {"help", no_argument, NULL, 'h'},
            {0, 0, 0, 0}};

        while (1)
        {
                int option_index = 0;
                c = getopt_long(argc, argv, "-hH:MSy:md", long_options, &option_index);
                if (c < 0)
                        break;
                switch (c)
                {
                case 1:
                        if (fp == stdout)
                        {
                                fp = fopen(argv[optind - 1], "w"); //下一个元素的索引是 optind ,那么当前元素的索引就是 optind-1
                                if (fp == NULL)
                                {
                                        perror("fopen()");
                                        fp = stdout; //如果文件打开失败,那么就往终端上写
                                }
                        }
                        break;
                case 'h':
                        printf("Usage:%s [OPTION]...[FILE]...\n", argv[0]);
                        printf("OPTION:\n");
                        printf("\t-H,--hour arg(12/24):show hour\n");
                        printf("\t-M,--minute:show minute\n");
                        printf("\t-S,--secend:show secend\n");
                        printf("\t-y,--year arg(2/4):show year\n");
                        printf("\t-m,--mouth :show mouth\n");
                        printf("\t-d,--day:show day\n");
                        break;
                case 'y': //年,需指定是两位年份还是4位年份
                        if (strcmp(optarg, "2") == 0)
                                strncat(fmtstr, "%y ", FMTSTRSIZE - 1);
                        else if (strcmp(optarg, "4") == 0)
                                strncat(fmtstr, "%Y ", FMTSTRSIZE - 1);
                        else
                                fprintf(stderr, "Invalid argument of -y\n");
                        break;
                case 'm': //月
                        strncat(fmtstr, "%m ", FMTSTRSIZE - 1);
                        break;
                case 'd': //日
                        strncat(fmtstr, "%d ", FMTSTRSIZE - 1);
                        break;
                case 'H': //时,需指定是12小时制还是24小时制
                        if (strcmp(optarg, "12") == 0)
                                strncat(fmtstr, "%I(%P) ", FMTSTRSIZE - 1); //%P用于打印am/pm,这在strftime的man手册中有
                        else if (strcmp(optarg, "24") == 0)
                                strncat(fmtstr, "%H ", FMTSTRSIZE - 1);
                        else
                                fprintf(stderr, "Invalid argument of -H\n");
                        break;
                case 'M': //分
                        strncat(fmtstr, "%M ", FMTSTRSIZE - 1);
                        break;
                case 'S': //秒
                        strncat(fmtstr, "%S ", FMTSTRSIZE - 1);
                        break;
                }
        }

        strcat(fmtstr, "\n");
        strftime(timestr, TIMESTRSIZE, fmtstr, tm);
        fputs(timestr, fp);
        if (fp != stdout)
                fclose(fp);

        exit(EXIT_SUCCESS);
}

4. 环境变量

简单来说就是:KEY = VALUE
export命令查看环境变量
如果把操作系统比作一个程序的话,那么环境变量就特别像这个程序的全局变量

shell的外部命令:存在在磁盘上的,你能找到它,能够正常调用
内部命令:作业调度,路径,管道,信息处理,没有在磁盘上存在一个二进制文件

打印系统的环境变量(输出结果同exprot命令)

#include <stdio.h>
#include <stdlib.h>
//#include <unistd.h> //如果包含了该头文件则不需要下面的extern声明

extern char ** environ;  //类似于char *argv[]这样的字符数组,声明在unistd.h中,可通过man 7 environ了解详细信息

int main(void)
{
        int i = 0;
        while (environ[i])
                puts(environ[i++]);

        exit(EXIT_SUCCESS);
}

了解:

  1. 获取环境变量的值

char *getenv(const char *name);

  1. 修改或增加环境变量:
    如果name是不存在的,setenv相当于add,如果name存在,setenv相当于change。修改时,如果原环境变量很短,而新环境变量很长,会不会造成写越界?不会,setenv()的做法是将原环境变量的空间释放掉然后在堆上申请一个新的空间用于存放新环境变量

int setenv(const char *name, const char *value, int overwrite);

  1. change or add an environment variable:
    不好用,参数没有const修饰,知道不好用就完了

int putenv(char *string);

5. C程序的存储空间布局

32位系统C程序存储空间布局

#include <stdio.h>
#include <stdlib.h>

int main(void)
{ 
        puts(getenv("PATH"));
        getchar();

        exit(EXIT_SUCCESS);
}

pmap命令

ps axf           #获取进程ID
pmap [进程ID]    #打印进程内存空间布局
pmap $$         #打印当前shell内存空间布局

使用示例
编译方式:

cc -m32 -no-pie getenv.c -o getenv

查看内存空间布局

在这里插入图片描述

验证setenv()修改字符串后是malloc在堆区

6. 库

静态库
共享库(动态库)

在这里插入图片描述

动态链接可以按以下两种方式进行:

  • 在第一次加载并运行时进行 (load-time linking).
    • Linux通常由动态链接器(ld-linux.so)自动处理
    • 标准C库 (libc.so) 通常按这种方式动态被链接
  • 在已经开始运行后进行(run-time linking).
    • 在Linux中,通过调用 dlopen()等接口来实现
  • 分发软件包、构建高性能Web服务器等

内核当中模块的本质实际上就是插件

开机启动时:
DHCP服务 [OK]
MYSQL服务 [Fail]

如果把计算机系统的运行看做是一个程序的话,那么按照咱们当前写程序的策略(一旦失败,程序马上exit()结束)是不行的。实际当中的做法是尝试启动服务,如果启动失败则跳过,等把机器启动起来再去修改配置文件然后手动启动服务

void *dlopen(const char *filename, int flags); //给出动态库和打开方式

int dlclose(void *handle);

void *dlsym(void *restrict handle, const char *restrict symbol); //在一个成功打开的共享库当中查找某一个符号,如果能找到,返回入口地址

char *dlerror(void); //相当于把strerror封装了一层

Link with -ldl

man手册中的一个例子:手工装载math库并打印cos(2.0)

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <gnu/lib-names.h> // Defines LIBM_SO (which will be a string such as "libm.so.6")

int main(void)
{
	void *handle;
	double (*cosine)(double); //指向函数的指针
	char *error;

	handle = dlopen(LIBM_SO, RTLD_LAZY); //指定math库,以lazy方式打开(推迟打开,意思就是到用的时候才打开)
	if (!handle)
	{
		fprintf(stderr, "%s\n", dlerror());
		exit(EXIT_FAILURE);
	}

	dlerror(); /* Clear any existing error */

	cosine = (double (*)(double))dlsym(handle, "cos");

	//根据ISO C标准,在函数指针和 'void *'之间的转换,像上面做的一样,会产生未定义的结果。POSIX.1 - 2001和POSIX.1 - 2008接受了这种状态并提出以下解决方案 :

	//*(void **)(&cos) = dlsym(handle, "cos"); //将&cosine强转为"void **"类型(这样就不是函数指针),然后取值变为"void *"类型

	//这种(不灵活的)转换符合ISO C标准,并将避免任何编译器警告。 POSIX.1 - 2008的2013年技术更正1要求符合规范的实现支持将“void *”强制转换为函数指针,从而改善了问题。 然而,一些编译器(例如,使用 '-pedantic'选项的GCC) 可能会抱怨这个程序中使用的强制转换。

	error = dlerror();
	if (error != NULL)
	{
		fprintf(stderr, "%s\n", error);
		exit(EXIT_FAILURE);
	}

	printf("%f\n", (*cosine)(2.0));
	dlclose(handle);
	exit(EXIT_SUCCESS);
}

按照我们以前的说法:void类型的指针赋值给任何类型的指针都是天经地义的,任何类型的指针赋值给void类型的指针也是天经地义的。问题只有一种情况是未定义的:在C99标准中,唯独将"void *"类型转换为函数指针类型是未定义的

使用"-pedantic"选项的GCC编译的结果

➜  process_environment gcc -pedantic dlopen.c -ldl -o dlopen
dlopen.c: In function ‘main’:
dlopen.c:19:12: warning: ISO C forbids conversion of object pointer to function pointer type [-Wpedantic]
   19 |   cosine = (double (*)(double))dlsym(handle, "cos");

有关*(void **)的讨论
个人感觉它报错的原因:把一个函数指针转换为void *就像把一个double数据类型的变量转换为一个int数据类型一样,double类型占8个字节,int类型占4个字节,把它强转后是什么意思呢?(double/int类型数据的长度应该记录在指针中)如果作为等号右边相当于把数据截断还差不多.(把指针当作一个变量来看待,那么二级指针就是这个变量的一级指针;我的想法是变量看做是一维数据,指针看做是二维数据)

7. 存储器管理,栈管理,静态区管理

8. 函数跳转

main
a
b
c
d

如果a,b,c,d是同一个函数,那么就是递归

goto语句是可以放心用的,但无法实现跨函数跳转(例如在这个例子中,如果从c函数goto到a函数中,则a,b函数的返回地址和参数都会保留在栈中,它们都不能正常返回,导致使用goto不安全)

跨函数跳转是普遍的,C++中的异常抛出机制需要实现跨函数跳转

从一棵树中查找一个元素,递归进去了900层找到了这个元素,这时就要一层一层返回到顶部,但其实这样是没必要的,而我们又不能使用goto。

int setjmp(jmp_buf env);

调用一次,返回两次,类似于fork():

ret = setjmp();
if(ret == 0)  //设置跳转点,setjmp()返回值为0
{
        ......
}
else     //从别处跳回到跳转点,setjmp()返回值为longjmp()参数val
{

}

noreturn void longjmp(jmp_buf env, int val);

#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>

static jmp_buf save;

static void d(void)
{
        printf("%s():Begin.\n", __FUNCTION__);
        printf("%s():Jump now!\n", __FUNCTION__);
        longjmp(save, 6);
        //longjmp(save, 0);  //如果设置返回值为0?

        printf("%s():End.\n", __FUNCTIONshuo
}

static void c(void)
{
        printf("%s():Begin.\n", __FUNCTION__);
        printf("%s():Call d().\n", __FUNCTION__);
        d();
        printf("%s():d() returned.\n", __FUNCTION__);
        printf("%s():End.\n", __FUNCTION__);
}

static void b(void)
{
        printf("%s():Begin.\n", __FUNCTION__);
        printf("%s():Call c().\n", __FUNCTION__);
        c();
        printf("%s():c() returned.\n", __FUNCTION__);
        printf("%s():End.\n", __FUNCTION__);
}

static void a(void)
{
        int ret;
        printf("%s():Begin.\n", __FUNCTION__); //__FUNCTION__是GCC提供的宏,可用于调试程序,类似的还有__LINE__打印行号
        ret = setjmp(save);
        if (ret == 0) //设置跳转点
        {
                printf("%s():Call b().\n", __FUNCTION__);
                b();
                printf("%s():b() returned.\n", __FUNCTION__);
                printf("%s():End.\n", __FUNCTION__);
        }
        else //从别处跳回来的
                printf("%s():Jumped back here with code %d\n", __FUNCTION__, ret);
}

int main(void)
{
        a();
        exit(EXIT_SUCCESS);
}

如果捣乱将longjmp中第二个参数val设置为0,longjmp函数会将val设置为1:
If the programmer mistakenly passes the value 0 in val, the “fake” return will instead return 1.

9. 资源的获取和控制

ulimit -a
ulimit -c unlimited //设置可产生core文件

在这里插入图片描述

int getrlimit(int resource, struct rlimit *rlim);

int setrlimit(int resource, const struct rlimit *rlim);

struct rlimit
{
        rlim_t rlim_cur; /* Soft limit;软限制,可以提高和降低资源值,最高不能超过硬限制,普通用户和root用户都要遵循 */
        rlim_t rlim_max; /* Hard limit (ceiling for rlim_cur);硬限制,硬性规定,只能降低该资源值(root用户可以提高) */
};

resource:资源
resource所有可能的取值(代表资源名)在man手册中列出

有这两个函数,就能写出一个ulimit的实现了

getrlimit和setrlimit的组合和扩展:

int prlimit(pid_t pid, int resource, const struct rlimit *new_limit, struct rlimit *old_limit);

man手册上关于prlimit()的例子

//The program below demonstrates the use of prlimit()
#define _GNU_SOURCE
#define _FILE_OFFSET_BITS 64
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/resource.h>

#define errExit(msg)                \
	do                          \
	{                           \
		perror(msg);        \
		exit(EXIT_FAILURE); \
	} while (0)

int main(int argc, char *argv[])
{
	struct rlimit old, new;
	struct rlimit *newp;
	pid_t pid;

	if (!(argc == 2 || argc == 4))
	{
		fprintf(stderr, "Usage: %s <pid> [<new-soft-limit> "
				"<new-hard-limit>]\n",
			argv[0]);
		exit(EXIT_FAILURE);
	}

	pid = atoi(argv[1]); /* PID of target process */

	newp = NULL;
	if (argc == 4)
	{
		new.rlim_cur = atoi(argv[2]);
		new.rlim_max = atoi(argv[3]);
		newp = &new;
	}

	/* Set CPU time limit of target process; retrieve and display
	   previous limit */

	if (prlimit(pid, RLIMIT_CPU, newp, &old) == -1)
		errExit("prlimit-1");
	printf("Previous limits: soft=%lld; hard=%lld\n",
	       (long long)old.rlim_cur, (long long)old.rlim_max);

	/* Retrieve and display new CPU time limit */

	if (prlimit(pid, RLIMIT_CPU, NULL, &old) == -1)
		errExit("prlimit-2");
	printf("New limits: soft=%lld; hard=%lld\n",
	       (long long)old.rlim_cur, (long long)old.rlim_max);

	exit(EXIT_SUCCESS);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值