系统编程内容

系统编程:

操作系统四大基本服务

cpu调度,存储管理(段内存),IO控制,文件系统
用户层程序如何使用系统服务(通过软中断调用系统服务):库函数,系统调用

  1. 文件IO(使用系统调用实现文件IO);IO控制(一切兼文件:1.数据文件 2.设备文件3.进程文件/proc)
  2. 进程(资源分配的基本单位),线程(cpu调度的基本单位); 进程间通信方式
  3. 网络编程(IO控制) udp,tcp
  4. 数据库编程:sqlite3

2.除函数调用外(连环招:步骤和函数),理论重要

linux中文件模型

文件静态模型 :struct inode;
文件动态模型:struct file;

C库中的文件操作与系统编程中的文件操作

//标准C库中的文件操作函数基于 FILE * ,带缓冲区功能
typedef struct _IO_FILE FILE;
struct _IO_FILE
{
int _fileno; //文件描述符
};
//系统编程中的文件操作基于文件描述符: 非负整数,不带缓冲功能,

extern struct _IO_FILE stdin; / Standard input stream. */
extern struct _IO_FILE stdout; / Standard output stream. */
extern struct _IO_FILE stderr; / Standard error output stream. */

#define STDIN_FILENO 0 /* Standard input. /
#define STDOUT_FILENO 1 /
Standard output. /
#define STDERR_FILENO 2 /
Standard error output. */

在linux系统中已经定义的标准文件描述符:
C库(FILE *) 系统编程(描述符unsigned int) 描述符的宏
键盘:(标准输入) stdin 0 STDIN_FILENO
屏幕(标准输出): stdout 1 STDOUT_FILENO
屏幕(标准错误输出): stderr 2 STDERR_FILENO

将FILE *转成fileno

int fileno(FILE *stream);
返回值为转换后的文件描述符

#include <stdio.h>
int main()
 {
    int fd = fileno(stdout);
    printf("fileno stdout fd = %d\n",fd);
    return 0;
 }

将文件描述符转成FILE *

FILE *fdopen(int fd, const char *mode);
@fd文件描述符
@mode:打开方式:r,w,a
返回值成功返回FILE *,失败返回NULL

#include <stdio.h>
#include <unistd.h>
int main()
{
    FILE *fp = fdopen(STDOUT_FILENO,"r");
    if(NULL == fp)
    {
        perror("fdopen error");
        return -1;
    }
    char buf[128] = "";
    fgets(buf,sizeof(buf)-1,fp);
    printf("buf = %s\n",buf);

}

文件的打开与关闭 open()、close()

头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
extern int open (const char *__file, int __oflag, …);

1.打开或创建并打开文件 
int open(const char *pathname, int flags); 读文件
int open(const char *pathname, int flags, mode_t mode); 写文件
@pathname:目标文件路径
@flags:打开模式
O_RDONLY,O_WRONLY,O_RDWR 互斥
O_APPEND,O_TRUNC //O_APPEND:当读写文件时会从文件尾开始移动,也就是所写入的数据会以附加的方式加入到文件后面。O_TRUNC:若文件存在并且以可写的方式打开时,使文件长度清0,清空文件之前的内容。
O_CREAT:若欲打开的文件不存在则自动建立该文件
O_NONBLOCK:非阻塞 无论有无数据的读取或等待,都会立刻返回进程之中
O_EXEC: 如果O_CREAT也被设置,此指令会去检查文件是否存在,文件若不存在则建立该文件,否则将导致打开文件错误,若O_EXEC与O_CREAT同时设置,并且欲打开的文件为符号连接,则会打开文件失败。
O_ASYNC异步,O_SYNC同步
@mode:八进制权限,实际权限由umask = 0022; 0666 - 0022 = 0644; 0777 - 0022 = 0755
返回值:成功返回文件描述符

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

int main()
{
    //1.打开文件
    const char *path = "./hello";
    int fd = open(path,O_RDWR | O_CREAT | O_TRUNC,0644);
    if(fd < 0)
    {
        perror("open file error");
        return -1;
    }
    //2.关闭文件
    close(fd);

}

向文件写入数据

ssize_t write(int fd, const void *buf, size_t count);
@buf:要写入的数据
@count:写入的字节数
返回写入的字节数

hexdump 以二进制形式查看文件

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

int main()
{
    //1.打开文件
    const char *path = "./hello";
    int fd = open(path,O_RDWR | O_CREAT | O_TRUNC,0644);
    printf("open fd = %d\n",fd);
    if(fd < 0)
	{
		perror("open file error");
		return -1;
	}
    const char *str = "hello world\n";
 // int len = write(fd,str,szieof(str));  
    int len = write(fd,str,strlen(str)); //写入字符串必须使用strlen()求写入的字数
    printf("write len = %d\n",len);

    //2.关闭文件
    close(fd);

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

#if 0
1.向文件写入数据:
ssize_t write(int fd, const void *buf, size_t count);
@buf:要写入的数据
@count:写入的字节数
返回写入的字节数
#endif

typedef struct student
{
	char name[16];
	int age;
}stu_t;

//写入二进制数据
int main()
{
	//1.打开文件
	const char *path = "./student.dat";
	int fd = open(path,O_RDWR | O_CREAT | O_TRUNC,0644);
	printf("open fd = %d\n",fd);	//3
	if(fd < 0)
	{
		perror("open file error");
		return -1;
	}

	stu_t stu = {"zhangsan",22};
	int len = write(fd,&stu,sizeof(stu));
	printf("write len = %d\n",len);

	//3.关闭文件
	close(fd);
}

从文件中读取内容

1.从文件读取内容:调用read()会使用进程进入阻塞状态,1)当读满指定字节数 2)读到结束符
ssize_t read(int fd, void *buf, size_t count);
@buf:读取的数据存入位置,要有写权限
@count:读取的字节数
返回读取到的字节数,如果读到文件结尾返回0

很重要的一点const能使不需要修改的数据变成只读的模式

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

#if 0
1.从文件读取内容:调用read()会使用进程进入阻塞状态,1)当读满指定字节数 2)读到结束符
ssize_t read(int fd, void *buf, size_t count);
@buf:读取的数据存入位置,要有写权限
@count:读取的字节数
返回读取到的字节数,如果读到文件结尾返回0
#endif

int main()
{
	//1.打开文件
	const char *path = "./hello";
	int fd = open(path,O_RDONLY);
	printf("open fd = %d\n",fd);	//3
	if(fd < 0)
	{
		perror("open file error");
		return -1;
	}

#if 0
	const char *str = "hello world";		//hello world存于.rodata, 
	int len = read(fd,str,strlen(str));		//error,段错误 不会开辟空间
#endif

	char buf[128] = "";
	int len = read(fd,buf,sizeof(buf) - 1);
	printf("read len = %d,str = %s\n",len,buf);

	//3.关闭文件
	close(fd);
}

从文件读取内容(二进制读取)

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
typedef struct student
{
	char name[16];
	int age;
}stu_t;

int main()
{
    //1.打开文件
    const char *path = "./student.dat";
    int fd = open(path,O_RDONLY);
    printf("open fd = %d\n",fd);
    if(fd < 0)
	{
		perror("open file error");
		return -1;
	}
	stu_t stu = {0};
    int size = read(fd,&stu,sizeof(stu));
    printf("read size = %d,name = %s,age = %d\n",size,stu.name,stu.age);


    //2.关闭文件
    close(fd);

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

//STDIN_FILENO,STDOUT_FILENO,默认已经打开
int main()
{
	char buf[128] = "";
	const char *str = "input string\n";
	write(STDOUT_FILENO,str,strlen(str));	//向屏幕写入->显示字符串 printf()
	int len = read(STDIN_FILENO,buf,sizeof(buf) - 1);	//scanf();
	write(STDOUT_FILENO,buf,strlen(buf));
}

从键盘设备文件中获取按键值

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

#if 0
接收或发送设备数据:
read(设备文件描述符);  接收设备数据   输入设备
write(设备文件描述符); 向设备发送数据 输出设备

从键盘设备文件中获取按键值:
1.键盘设备文件:/dev/input/event1
root@student-machine:~# hexdump /dev/input/event1
2.open(),read(),close();
3.输入设备数据结构体: struct input_event 驱动编写人员

struct input_event {
	struct timeval time; 时间:
	__u16 type;		事件类型 EV_KEY
	__u16 code;		按键值  KEY_UP,KEY_DOWN
	__s32 value;	在按键中是电平值,1表示按下,0表示松开
};

struct timeval {
	__kernel_time_t		tv_sec;		/* seconds */秒数
	__kernel_suseconds_t	tv_usec;	/* microseconds */毫秒
}
#endif

int main(int argc,char **argv)
{
	if(argc < 2)
	{
		printf("usage : ./a.out /dev/input/evetn1\n");
		return -1;
	}

	//1.打开按键设备文件
	int fd = open(argv[1],O_RDONLY);
	if(fd < 0)
	{
		perror("open file error");
		return -1;
	}

	//2.读取按键设备文件数据
	struct input_event event = {0};
	while(1)
	{
		memset(&event,0,sizeof(event));
		read(fd,&event,sizeof(event));	//接收按键数据

		if(event.type == EV_KEY)	//判断是否是按键事件
		{
			if(event.code == KEY_LEFT)	//判断用户按哪个键
				printf("left key ");
			else if(event.code == KEY_RIGHT)
				printf("right key ");
			else if(event.code == KEY_ESC)
				break;

			if(event.value == 1)	//判断是按下还是松开
				printf("pressed\n");
			else
				printf("released\n");
		}
	}

	//3.关闭设备文件
	close(fd);
}

实现cat命名

1.cat 目标文件名
2.open(),while(read())返回值为0表示到文件结尾,close();
3.gcc 2_mycat.c -o mycat

作业:简单实现cp命令,将一个文件复制成另一个文件
void mycp(const char *srcfile,const char *destfile);

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

#if 0
实现cat命令:
1.cat 目标文件名
2.open(),while(read())返回值为0表示到文件结尾,close();
3.gcc 2_mycat.c -o mycat

作业:简单实现cp命令,将一个文件复制成另一个文件
void mycp(const char *srcfile,const char *destfile);
#endif

void mycat(const char *path)
{
	//1.检查
	if(NULL == path)
	{
		perror("path is null");
		return;
	}

	//2.open();
	int fd = open(path,O_RDONLY);
	if(fd < 0)
	{
		perror("open file error");
		return;
	}

	//3.while(read)
	char buf[128] = "";
	while(read(fd,buf,sizeof(buf) - 1) > 0)
	{
		printf("%s",buf);
		memset(buf,0,sizeof(buf));
	}

	//4.close()
	close(fd);
}


//argc位置参数的个数  argv位置参数的内容 argv[0]命令./a.out   argv[1] filepath
int main(int argc,char **argv)
{
	if(argc < 2)
	{
		printf("usage : ./a.out filepath\n");
		return -1;
	}
	mycat(argv[1]);
}

作业代码:

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

int main(int arg,char **argv)
{
    if(arg < 3)
    {
        perror("useag: ./a.out filename1 filename2\n");
        return -1;
    }
  
    //打开源文件(文件不存在就打开不了)
    int fd = open(argv[1],O_RDWR,0666);
    if(fd < 0)
    {
        perror("open file1 error or file is not created\n");
        return -1;
    }
    //打开并创建目标文件
    int f1 = open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0666);
    if(f1 < 0)
    {
        perror("open file2 error\n");
        return -1;
    }
    //读取源文件数据
    char buf[128] = "";
	int len = read(fd,buf,sizeof(buf) - 1);
    //将读取到的数据写入目标文件
    write(f1,buf,strlen(buf));
    //关闭目标文件
    close(f1);
    //关闭源文件
    close(fd);
}

读写学生(结构体)

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

#if 0
写读多个学生
#endif

typedef struct student
{
	char name[16];
	int age;
}stu_t;

int main()
{
	//1.打开文件
	const char *path = "./student.dat";
#if 0
	int fd = open(path,O_RDWR | O_CREAT | O_TRUNC,0644);
#endif

	int fd = open(path,O_RDONLY);
	if(fd < 0)
	{
		perror("open file error");
		return -1;
	}

	stu_t stus[3] = {
		[0] = {"zhangsan",21},
		[1] = {"lisi",19},
		[2] = {"wangwu",20},
	};

#if 0	//1次写完
	int len = write(fd,stus,sizeof(stus));
	printf("write len = %d\n",len);
#endif

	int i;
#if 0 	//遍历数组,1次写入1个学生
	for(i = 0; i < 3; i++)
		write(fd,stus + i,sizeof(stus[0]));
#endif

#if 0	//1次读完
	stu_t stu_arr[3] = {0};
	read(fd,stu_arr,sizeof(stu_arr));
	for(i = 0; i < 3; i++)
		printf("name = %s,age = %d\n",stu_arr[i].name,stu_arr[i].age);
#endif

#if 0	//每次读一个学生,直到读完所有学生
	stu_t temp = {0};
	while(read(fd,&temp,sizeof(temp)) > 0)
		printf("name = %s,age = %d\n",temp.name,temp.age);
#endif	

	//3.关闭文件
	close(fd);
}

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

typedef struct student
{
	char name[16];
	int age;
}stu_t;

int main()
{
	//1.打开文件
	const char *path = "./student.dat";

	int fd = open(path,O_RDWR | O_CREAT | O_TRUNC,0644);
	if(fd < 0)
	{
		perror("open file error");
		return -1;
	}

	stu_t stus[3] = {
		[0] = {"zhangsan",21},
		[1] = {"lisi",19},
		[2] = {"wangwu",20},
	};

#if 1	//1次写完
	int len = write(fd,stus,sizeof(stus));
	printf("write len = %d\n",len);
#endif

#if 1	//1次读完
	int i;
	stu_t stu_arr[3] = {0};
	read(fd,stu_arr,sizeof(stu_arr));
	for(i = 0; i < 3; i++)
		printf("name = %s,age = %d\n",stu_arr[i].name,stu_arr[i].age);
#endif

	close(fd);
}

顺序读写与随机读写

写完后能直接读?
不能,读read()或写write()数据时,同时移动文件读写指针,当写完时文件读写指针在文件结尾,

顺序读写: while(read());
随机读写: lseek()移动文件读写指针,对二进制才有意义

移动文件读写指针:
off_t lseek(int fd, off_t offset, int whence);
@offset:偏移量, 正数:向右移,负数:向左移
@whence:相当位置 SEEK_SET:文件开头 SEEK_CUR:当前位置 SEEK_END:文件结尾
返回偏移量:
正数 负数
SEEK_SET 可以 不可以
SEEK_CUR 可以 可以
SEEK_END 不可以 可以

lseek(fd,0,SEEK_SET); 移动到文件开头
lseek(fd,0,SEEK_CUR); 获取文件指针所在位置
lseek(fd,0,SEEK_END); 移动到文件结尾

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

#if 0
写完后能直接读?
不能,读read()或写write()数据时,同时移动文件读写指针,当写完时文件读写指针在文件结尾,

顺序读写: while(read());
随机读写: lseek()移动文件读写指针,对二进制才有意义

移动文件读写指针:
off_t lseek(int fd, off_t offset, int whence);
@offset:偏移量,  正数:向右移,负数:向左移
@whence:相当位置  SEEK_SET:文件开头  SEEK_CUR:当前位置  SEEK_END:文件结尾
返回偏移量:
				正数			负数
SEEK_SET		可以			不可以
SEEK_CUR		可以			可以
SEEK_END		不可以			可以

lseek(fd,0,SEEK_SET);	移动到文件开头
lseek(fd,0,SEEK_CUR);	获取文件指针所在位置
lseek(fd,0,SEEK_END);	移动到文件结尾
#endif

typedef struct student
{
	char name[16];
	int age;
}stu_t;

int main()
{
	//1.打开文件
	const char *path = "./student.dat";

	int fd = open(path,O_RDWR | O_CREAT | O_TRUNC,0644);
	if(fd < 0)
	{
		perror("open file error");
		return -1;
	}

	stu_t stus[3] = {
		[0] = {"zhangsan",21},
		[1] = {"lisi",19},
		[2] = {"wangwu",20},
	};

#if 1	//1次写完
	int len = write(fd,stus,sizeof(stus));
	printf("write len = %d\n",len);
#endif

	int offset = lseek(fd,0,SEEK_SET);		//移动到文件开头
	printf("offset = %d\n",offset);

	stu_t temp = {0};
	read(fd,&temp,sizeof(temp));
	printf("name = %s,age = %d\n",temp.name,temp.age);		//zhangsan

	lseek(fd,sizeof(temp),SEEK_CUR);		//跳过1个学生
	
	read(fd,&temp,sizeof(temp));			
	printf("name = %s,age = %d\n",temp.name,temp.age);		//wangwu

	offset = lseek(fd,0,SEEK_CUR);			//获取文件指针当前位置
	printf("offset = %d\n",offset);

	close(fd);
}

检测文件属性

检测文件是否有读,写,执行权限
int access(const char *pathname, int mode);
@mode:模式:F_OK 是否存在 R_OK W_OK X_OK
如果文件有相应权限返回0,没有则返回非0值

普通文件删除函数:
int unlink(const char *pathname);
删除成功返回0

删除目录文件或普通文件
int remove(const char *pathname);

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

#if 0
检测文件是否有读,写,执行权限
int access(const char *pathname, int mode);
@mode:模式:F_OK 是否存在 R_OK  W_OK X_OK
如果文件有相应权限返回0,没有则返回非0值

普通文件删除函数:
int unlink(const char *pathname);
删除成功返回0

删除目录文件或普通文件
int remove(const char *pathname);

#endif

int main()
{
	const char *file = "hello";
	int ret = access(file,F_OK);	//判断文件是否存在
	printf("ret = %d\n",ret);

	ret = access(file,X_OK);	//判断文件是否存在
	printf("ret = %d\n",ret);

	ret = unlink(file);
	printf("unlink ret = %d\n",ret);
	
	ret = remove("mydir");
	printf("remove ret = %d\n",ret);
}

获取要发送邮件文件名

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

#if 0
获取要发送邮件文件名 zhangsan : zhangsan_0.mail
int sprintf(char *str, const char *format, ...);
实现:char *getCreateMailName(const char *username,char *mailname);

作业:编写程序,用于获得要发送的邮件文件名,如果文件名存在则返回,如果文件不存在则向下找,
char *getSendMailname(const char *username,char *mailname);
#endif

char *getCreateMailName(const char *username,char *mailname)
{
	//1.参数检查
	if(NULL == username || NULL == mailname)
		return NULL;
	//2.根据用户名组装一个文件名sprintf();先指定一个格式,按格式填充
	const char *format = "%s_%d.mail";
	char name[128] = "";
	int i;
	//3.判断当前文件是否存在:access:如果不存在,就找到存入mailname,
	//如果存在就向下找,循环找
	for(i = 0; i < 10000; i++)
	{
		memset(name,0,sizeof(name));
		sprintf(name,format,username,i);	//组装文件名
		if(access(name,F_OK))				//检测文件是否存在,
		{
			strcpy(mailname,name);	//如果不存在,则此文件名就是要找的文件名
			return mailname;
		}
	}
	return NULL;
}
int main()
{
	const char *user = "zhangsan";
	char buf[128] = "";
	getCreateMailName(user,buf);
	printf("new flie name = %s\n",buf);
}

作业:

设置或者获取文件属性

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

#if 0
设置文件或获取文件属性:
int fcntl(int fd, int cmd);		获取属性时
int fcntl(int fd, int cmd,unsigned int args);  设置属性时使用,F_GETLK
@cmd:
F_SETFL  设置描述符O_ASYNC,O_NONBLOCK等
F_GETFL  获取描述符的flags
F_SETLK 设置文件锁,非阻塞锁
F_SETLKW  设置阻塞锁
F_GETLK  获取文件锁数据
F_SETOWN  设置指定进程接收信号
返回值:根据cmd不同情况表达意义不同

#endif

//设置为非阻塞:只是对当前程序有效
//#define O_NONBLOCK	00004000

int main()
{
	//使用STDIN_FILENO默认阻塞读,没有数据时会等待
	
	//1.获取旧属性
	int oldflag = fcntl(STDIN_FILENO,F_GETFL);

	//2.设置新属性
	int newflag = O_NONBLOCK | oldflag;		//在原有属性上加入非阻塞O_NONBLOCK
	if(fcntl(STDIN_FILENO,F_SETFL,newflag) < 0)	//设置STDIN_FILENO为新属性
	{
		perror("fcntl error");
		return -1;
	}
	
	//3.测试是否为非阻塞,非阻塞模式下必须使用轮询
	int len = -1;
	char buf[128] = "";
	while(1)
	{
		len = read(STDIN_FILENO,buf,sizeof(buf) - 1);		
		printf("read len = %d,buf = %s\n",len,buf);
		if(len > 0)
			break;
		sleep(3);
	}
	//4.还原
	fcntl(STDIN_FILENO,F_SETFL,oldflag);
}

getpid()

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

#if 0
设置文件或获取文件属性:
int fcntl(int fd, int cmd);		获取属性时
int fcntl(int fd, int cmd,unsigned int args);  设置属性时使用,F_GETLK
@cmd:
F_SETFL  设置描述符O_ASYNC,O_NONBLOCK等
F_GETFL  获取描述符的flags
F_SETLK 设置文件锁,非阻塞锁
F_SETLKW  设置阻塞锁
F_GETLK  获取文件锁数据
F_SETOWN  设置指定进程接收信号
返回值:根据cmd不同情况表达意义不同

在嵌入式中:
//&:1)置0 要置0的位&0,要保留的位&1 2)判断某数据是否有某设置 
//|:2)置1 要置1的位|1,要保留的位|0 2)向某数据加入某设置


//设置阻塞的第1种方式:open()时设置:open(fd,O_RDONLY | O_NONBLOCK);
//设置阻塞的第2种方式:open()已经打开文件,中间要设置为O_NONBLOCK就使用fcntl()
#endif

//设置当前进程接收信号
//getpid()用于获取当前进程号
int main()
{
	fcntl(STDIN_FILENO,F_SETOWN,getpid());
}

文件锁

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

#if 0
设置文件或获取文件属性:
int fcntl(int fd, int cmd);		获取属性时
int fcntl(int fd, int cmd,unsigned int args);  设置属性时使用,F_GETLK
int fcntl(int fd, int cmd, struct flock *lock);  获取锁状态,设置锁状态
@cmd:
F_SETFL  设置描述符O_ASYNC,O_NONBLOCK等
F_GETFL  获取描述符的flags
F_SETLK 设置文件锁,非阻塞锁
F_SETLKW  设置阻塞锁
F_GETLK  获取文件锁数据
F_SETOWN  设置指定进程接收信号
返回值:根据cmd不同情况表达意义不同

当多个进程或线程同时访问一个共享资源时,有可能读取脏数据:(未处理完成的数据): 多进程或多线程并发时的竞争问题
使用fcntl给文件加锁:操作数据前加锁,操作完成后才解锁,如果在操作数据过程中有进程或线程来加锁则不能加锁成功:1)阻塞锁,2)非阻塞锁,轮询;

锁结构体
struct flock {
	short	l_type;		锁类型:F_RDLCK:读锁 F_WRLCK:写锁 F_UNLCK:解锁 
	short	l_whence;
	__kernel_off_t	l_start;
	__kernel_off_t	l_len;
	__kernel_pid_t	l_pid;
	__ARCH_FLOCK_PAD
};

#endif


int main(int argc,char **argv)
{
	if(argc < 2)
	{
		printf("usage : ./a.out filepath\n");
		return -1;
	}

	//1.打开文件
	int fd = open(argv[1],O_RDWR | O_CREAT | O_TRUNC,0666);
	if(fd < 0)
	{
		perror("open error");
		return -1;
	}

	//2.在读写文件前加锁
	struct flock lock = {0};
//	lock.l_type = F_WRLCK;	//写锁
	lock.l_type = F_RDLCK;	//读锁
	lock.l_whence = SEEK_SET;
	lock.l_start = 0;
	lock.l_len = 10;
	lock.l_pid = getpid();	//表示当前进程加锁
	if(fcntl(fd,F_SETLKW,&lock) < 0)		//加锁
	{
		perror("fcntl set lock error");
		close(fd);
		return -1;
	}

	struct flock lockstatus = {0};
	if(fcntl(fd,F_GETLK,&lockstatus) < 0)		//获取锁的状态,存入lockstatus
	{
		perror("fcntl error");
		close(fd);
		return -1;
	}

	//根据获取到的锁状态lockstatus,判断是哪种锁,实际过程中会显示F_UNLCK
	if(lockstatus.l_type == F_RDLCK)
		printf("lock is F_RDLCK\n");
	else if(lockstatus.l_type == F_WRLCK)
		printf("lock is F_WRLCK\n");
	else if(lockstatus.l_type == F_UNLCK)
		printf("lock is F_UNLCK\n");

	printf("file option data : read ,write\n");

	lock.l_type = F_UNLCK;					//解锁
	if(fcntl(fd,F_SETLKW,&lock) < 0)
	{
		perror("fcntl set lock error");
		close(fd);
		return -1;
	}
	close(fd);
}

阻塞锁与非阻塞锁的设定

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

#if 0
int fcntl(int fd, int cmd, struct flock *lock);  获取锁状态,设置锁状态
@cmd:
F_SETLK 设置文件锁,非阻塞锁
F_SETLKW  设置阻塞锁
F_GETLK  获取文件锁数据
返回值:根据cmd不同情况表达意义不同

当多个进程或线程同时访问一个共享资源时,有可能读取脏数据:(未处理完成的数据): 多进程或多线程并发时的竞争问题
使用fcntl给文件加锁:操作数据前加锁,操作完成后才解锁,如果在操作数据过程中有进程或线程来加锁则不能加锁成功:1)阻塞锁,2)非阻塞锁,轮询;
目标:同一时间只能让一个进程访问数据

锁结构体
struct flock {
	short	l_type;		锁类型:F_RDLCK:读锁 F_WRLCK:写锁 F_UNLCK:解锁 
	short	l_whence;
	__kernel_off_t	l_start;
	__kernel_off_t	l_len;
	__kernel_pid_t	l_pid;
	__ARCH_FLOCK_PAD
};

#endif


int main(int argc,char **argv)
{
	if(argc < 2)
	{
		printf("usage : ./a.out filepath\n");
		return -1;
	}

	//1.打开文件
	int fd = open(argv[1],O_RDWR | O_CREAT | O_TRUNC,0666);
	if(fd < 0)
	{
		perror("open error");
		return -1;
	}

	//2.在读写文件前加锁
	struct flock lock = {0};
	lock.l_type = F_WRLCK;	//写锁
	lock.l_whence = SEEK_SET;
	lock.l_start = 0;
	lock.l_len = 10;
	lock.l_pid = getpid();	//表示当前进程加锁
//	if(fcntl(fd,F_SETLKW,&lock) < 0)	/加阻塞锁,如果加锁不成功会阻塞,直到对方进程解锁后,才能加锁成功结束阻塞状态向下运行
	while(fcntl(fd,F_SETLK,&lock) < 0)			//非阻塞锁,如果加锁不成功不会阻塞,为保证同一时间只有一个进程访问数据,所以必须轮询
	{
		perror("fcntl set lock error\n");
		sleep(1);
	}

	printf("1th lock ok\n");
	
	printf("file option data : read ,write\n");
	getchar();		//进程阻塞

	lock.l_type = F_UNLCK;					//解锁
//	if(fcntl(fd,F_SETLKW,&lock) < 0)
	if(fcntl(fd,F_SETLK,&lock) < 0)			//解锁
	{
		perror("fcntl set lock error");
		close(fd);
		return -1;
	}
	
	close(fd);
}

共享锁

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

#if 0

当多个进程或线程同时访问一个共享资源时,有可能读取脏数据:(未处理完成的数据): 多进程或多线程并发时的竞争问题
使用fcntl给文件加锁:操作数据前加锁,操作完成后才解锁,如果在操作数据过程中有进程或线程来加锁则不能加锁成功:1)阻塞锁,2)非阻塞锁,轮询;
目标:同一时间只能让一个进程访问数据

共享锁:(两个进程同时加锁)1个进程加读锁,另一个进程也加读锁(读读锁)
独占锁:1个进行加了读锁或写锁,另一个进行要去加写锁或读锁,会导致进程加锁不成功,写写锁,读写锁

#endif


int main(int argc,char **argv)
{
	if(argc < 2)
	{
		printf("usage : ./a.out filepath\n");
		return -1;
	}

	int fd = open(argv[1],O_RDWR | O_CREAT | O_TRUNC,0666);
	if(fd < 0)
	{
		perror("open error");
		return -1;
	}

	struct flock lock = {0};
	lock.l_type = F_RDLCK;
	lock.l_whence = SEEK_SET;
	lock.l_start = 0;
	lock.l_len = 10;
	lock.l_pid = getpid();
	while(fcntl(fd,F_SETLK,&lock) < 0)
	{
		perror("fcntl set lock error\n");
		sleep(1);
	}

	printf("1th lock ok\n");
	
	printf("file option data : read ,write\n");
	getchar();		

	lock.l_type = F_UNLCK;					
	if(fcntl(fd,F_SETLK,&lock) < 0)		
	{
		perror("fcntl set lock error");
		close(fd);
		return -1;
	}
	
	close(fd);
}

独占锁

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

#if 0
共享锁:(两个进程同时加锁成功)1个进程加读锁,另一个进程也加读锁(读读锁)
独占锁:(两个进程只能有一个进程加锁成功)1个进行加了读锁或写锁,另一个进行要去加写锁或读锁,会导致进程加锁不成功,写写锁,读写锁

功能:执行用户自定义的命令,也可以执行系统命令   F_GETFL  F_SETFL
int ioctl(int fd, unsigned long request, ...);
@request:命令
#endif

int main(int argc,char **argv)
{
	int oldflag = fcntl(STDIN_FILENO,F_GETFL);

	//2.设置新属性
	int newflag = O_NONBLOCK | oldflag;		//在原有属性上加入非阻塞O_NONBLOCK
	if(fcntl(STDIN_FILENO,F_SETFL,newflag) < 0)	//设置STDIN_FILENO为新属性
	{
		perror("fcntl error");
		return -1;
	}
	
	//3.测试是否为非阻塞,非阻塞模式下必须使用轮询
	int len = -1;
	char buf[128] = "";
	while(1)
	{
		len = read(STDIN_FILENO,buf,sizeof(buf) - 1);		
		printf("read len = %d,buf = %s\n",len,buf);
		if(len > 0)
			break;
		sleep(3);
	}
}

时间相关函数

#include <stdio.h>
#include <time.h>

#if 0
时间相关函数:
1.获取1900年至今秒数
time_t time(time_t *tloc);
@tloc:用于存储秒数
返回值返回秒数

2.将时间秒数转成时间结构体
struct tm *gmtime(const time_t *timep);	//转成0时区时间
struct tm *localtime(const time_t *timep);	//转成本地时区时间
struct tm
{
  int tm_sec;			/* Seconds.	[0-60] (1 leap second) */
  int tm_min;			/* Minutes.	[0-59] */
  int tm_hour;			/* Hours.	[0-23] */
  int tm_mday;			/* Day.		[1-31] */
  int tm_mon;			/* Month.	[0-11] 月数+1*/
  int tm_year;			/* Year	- 1900. 年数 + 1900 */
  int tm_wday;			/* Day of week.	[0-6] */
  int tm_yday;			/* Days in year.[0-365]	*/
  int tm_isdst;			/* DST.		[-1/0/1]*/
};
@timep:秒数
返回转化后的时间 

3.将日期结构体转为字符串
char *asctime(const struct tm *tm);
@tm:日期结构体

4.将秒数直接转成字符串(本地时间)
char *ctime(const time_t *timep);

5.将指定日期转成秒数
time_t mktime(struct tm *tm);

#endif

int main()
{
	//1.获取秒数
	long temp = 0;
	long sec = time(&temp);
	printf("sec = %ld,temp = %ld\n",sec,temp);

	//2.将秒数转化为日期结构体
	struct tm *tms = localtime(&sec);
	printf("%d-%d-%d %d:%d:%d weekday = %d,%dth day in year\n",tms->tm_year + 1900,tms->tm_mon + 1,tms->tm_mday,tms->tm_hour,tms->tm_min,tms->tm_sec,tms->tm_wday,tms->tm_yday);

	//3.将日期结构体转成字符串
	char *timstr = asctime(tms);
	printf("timstr = %s\n",timstr);

	char *tstr2 = ctime(&sec);
	printf("tstr2 = %s\n",tstr2);

#if 0
sec = 1622432693,temp = 1622432693
	2021-5-31 11:44:53 weekday = 1,150th day in year
	timstr = Mon May 31 11:44:53 2021
#endif
	//4.获取
	struct tm tstruct = {0};
	tstruct.tm_year = 2021 - 1900;
	tstruct.tm_mon = 5 - 1;
	tstruct.tm_mday = 31;
	tstruct.tm_hour = 11;
	tstruct.tm_min = 44;
	tstruct.tm_sec = 53;
	temp = mktime(&tstruct);
	printf("temp = %ld,sec = %ld\n",temp,sec);	//temp与1622432693,,sec是随系统变化 
}

获取文件的详细属性

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

#if 0
root@student-machine:~/sys_2104/day3# ls -l		//查看文件的详细信息
-rw-r--r-- 1 root root 1050 531 09:25 1_fcntl.c

root@student-machine:~/sys_2104/day3# grep root /etc/passwd	//查看用户的用户编号
root:x:0:0:root:/root:/bin/bash

1.获取文件的详细属性
int stat(const char *pathname, struct stat *buf);  
int lstat(const char *pathname, struct stat *buf);	//获取指定文件路径的文件详细信息
int fstat(int fd, struct stat *buf);	//获取指定文件描述符对应的文件详细信息
返回成功失败

struct stat {
	dev_t     st_dev;         /* 设备号:ID of device containing file */
	ino_t     st_ino;         /* inode编号:inode number */
	mode_t    st_mode;        /* 文件权限,文件类型protection */
	nlink_t   st_nlink;       /* 链接个数number of hard links */
	uid_t     st_uid;         /* 用户编号:user ID of owner */
	gid_t     st_gid;         /* 用户组编号:group ID of owner */
	dev_t     st_rdev;        /* 设备号:device ID (if special file) */
	off_t     st_size;        /* 文件大小:total size, in bytes */
	blksize_t st_blksize;     /* blocksize for filesystem I/O */
	blkcnt_t  st_blocks;      /* number of 512B blocks allocated */
	struct timespec st_atim;  /* 最后访问时间:time of last access */
	struct timespec st_mtim;  /* 最后修改时间:time of last modification */
	struct timespec st_ctim;  /* 创建时间:time of last status change */
};

struct timespec {
	__kernel_time_t	tv_sec;			/*秒数seconds */
	long		tv_nsec;		/* 纳秒nanoseconds */
};
#endif

int main(int argc,char **argv)
{
	if(argc < 2)
	{
		printf("usage : ./a.out filepath\n");
		return -1;
	}

	struct stat buf = {0};
	if(stat(argv[1],&buf) < 0)
	{
		perror("stat error");
		return -1;
	}

	printf("st_ino = %ld,uid = %d,gid = %d,size = %ld\n",buf.st_ino,buf.st_uid,buf.st_gid,buf.st_size);
	printf("st_atim = %s\n",ctime(&buf.st_atim.tv_sec));
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>

#if 0
root@student-machine:~/sys_2104/day3# ls -l		//查看文件的详细信息
-rw-r--r-- 1 root root 1050 531 09:25 1_fcntl.c
获取文件详细信息中的文件类型和权限信息 st_mode
使用以下宏检测文件类型
S_ISREG(m)  is it a regular file?
S_ISDIR(m)  directory?		..
S_ISCHR(m)  character device?	/dev/input/mouse0
S_ISBLK(m)  block device?		/dev/sda
S_ISFIFO(m) FIFO (named pipe)?	
S_ISLNK(m)  symbolic link?  (Not in POSIX.1-1996.)
S_ISSOCK(m) socket?  (Not in POSIX.1-1996.)

使用以下宏检测文件权限(使用&判断有没有)
S_IRUSR     00400   owner has read permission	检测文件属主是否有读写执行权限
S_IWUSR     00200   owner has write permission
S_IXUSR     00100   owner has execute permission

S_IRGRP     00040   group has read permission  检测文件属组是否有读写执行权限
S_IWGRP     00020   group has write permission
S_IXGRP     00010   group has execute permission

S_IROTH     00004   others have read permission	 检测其它用户是事有读写执行权限
S_IWOTH     00002   others have write permission
S_IXOTH     00001   others have execute permission

作业:实现ll命令,ll命令用于显示指定文件的详细信息 gcc a.c -o myll
#endif

int main(int argc,char **argv)
{
	if(argc < 2)
	{
		printf("usage : ./a.out filepath\n");
		return -1;
	}

	struct stat buf = {0};
	if(stat(argv[1],&buf) < 0)
	{
		perror("stat error");
		return -1;
	}
	
	if(S_ISREG(buf.st_mode))
		printf("-");
	else if(S_ISDIR(buf.st_mode))
		printf("d");
	else if(S_ISCHR(buf.st_mode))
		printf("c");

	if(S_IRUSR & buf.st_mode)
		printf("r");
	else 
		printf("-");
	
	if(S_IWUSR & buf.st_mode)
		printf("w");
	else 
		printf("-");
	
	if(S_IXUSR & buf.st_mode)
		printf("x");
	else 
		printf("-");
	printf("\n");
}


修改文件信息

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <utime.h>

#if 0
修改文件信息的命令:chmod chown truncate utime 
1.修改文件权限:
int chmod(const char *pathname, mode_t mode);
int fchmod(int fd, mode_t mode);
@mode:新权限

2.修改文件属主或属组
int chown(const char *pathname, uid_t owner, gid_t group);	
int fchown(int fd, uid_t owner, gid_t group);
int lchown(const char *pathname, uid_t owner, gid_t group);
@owner新属主 都是通过uid,gid去修改
@group:新属组

3.修改大小:
int truncate(const char *path, off_t length);
int ftruncate(int fd, off_t length);
@length:新大小 
可以将文件变大(空洞文件,文件大小>文件的实际内容大小: 内存内容映射),可以将文件变小(截取)

4.修改文件最后访问时间
int utime(const char *filename, const struct utimbuf *times);
@times:新时间,需要初始化

struct utimbuf {
	__kernel_time_t actime;	//最后访问时间,秒数
	__kernel_time_t modtime;//最后修改时间
};
以上函数返回值都是成功失败
函数的功能和函数名,能通过形参名和形参类型知道应该传入什么数据
#endif

int main(int argc,char **argv)
{
	if(argc < 2)
	{
		printf("usage : ./a.out filepath\n");
		return -1;
	}

#if 0
	if(chmod(argv[1],0600) < 0)
	{
		perror("chmode error");
		return -1;
	}
#endif

#if 0
	if(chown(argv[1],1000,1000) < 0)	//如果将普通文件属主修改为root需要sudo
	{
		perror("chown error");
		return -1;
	}
#endif

#if 0
	//	if(truncate(argv[1],100) < 0)			//空洞文件
	if(truncate(argv[1],5))					//将文件剪裁
	{
		perror("truncate error");
		return -1;
	}
#endif

	struct utimbuf utim = {0};
	utim.actime = time(NULL);
	utim.modtime = time(NULL);
	if(utime(argv[1],&utim) < 0)
	{
		perror("utime error");
		return -1;
	}
}

目录

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

#if 0
目录操作:mkdir rmdir pwd cd
1.创建目录:
int mkdir(const char *pathname, mode_t mode);
@mode:权限,实际按系统的umask来决定

2.删除目录
int rmdir(const char *pathname);

3.获取当前绝对路径
char *getcwd(char *buf, size_t size);  
char *get_current_dir_name(void);
@buf:存储获取到的路径
@size:buf的大小,防止内存溢出

4.切换当前路径:
int chdir(const char *path);
int fchdir(int fd);
所有修改只对当前进程有效
#endif

int main(int argc,char **argv)
{
	if(argc < 2)
	{
		printf("usage : ./a.out filepath\n");
		return -1;
	}

#if 0
	if(mkdir(argv[1],0777) < 0)
	{
		perror("mkdir error");
		return -1;
	}
#endif

#if 0
	if(rmdir(argv[1]) < 0)
	{
		perror("rmdir error");
		return -1;
	}
#endif

	
	if(chdir(argv[1]) < 0)
	{
		perror("argv[1] error");
		return -1;
	}

#if 1
	char path[128] = "";
	if(NULL == getcwd(path,sizeof(path) - 1))
	{
		perror("getcwd error");
		return -1;
	}
	printf("path = %s\n",path);
#endif


}

读取目录

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>

#if 0
目录文件内容的读取:
1.打开目录
DIR *opendir(const char *name);
DIR *fdopendir(int fd);
typedef struct __dirstream DIR;
返回值:打开目录成功返回DIR*,失败返回NULL

2.读取目录内容
struct dirent *readdir(DIR *dirp);
返回目录结构体,读到目录结尾返回NULL
struct dirent
{
	unsigned short int d_reclen;  路径长度
	unsigned char d_type;	路径的类型:绝对路径 相对路径
	char d_name[256];		/*目录名:目录名不能超过255个字符 We must not include limits.h! */
};

3.关闭目录文件
int closedir(DIR *dirp);

实现ls -Rl
#endif

int main(int argc,char **argv)
{
	if(argc < 2)
	{
		printf("usage : ./a.out dirname\n");
		return -1;
	}

	DIR * dirp = opendir(argv[1]);
	if(NULL == dirp)
	{
		perror("open dir error");
		return -1;
	}

	struct dirent *dirent = NULL;
	
	while((dirent = readdir(dirp)) != NULL)	//把当前目录下的内容打印出来
	{
		printf("d_name = %s\n",dirent->d_name);
		memset(dirent->d_name,0,sizeof(dirent->d_name));
	}

	closedir(dirp);
}

目录文件内容的读取

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

#if 0
目录文件内容的读取:
实现ls -Rl
root@student-machine:~/sys_2104# ls -Rl .
.:
总用量 16
drwxr-xr-x 2 root root 4096 531 09:02 day1
drwxr-xr-x 2 root root 4096 531 11:18 day2
drwxr-xr-x 2 root root 4096 531 11:58 day3
drwxr-xr-x 2 root root 4096 531 16:41 day4

./day1:
总用量 52
-rw-r--r-- 1 root root  566 528 09:29 1_base.txt
-rw-r--r-- 1 root root 1654 531 09:02 2_inode.c
-rw-r--r-- 1 root root 1084 528 10:48 3_open.c
-rw-r--r-- 1 root root  767 528 11:21 4_write.c
-rw-r--r-- 1 root root  728 528 11:21 5_write_binary.c
-rw-r--r-- 1 root root  894 528 11:48 6_read.c
-rw-r--r-- 1 root root  578 528 11:48 7_read_binary.c
-rw-r--r-- 1 root root  521 528 14:01 8_STDIN_FILENO.c
-rwxr-xr-x 1 root root 8784 528 11:52 a.out
-rw-r--r-- 1 root root   10 528 11:34 hello
-rw-r--r-- 1 root root   20 528 11:01 student.dat

#endif

int showmode(const struct stat *buf,const char *name)
{
	if(NULL == buf || NULL == name)
		return -1;

	if(S_ISREG(buf->st_mode))
		printf("-");
	else if(S_ISDIR(buf->st_mode))
		printf("d");

	buf->st_mode & S_IRUSR ? printf("r") : printf("-");
	buf->st_mode & S_IWUSR ? printf("w") : printf("-");
	buf->st_mode & S_IXUSR ? printf("x") : printf("-");
	buf->st_mode & S_IRGRP ? printf("r") : printf("-");
	buf->st_mode & S_IWGRP ? printf("w") : printf("-");
	buf->st_mode & S_IXGRP ? printf("x") : printf("-");
	buf->st_mode & S_IROTH ? printf("r") : printf("-");
	buf->st_mode & S_IWOTH ? printf("w") : printf("-");
	buf->st_mode & S_IXOTH ? printf("x") : printf("-");
	printf("\t");

	char *timestr = ctime(&buf->st_atime);
	char timebuf[128] = "";
	strcpy(timebuf,timestr);
	timebuf[strlen(timebuf) - 1] = 0;	//去除\n
	printf("%d\t%d\t%ld\t%s\t%s\n",buf->st_uid,buf->st_gid,buf->st_size,timebuf,name);
	return 0;
}

int myls(const char *path)
{
	//1.检查参数
	if(NULL == path)
	{
		perror("path is null");
		return -1;
	}

	//2.打开目录opendir();
	char pwd[128] = "";
	getcwd(pwd,sizeof(pwd) - 1); //获取当前路径的绝对路径
	DIR *dirp = opendir(pwd);
	if(NULL == dirp)
	{
		perror("opendir error");
		return -1;
	}

	//3.读取目录while(drient = readdir())
	struct dirent *dirent = NULL;
	struct stat buf = {0}; 
	while((dirent = readdir(dirp)) != NULL)
	{
	//1)如果是.或..则不打印strcmp()
		if(!strcmp(".",dirent->d_name) || !strcmp("..",dirent->d_name))
			continue;

		if(stat(dirent->d_name,&buf) < 0)
		{
			perror("stat error");
			break;
		}

	//2)如果是文件(stat: S_ISREG())则显示详细信息:
		if(S_ISREG(buf.st_mode))
			showmode(&buf,dirent->d_name);
	//3)如果是目录(S_ISDIR())则递归调用
		else if(S_ISDIR(buf.st_mode))
		{
			printf("%s\n",dirent->d_name);
			chdir(dirent->d_name);	//切换到子目录
			myls(dirent->d_name);
			chdir("..");			//切换回父目录 
		}
	}
	//4.关闭目录
	closedir(dirp);
}

int main(int argc,char **argv)
{
#if 0
	if(argc < 2)
	{
		printf("usage : ./a.out dirname\n");	//myls /root/sys_2104/
		return -1;
	}
#endif

	chdir(argv[1]);	//切换到指定目录
	myls(argv[1]);
}

获取用户编号信息

#include <sys/types.h>
#include <pwd.h>
#include <stdio.h>
#include <shadow.h>
#include <crypt.h>

#if 0
/etc/passwd : 用户文件
student:x:1000:1000:,,,:/home/student:/bin/bash
root:x:0:0:root:/root:/bin/bash
用户名:密码:用户编号:用户组号:用户根目录:使用的shell

1.获取指定用户名或用户编号的/etc/passwd文件信息
struct passwd *getpwnam(const char *name); //通过用户名获取
struct passwd *getpwuid(uid_t uid);		   //通过用户编号获取
struct passwd {
	char   *pw_name;       /* username */
	char   *pw_passwd;     /* user password */
	uid_t   pw_uid;        /* user ID */
	gid_t   pw_gid;        /* group ID */
	char   *pw_gecos;      /* user information */
	char   *pw_dir;        /* home directory */
	char   *pw_shell;      /* shell program */
};

2.获取指定用户的密码文件信息:/etc/shadow
student:$6$ZqEnV1wT$QYdMz4I4078T7rVDqyGMiKon1e9t1.KwxJ1QM4DA7ajOLkNtAGttZqpLyKlb9.5lTl1aLK4GmqF3Ym/qjPe2d.:18345:0:99999:7:::
root:$6$BJerwWJD$UnvtSmaGub41.dQW6ecpcwLsBBV4pxbwX6h4dcE3fjc2wWm6FgshauJD4/rRgKohpSTFplFhcRZf07B4kxXG20:18345:0:99999:7:::
struct spwd *getspnam(const char *name);	//获取指定用户名
struct spwd *getspent(void);	//获取所有用户信息:每获取一个向后移动一个

struct spwd
  {
    char *sp_namp;		/* 用户名Login name.  */
    char *sp_pwdp;		/* 密码Encrypted password.  */
    long int sp_lstchg;		/* Date of last change.  */
    long int sp_min;		/* Minimum number of days between changes.  */
    long int sp_max;		/* Maximum number of days between changes.  */
    long int sp_warn;		/* Number of days to warn user to change
				   the password.  */
    long int sp_inact;		/* Number of days the account may be
				   inactive.  */
    long int sp_expire;		/* Number of days since 1970-01-01 until
				   account expires.  */
    unsigned long int sp_flag;	/* Reserved.  */
  };
#endif

int main(int argc,char **argv)
{
	if(argc < 2)
	{
		printf("usage : ./a.out username\n");
		return -1;
	}

	struct passwd * pwd = getpwnam(argv[1]);
	if(NULL == pwd)
	{
		perror("getpwnam error");
		return -1;
	}

	printf("%s:%d:%d:%s:%s\n",pwd->pw_name,pwd->pw_uid,pwd->pw_gid,pwd->pw_dir,pwd->pw_shell);


	struct spwd *spwd = getspnam(argv[1]);
	if(NULL == spwd)
	{
		perror("getspnam error");
		return -1;
	}

	printf("%s:%s\n",spwd->sp_namp,spwd->sp_pwdp);


	while((spwd = getspent()) != NULL)	//遍历/etc/shadow文件中所有用户
		printf("%s:%s\n",spwd->sp_namp,spwd->sp_pwdp);
}

加密

#include <sys/types.h>
#include <pwd.h>
#include <stdio.h>
#include <shadow.h>
#include <unistd.h>
#include <crypt.h>

#if 0
student:$6$ZqEnV1wT$QYdMz4I4078T7rVDqyGMiKon1e9t1.KwxJ1QM4DA7ajOLkNtAGttZqpLyKlb9.5lTl1aLK4GmqF3Ym/qjPe2d.:18345:0:99999:7:::
root:$6$BJerwWJD$UnvtSmaGub41.dQW6ecpcwLsBBV4pxbwX6h4dcE3fjc2wWm6FgshauJD4/rRgKohpSTFplFhcRZf07B4kxXG20:18345:0:99999:7:::
用户密码加密:MD5加密:单向加密
char *crypt(const char *key, const char *salt);
@key:要加密的字符串
@salt:密钥
返回加密后的字符串

1.crypt库没有在C标准库
gcc 2_ctrpt.c -lcrypt
2.crypt对应:/lib/x86_64-linux-gnu/libcrypt-2.23.so 
#endif

int main(int argc,char **argv)
{
	
	const char *key = "8";
	const char *salt = "$6$BJerwWJD$";
	char *pass = crypt(key,salt);
	if(NULL == pass)
	{
		perror("crypt error");
		return -1;
	}

	printf("pass = %s\n",pass);
}

Linux登陆

#include <sys/types.h>
#include <pwd.h>
#include <stdio.h>
#include <shadow.h>
#include <unistd.h>
#include <crypt.h>
#include <string.h>

#if 0
1.crypt库没有在C标准库
gcc 2_ctrpt.c -lcrypt
2.crypt对应:/lib/x86_64-linux-gnu/libcrypt-2.23.so 
作业:模拟linux系统用户登陆
#endif

int main(int argc,char **argv)
{
	//linux登陆:
	const char *user = "root";
	char username[128] = "";
	char password[128] = "";
	const char *salt = "$6$BJerwWJD$";
	char *pass = NULL;
	int count = 3;
	struct spwd *spwd = NULL; 
	int flag = 0;	//0表示登陆失败,1表示成功

	while(count--)
	{
		printf("input your username and password\n");
	//1.输入用户名密码
		scanf("%s",username);
		//检测用户名
		scanf("%s",password);
	//2.验证:
	//1)密码验证:将用户输入的密码进行md5加密得到加密后的字符串,
		pass = crypt(password,salt);
		if(NULL == pass)
		{
			perror("crypt error");
			printf("reinput your username or password\n");
			continue;
		}
	//2)getspnam()获取指定用户的密码
		
		spwd = getspnam(username);
		if(NULL == spwd)
		{
			perror("getspnam error");
			printf("reinput your username or password\n");	
			continue;
		}

	//3)将两个密码进行比较 
		if(!strcmp(pass,spwd->sp_pwdp))
		{
			flag = 1;
			break;
		}
	}
	
	if(flag)
		printf("longin ok\n");
	else
		printf("longin error\n");
}

进程

程序与进程

#include <stdio.h>

#if 0
编译好的程序在永久存储介质中:程序要运行必须加载到内存中(进程):
程序与进程的区别;
1.程序在永久存储介质中,进程在内存中
2.程序是静态的,而进程是动态(运行时创建,运行结束时);
3.由程序产生进程,一个程序可以创建多个进程

1.程序进入内存要完成的任务;
反汇编:objdump -D a.out > a.out.s  程序信息设置,进程初始化,堆栈空间初始化,_start,.init, main
2.程序要分配的内存大小
root@student-machine:~/sys_2104/day5# ll a.out
-rwxr-xr-x 1 root root 8936 61 10:37 a.out*
root@student-machine:~/sys_2104/day5# size a.out
text	   data	    bss	    dec	    hex	filename
2489	    616	      8	   3113	    c29	a.out
3.程序加载到内存要进行内存分段:段页式(分页,分段:应用程序加载到内存);
段内:编译时确定大小,加载时确定具体位置
1).text 程序代码区(编译后二进制指令集)
2).data:已初始化数据区(静态变量,已初始化的全局变量)
3).bss:未初始化数据区(未初始化的全局变量)
4).rodata:常量数据区(只读不改)  char *str = "hello"; *str = 'H';
5).init:进程初始化
堆栈:在程序运行时才分配 
4.进程相关的命令:
1)查询进程:ps 查询本终端进程   ps -aux或ps -ef查询系统中所有进程
root@student-machine:~/sys_2104/day5# ps -aux
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root          1  0.0  0.0 185100  1860 ?        Ss   531   0:03 /sbin/init splash	第1个进程
root          2  0.0  0.0      0     0 ?        S    531   0:00 [kthreadd] 启动后创建第1个内核线程
root          3  0.0  0.0      0     0 ?        S    531   0:02 [ksoftirqd/0]
进程创建者 进程号 								状态:
2)向指定进程发送信号:kill -9 进程号: 结束某进程;
3)动态查询进程的内存等占用情况:top
4)pstree以树状结构显示所有进程

