1. 程式人生 > 其它 >xunwei筆記3---linux系統程式設計

xunwei筆記3---linux系統程式設計

LINUX系統程式設計:
===============================
生成LINUX最小系統:
1)使用BUSY BOX-1.21.1軟體生成最小系統,需要解壓,設定,編譯BUSY BOX軟體。
(make menuconfig命令,開啟設定介面;make命令,編譯;make install命令,安裝,即生成了最小系統檔案)
網址:busybox.net
2)BUSY BOX設定和編譯選項如下:
– 進入介面“Busybox Settings”→“Build Optiions”→“Cross Compiler prefix”將其配置為“arm-none-linux-gnueabi-”
– 返回到“Build Optiions”
• 配置二進位制檔案安裝目錄
– 進入介面“Installation Options”→“BusyBox installation prefix”將其配置為“../system”(該目錄即生成最小檔案系統的儲存目錄)
– 儲存退出
3)作者舉例的最小系統目錄為/home/minilinux/system,新增其它檔案到指定目錄。(其它檔案由迅為提供,該目錄中的最小系統此後可以NFS網路檔案方式載入)
4)進入/home/minilinux目錄,使用make_ext4fs軟體打包最小系統檔案,命令:make_ext4fs -s -l 314572800 -a root -L linux system.img system(make_ext4fs軟體壓縮包需要放到/目錄下(即根目錄),並解壓)
5)使用usb-OTG連線開發板,使用fastboot工具在cmd視窗燒寫到開發板。(要先在串列埠介面格式化)
6)只需燒寫三個檔案:zImage,ramdisk-uboot.img,system.img(對此開發板,uboot檔案可以不燒寫)
==================================
執行HELLO WORLD程式:
最小LINUX系統,沒有圖形介面;
在ubuntu下編譯.c檔案,變為linux可執行檔案。根目錄下編譯環境變數檔案:vim -bashrc
編譯命令:arm -none-linux-gnueabi -gcc -o 目標檔名 helloworld.c -static
LINUX系統識別優盤,需要掛載,使用掛載命令。
掛載命令:mount /dev/sda1 /mnt/udisk(此後該目錄與U盤目錄等價操作)
在該目錄下輸入程式檔名,即執行應用程式。
===================================
掛載TF卡:
與U盤類似,命令:mount /dev/mmcblk1p1 /mnt/udisk1
linux識別TF卡時顯示:mmcblk1:p1
須事先建立udisk1目錄。
注意:udisk1目錄不能已經掛載其它裝置,否則要解除掛載,或關機重啟。
==================================
如果沒有U盤或TF卡:
可以把編譯完的hello_world目標程式放到linux最小系統的bin目錄下,
然後使用make_ext4fs軟體命令打包system.img,然後只燒寫該檔案到開發板即可。
如果沒有許可權執行,使用chmod命令修改程式許可權即可。
==================================
在ubuntu下建立和刪除使用者命令:
– 建立使用者:useradd xunwei
– 檢視使用者:cat /etc/passwd
– 刪除使用者:userdel xunwei
– 提醒:最好不要刪除系統自帶的使用者;最小系統下沒有這些命令;

linux使用者的組織
– 檢視所有帳戶資訊命令:more /etc/passwd


使用者組的操作
– 新增組:groupadd mygroup
– 檢視組:cat /etc/group
– 刪除組:groupdel mygroup
– 使用者組的組織:一個使用者可以屬於不同的組。
linux中只有唯一的組有權訪問系統資源。
=================================
檔案許可權修改:
許可權排列順序:本使用者許可權,組許可權,其它使用者許可權
許可權數字從左至右:讀r,寫w,執行x;讀,寫,執行;讀,寫,執行;共9位;(採用8進位制,即0--7)
許可權在二進位制下:0無權,1有權;
chmod 777 filename
==========================
cd /(進入根目錄)
pwd(顯示當前路徑)
cd hellowold/(相對路徑,前面沒有/,最後有/代表目錄,進入當前目錄的helloworld目錄下)
cd ../(進入上級目錄)
clear(清除螢幕列印的資訊)
==========================
perror()函式與fprintf()函式:由自己呼叫,協助除錯;
==========================
開啟檔案:
int open(const char *path, int oflags);
• int open(const char *path, int oflags,mode_tmode);
– 引數path表示:路徑及檔名。(絕對路徑)
– 引數oflags表示:開啟檔案所採取的方式
O_RDONLY檔案只讀;O_WRONLY檔案只寫;O_RDWR檔案可讀可寫;
O_APPEND每次寫操作都寫入檔案的末尾
O_NOCTTY如果路徑名指向一個終端裝置,不要把這個裝置用作控制終端(控制終端應該就是命令輸入視窗終端)
O_NDELAY非阻塞(非等待)方式操作檔案
O_NONBLOCK如果路徑名指向FIFO/塊檔案/字元檔案,則把檔案的開啟和後繼I/O設定為非阻塞模式
O_EXCL如果要建立的檔案已存在,則返回-1,並且修改errno的值
O_TRUNC如果檔案存在,並且以只寫/讀寫方式開啟,則清空檔案全部內容(即將其長度截短為0)
– mode_tmode表示:設定建立檔案的許可權。可以直接用數字,如:0777(八進位制)。
– 返回值:出錯返回-1;否則返回檔案控制代碼(檔案控制代碼,就是對應的檔案控制塊(fcb)結構體中的一個char型描述符,用於區分檔案。呼叫open函式,會自動生成相應的檔案控制塊,最後將這個char型描述符的值返回成int型,是個正整數)
程式設計舉例:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

