Unix程式設計實踐教程筆記(二)使用者,檔案操作,聯機幫助,實現who,cp
命令也是程式
在unix中,將自己寫的程式的可執行檔案放到/bin,/usr/bin,/usr/local/bin任一目錄中即可增加新的命令
who命令
從第一列開始分別為:使用者名稱,終端名,登入時間
通過查詢man who,得知已登入使用者的資訊放在/var/run/utmp
中
使用man -k xxx
可以根據關鍵字搜尋聯機幫助
由查詢可知,utmp檔案中儲存的是結構體陣列,陣列元素是utmp型別的結構
who命令的工作方式:
已登入的檔案資訊放在utmp中
who執行時,開啟utmp->讀取記錄->顯示記錄->關閉utmp檔案
所以需要從檔案中讀取一個整個資料結構
一次讀出整個資料結構(這裡是結構體)
使用getc是逐個位元組讀取
man -k file | grep read
-k選項只支援一個關鍵字
配合grep來查詢file相關的主題中與read相關的主題
一次讀取一個數據結構的方法:read系統呼叫
//read函式傳入一個檔案描述符,讀取檔案,將檔案中一定數目的位元組讀入一個緩衝區 //成功返回讀取到的位元組數 //On error, -1 is returned, and errno is set appropriately. #include <unistd.h> ssize_t read(int fd, void *buf, size_t count);
使用open開啟檔案,獲取檔案描述符
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
最後使用close系統呼叫關閉程序和檔案fd之間的連線
過濾使用者名稱為空和非真實使用者的條目:
當ut_type為7時,代表這是已經登入的使用者
顯示可讀的時間:
unix儲存時間使用的是time_t型別,時間用一個整數表示(long int)
#include <time.h>
char *asctime(const struct tm *tm);
char *asctime_r(const struct tm *tm, char *buf);
char *ctime(const time_t *timep);
char *ctime_r(const time_t *timep, char *buf);
ctime將表示時間的整數值轉化為字串型別
#include<time.h>
#include<stdio.h>
#include<unistd.h>
#include<utmp.h>
#include<fcntl.h>
#include<stdlib.h>
void showtime(long timeval)
{
char* cp;
cp = ctime(&timeval);
printf("%12.12s",cp);
}
void show_info(struct utmp *utbufp)
{
if(utbufp->ut_type!=USER_PROCESS)return;
printf("%-8.8s",utbufp->ut_name);
printf(" ");
printf("%-8.8s",utbufp->ut_line);
printf(" ");
showtime(utbufp->ut_tv.tv_sec);
if(utbufp->ut_host[0]!='\0')
{
printf("(%s)",utbufp->ut_host);
}
printf("\n");
}
int main()
{
struct utmp utbuf;
int fd;
if((fd = open(UTMP_FILE,O_RDONLY))==-1){
perror(UTMP_FILE);
exit(1);
}
while(1)
{
if(read(fd,&utbuf,sizeof(utbuf))!=sizeof(utbuf))
{
break;
}
else{
show_info(&utbuf);
}
}
close(fd);
return 0;
}
cp命令
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
int creat(const char *pathname, mode_t mode);
通過讀寫來複制檔案
//create告知核心建立一個名為pathname的檔案,如果不存在則建立,存在則將內容情況,檔案長度設為0
//檔案許可位被設定為第二個引數指定的值
write函式返回寫入檔案的位元組數
從原始檔中讀取資料存入緩衝,將緩衝中的資料寫入目標檔案
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
#define BUF 4096
#define MODE 0644
void print_errors(char *s1,char* s2)
{
fprintf(stderr,"error:%s",s1);
perror(s2);
exit(1);
}
int main(int argc,char *argv[])
{
int fd,out_fd,chars;
char buf[BUF];
if(argc!=3)
{
fprintf(stderr,"usage:%s source destination\n",argv[0]);
exit(1);
}
if((fd=open(argv[1],O_RDONLY))==-1)
{
print_errors("can't open ",argv[1]);
}
if((out_fd=creat(argv[2],MODE))==-1)
{
print_errors("can't creat ",argv[2]);
}
while((chars=read(fd,buf,BUF))>0)
{
if(write(out_fd,buf,chars)!=chars)
{
print_errors("write error to ",argv[2]);
}
}
if(chars==-1)
{
print_errors("read error from ",argv[1]);
}
if(close(fd)==-1||close(out_fd)==-1)
{
print_errors("error closing files","");
}
return 0;
}
使用快取提高檔案I/O效率
防止頻繁的系統呼叫
磁碟只能被核心訪問,系統呼叫時,要執行核心程式碼,系統呼叫結束時,CPU要切換回使用者模式,環境的切換花費很多時間
運用緩衝,對於who1.c:一次讀取多個記錄寫入緩衝中
當緩衝中所有記錄都被取走,才再次呼叫核心服務重新讀取資料
#include<stdio.h>
#include<fcntl.h>
#include<sys/types.h>
#include<utmp.h>
#include<fcntl.h>
#include<time.h>
#include<stdlib.h>
#define NRECS 16
#define UTSIZE (sizeof(struct utmp))
static char utmpbuf[NRECS*UTSIZE];
static int num_recs;
static int cur_rec;
static int fd = -1;
void showtime(long timeval)
{
char* cp;
cp = ctime(&timeval);
printf("%12.12s",cp);
}
void show_info(struct utmp *utbufp)
{
if(utbufp->ut_type!=USER_PROCESS)return;
printf("%-8.8s",utbufp->ut_name);
printf(" ");
printf("%-8.8s",utbufp->ut_line);
printf(" ");
showtime(utbufp->ut_tv.tv_sec);
if(utbufp->ut_host[0]!='\0')
{
printf("(%s)",utbufp->ut_host);
}
printf("\n");
}
int utmp_open(char * filename)
{
fd = open(filename,O_RDONLY);
cur_rec = num_recs = 0;
return fd;
}
struct utmp* utmp_next()
{
struct utmp* recp;
if(fd==-1)
{
return NULL;
}
if((cur_rec==num_recs)&&utmp_load()==0)
{
return NULL;
}
recp = (struct utmp*)&utmpbuf[cur_rec*UTSIZE];
cur_rec++;
return recp;
}
//一次讀num_recs個,cur_rec用在從快取中取資料的時候來計數
int utmp_load()
{
int amt_read;
amt_read = read(fd,utmpbuf,NRECS*UTSIZE);
num_recs = amt_read/UTSIZE;
cur_rec = 0;
return num_recs;
}
void utmp_close()
{
if(fd!=-1)
{
close(fd);
}
}
int main()
{
struct utmp* utbufp;
struct utmp* ut = utmp_next();
if(utmp_open(UTMP_FILE)==-1)
{
perror(UTMP_FILE);
exit(1);
}
while((utbufp = utmp_next())!=NULL)
{
show_info(utbufp);
}
utmp_close();
return 0;
}
核心緩衝技術
相比核心和使用者模式之間的切換,I/O更花時間
核心會將磁碟上的資料複製到核心緩衝區中,一個使用者空間中的程序要從磁碟讀取資料時,不直接讀磁碟
而是將核心緩衝區中的資料複製到程序的緩衝區中
檔案讀寫
登出過程的工作
開啟utmp->找到所在終端的登入記錄->修改記錄->關閉檔案
找到:while迴圈,每次讀入一條記錄,作比較
修改記錄:將ut_type改為DEAD_PROCESS,tv改為登出時間
使用系統呼叫lseek,(找到當前檔案位置的指標)
系統每次開啟一個檔案,都會儲存一個指向檔案當前位置的指標,當讀寫操作完成時,指標會移到下一個記錄位置
這個指標與檔案描述符相關聯。在這種情況下,指標是指向下一條登入記錄的頭一個位元組
lseek(fd,10*sizeof(struct utmp),SEEK_SET);//將指標指向第11個記錄的開始位置
lseek(fd,0,SEEK_END);//指向末尾
write(fd,"hello",strlen("hello"));//將一個字串寫入檔案末尾
lseek(fd,0,SEEKK_CUR);//返回指標指向的當前位置
//注意偏移量可以為負數
lseek(fd,-(sizeof(struct utmp)),SEEK_CUR);
//終端登出程式碼:
#include<stdio.h>
#include<fcntl.h>
#include<sys/types.h>
#include<utmp.h>
#include<fcntl.h>
#include<time.h>
#include<stdlib.h>
#include<string.h>
int logout_tty(char *line)
{
int fd;
struct utmp rec;
int len = sizeof(struct utmp);
int retval = -1;
if((fd=open(UTMP_FILE,O_RDWR))==-1)
return -1;
while(read(fd,&rec,len)==len)
{
if(strncmp(rec.ut_line,line,sizeof(rec.ut_line))==0)
{
rec.ut_type = DEAD_PROCESS;
// if(time(&rec.ut_tv.tv_sec)!=-1)
// {
//回到此記錄的開頭
if(lseek(fd,-len,SEEK_CUR)!=-1)
{
//更新
if(write(fd,&rec,len)==len)
{
retval=0;
}
}
// }
break;
}
}
if(close(fd)==-1)
retval = -1;
return retval;
}
int main(int argc,char *argv[])
{
logout_tty(argv[1]);
printf("%s\n",argv[1]);
return 0;
}
系統呼叫中的錯誤處理
errno
用於確定發生什麼種類的錯誤
#include<errno.h>
perror
用於顯示錯誤資訊
#include<stdio.h>
#include<fcntl.h>
#include<sys/types.h>
#include<utmp.h>
#include<fcntl.h>
#include<time.h>
#include<stdlib.h>
#include<string.h>
int main()
{
int fd;
fd = open("file",O_RDONLY);
if(fd==-1)
{
perror("cannot open file");
}
return 0;
}