5.程序运行时以进程形式存在,描述进程:
linux内核使用struct task_struct来描述进程;
进程状态:就绪(只是分配内存,未分配cpu时间片)->运行(分配cpu时间片) ->等待(可被打断,不可被打断) 
								|				|					|
								-------------------------------------
												|正常结束
												|非正常结束:资源没有回收:僵尸态

6.cpu运行哪个进程有调度策略:先来先服务,时间片轮转法,优先级(-20~20)+抢占式
7.内存分用户区和内核区:用户区进程无法访问内核区内存,内核区进程可以访问用户区内存

#endif

进程相关信息、用户编号和用户组号、优先级获取与设置

#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>

#if 0
1.获取当前进程相关信息;
pid_t getpid(void);获得当前进程号
pid_t getppid(void);获取当前进程父进程号
2.获取当前用户编号和用户组号
uid_t getuid(void);
gid_t getgid(void);

main函数运行时会创建进程,main()进程的父进程是bash终端
bash会话进程:通过会话进程可以控制子进程

进程都有优先级:
1.获取优先级:
int getpriority(int which, id_t who);  获取优先级,返回是优先级
int setpriority(int which, id_t who, int prio); 设置优先级
@which:哪一种
PRIO_PROCESS:对应进程号
PRIO_PGRP:对应进程组号
PRIO_USER:对应用户编号
@who:根据which来进行选择
@prio:要设置的优先级

linux优先级:-20~20:数字越小优先级越高,数据越大优先级越低
#endif

int main()
{
	printf("%s pid = %d,ppid = %d,uid = %d,gid = %d\n",__func__,getpid(),getppid(),getuid(),getgid());
	int pri = getpriority(PRIO_PROCESS,getpid());
	printf("current pid = %d,pri = %d\n",getpid(),pri);  //新建进程默认优先级为0

	if(setpriority(PRIO_PROCESS,getpid(),5) < 0)
	{
		perror("setpriority error");
		return -1;
	}

	pri = getpriority(PRIO_PROCESS,getpid());
	printf("current pid = %d,pri = %d\n",getpid(),pri);  //新建进程默认优先级为5
}

进程创建

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

#if 0
创建进程:fork(),vfork(),system();exec()函数族
1.fork()创建进程
pid_t fork(void);
返回子进程的进程号

2.进程退出:
extern void exit (int __status) __THROW __attribute__ ((__noreturn__));
@__status:子进程的退出状态

1.root@student-machine:~/sys_2104/day6# ./a.out 
child process		//在子进程中fork()函数返回值为0
parent process		//在父进程中fork()函数返回子进程号
以上结果是由于两个进程运行的结果:

2.没有设置优先级时,父子进程抢占运行
#endif

int main()
{
	pid_t pid = fork();  //创建子进程
	if(pid < 0)
	{
		perror("fork error");
	}
	else if(0 == pid)	
	{
		printf("child process\n");
		exit(0);
	}
	else if(pid > 0)
	{
		sleep(1);
		printf("parent process\n");
	}
}

父子进程的判断

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

#if 0
fork()创建子进程后父子进程的判断:

root@student-machine:~/sys_2104/day6# ./a.out 
child process : fork return pid = 0
child process pid = 11519,ppid = 11518
parent process : fork return pid = 11519
parent process : pid = 11518,ppid = 2597

root@student-machine:~/sys_2104/day6# ps 
PID TTY          TIME CMD
2597 pts/3    00:00:02 bash
11498 pts/3    00:00:00 ps

1.bash是爷->main()父进程->fork()子进程
2.根据fork()返回值判断是子进程还是父进程:如果pid==0是子进程,如果pid>0是父进程(此时pid的值为子进程号)
#endif

int main()
{
	pid_t pid = fork();  
	if(pid < 0)
	{
		perror("fork error");
	}
	else if(0 == pid)				
	{
		printf("child process : fork return pid = %d\n",pid);
		printf("child process pid = %d,ppid = %d\n",getpid(),getppid());
		exit(0);
	}
	else if(pid > 0)
	{
		sleep(1);
		printf("parent process : fork return pid = %d\n",pid);
		printf("parent process : pid = %d,ppid = %d\n",getpid(),getppid());
	}
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

#if 0
LINUX中创建进程技术:cow(拷贝进程技术): 会将父进程的.text,.data,pc(程序计数器:下一条指令的地址),sp,; //1,2,2,3,5,4,5,

root@student-machine:~/sys_2104/day6# ./a.out 
test 1	父进程
test 2	父进程
test 2	子进程
test 3	子进程
test 5	子进程
test 4	父进程
test 5	父进程
作业:
1.为子进程创建3个兄弟进程,
2.为子进程再创建3个子进程
#endif

int main()
{
	printf("test 1\n");
	pid_t pid = fork();  
	printf("test 2\n");
	if(pid < 0)
	{
		perror("fork error");
	}
	else if(0 == pid)				
	{
		printf("test 3\n");   //只有子进程才运行
	}
	else if(pid > 0)
	{
#if 0
		sleep(1);
#endif
		printf("test 4\n");	  //只有父进程才运行
	}
	printf("test 5\n");
}

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

#if 0
LINUX中创建进程技术:cow(拷贝进程技术): 会将父进程的.text,.data,pc(程序计数器:下一条指令的地址),sp,; //1,2,2,3,5,4,5,
#endif

int data = 100;

int main()
{
	pid_t pid = fork();  
	if(pid < 0)
	{
		perror("fork error");
	}
	else if(0 == pid)				
	{
		data++;
		printf("child process data = %d,data addr = %p\n",data,&data);   //101
	}
	else if(pid > 0)
	{
		sleep(1);
		data--;
		printf("parent process data = %d,data addr = %p\n",data,&data);	  //99
	}
}

僵尸进程

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

#if 0
创建进程时的两种异常进程:
子进程结束后,它所使用段内存的回收由父进程完成
1.僵尸进程:子进程先结束,此时父进程由于完成任务未及时回收子进程的段内存
2.僵尸进程的危害:子进程结束后空间未及时回收,造成一段时间的内存泄露
root@student-machine:~/sys_2104/day5# ps -aux
root      11760  0.0  0.0      0     0 pts/3    Z+   15:43   0:00 [a.out] <defunct> 僵尸进程
root      11763  0.0  0.1  39100  3168 pts/19   R+   15:43   0:00 ps -aux
#endif

int main()
{
	pid_t pid = fork();  //创建子进程
	if(pid < 0)
	{
		perror("fork error");
	}
	else if(0 == pid)	
	{
		printf("child process\n");
		exit(0);
	}
	else if(pid > 0)
	{
		sleep(1);
		int i;
		for(i = 0; i < 20; i++)
		{
			printf("parent process\n");
			sleep(1);
		}
	}
}

孤儿进程

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

#if 0
父进程是子进程的管理进程,结束ctrl+c的信号由终端发给主进程,主进程再把信号转给子进程
孤儿进程:
1.父进程先结束,子进程仍然运行;
2.孤儿进程的危害,不受控制
#endif

int main()
{
	pid_t pid = fork();  //创建子进程
	if(pid < 0)
	{
		perror("fork error");
	}
	else if(0 == pid)	
	{
		int i;
		for(i = 0; i < 20; i++)
		{
			printf("child process\n");
			sleep(1);
		}
		exit(0);
	}
	else if(pid > 0)
	{
		printf("parent process\n");
	}
}

进程等待

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>

#if 0
进程等待(父进程):
pid_t wait(int *status);	//等待任意子进程的结束
@status:子进程的退出状态
pid_t waitpid(pid_t pid, int *status, int options);	//等待指定的子进程结束
@pid可为下面4< -1   meaning wait for any child process whose process group ID is equal to the absolute value of pid. -1110
-1     meaning wait for any child process.  相当于wait();
0      meaning wait for any child process whose process group ID is equal to that of the calling process.
> 0    meaning wait for the child whose process ID is equal to the value of pid.
@status:子进程的退出状态
@options:
WNOHANG     return immediately if no child has exited.
WUNTRACED   also return if a child has stopped (but not traced via ptrace(2)).  Status for traced  children
WCONTINUED (since Linux 2.6.10)
返回值:子进程号

WEXITSTATUS(status) 获取子进程退出状态
#endif

int main()
{
	pid_t pid = fork();  //创建子进程
	if(pid < 0)
	{
		perror("fork error");
	}
	else if(0 == pid)	
	{
#if 0
		sleep(2);
#endif
		printf("child process\n");
		sleep(3);
		exit(5);
	}
	else if(pid > 0)
	{
#if 0
		sleep(1);
#endif
		int status = 0;
		pid_t waitp = -1;
		waitp = wait(&status);	//调时会导致本进程阻塞,直到收到子进程退出信号:SIGCHLD信号才会结束等待状态,继续向下执行
//		printf("parent process sub exit status = %d\n",WEXITSTATUS(status));
		printf("parent process sub exit status = %d,waitp = %d,pid = %d\n",status >> 8,waitp,pid);
	}
}

解决僵尸进程的办法

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/wait.h>

#if 0
解决僵尸进程的办法:
1.使用wait();	进程的联合
2.signal(SIGCHLD,SIG_IGN);系统会在子进程结束后立即回收 进程的分离态
#endif

int main()
{
#if 0
	signal(SIGCHLD,SIG_IGN);
#endif
	pid_t pid = fork();  //创建子进程
	if(pid < 0)
	{
		perror("fork error");
	}
	else if(0 == pid)	
	{
		printf("child process\n");
		exit(0);
	}
	else if(pid > 0)
	{
		int i;
		//父进程有更重要任务
#if 1
		wait(NULL);
#endif
		for(i = 0; i < 20; i++)
		{
			printf("parent process\n");
			sleep(1);
		}
	}
}

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

#if 0
实现守护进程:先就是孤儿进程
#endif

int main()
{
	pid_t pid = fork();  //创建子进程
	if(pid < 0)
	{
		perror("fork error");
	}
	else if(0 == pid)	
	{
		int i;
		for(i = 0; i < 20; i++)
		{
			printf("child process\n");
			sleep(1);
		}
		exit(0);
	}
	else if(pid > 0)
	{
		wait(NULL);
		printf("parent process\n");
	}
}

vfork与fork区别

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

#if 0
pid_t vfork(void);
vfork()一定保证子进程先运行
fork()vfork()的区别:
#endif

int main()
{
	pid_t pid = vfork();  
	if(pid < 0)
	{
		perror("fork error");
	}
	else if(0 == pid)				
	{
		sleep(3);
		printf("child process pid = %d,ppid = %d\n",getpid(),getppid());		//1.
		exit(0);
	}
	else if(pid > 0)
	{
		printf("parent process : pid = %d,ppid = %d\n",getpid(),getppid());		//2.
	}
}

clone与exec函数族

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

#if 0
运行linux shell命令方式创建进程
linux中创建进程或线程都clone()函数
1.int system(const char *command);
@command:字符串形式的命令
不会覆盖原有进程

2.exec函数族
会覆盖原有进程
int execl(const char *path, const char *arg, ...
		/* (char  *) NULL */);  以绝对路径执行命令
int execlp(const char *file, const char *arg, ...
		/* (char  *) NULL */);	以相对路径执行命令,$PATH
root@student-machine:~/sys_2104/day5# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/

int execle(const char *path, const char *arg, ...
		/*, (char *) NULL,
		 * char * const
		 * envp[] */);			以数组形式存储命令
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],
		char *const envp[]);
#endif

int main()
{
	printf("%s start\n",__func__);

#if 0
	system("ls -l");	
#endif

#if 0
	execl("/bin/ls","ls","-l",(char *)NULL);		//ls -l
#endif

#if 0
	execlp("ls","ls","-l",(char *)NULL);			//ls -l
#endif

	char *const args[] = {"ls","-l",NULL};
	execv("/bin/ls",args);

	printf("%s stop\n",__func__);
}

进程退出处理函数

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

#if 0
进程的退出:
1.
void _exit(int status);
void _Exit(int status);
2.
void exit(int status);	//一般进程都使用exit():它会调用进程退出处理函数
@status:子进程的退出状态值

进程退出:
1)把空间回收,a.由父进程在wait()时回收(进程联合) b.由操作系统在子进程结束后立即回收(进程分离)
2)文件操作:强制刷空缓冲区等

进程退出处理函数:用来完成进程退出时的清空缓冲区,释放堆内存,关闭文件等释放操作
int atexit(void (*function)(void));	注册进程退出处理函数
@function:函数指针,要传一个void (*)(void)的函数名
功能:atexit注册进程退出处理函数,此进程退出处理函数在进程退出时自动执行

exit()_exit()函数的区别:
1.exit()会调用进程退出处理函数,_exit()不会调用进程退出处理函数

root@student-machine:~/sys_2104/day7# ./a.out 
child process pid = 3178
exit2_fun 34
exit1_fun 29	//先注册的后执行,后注册的先执行(后进先出)
parent process pid = 3177

#endif

//声明两个进程退出处理函数
void exit1_fun(void)
{
	printf("%s %d\n",__func__,__LINE__);	//释放操作
}

void exit2_fun(void)
{
	printf("%s %d\n",__func__,__LINE__);
}

int main()
{
	int pid = fork();
	if(pid < 0)
	{
		perror("fork error");
		return -1;
	}
	else if(0 == pid)
	{
		atexit(exit1_fun);	//注册进程退出处理函数
		atexit(exit2_fun);
		printf("child process pid = %d\n",getpid());

#if 1
		exit(0);		//子进程退出,会调用注册的进程退出处理函数
#endif

#if 0
		_exit(0);		//不会调用进程退出处理函数
		_Exit(0);
#endif

	}
	else if(pid > 0)
	{
		wait(NULL);
		printf("parent process pid = %d\n",getpid());
	}
}

环境变量 查询和设置运行限制

进程运行环境相关:
1.设置当前进程的环境变量  USERNAME=zhangsan USERNAME=""
int setenv(const char *name, const char *value, int overwrite); //设置环境变量
@name:要设置的环境变量名
@value:环境变量值
@overwrite:是否覆盖 
int unsetenv(const char *name);   //删除环境变量

2.获取环境变量值
char *getenv(const char *name);
环境变量应用范围在当前进程

3.查询和设置进程运行限制
int getrlimit(int resource, struct rlimit *rlim);		//查询
int setrlimit(int resource, const struct rlimit *rlim); //设置
@resource:资源种类
RLIMIT_STACK:栈资源限制
RLIMIT_SIGPENDING:搁置信号集限制
RLIMIT_MSGQUEUE:消息队列限制
RLIMIT_RTPRIO:实时优先级限制
struct rlimit {
	rlim_t rlim_cur;  /* Soft limit */
	rlim_t rlim_max;  /* Hard limit (ceiling for rlim_cur) */
};
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/resource.h>

int main()
{
    //1.获取环境变量值
    char *username = getenv("USERNAME");
    if (NULL == username)
    {
        printf("env have not USERNAME\n");
    }
    //2.设置环境变量
    if (setenv("USERNAME", "zhangsan", 1) < 0)
    {
        perror("setenv error");
        return -1;
    }
    //再次获取环境变量值
    username = getenv("USERNAME");
    if (NULL == username)
    {
        printf("env have not USERNAME\n");
    }
    printf("username = %s\n",username);
    //删除环境变量
    unsetenv("USERNAME");
    printf("after delete env\n");
    //再次获取环境变量值
    username = getenv("USERNAME");
    if (NULL == username)
    {
        printf("env have not USERNAME\n");
    }
    //查询进程运行限制
    struct rlimit limit = {0};
    if(getrlimit(RLIMIT_STACK,&limit))
    {
        perror("getrlimit error");
        return -1;
    }
    printf("limt soft = %ld,hard = %ld\n",limit.rlim_cur,limit.rlim_max);
    //设置进程运行限制
    struct rlimit slimit = {1024 * 100,-1};
    if(setrlimit(RLIMIT_STACK,&slimit))
    {
        perror("setrlimit error");
        return -1;
    }
    //查询进程运行限制
     if(getrlimit(RLIMIT_STACK,&limit))
    {
        perror("getrlimit error");
        return -1;
    }
    printf("limt soft = %ld,hard = %ld\n",limit.rlim_cur,limit.rlim_max);
}