main(){
int fd;
char *leds = "/dev/leds";
char *test1 = "/bin/test1";
char *test2 = "/bin/test2";

if((fd = open(leds,O_RDWR|O_NOCTTY|O_NDELAY))<0){
printf("open %s failed!\n",leds);
}
printf("\n%s fd is %d\n",leds,fd);

if((fd = open(test1,O_RDWR,0777))<0){
printf("open %s failed!\n",test1);
}
printf("%s fd is %d\n",test1,fd);

if((fd = open(test2,O_RDWR|O_CREAT,0777))<0){
printf("open %s failed!\n",test2);
}
printf("%s fd is %d\n",test2,fd);
}
=========================================
寫檔案write函式:
ssize_t write(int fd, const void *buf, count);
- ssize_t是自定義型別,可能是資料長度;
- const void *:指向任意型別的常量物件的指標;
– 引數fd表示:使用open 函式開啟檔案之後返回的控制代碼。
– 引數*buf表示:寫入的資料
– 引數count表示:最多寫入位元組數
– 返回值:出錯-1,;其它數值表示實際寫入的位元組數
舉例:
//標準輸入輸出標頭檔案
#include <stdio.h>

//檔案操作函式標頭檔案
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

main()
{
int fd;
char *testwrite = "/bin/testwrite";
ssize_t length_w;
char buffer_write[] = "Hello Write Function!";

if((fd = open(testwrite, O_RDWR|O_CREAT,0777))<0){
printf("open %s failed\n",testwrite); //注意:testwrite是個地址,輸出字串%s,與輸出字元%c不一樣;
}

//將buffer寫入fd檔案
length_w = write(fd,buffer_write,strlen(buffer_write));
if(length_w == -1)
{
perror("write");
}
else{
printf("Write Function OK!\n");
}
close(fd);
}

===================================
帶形參的main函式:
int main(int argc,char **argv)
– 引數argc:argument count,表示命令字串的個數(除程式名及路徑之外,至少輸入1個字串,以空格分開)
– 引數**argv:等價於*argv[],argument vector,儲存輸入字串的陣列
parameter=形參(formal parameter), argument=實參(actual parameter)。
例如:
#include<stdio.h>
#include<string.h>

int main(int argc,char *argv[])
{
int i,j;
i = atoi(argv[1]);//強制型別轉換,字元轉為int型
j = atoi(argv[2]);

printf("the Program name is %s\n",argv[0]);//argv[0]儲存的是程式名及路徑字串

printf("The command line has %d argument:\n",argc-1);//輸出字串個數,不算程式名字串

printf("%d,%d\n",i,j);//列印程式名後面2個字串的Int值,實際argv[3]還有一個null值。

return 0;
}
==================================
ioctl函式:
int main(int argc,char *argv[])
{
int fd;
char *leds = "/dev/leds";//驅動程式路徑

//使用ioctl函式將引數傳入核心
if((fd = open(leds, O_RDWR|O_NOCTTY|O_NDELAY))<0)
printf("open %s failed\n",leds);
else{
ioctl(fd,atoi(argv[1]),atoi(argv[2]));
printf("ioctl %s success\n",leds);
}
close(fd);

return(1);
}
=====================================
讀檔案read函式:
ssize_t read(int fd,void *buf,len);
– 引數fd:使用open 函式開啟檔案之後返回的控制代碼
– 引數*buf:讀出的任意型別資料儲存的位置
– 引數len:每次最多讀len個位元組
– 返回值:錯誤返回-1,執行成功返回實際讀取資料量(ssize_t型別)
舉例:
int main(void){
int fd;
char *adc = "/dev/adc";
char buffer[512];//字元型別陣列
int len=0, r=0;

memset(buffer,0,sizeof(buffer));//將陣列清零

if((fd = open(adc, O_RDWR|O_NOCTTY|O_NDELAY))<0)
printf("open adc err!\n");
else{
printf("open adc success!\n");

len=read(fd,buffer,10); //每次最多讀10個位元組,放到buffer裡,AD的資料為0x000---0xfff

if(len == 0)
printf("return null\n");
else{
r = atoi(buffer);//將buffer中儲存的字元型別的AD資料轉為整型
r = (int)(r*10000/4095); //Datas transition to Res
printf("res value is %d\n",r);//%d即double方式
}
}
}
=====================================
串列埠程式設計:
串列埠驅動是linux自帶的,只需呼叫即可。
開機啟動程式設定:開啟etc/init.d/rcs檔案,將“路徑/程式名”加入即可。
列印串列埠輸出的開機資訊:控制---日誌設定---standard---儲存位置
串列埠初始化舉例:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>

int set_opt(int,int,int,char,int);
//"/dev/ttySAC3"是con2,靠近耳機介面的串列埠
void main()
{
int fd,nByte;
char *uart3 = "/dev/ttySAC3";//驅動路徑
char buffer[512];
char *uart_out = "please input\r\n";
memset(buffer, 0, sizeof(buffer));
if((fd = open(uart3, O_RDWR|O_NOCTTY))<0)//開啟串列埠
printf("open %s is failed",uart3);
else{
set_opt(fd, 115200, 8, 'N', 1);//初始化串列埠,呼叫迅為打包的函式,內部配置了結構體termios引數。
write(fd,uart_out, strlen(uart_out));//串列埠傳送一次
while(1){
while((nByte = read(fd, buffer, 512))>0){//串列埠接收一次
buffer[nByte+1] = '\0'; //結尾插入字串結束符
write(fd,buffer,strlen(buffer));//串列埠傳送一次,將收到的發回去
memset(buffer, 0, strlen(buffer));//傳送完畢清空快取
nByte = 0;
}
}
}
}
=====================================
TFTP伺服器搭建:(簡單檔案傳輸)
將ubuntu作為伺服器,用開發板通過交換機連線到ubuntu,並下載檔案,比掛載U盤方便;
無法列出檔名和目錄,也不能認證登入,只是直接下載。
1)在ubuntu安裝軟體xinetd,命令:sudo apt-get install xinetd;(ubuntu應連線網際網路:ping baidu.com,否則apt-get命令無法使用。root使用者無需輸入sudo)
2)在ubuntu安裝軟體tftp 和tftpd,命令:sudo apt-get install tftp tftpd;
3)在ubuntu編輯和配置/etc/xinetd.d/tftp檔案;(內含伺服器的檔案儲存目錄,應先建立這個目錄,並修改許可權為777)
4)在ubuntu重啟xinetd服務,命令:sudo /etc/init.d/xinetd restart;
5)在ubuntu中自測試搭建是否成功:新開視窗,輸入命令tftp 127.0.0.1,進入伺服器,輸入get test獲取檔案(事先在伺服器目錄準備好test檔案),輸入q退出。
6)將開發板設定到與ubuntu同一網段;(修改/etc/eth0-setting檔案,注意路由器的網址範圍限制,檢視ubuntu的IP地址命令:ifconfig)
7)下載檔案命令:tftp -g -l test -r test +IP地址(ubuntu伺服器地址,如:192.168.0.225)(前面test是伺服器內檔名,後面test是自定義檔名)
======================================
NFS伺服器搭建:(用於啟動最小Linux系統,不適合啟動安卓,太大太慢)
功能:不同的作業系統彼此共享檔案,可以通過NFS 掛載遠端主機的目錄,訪問該目錄就像訪問本地目錄一樣;可以實現線上啟動檔案系統或除錯應用程式;
1)在ubuntu系統安裝NFS服務軟體,命令:apt-get install nfs-kernel-server;
2)修改/etc/exports檔案,最後一行新增:/home/minilinux/ *(rw,sync,no_root_squash);
其中,/home/minilinux是共享目錄,*代表允許所有的網路段訪問,rw是可讀寫訪問許可權,sync是資料同步寫入記憶體和硬碟,no_root_squash是NFS客戶端使用者的許可權,如果客戶端使用的是root使用者,那麼對於該共享目錄而言,該客戶端就具有root許可權;
3)重啟portmap服務,命令:/etc/init.d/portmap restart
4)重啟nfs伺服器,命令:/etc/init.d/nfs-kernel-server restart
在ubuntu視窗下測試搭建是否成功:
1)命令:mount -t nfs localhost:/home/minilinux/system /mnt(最小檔案系統在/home/minilinux目錄下,根據實際情況設定)
2)命令:df(檢視是否掛載成功)
=====================================
核心修改:(以適應NFS伺服器訪問,開發板上只有一個核心執行,修改後方可從NFS啟動最小LINUX系統)
1)進入核心解壓目錄,輸入命令:cp -r config_for_linux_scp_elite .config(複製原檔案為QTE的config檔案,用於配置,-r可以不加)
2)進入核心設定介面,命令:make menuconfig,設定如下幾項:
進入“Networking support”→“Networking options”→選上“IP: kernel level autoconfiguration;
進入“File systems”→“Network File Systems”選中“NFS client support”,“NFS client support for NFS version 3”,
“NFS client support for the NFSv3 ACL protocol extension”,“NFS client support for NFS version4”,
“NFS client support for NFSv4.1”,“Root file system on NFS”
進入“Boot options”→“Default kernel command”修改路徑:root=/dev/nfs rw nfsroot=192.168.1.103:/home/minilinux/system
ip=192.168.1.230:192.168.1.103:192.168.1.1:255.255.255.0:iTOP:eth0:off rootfstype=ext4 init=/linuxrc console=ttySAC2,115200
(這個路徑和IP等是在一行內,沒有回車;192.168.1.103:/home/minilinux/system 表示掛載的nfs伺服器ip 是192.168.1.103,掛載的目錄是/home/minilinux/system;192.168.1.230 是我們開發板的ip地址,第二個192.168.1.103 是nfs伺服器的ip,第三個ip192.168.1.1是開發板的路由器閘道器,255.255.255.0 是子網掩碼,開發板與nfs伺服器在同一網段!iTOP 是開發主機的名字(一般無關緊要,可以隨便填寫),eth0是網絡卡裝置的名稱)
3)編譯核心,下載到開發板;(此後,從伺服器啟動最小linux檔案系統,該系統檔案需事先放在/home/minilinux/system目錄下,作者沒講這部分內容)
=====================================
使用NFS功能測試程式:
將編譯好的程式放到NFS伺服器的共享目錄後,開發板也會同步擁有該程式。例如:
伺服器/home/minilinux/system/bin目錄與開發板/bin目錄同步。
NFS伺服器的/home/minilinux/system目錄,對應開發板的根目錄。
=====================================
沒有交換機或路由器,網路直連情況下的tftp和nfs配置:
1)開發板與電腦網線直連,將開發板、電腦、ubuntu的IP地址設為同一網段,虛擬機器軟體設為橋接模式;
2)配置ubuntu前,開發板應與電腦事先連線好,否則可能顯示無連線。然後使用Ping命令測試是否互通;
3)將前述搭建方式的IP地址做相應修改;
4)對於NFS功能,則需要重新配置核心,將“Boot options”→“Default kernel command”中的ip地址做相應修改,並編譯下載核心;
====================================
NFS共享目錄:(參考前述伺服器搭建,最小LINUX系統已經在開發板上)
1)修改ubuntu配置檔案/etc/exports,最後一行新增共享目錄路徑:/home/topeet/linux/ *(rw,sync,no_root_squash)
2)如果Ubuntu上沒有上述共享目錄,則需要新建/home/topeet/linux目錄;
3)修改核心選項:
進入Networking support
– 選中Networking options然後進入
• 選中IP: kernel level autoconfiguration

