系统调用

系统调用是操作系统提供给用户的一组“特殊接口”。系统调用并非直接和程序员或系统管理员直接打交道,而是通过软中断的方式向内核提交请求,从而获取内核函数的服务入口(系统调用表)。系统调用让系统从用户空间进入内核空间内运行,运行后将结果返回给应用程序(内核态->用户空间)。 <br>

系统调用和系统API等区别

<br> 系统API 主要是通过C库libc来实现,程序员多采用这种方式与内核交互,这些API通过系统调用来实现 <br> 系统命令 系统管理员采用系统命令与内核交互,是一个可执行文件,通过系统API及系统调用来实现 <br> 外壳程序 一系列系统命令和SHELL脚本共同组合成的程序。 <br>

函数库调用 与 系统调用

<br>

文件描述符

<br> -每个进程PCB结构中有文件描述符指针,指向files_struct的文件描述符表,记录每个进程打开的文件列表 -系统内核不允许应用程序访问进程的文件描述符表,只返回这些结构的索引即文件描述符ID(File Description)给应用程序 -Linux系统中,应用程序通过这些文件描述符来实现让内核对文件的访问 -每个进程能够访问的文件描述符是有限制的,通过#ulimit –n可以查看 <br>

特殊文件描述符

<br>标准输入STDIN_FILENO 标准输出STDOUT_FILENO 标准错误STDERR_FILENO <br>每个进程被加载后,默认打开0,1,2这三个文件描述符

<br>

open

<br> -函数原型 int open(const char *path, int flags,mode_t mode); -参数 path :文件的名称,可以包含(绝对和相对)路径 flags:文件打开模式 mode: 用来规定对该文件的所有者,文件的用户组及系统中其他用户的访问权限,则文件权限为:mode&(~umask) -返回值 打开成功,返回文件描述符; 打开失败,返回-1

<br>

打开方式

访问权限

<br> O_CREATE会产生特殊权限,需要设置访问权限: S_IRWXU等价于 S_IRUSR|S_IWUSR|S_IXUSR (文件所有者) S_IRWXG 等价于 S_IRGRP|S_IWGRP|S_IXGRP (文件用户组) S_IRWXO 等价于 S_IROTH|S_IWOTH|S_IXOTH (文件其他用户) <br>

close

关闭文件close(将进程中fd对应的文件描述表结构释放): 函数原型:int close(int fd); //如果出现错误,返回-1;调用成功返回0 <br>

read

-函数原型: int read(int fd, void *buf, size_t nbytes); -参数 fd :想要读的文件的文件描述符 buf: 指向内存块的指针,从文件中读取来的字节放到这个内存块中 nbytes: 从该文件复制到buf中的字节个数 -返回值 如果出现错误,返回-1; 返回从该文件复制到规定的缓冲区中的字节数! <br>

write

-函数原型:int write(int fd,void *buf,size_t nbytes); -函数参数: fd :要写入的文件的文件描述符 buf: 指向内存块的指针,从这个内存块中读取数据写入 到文件中 nbytes: 要写入文件的字节个数 -返回值 如果出现错误,返回-1 如果写入成功,则返回写入到文件中的字节个数 <br> 练习:采用文件系统调用的方式,完成串口通讯配置文件的读写 <br> 配置文件serial.cfg如下: <br> 要求:1、程序运行命令格式: serialchat [-options]; [options]: p-输出打印serial.cfg各个配置项信息,格式:参数-----参数值; s-进行菜单(有保存和退出)让用户设置; f-指定配配置项设置,输出各选项置文件名,并输出各配置项信息 其他选项提示该程序用法帮助。 2、不支持serialchat运行两次 3、如果无法找到配置文件,则提示运行失败,原因:配置文件无法找到 <br> 代码如下:

#include   < stdio.h>
#include   < string.h>
#include   < sys/types.h>
#include   < sys/stat.h>
#include   < unistd.h>
#include    < fcntl.h>

int icount = 0;

//去空格 '\0'
void rm_space(char *pStr)
{
	char *pos = pStr;
	pos = strchr(pStr,' ');
	while(pos != NULL)
	{
		strcpy(pos,pos+1);
		pos = strchr(pos,' ');
	}
}

//去注释 '#' '\n#'和中文,留下\n ... \r之间的数据
void rm_annotation(char *pStr)
{
	icount++;
	char *pos = pStr;
	char *end = NULL;
	if(icount == 1)	//第一行有可能顶格
	{
		pos = strchr(pStr,'#');
	}
	else
	{
		pos = strstr(pStr,"\n#");
	}

	while(pos != NULL)
	{
		end = strchr(pos+1,'\n');
		strcpy(pos,end);
		pos = strstr(pStr,"\n#");
	}	

}