进程间通信-信号 kill

#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

#if 0
进程间通信方式:进程使用内存段,而段页式管理机制中基本原则:段与段是独立
但是实际过程中,进程间是需要通信,所以需要专用的进程间通信方式
操作系统提供的进程通信方式:1)信号 2)管道 3)共享内存 4)消息队列 5)原始套接字(异构环境)
解决进程并发时竞争问题的方法:信号量

信号:
1.linux支持的信号:
root@student-machine:~/sys_2104/day7# kill -l
 1) SIGHUP终端挂起	 2) SIGINT ctrl+c	 3) SIGQUIT	ctrl+\ 4) SIGILL	 5) SIGTRAP
  6) SIGABRT abort()	 7) SIGBUS 总线信号	 8) SIGFPE 浮点数异常	 9) SIGKILL kill -9 	10) SIGUSR1 自定义
  11) SIGSEGV 段错误信号	12) SIGUSR2	13) SIGPIPE 管道信号	14) SIGALRM	闹钟信号 15) SIGTERM
  16) SIGSTKFLT	17) SIGCHLD	子进程退出信号 18) SIGCONT	19) SIGSTOP 停止信号	20) SIGTSTP
  21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
  26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO IO信号	30) SIGPWR
  31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
  38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
  43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
  48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
  53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
  58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
  63) SIGRTMAX-1	64) SIGRTMAX	
2.信号常用命令: 
kill:向指定进程发送指定信号
kill 信号值(kill -1) 进程号(ps -ef);

4.信号的处理流程:信号产生(会停止原程序运行,进入信号处理函数运行) -> (保存现场) 进入信号处理函数(返回现场)->恢复用程序的运行,信号处理结束

5.信号的处理方式:1)SIG_DFL 按系统缺省(没有注册都是此种) 2)SIG_IGN 忽略信号  3)捕获:需要注册信号处理函数

3.信号来源:硬件信号:由硬件产生  软件信号:由函数产生:kill(),abort(),alarm(),setitimer();

1)可向任意进程发送任意信号
int kill(pid_t pid, int sig);
@pid:进程号
pid == 0, then sig is sent to every process in the process group of the calling process.
pid == -1, then sig is sent to every process for which the calling process has permission to send signals, except for process 1 (init), but see below.
pid < -1, then sig is sent to every process in the process group whose ID is -pid.
pid > 0, 向指定进程pid发送信号
@sig:信号值
#endif

int main()
{
	printf("%s start\n",__func__);

#if 0
	if(kill(getpid(),SIGKILL) < 0)
	{
		perror("kill error");
		return -1;
	}
#endif

#if 1
	int pid = fork();
	if(pid < 0)
	{
		perror("fork error");
		return -1;
	}
	else if(0 == pid)
	{
		printf("child process pid = %d start\n",getpid());
		int i;
		for(i = 0; i < 10; i++)
		{
			printf("child process pid = %d,test\n",getpid());
			sleep(1);
		}
		printf("child process pid = %d stop\n",getpid());
		exit(0);
	}
	else if(pid > 0)
	{
		sleep(1);
		printf("parent process pid = %d\n",getpid());
		kill(pid,SIGKILL);
	}
#endif

	printf("%s stop\n",__func__);
}

#if 0
root@student-machine:~/sys_2104/day7# ./a.out 
main start
child process pid = 3847 start
child process pid = 3847,test
parent process pid = 3846
main stop
#endif

注册信号的捕获 函数产生信号

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

#if 0
产生信号函数:setitimer();
1.向自己发送指定信号
int raise(int sig); 相当于 kill(getpid(), sig);
//pthread_kill(pthread_self(), sig);用于向指定线程发送信号

2.向本进程发送SIGABRT信号
void abort(void);

3.向本进程发送SIGALRM信号,信号在seconds秒数之后才产生
unsigned int alarm(unsigned int seconds);

4.进程挂起,收到任意信号时结束挂起
int pause(void);
#endif

void sig_fun(int signo)
{
	printf("%s %d\n",__func__,__LINE__);
	if(SIGALRM == signo)
	{
		printf("%s %d SIGALRM get\n",__func__,__LINE__);
	}
}

int main()
{
	printf("%s start\n",__func__);
#if 0
	abort();	//如果指令错误
#endif

#if 0
	alarm(3);	//3秒后产生SIGALRM信号
	int i;
	for(i = 0; i < 10; i++)
	{
		printf("%s pid = %d i = %d\n",__func__,getpid(),i);
		sleep(1);
	}
#endif
	
	signal(SIGALRM,sig_fun);	//注册信号捕获
	alarm(3);
	printf("alarm start,after 3 seconds stop\n");
	pause();

	printf("%s stop\n",__func__);
}

#if 0
root@student-machine:~/sys_2104/day7# ./a.out 
main start
alarm start,after 3 seconds stop
sig_fun 27
sig_fun 30 SIGALRM get
main stop

root@student-machine:~/sys_2104/day7# ./a.out 
main start
main pid = 4043 i = 0
main pid = 4043 i = 1
main pid = 4043 i = 2
闹钟
#endif

signal函数(注册信号函数)

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

#if 0
改变信号的默认处理方式,改为捕获:
1.注册信号处理方式:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
@signum:信号值
@handler:信号处理方式,值可以为
#define SIG_DFL	((__sighandler_t) 0)		/* 注册为系统默认处理,Default action.  */
#define SIG_IGN	((__sighandler_t) 1)		/* 注册为忽略:删除信号Ignore signal.  */
还可以使用类型为 void (*)(int)的函数名
返回值:旧处理方式
#endif

//注册SIGINT:ctrl+c到信号处理函数中sigfun();
void sigfun(int args)	//args:自动传入信号值
{
	printf("%s %d start\n",__func__,__LINE__);
	if(SIGINT == args)
		printf("this signal is SIGINT\n"); 	//ctrl+c
	else if(SIGQUIT == args)
		printf("this signal is SIGQUIT\n");	//ctrl+/
	printf("%s %d stop\n",__func__,__LINE__);
}

int main()
{
	printf("%s %d start\n",__func__,__LINE__);
#if 0	//注册为捕获
	signal(SIGINT,sigfun);
	signal(SIGQUIT,sigfun);
#endif

#if 0	//注册为系统缺省处理
	signal(SIGINT,SIG_DFL);
	signal(SIGQUIT,SIG_DFL);
#endif

#if 0	//注册为忽略
	signal(SIGINT,SIG_IGN);
	signal(SIGQUIT,SIG_IGN);
#endif
	int i;
	for(i = 0; i < 5; i++)
	{
		printf("current pid = %d,i = %d\n",getpid(),i);
		sleep(1);
	}
	printf("%s %d stop\n",__func__,__LINE__);
}

注册为捕获 则会执行信号处理函数
注册为缺省则会按系统默认信号处理
注册为忽略则会删除信号 (此时按Ctrl +c 等都不会有反应)

信号默认处

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

#if 0
改变信号的默认处理方式,改为捕获:
1.注册信号处理方式:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
@signum:信号值
@handler:信号处理方式,值可以为
#define SIG_DFL	((__sighandler_t) 0)		/* 注册为系统默认处理,Default action.  */
#define SIG_IGN	((__sighandler_t) 1)		/* 注册为忽略:删除信号Ignore signal.  */
还可以使用类型为 void (*)(int)的函数名
返回值:旧处理方式
#endif

typedef void (*sighandler_t)(int);

void sigfun(int args)	//args:自动传入信号值
{
	printf("%s %d start\n",__func__,__LINE__);
	if(SIGINT == args)
		printf("this signal is SIGINT\n"); 	//ctrl+c
	else if(SIGQUIT == args)
		printf("this signal is SIGQUIT\n");	//ctrl+/
	printf("%s %d stop\n",__func__,__LINE__);
}

int main()
{
	printf("%s %d start\n",__func__,__LINE__);

	void (*oldopt)(int) = signal(SIGINT,sigfun);
	if(SIG_DFL == oldopt)
		printf("option is SIG_DFL\n ");
	else if(SIG_IGN == oldopt)
		printf("option is SIG_IGN\n ");
	else 
		printf("option is sigfun\n");

	
	oldopt = signal(SIGINT,sigfun);
	if(SIG_DFL == oldopt)
		printf("option is SIG_DFL\n ");
	else if(SIG_IGN == oldopt)
		printf("option is SIG_IGN\n ");
	else 
		printf("option is sigfun\n");

	int i;
	for(i = 0; i < 5; i++)
	{
		printf("current pid = %d,i = %d\n",getpid(),i);
		sleep(1);
	}

	signal(SIGINT,oldopt);	//还原
	printf("%s %d stop\n",__func__,__LINE__);
}

注意点

signal 返回先前信号处理函数指针

注册信号处理方式的方法2

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

#if 0
2.注册信号处理方式的方法2
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
@signum:信号值
@act:要设置的信号处理方式
@oldact:保存旧处理方式
struct sigaction {
	void     (*sa_handler)(int);		//SIG_DFL,SIG_IGN,信号处理函数名	
	void     (*sa_sigaction)(int, siginfo_t *, void *);	//当sa_flags为SA_SIGINFO 
	sigset_t   sa_mask;		//屏蔽信号集(不会删除会存入搁置信号集):与忽略不同(会删除)
	int        sa_flags;	//标志位:SA_RESTART:重新调用系统调用,SA_INTERRUPT:中断 SA_SIGINFO:查询	
	void     (*sa_restorer)(void);
};
#endif

void sigfun(int args)
{
	printf("%s %d start\n",__func__,__LINE__);
	if(SIGINT == args)
		printf("this signal is SIGINT\n"); 	//ctrl+c
	else if(SIGQUIT == args)
		printf("this signal is SIGQUIT\n");	//ctrl+/
	printf("%s %d stop\n",__func__,__LINE__);
}

int main()
{
	printf("%s %d start\n",__func__,__LINE__);
	
	struct sigaction act = {0},oldact = {0};
	//act.sa_handler = SIG_DFL;	act.sa_handler = SIG_IGN;
	act.sa_handler = sigfun;
#if 1
	act.sa_flags = SA_RESETHAND;
#endif
	sigaction(SIGINT,&act,&oldact);	//使用sigaction注册新的信号处理方式 

	int i;
	for(i = 0; i < 5; i++)
	{
		printf("current pid = %d,i = %d\n",getpid(),i);
		sleep(1);
	}

	sigaction(SIGINT,&oldact,NULL);	//还原
	printf("%s %d stop\n",__func__,__LINE__);
}

信号集操作相关函数

#include <signal.h>
#include <stdio.h>

#if 0
信号集操作相关函数:

#define _NSIG		64
#define _NSIG_BPW	__BITS_PER_LONG   //32
#define _NSIG_WORDS	(_NSIG / _NSIG_BPW)
1.typedef struct {
	unsigned long sig[_NSIG_WORDS];  //2个long: 1个64位,128位 
} sigset_t;

2.
int sigemptyset(sigset_t *set); //清空信号集
int sigfillset(sigset_t *set);	 //填充信号集
int sigaddset(sigset_t *set, int signum);	//添加指定信号到信号集
int sigdelset(sigset_t *set, int signum);	//从信号集删除指定信号
返回成功失败:

int sigismember(const sigset_t *set, int signum);	//查询信号集中是否有指定信号
如果有返回1,如果没有返回0
#endif

int main()
{
	sigset_t set = {0};
	if(sigemptyset(&set) < 0)
	{
		perror("sigemptyset error");
		return -1;
	}

#if 0
	sigaddset(&set,SIGINT);
	sigaddset(&set,SIGQUIT);
#endif

	sigfillset(&set);
	if(sigismember(&set,SIGINT))
		printf("set have SIGINT\n");
	else
		printf("set not have SIGINT\n");

	sigdelset(&set,SIGINT);

	if(sigismember(&set,SIGINT))
		printf("set have SIGINT\n");
	else
		printf("set not have SIGINT\n");
}

信号屏蔽函数

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

#if 0
信号屏蔽:
信号屏蔽(信号搁置)与忽略信号的区别:
1.信号屏蔽并没有删除信号,只是将未处理的信号存入了搁置信号集,而忽略信号是将未处理的信号直接删除

信号屏蔽函数:
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
@how:
{ 级联方式:在原有基础上加减:   5 + 1 = 6,  6 - 1 = 5
SIG_BLOCK  +信号,屏蔽
The set of blocked signals is the union of the current set and the set argument.
SIG_UNBLOCK -信号,解屏蔽
The signals in set are removed from the current set  of  blocked  signals.   It  is  permissible  to
attempt to unblock a signal which is not blocked.
}
SIG_SETMASK:覆盖方式, int a = 100; int olda = a;  a = 200;
#endif

void sigfun(int args)	
{
	printf("%s %d start\n",__func__,__LINE__);
	if(SIGINT == args)
		printf("this signal is SIGINT\n"); 	//ctrl+c
	else if(SIGQUIT == args)
		printf("this signal is SIGQUIT\n");	//ctrl+/
	printf("%s %d stop\n",__func__,__LINE__);
}

int main()
{
	printf("%s %d start\n",__func__,__LINE__);
	
	signal(SIGINT,sigfun);
	signal(SIGQUIT,sigfun);
	
	int i;
	for(i = 0; i < 5; i++)
	{
		printf("current pid = %d,i = %d\n",getpid(),i);
		sleep(1);
	}
	
	sigset_t newset = {0};
	sigaddset(&newset,SIGINT);

	printf("after sigprocmask block\n");
	sigprocmask(SIG_BLOCK,&newset,NULL);	//屏蔽SIGINT
	for(i = 0; i < 5; i++)
	{
		printf("current pid = %d,i = %d\n",getpid(),i);
		sleep(1);
	}

	printf("after sigprocmask unblock\n");
	sigprocmask(SIG_UNBLOCK,&newset,NULL);	//解屏蔽SIGINT
	for(i = 0; i < 5; i++)
	{
		printf("current pid = %d,i = %d\n",getpid(),i);
		sleep(1);
	}

	printf("%s %d stop\n",__func__,__LINE__);
}

屏蔽函数 覆盖方式

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

#if 0
信号屏蔽:
信号屏蔽(信号搁置)与忽略信号的区别:
1.信号屏蔽并没有删除信号,只是将未处理的信号存入了搁置信号集,而忽略信号是将未处理的信号直接删除

信号屏蔽函数:
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
@how:
{ 级联方式:在原有基础上加减:   5 + 1 = 6,  6 - 1 = 5
SIG_BLOCK  +信号,屏蔽
The set of blocked signals is the union of the current set and the set argument.
SIG_UNBLOCK -信号,解屏蔽
The signals in set are removed from the current set  of  blocked  signals.   It  is  permissible  to
attempt to unblock a signal which is not blocked.
}
SIG_SETMASK:覆盖方式, int a = 100; int olda = a;  a = 200;
#endif

void sigfun(int args)	
{
	printf("%s %d start\n",__func__,__LINE__);
	if(SIGINT == args)
		printf("this signal is SIGINT\n"); 	//ctrl+c
	else if(SIGQUIT == args)
		printf("this signal is SIGQUIT\n");	//ctrl+/
	printf("%s %d stop\n",__func__,__LINE__);
}

int main()
{
	printf("%s %d start\n",__func__,__LINE__);
	
	signal(SIGINT,sigfun);
	signal(SIGQUIT,sigfun);
	
	int i;
	for(i = 0; i < 5; i++)
	{
		printf("current pid = %d,i = %d\n",getpid(),i);
		sleep(1);
	}
	
	sigset_t newset = {0},oldset = {0};
	sigaddset(&newset,SIGINT);

	printf("after sigprocmask block\n");
	sigprocmask(SIG_SETMASK,&newset,&oldset);	//屏蔽SIGINT
	for(i = 0; i < 5; i++)
	{
		printf("current pid = %d,i = %d\n",getpid(),i);
		sleep(1);
	}

	printf("after sigprocmask unblock\n");
	sigprocmask(SIG_SETMASK,&oldset,NULL);		//解屏蔽SIGINT
	for(i = 0; i < 5; i++)
	{
		printf("current pid = %d,i = %d\n",getpid(),i);
		sleep(1);
	}

	printf("%s %d stop\n",__func__,__LINE__);
}

查询搁置信号集:

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

#if 0
信号屏蔽:
查询搁置信号集:
int sigpending(sigset_t *set);
@set:用于存储查询到的未处理的信号
#endif

void sigfun(int args)	
{
	printf("%s %d start\n",__func__,__LINE__);
	if(SIGINT == args)
		printf("this signal is SIGINT\n"); 	//ctrl+c
	else if(SIGQUIT == args)
		printf("this signal is SIGQUIT\n");	//ctrl+/
	printf("%s %d stop\n",__func__,__LINE__);
}

int main()
{
	printf("%s %d start\n",__func__,__LINE__);
	
	signal(SIGINT,sigfun);
	signal(SIGQUIT,sigfun);
	
	int i;
	for(i = 0; i < 5; i++)
	{
		printf("current pid = %d,i = %d\n",getpid(),i);
		sleep(1);
	}
	
	sigset_t newset = {0},oldset = {0},retset = {0};
	sigaddset(&newset,SIGINT);
	sigaddset(&newset,SIGQUIT);

	printf("after sigprocmask block\n");
	sigprocmask(SIG_SETMASK,&newset,&oldset);	//屏蔽SIGINT
	for(i = 0; i < 5; i++)
	{
		printf("current pid = %d,i = %d\n",getpid(),i);
		sleep(1);
	}

	if(sigpending(&retset) < 0)
	{
		perror("sigpending error");
		return -1;
	}

	if(sigismember(&retset,SIGINT))
		printf("retset have SIGINT\n");
	else 
		printf("retset not have SIGINT\n");

	if(sigismember(&retset,SIGQUIT))
		printf("retset have SIGQUIT\n");
	else 
		printf("retset not have QUIT\n");

	printf("after sigprocmask unblock\n");
	sigprocmask(SIG_SETMASK,&oldset,NULL);		//解屏蔽SIGINT
	for(i = 0; i < 5; i++)
	{
		printf("current pid = %d,i = %d\n",getpid(),i);
		sleep(1);
	}

	if(sigpending(&retset) < 0)
	{
		perror("sigpending error");
		return -1;
	}

	if(sigismember(&retset,SIGINT))
		printf("retset have SIGINT\n");
	else 
		printf("retset not have SIGINT\n");

	if(sigismember(&retset,SIGQUIT))
		printf("retset have SIGQUIT\n");
	else 
		printf("retset not have QUIT\n");

	printf("%s %d stop\n",__func__,__LINE__);
}

无名管道

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

#if 0
进程间通信2:管道
管道命令:|,,,,,,
root@student-machine:~# ls -l | grep ^d | wc -l  统计当前目录下的子目录个数
10
管道有方向:管道左边命令的输出作管道右边命令的输入

管道的特征:
1.管道是半双工
2.无名管道只能用于有亲缘关系的进程,命名管道用于非亲缘关系的进程 
3.数据的读出和写入必须同步
4.管道不能使用lseek();

无名管道:
1.创建无名管道
int pipe(int pipefd[2]);
@pipefd:描述符数组: pfd[0]:只能作读端(read()),  pfd[1]:只能作写端(write());
2.一个进程write()发数据,一个进程read()收数据,同步
3.关闭close();
#endif

int main()
{
	int pfds[2] = {0};
	if(pipe(pfds) < 0)
	{
		perror("pipe error");
		return -1;
	}

	printf("pfds[0] = %d,pfds[1] = %d\n",pfds[0],pfds[1]);
	const char *data = "hello world";
	int len = write(pfds[1],data,strlen(data));
	printf("pfds[1] send data len = %d\n",len);

	char buf[128] = "";
	len = read(pfds[0],buf,sizeof(buf) - 1);
	printf("pfds[0] recv data len = %d,buf = %s\n",len,buf);

	close(pfds[0]);
	close(pfds[1]);
}

管道文件的读写

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

int main()
{
	int pfds[2] = {0};
	if(pipe(pfds) < 0)
	{
		perror("pipe error");
		return -1;
	}

	const char *data = "hello world";
	int len = write(pfds[1],data,strlen(data));
	printf("pfds[1] send data len = %d\n",len);

#if 0
	if(lseek(pfds[0],5,SEEK_CUR) < 0)
	{
		printf("lseek error\n");
		close(pfds[1]);
		close(pfds[0]);
		return -1;
	}
#endif

	char buf[128] = "";
	len = read(pfds[0],buf,5);
	printf("pfds[0] recv data len = %d,buf = %s\n",len,buf);

	memset(buf,0,sizeof(buf));
	len = read(pfds[0],buf,5);
	printf("pfds[0] recv data len = %d,buf = %s\n",len,buf);

	close(pfds[0]);
	close(pfds[1]);
}

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

#if 0
管道文件与普通文件读写区别:
1.管道文件只能顺序读,不能随机读写lseek;
2.读完会清除已读取的数据
3.写数据是追加写
#endif

int main()
{
	int pfds[2] = {0};
	if(pipe(pfds) < 0)
	{
		perror("pipe error");
		return -1;
	}

	const char *data = "hello";
	int len = write(pfds[1],data,strlen(data));
	printf("pfds[1] send data len = %d\n",len);

	const char *data2 = " world";
	len = write(pfds[1],data2,strlen(data2));
	printf("pfds[1] send data len = %d\n",len);
	
	char buf[128] = "";
	len = read(pfds[0],buf,sizeof(buf) - 1);
	printf("pfds[0] recv data len = %d,buf = %s\n",len,buf);

	close(pfds[0]);
	close(pfds[1]);
}

注意点

管道文件与普通文件读写区别:
1.管道文件只能顺序读,不能随机读写lseek;
2.读完会清除已读取的数据
3.写数据是追加写

高级管道

#include <stdio.h>
#include <string.h>

#if 0
高级管道:将命令运行结果读入到程序,或将程序运行结果输出到命令
ls -l | grep ^d
  "r"    "w"
将左边命令的输出作右边命令的输入

1.调用popen()打开高级管道
FILE *popen(const char *command, const char *type);
@command:shell命令
@type:读写类型,"r":"w":写
返回文件指针

读fread()或写fwrite();
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

2.调用pclose()关闭高级管道 
int pclose(FILE *stream);
#endif

int main()
{
	//1.将ls -l命令运行的结果存入buf
	FILE *fp = popen("ls -l","r");
	if(NULL == fp)
	{
		perror("popen error");
		return -1;
	}
	char buf[128] = "";
	int count = 0;
	while((count = fread(buf,1,sizeof(buf) - 1,fp)) > 0)
	{
		printf("count = %d,buf = %s\n",count,buf);
		memset(buf,0,sizeof(buf));
	}
	pclose(fp);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值