進入File systems
– 選中Network File Systems然後選中以下選項
• “NFS client support”,“NFS client support for NFS version 3”,“NFS client support for the NFSv3 ACL protocol
extension”,“NFS client support for NFS version 4”,“NFS client support for NFSv4.1”,“Root file system on
NFS”一共六個選項
4)核心的網絡卡驅動可能有問題,會列印無用資訊。註釋掉網絡卡除錯資訊drivers/net/usb/dm9620.c,關鍵詞LEN_PLOAD,註釋掉三行列印資訊。
5)編譯核心,下載到開發板。
6)在開發板新建目錄/mnt/nfs;
7)掛載NFS目錄,命令:mount -t nfs -o nolock 192.168.3.84:/home/topeet/linux /mnt/nfs(IP地址根據實際情況修改)
=======================================
延時函式:(標頭檔案:#include<unistd.h>)
linux系統程式設計中常用的延時函式:sleep、usleep等
linux核心中的常用的延時函式:ndelay、udelay、mdelay等
unsigned int sleep(unsigned int seconds);
例如:sleep(1),即延時1秒。
返回值:如果延時成功則返回0,如果延時過程中被打斷,則返回剩餘的秒數。
int usleep(useconds_t usec);
usec需要小於1000000
例如:usleep(10),表示延時10微秒。
延時成功則返回0,失敗則返回-1
======================================
GMT時間:格林尼治標準時間,即世界時區基準時間。已經不再被作為標準時間使用,而是使用下面的UTC時間。
UTC時間:世界統一時間,與GMT規則等同,但更精確,採用原子秒長計時。
UNIX紀元時間:從1970年1月1日0時算起,到現在的秒數。
機器日曆時間:對Linux來說,與UNIX紀元時間等同。
======================================
時間函式:(標頭檔案:#include<time.h>)
獲取機器時間函式
time_t time(time_t *t);
引數*t:以秒為單位的機器時間
返回值:如果引數為NULL,則返回機器時間;錯誤返回-1;
time_t型別實際是一個long int型別
c語言語法:0x%08x:先顯示0x,隨後是8位寬的16進位制數;
======================================
時間轉換函式:(標頭檔案:#include<time.h>)
將時間轉化為字串格式:char *ctime(const time_t *timep);
將時間轉化為格林威治時間:struct tm *gmtime(const time_t *timep);
時間轉換為字元格式:char *asctime(const struct tm *tm);
時間轉化為本地時間:struct tm *localtime(const time_t *clock);
tm結構體:包含tm_sec;tm_min;tm_hour;int tm_mday等成員變數及函式ctime;
舉例:
#include <stdio.h>
#include <time.h>
int main(void){
time_t timep;
struct tm *tblock;

time(&timep);
printf("ctime/timep is %s\n",ctime(&timep));
printf("asctime is %s\n",asctime(gmtime(&timep)));

tblock = localtime(&timep);
printf("localtime is :%s\n",asctime(tblock));
printf("localtime is:%s\n",ctime(&timep));
return 0;
}
=========================================
高精度的設定時間函式和讀取時間函式:(標頭檔案:#include<sys/time.h>)
int gettimeofday(struct timeval *tv, struct timezone *tz);
int settimeofday(const struct timeval *tv, const struct timezone
*tz);
– 引數tv:用於儲存獲取的時間
– 引數tz:可以預設,傳入NULL
– 上面的函式比time要高6個數量級,可以達到微妙,這個精度就可以粗略的計算程式碼執行時間了;
timeval結構體:包含tv_sec(秒),tv_usec(微秒)成員變數。(tv是timeval縮寫)
舉例:(測試CPU執行速度)
#include<time.h>
#include<sys/time.h>
#include<stdio.h>

void function() //耗時操作
{
unsigned int i,j;
double y;
for(i=0;i<1000;i++)
for(j=0;j<1000;j++)
y=i/(j+1);
}

main()
{
struct timeval tpstart,tpend;
float timeuse;

gettimeofday(&tpstart,NULL); //記錄開始時間
function();
gettimeofday(&tpend,NULL); //記錄結束時間

timeuse = 1000000*(tpend.tv_sec-tpstart.tv_sec)+
tpend.tv_usec-tpstart.tv_usec; //計算差值
timeuse /= 1000000;

printf("Used Time:%f\n",timeuse);
}
=========================================
ubuntu終端下man命令的使用:
man 1:一般命令。常見的linux命令,例如ls,cd,cat等等
man 2:用來放核心提供的系統呼叫或者函式。例如man 2 fork等
man 3:C庫函式。
man 4:特殊檔案,例如裝置和驅動程式
man 5:檔案格式。包括完全使用文字配置檔案定製系統的操作,大量的配置檔案,網路服務列表,可用的shell列表等等
man 6:遊戲和螢幕保護程式。
man 7:雜類檔案。
man 8:系統管理命令,超級使用者可能需要用到它們。
舉例:man 3 sleep
================================
檢視檔案索引號:
命令:ls -i;ls -al;ls -ail;
硬連結:檔名不同,檔案屬性相同,內容也相同的檔案。相當於同步更新的副本。
軟連結:檔名不同,檔案屬性不同,內容相同的檔案。相當於快捷方式。
=================================
檔案函式:(所需的標頭檔案用man命令檢視)
函式int stat(const char *path, struct stat *buf);//直接檢視檔案資訊
– 引數*path:檔案路徑(含檔名)
– 引數*buf:檔案資訊
– 返回值:成功為0,否則為-1
函式int fstat(int fd, struct stat *buf);//先開啟檔案,再檢視檔案資訊
– 引數fd:檔案描述符
– 引數*buf:檔案資訊
– 返回值:成功為0,否則為-1
函式int lstat(const char *path, struct stat *buf);
– 引數*path:檔案路徑
– 引數*buf:返回檔案的資訊,類似於stat.但是當命名的檔案是一個符號連結時,lstat返回該符號連結的有關資訊,而不是
由該符號連結引用檔案的資訊。(也就是返回快捷方式的資訊,而不是原始檔資訊)
– 返回值:成功為0,否則為-1
語法:%ld:長整型
stat結構體:含有與檔案描述相關的成員變數,包括索引號,訪問時間,資料量等等。
------------------------------------------
int chmod(const char *path, mode_t mode);//修改檔案許可權
– 引數*path:檔案路徑。(含檔名)
– 引數mode:直接使用數字即可。和前面命令中chmod 777 xxx 中的777 這
個引數含義類似,也可以使用文件中的組合值。
– 返回值:成功返回0,錯誤返回-1。
int fchmod(int fd, mode_t mode);//先開啟檔案,再修改許可權
– 引數fd:檔案描述符。
– 引數mode:直接使用數字即可。和前面命令中chmod 777 xxx 中的777 這
個引數含義類似,也可以使用文件中的組合值。
– 返回值:成功返回0,錯誤返回-1。
-------------------------------------------
char *getcwd(char *buf, size_t size);//獲取當前檔案的路徑
– 引數*buf:儲存當前檔案路徑的緩衝區
– 引數size:在現代linux 中,buf 的長度至少可以為255 位元組
– 返回值:成功返回指向當前目錄的指標,和buf 的值一樣,錯誤返回NULL
char *getwd(char *buf);//該函式已經過時
char *get_current_dir_name(void);
– 引數:無
– 返回值:成功返回指向當前目錄的指標,錯誤返回NULL
------------------------------------------
int mkdir(const char *pathname,mode_t mode);//建立目錄
– 引數*path:檔案路徑。
– 引數mode:直接使用數字即可。和前面命令中chmod 777 xxx 中的777 這
個引數含義類似,也可以使用文件中的組合值。
– 返回值:成功返回0,錯誤返回-1。
int rmdir(const char *pathname);//刪除目錄
– 引數*pathname:檔案和目錄的路徑
– 返回值:成功返回0,錯誤返回-1
--------------------------------------------
int chdir(const char *path);//跳轉到其它目錄
– 引數*path:目標檔案路徑。
– 返回值:成功返回0,錯誤返回-1。
-------------------------------------------
DIR *opendir(const char *name);//開啟目錄,相當於ls命令
– 引數:目錄的路徑。
– 返回值:成功返回指向目錄流的指標,錯誤返回NULL
int closedir(DIR *dirp);//關閉目錄
– 引數:opendir 返回的dir 指標
– 返回值:成功返回0, 失敗返回-1
struct dirent *readdir(DIR *dirp);//讀目錄資訊函式
– 引數dirp:opendir 函式開啟目錄後返回的檔案指標。
– 返回值:成功返回指向dirp 的指標dirent ,錯誤返回NULL。
=================================
使用ln命令可以建立檔案的硬連結,也有相應函式link。
int link(const char *oldpath, const char *newpath);//硬連結函式
– 引數*oldpath:已有的檔案路徑。
– 引數*newpath:新建的硬連結檔案路徑。
– 返回值:成功返回0,錯誤返回-1。
符號連結也叫軟連結,symlink函式。
int symlink(const char *oldpath, const char *newpath);//軟連結函式
– 引數*oldpath:已有的檔案路徑
– 引數*newpath:新建的符號連結檔案路徑
– 返回值:成功返回0,錯誤返回-1
int unlink(const char *pathname);//解除連結函式
– 引數*pathname:連結檔案的路徑
– 返回值:成功返回0,錯誤返回-1
– 若unlink指向軟連結,則刪除軟連結;若指向最後一個硬連結,則相當於刪除檔案
=================================
檔案複製:通過開啟、讀取、寫入函式實現,沒有複製函式。相當於cp命令。
檔案移動:命令為mv,函式為rename
int rename(const char *oldpath, const char *newpath);//檔案移動
– 引數*oldpath:舊的檔案路徑(含檔名)
– 引數*newpath:新的檔案路徑
– 返回值:成功返回0,錯誤返回-1
===============================
程序檢視命令:top,ps
程序終止命令:kill 程序號
獲取子程序ID的函式:pid_t getpid(void)
– 引數:無
– 返回值:成功返回程序號
獲取父程序ID的函式:pid_t getppid(void);
– 引數:無
– 返回值:成功返回父程序號
在當前程式中,可以使用exec函式族執行其它程式:
exec函式族引數:
– “l”和“v”表示引數是以列表還是以陣列的方式提供的
– “p”表示這個函式的第一個引數是*path,就是以絕對路徑來提供程式的路徑,也可以以當前目錄作為目標
– “e”表示為程式提供新的環境變數
舉例:
int main(void)
{
if(execl("/mnt/udisk/helloexec","helloexec","execl",NULL) == -1){//執行的helloexec程式,需事先準備好
perror("execl error");//程式已經跳轉走,如果正常execl不返回錯誤,這條程式碼不會執行!
exit(1);
}
//程式已經跳轉走,如果正常execl不返回錯誤,下面的程式碼不會執行!
printf("execl error!\n");
return 0;
}
在當前程式中,可以使用fork函式建立和當前程式一模一樣的程序,叫子程序,當前的程式叫父程序。
建立程序函式:pid_t fork(void);
– 引數:無
– 返回值:執行成功,返回子程序pid給父程序,返回0給子程序;出現錯誤,返回-1給父程序。執行失敗的唯一情況是記憶體不夠或者id號用盡,(超過32768)
不過這種情況幾乎很少發生。
fork函式會建立一個新程式,與當前程式一模一樣,只是新程式的fork返回值為0,當前程式的fork返回值是新程式的pid。除非建立失敗。
if(fork() == 0):只會在子程序中被執行,因為子程序的fork返回值是0.
exit函式會導致程序執行後退出,即pid消失。
其它exec函式:execv,execlp,execvp,execle,execve;
=====================================
無名管道:
int pipe(int pipefd[2])
– 引數pipefd[0]:用於讀管道
– 引數pipefd[1]:用於寫管道
– 返回值:執行成功返回0,失敗返回-1
注意:只能是具有親緣關係的程序間可以使用無名管道。
實際是建立了兩個檔案,一個只寫,另一個只讀。函式形參就是檔案指標。
=====================================
有名管道:
int mkfifo(const char *pathname, mode_t mode)
– 引數*pathname:路徑及管道名稱,若在當前目錄只寫名稱即可
– 引數mode:管道的許可權,如:0777
– 返回值:成功返回0,錯誤返回-1
能實現沒有親緣關係的程序間通訊,資料是先進先出,一個程序寫,另一個程序讀。
getchar();//掃描輸入的字元,其返回值是ASCII碼,可存於int變數中,再用putchar()將int變數輸出,即將ASCII碼變回字元。
putchar();//將字元輸出或列印到標準輸出裝置上。
putc();//將ASCII碼所代表的字元寫到檔案中。
======================================
訊息佇列:
訊息佇列就是具有特定的格式以及特定優先順序的記錄的連結串列。
訊息接收:
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg)
– 引數msqid:訊息佇列的標識碼
– 引數*msgp:指向訊息緩衝區的指標,此位置用來暫時儲存接收的訊息,是一個使用者可定義的通用結構(見下面訊息結構體)
– 引數msgsz:訊息的長短
– 引數msgtyp: msgtyp等於0,則返回佇列的最早的一個訊息
msgtyp大於0,則返回第一個型別為msgtyp的訊息
msgtyp小於0,則返回第一個型別小於或等於msgtyp的絕對值的訊息
– 引數msgflg:在佇列沒有資料的情況下所應採取的行動,為0表示忽略。
– 返回值:成功返回資料長度,錯誤返回-1
訊息傳送:
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)
– 引數msqid:訊息佇列的標識碼
– 引數*msgp:指向訊息緩衝區的指標,此位置用來暫時儲存傳送的訊息,是一個使用者可定義的通用結構
– 引數msgsz:訊息的長短
– 引數msgflg:標誌位
– 返回值:成功返回0,錯誤返回-1
訊息結構體:
結構體msgp,是一個標準的通用結構
– struct msgstru{
long mtype; //必須大於0,訊息型別
char mtext[nbyte];}
訊息建立,獲取訊息佇列標識碼:(接收端和傳送端都要呼叫該函式,通過key聯絡在一起)
int msgget(key_t key, int msgflg)
– 引數“key”:訊息佇列關聯的識別符號,自定義數字
– 引數“msgflg”:訊息佇列的建立標誌和存取許可權。
IPC_CREAT:如果核心中沒有此佇列則建立它;
IPC_EXCL:當和IPC_CREAT一起使用時,如果佇列已經存在,則失敗
– 返回值:執行成功則返回接收和傳送的訊息佇列的標識碼,否則返回-1

自定義的訊息緩衝區結構體:(用於儲存待發資料和待收資料,傳送程式和接收程式都要有自己的緩衝區例化體)
struct msg_st
{
long int msg_type;
char text[BUFSIZ];
};

刪除訊息佇列:(接收完畢後,由接收端刪除)
msgctl(msgid, IPC_RMID, 0)
-引數1:訊息佇列的標識碼;
-成功返回0,失敗返回-1;
==================================
訊號:(類似於軟體中斷)
unsigned int alarm(unsigned int seconds)
– 引數seconds:鬧鐘的時間,單位為秒
– 返回值:成功返回0 或者返回剩餘時間;錯誤返回-1
sighandler_t signal(int signum, sighandler_t handler);
– 引數signum:等待的訊號,例如:SIGALRM(鬧鐘)
– 引數handler:訊號到來之後,觸發的處理方式,可以是個函式
– 返回值:成功返回0,錯誤返回-1
常見訊號:
– SIGALRM:鬧鐘
– SIGHUP:終端發出的結束訊號
– SIGINT:鍵盤的ctrl+c
– SIGKILL:kill命令產生的訊號
– SIGSTOP:鍵盤的ctrl+z
函式pause():–用於程序掛起直到捕捉到訊號
舉例:
signal(SIGALRM, handler);//handler是中斷函式名
alarm(5);//鬧鐘5秒
訊號集:
訊號集變數型別:sigset_t sigset;
結構體型別:struct sigaction act;
函式:
sigemptyset(&sigset); //清空訊號集
sigaddset(&sigset, SIGINT); //增加訊號SIGINT到訊號集sigset,SIGINT可能代表中斷
act.sa_handler = handler; //指定中斷函式
act.sa_flags = 0; //指定標誌位
sigaction(SIGINT, &act, 0); //在SIGINT訊號下,設定act的訊號處理方式
sigprocmask(SIG_SETMASK, &sigset, 0);//訊號遮蔽字設定為sigset
sigpending(&ign);//將被阻塞待處理的訊號寫入ign
sigismember(&ign, SIGINT);//測試SIGINT訊號是否已入訊號集
sigdelset(&sigset, SIGINT); //刪除訊號集
sigsuspend(&sigset); //將遮蔽字替換為sigset訊號集,然後掛起程序
參考資料:https://www.cnblogs.com/52php/p/5815125.html
==================================
訊號量:(協調程序之間對公共資源的訪問,例如對stdout輸出緩衝區資源)
建立訊號量:int semget(key_t key,int nsems,int semflg)
- 引數key:不相關的程序可以通過它訪問一個訊號量,是自定義的一個值,為0時是該程序私有訊號量。
- 引數nsems:指定需要的訊號量數目,實際使用通常是1
- 引數semflg:是一組標誌,許可權+建立(如果不存在則建立,存在則開啟)
- 返回值:訊號量識別符號,其他的訊號量函式使用該返回值進行操作
例如:sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT);
訊號量建立後,會產生一個結構體:struct sembuf,其它函式通過識別符號操作這個結構體。
結構體如下:
struct sembuf{short sem_num;short sem_op;short sem_flg;}
- sem_num:訊號量在訊號量集中的序號,從0開始
- sem_op:在訊號量初始值基礎上相加的資料,-1即P等待,+1即V傳送訊號
- sem_flg:訊號量標誌,通常為SEM_UNDO,這樣,程序終止時,作業系統會自動撤消訊號量。
操作訊號量:int semop(int semid,struct sembuf *sops,size_t nsops)
- semid:訊號量集的識別碼
- sops:指向訊號量操作結構體的陣列首地址的指標
- nsops:訊號量操作結構體的數量,恆大於或等於1
- 返回值:呼叫成功返回0,失敗返回-1。
例如:semop(sem_id, &sem_b, 1);//其中的sem_b是自己事先宣告的結構體,內部變數要先賦值,然後呼叫semop對建立的訊號量操作,也就是對那個建立的結構體操作。
控制訊號量:int semctl(int semid,int semnum,int cmd, union semun arg)
- semid:訊號量集識別符號
- semnum:操作訊號在訊號集中的編號,從0開始
- cmd:指出要操作的具體命令,SETVAL設定訊號量集中的一個訊號量的值,IPC_RMID將訊號量集從記憶體中刪除
- semun arg:核心中使用的聯合體變數,只有當cmd取某些特定的值的時候,才會用到,例如,初始化訊號量。(另外,在cmd命令IPC_STAT/IPC_SET中使用該聯合體,它代表核心中所使用的訊號量資料結構的一個複製 ,涉及成員:buf;在cmd命令GETALL/SETALL中使用該聯合體,代表指向一個整數值陣列的指標,在設定或獲取集合中所有訊號量的值的過程中,將會用到該陣列,涉及成員:array)
使用semctl函式可以初始化訊號量,或刪除訊號量。其cmd命令有多種。事先宣告一個semun型別聯合體,並賦值,然後用semctl函式呼叫這個聯合體,最終控制訊號量的聯合體。
訊號量聯合體需要在main前宣告,如下:
union semun
{
int val; //訊號量初始值
struct semid_ds *buf;
unsigned short *arry;
};
ubuntu命令:./seml a&./seml(執行兩次seml程式,第一次是seml a,第二次是seml。在&之後可以換行輸入)
======================================
共享記憶體:(在各種程序間通訊方式中具有最高的效率,通過訊號量進行協調)
共享記憶體的結構體:
struct shared_use_st { int written; char text[TEXT_SZ];};
- written作為一個標誌,非0:表示可讀,0表示可寫
- text記錄寫入和讀取的文字
共享記憶體建立:
int shmget(key_t key, size_t size, int shmflg)
- key:鍵值,指向該新建的共享記憶體物件,被其他函式和程序引用
- size:新建的記憶體大小
- shmflg:識別符號,許可權+建立
- 返回值:共享記憶體的識別符號,返回-1代表建立失敗
例如:shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT)
共享記憶體地址對映:
void *shmat(int shmid, const void *shmaddr, int shmflg)
- shmid:之前建立的共享記憶體識別符號
- shmaddr:指定共享記憶體出現在程序記憶體地址的什麼位置,直接指定為NULL(或0值)是讓核心自己決定一個合適的地址位置
- 返回值:共享的記憶體地址,失敗返回-1
例如:shmat(shmid, (void*)0, 0);
- shmflg:SHM_RDONLY:為只讀模式,其他為讀寫模式(根據示例推斷:為0代表讀寫模式)
語法:(void*)-1;//將-1轉化為指標地址,這樣書寫便於移植
共享記憶體控制:
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
- shmid:共享記憶體識別符號
- cmd:如果是IPC_RMID,則刪除這片共享記憶體
- buf:共享記憶體的管理結構體指標,該結構體struct shmid_ds定義如下。
- 返回值:失敗返回-1
例如:shmctl(shmid, IPC_RMID, 0);//刪除共享記憶體
struct shmid_ds {
struct ipc_perm shm_perm;
int shm_segsz;
time_t shm_atime;
time_t shm_dtime;
time_t shm_ctime;
unsigned short shm_cpid;
unsigned short shm_lpid;
short shm_nattch;
unsigned short shm_npages;
unsigned long *shm_pages;
struct vm_area_struct *attaches;
};
總體過程:
1)建立共享記憶體,兩個main程式或程序通過shmget函式,並使用相同key鍵值,來實現建立和共享;
2)呼叫shmat函式時,應事先宣告一個指標shm,並將返回值(記憶體地址)賦值給shm。(shm宣告:void *shm = NULL;)
3)然後將該指標轉換為結構體型別指標:shared = (struct shared_use_st*)shm; (shared宣告:struct shared_use_st *shared = NULL;)
4)利用結構體指標shared判斷共享記憶體的讀寫狀態,並進行讀寫。(while(shared->written == 1))
5)操作完畢後,使用shmdt(shm)進行分離,從當前程序的地址對映中分離共享記憶體。(分離失敗,則shmdt返回-1;分離後不可用,不是真正刪除)
6)最後,由最後一次訪問共享記憶體的程序執行刪除操作,shmctl(shmid, IPC_RMID, 0);在此之前訪問的程序只做分離,不做刪除。
=======================================
TCP網路通訊:面向連線的傳輸控制協議(標頭檔案:#include <sys/socket.h>,在上層應用程式中程式設計)
結構體定義:(在netinet/in.h中)
struct sockaddr_in {
short sin_family;//Addressfamily,一般來說AF_INET(地址族)PF_INET(協議族)
unsigned short sin_port;//Portnumber(必須要採用網路資料格式,普通數字可以用htons()函式轉換成網路資料格式的數字
struct in_addr sin_addr;//Internet address
unsigned char sin_zero[8];//Same size as struct sockaddr,沒有實際意義,只是為了跟SOCKADDR結構在記憶體中對齊
};
socket函式:建立套介面,用於根據指定的地址族、資料型別和協議來分配一個套介面的描述符及其所用的資源。
int socket(int domain, int type, int protocol);
- domain:目前僅支援AF_INET格式,也就是ARPA Internet地址格式;
- type:新套介面的型別描述,SOCK_STREAM提供面向連線的穩定資料傳輸,即TCP協議;
- protocol:套介面所用的協議。如不想指定,可用0,表示預設;
- 返回值:建立的套介面描述符,失敗返回-1;
例如:socket(AF_INET, SOCK_STREAM, 0);
bzero函式:將某段地址清零。用於給struct sockaddr_in變數清零。
extern void bzero(void *s, int n);
- s:要置零的資料的起始地址;
- n:要置零的資料位元組個數。
例如:bzero(&s_add,sizeof(struct sockaddr_in))
inet_addr函式:將小數點分隔的十進位制的IP地址轉換成一個長整型數(u_long型別)
in_addr_t inet_addr(const char *cp)
例如:inet_addr(argv[1])//將命令列中的第二個引數(IP地址)變成長整型數
htons函式:將一個無符號短整型數(16位)轉換成網路位元組順序的無符號短整型數。(我理解為hex to net short)
u_short htons( u_short hostshort)
例如:s_add.sin_port=htons(portnum)
connect函式:建立與一個主機埠的連線
int connect (int sockfd,struct sockaddr * serv_addr,int addrlen)
- sockfd:本地待連線的套介面的描述符
- serv_addr:想要連線的主機結構體(sockaddr_in)地址指標
- addrlen:緩衝區的長度(struct sockaddr)
例如:connect(cfd,(struct sockaddr *)(&s_add), sizeof(struct sockaddr));//其中的s_add結構體應事先宣告和賦值
struct sockaddr:尚不清楚其定義
讀取資料,直接用read函式,例如:read(cfd, buffer, 1024)
斷開連線:close(cfd);
--------------------------
bind()繫結函式:通過給一個未命名套介面分配一個本地名字來為套介面建立本地捆綁(主機地址/埠號)
int bind(int sockfd, struct sockaddr *addr, socklen_t addrlen)
返回值:失敗返回-1;
例如:bind(sfp,(struct sockaddr *)(&s_add), sizeof(struct sockaddr));//其中的s_add結構體應事先宣告和賦值
監聽函式:指定相應的套接字變為被動連線套介面,使得該程序可以接受其它程序的請求,從而成為一個伺服器程序。
int listen(SOCKET sockfd, int backlog)
- sockfd:一個已繫結但未被連線的套接字描述符
- backlog:連線請求佇列的最大長度(尚不清楚其含義)
返回值:失敗返回-1;
例如:listen(sfp,5);
接受函式:預設會阻塞程序,直到有一個客戶連線建立後返回,它返回的是一個新可用的套接字,這個套接字是連線套接字(用新套接字來發送資料)
int accept(int sockfd, struct sockaddr * addr, socklen_t* len)
返回值:失敗返回-1;
例如:accept(sfp, (struct sockaddr *)(&c_add), &sin_size);
ntohl函式:將一個無符號長整形數從網路位元組順序轉換為主機位元組順序(我理解為net to hex long)
uint32_t ntohl(uint32_t netlong);
- netlong:一個以網路位元組順序表達的32位數
ntohs()函式:與前述htons函式相反。
uint16_t ntohs(uint16_t netshort)
傳送函式:(使用accept函式返回的新套接字)
ssize_t send (int s,const void *msg,size_t len,int flags)
- s:指定傳送端套接字描述符
- msg:一個存放應用程式要傳送資料的緩衝區
- len:指明實際要傳送的資料的位元組數
- flags:一般置0
例如:send(nfp, buffer, strlen(buffer), 0);
------------------------------
通訊流程:
1)伺服器端:建立socket,清零結構體,繫結,監聽,接受;(socket,bzero,bind,listen,accept函式)
2)客戶端:建立socket,清零結構體,連線;(socket,bzero,connect函式)
3)雙方交替讀寫資料;(read,send函式)
4)關閉套接字;(close函式)
=====================================
UDP網路通訊:(無連線的傳輸層協議,提供面向事物的簡單不可靠資訊傳送服務,資源消耗小,處理速度快的優點,適合音訊,視訊及重新整理類的資料)
與TCP的伺服器不同,UDP的伺服器不需要listen和accept函式,客戶端不需要connect函式;同樣使用struct sockaddr_in結構體;
通訊流程:
1)伺服器端:建立建立socket,清零結構體,繫結;(socket,bzero,bind函式)
2)客戶端:建立socket,清零結構體;(socket,bzero函式)
3)相互通訊;(sendto,recvfrom函式)
4)關閉套接字;(close函式)
整個過程使用同一個套介面識別符號。
傳送函式:
ssize_t sendto(int socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len)
- socket:套介面的描述符
- message:待發送資料的緩衝區首地址
- length buf:緩衝區中資料的長度
- flags:呼叫方式標誌位
- dest_addr:指標,指向目的套介面的地址,內含地址和埠號資訊;
- dest_len:所指地址的長度
- 返回值:成功則返回傳送出去的位元組數,失敗返回-1;
例如:sendto(sockfd, sendline, strlen(sendline), 0, (struct sockaddr *)&servaddr, sizeof(servaddr))
接收函式:
ssize_t recvfrom(int sockfd,void *buf,int len,unsigned int flags, struct sockaddr *from,socket_t *fromlen);
- sockfd:套介面的描述符
- buf:接收資料緩衝區首地址
- len:緩衝區長度
- flags:呼叫操作方式。是一個或者多個標誌的組合體,可通過|操作連在一起
- from:(可選)指標,指向裝有源地址的緩衝區
- fromlen:(可選)指標,指向from緩衝區長度值
- 返回值:返回讀入的位元組數;
例如:recvfrom(sockfd, recvline, 1024, 0, NULL, NULL)
============================================
訪問web伺服器頁面:
1)下載BOA,並在ubuntu下解壓;www.boa.org;
2)進入src目錄,執行.config,生成makefile檔案,vi makefile,修改:
CC = gcc改為:CC = arm-none-linux-gnueabi-gcc -static
CPP = gcc -E改為:CPP = arm-none-linux-gnueabi-gcc -E -static
3)執行make命令,若出錯修改compat.h:
#define TIMEZONE_OFFSET(foo) foo##->tm_gmtoff改為
#define TIMEZONE_OFFSET(foo) foo->tm_gmtoff
4)重新編譯;
5)使用arm-none-linux-gnueabi-strip boa命令為boa檔案瘦身;
6)使用cp命令拷貝編譯最終生成的boa到NFS檔案系統的bin目錄下面;
7)在NFS檔案系統下面建立幾個資料夾,並修改若干配置檔案,在自動執行指令碼檔案中新增自動啟動命令“boa &”;
8)在www目錄下,使用vi index.html命令建立index.html檔案;
9)開發板是掛載NFS網路檔案系統;
10)在pc機器開啟網頁瀏覽器,輸入開發板的IP地址,可以看到在開發板上建立的index.html;
=======================================
通過web伺服器網頁進行控制led:
CGI(CommonGatewayInterface)規範定義了web伺服器如何向擴充套件應用程式傳送訊息,在收到擴充套件應用程式的資訊後又如何進行處理等
1)修改網頁原始碼:通過表單向伺服器提交資訊,在表單裡面指定了伺服器端處理接收資訊的CGI程式;
2)根據boa的配置檔案boa.conf裡面指定的CGI程式儲存目錄,建立cgi程式;(*.c檔案,獲取web客戶端提交過來的資料,然後對資料解析,
最後呼叫led的ioctl函式來點亮或關閉led)
3)編譯*.c檔案,生成*.cgi檔案;(命令:arm-none-linux-gnueabi-gcc -o *.cgi *.c -static)
4)使用chmod 命令修改*.cgi檔案的許可權,變為可執行;(chmod 777 *.cgi)
5)通過PC機的瀏覽器訪問開發板網頁即可;(沒看到控制結果)
CGI函式://該函式用來取得引數envvar環境變數的內容
char *getenv(char *envvar)
例如:getenv("QUERY_STRING")
QUERY_STRING:獲取網頁傳過來的資料
========================================