//输出配置项信息
void printMsg(char *buf)
{
	char key[20] = " ";
	char value[20] = " ";
	char line[50] = " ";
	char *pos = buf;
	char *end = NULL;
	int flag = 0;

	printf("配置项信息如下:\n");
	
	pos = strchr(buf,'\n');
	end = strchr(pos,'=');
	
	while(end != NULL)
	{
		memset(key,0,sizeof(key));
		memset(value,0,sizeof(value));
		memcpy(key,pos+1,end - (pos + 1));
	
		pos = end;
		end = strchr(pos,'\r');
		if(end == NULL)	//if the final data
		{
			flag = 1;
			break;
		}
		memcpy(value,pos+1,end - (pos + 1));

		sprintf(line,"%s-----%s",key,value);
		printf("%s\n",line);

		pos = end + 1;
		end = strchr(pos,'=');	
	}

	if(flag)	
	{
		end = strchr(pos,'\0');
		memcpy(value,pos+1,end - (pos + 1));
		sprintf(line,"%s-----%s",key,value);
		printf("%s\n",line);
	}
}

//修改配置文件 (传入的buf是未经处理的)
void updateFile(int fd,char *buf)
{
	char *pos = buf;
	char *end = NULL;
	char key[20] = "";
	char newvalue[20] = "";
	int lse;
	int count;

	printf("请输入配置参数: ");
	scanf("%s",key);
	printf("请输入配置参数的值: ");
	scanf("%s",newvalue);
	strcat(key,"=");
	pos = strstr(buf,key);
	if(pos == NULL)
	{
		printf("输入参数不存在!");
		return;
	}
	pos = pos + strlen(key);
	while(*pos == ' ')	//处理'='后面有空格的情况
	{
		pos = pos + 1;
	}
	end = strchr(pos,'\r');
	if(end == NULL)	//if the final data
	{
		end = strchr(pos,'\0');
	}
	//修改文件
	lse = lseek(fd,pos-buf,SEEK_SET);
	write(fd,newvalue,end-pos); //注意新旧值的长度
	printf("修改成功!\n");
}


/*--------------------主函数---------------------*/
int main(int argc,char *argv[])
{
	int fd;
	int readByte = 0;
	char buf[512] = "";
	char option[3] = "";
	char filename[20] = "";
	int  index;
	int s_flag = 0;
	int f_flag = 0;
	struct flock lock = {0};

	//判断参数个数
	if(argc !=2 && argc != 3)
	{
		printf("输入格式有误!\n");
		return 1;
	}

	//打开配置文件
	strcpy(option,argv[1]);
	if(strcmp(option,"-f") == 0)	//打开指定文件
	{
		if(argc != 3)
		{
			printf("输入格式有误!\n");
			return 1;
		}
		f_flag = 1;
		strcpy(filename,argv[2]);
		fd = open(filename,O_RDWR,S_IRWXU|S_IRGRP);
	}
	else
	{
		fd = open("serial.cfg",O_RDWR,S_IRWXU|S_IRGRP);
	}
	
	if(fd == -1)
	{
		printf("运行失败,无法找到配置文件!\n");
		return 1;
	}

	//读取配置文件
	readByte = read(fd,buf,512);
	if(readByte <= 0)
	{
		printf("读文件失败!\n");
		return 1;
	}
	
	//判断是否已经加锁
	fcntl(fd,F_GETLK,&lock);
	if(lock.l_type != F_UNLCK)
	{
		printf("文件已经被上锁!执行错误\n");
		return 1;
	}
	//上锁
	memset(&lock,0,sizeof(struct flock));
	lock.l_whence	= SEEK_SET;
	lock.l_start		= 0;
	lock.l_len		= sizeof(buf);
	lock.l_type		=F_WRLCK;
	if((fcntl(fd,F_SETLK,&lock)) == -1)
	{
		printf("设置写锁失败!\n");
		return 1;
	}

	//操作
	if(f_flag || strcmp(option,"-p") == 0)	//打印配置信息
	{
		rm_space(buf);
		rm_annotation(buf);
		printMsg(buf);
	}
	else if(strcmp(option,"-s") == 0)	//进行菜单
	{
		while(!s_flag)
		{
			printf("1.修改配置项\n2.退出\n请输入选择: ");
			scanf("%d",&index);
			switch(index)
			{
				case 1:
					updateFile(fd,buf);
					break;
				case 2:
					s_flag = 1;
					break;
			}
		}

	}
	else
	{
		printf("serialchat用法:\n");
		printf("\t命令格式:serialchat [-options]\n ");
		printf("\t   [options]: p-输出打印serial.cfg各个配置项信息\n ");
		printf("\t   [options]: s-进行菜单\n ");
		printf("\t   [options]: f-指定配置文件\n ");
		printf("\t   [options]: 其他-查看帮助\n ");
	}
	
	//关闭文件
	close(fd);

	//释放锁
	lock.l_type		=F_UNLCK;
	fcntl(fd,F_SETLK,&lock);
	
	return 0;
}

运行结果: 1、 serialchat –p 2、 serialchat-s

3、 serialchat-f 4、 异常处理 5、 上锁 终端1: 终端2: