C語言常見的函數調用
isatty,函數名,主要功能是檢查設備類型,判斷文件描述詞是否為終端機。
函數名: isatty
用 法: int isatty(int desc);
返回值:如果參數desc所代表的文件描述詞為一終端機則返回1,否則返回0。
程序例:
#include <stdio.h>
#include <io.h>
int main(void)
{
int handle;
handle = fileno(stdout);
if (isatty(handle))
printf("Handle %d is a device type\n", handle);
else
printf("Handle %d isn't a device type\n", handle);
re
函數名稱:fileno(在VC++6.0下為_fileno)
函數原型:int _fileno( FILE *stream );
函數功能:fileno()用來取得參數stream指定的文件流所使用的文件描述符
返回值:某個數據流的文件描述符
頭文件:stdio.h
相關函數:open,fopen,fclose
void *memset(void *s, int ch, size_t n);
函數解釋:將s中當前位置后面的n個字節 (typedef unsigned int size_t )用 ch 替換並返回 s 。
memset:作用是在一段內存塊中填充某個給定的值,它是對較大的結構體或數組進行清零操作的一種最快方法
函數原型
char *fgets(char *buf, int bufsize, FILE *stream);
參數
*buf: 字符型指針,指向用來存儲所得數據的地址。
bufsize: 整型數據,指明存儲數據的大小。
*stream: 文件結構體指針,將要讀取的文件流。
返回值
- 成功,則返回第一個參數buf;
- 在讀字符時遇到end-of-file,則eof指示器被設置,如果還沒讀入任何字符就遇到這種情況,則buf保持原來的內容,返回NULL;
- 如果發生讀入錯誤,error指示器被設置,返回NULL,buf的值可能被改變。
chdir 是C語言中的一個系統調用函數(同cd),用於改變當前工作目錄,其參數為Path 目標目錄,可以是絕對目錄或相對目錄。
exec函數
linux下c語言編程exec函數使用
2012年04月10日 09:39:27
閱讀數:19800
exec用被執行的程序完全替換調用它的程序的影像。fork創建一個新的進程就產生了一個新的PID,exec啟動一個新程序,替換原有的進程,因此這個新的被exec執行的進程的PID不會改變,和調用exec函數的進程一樣。
下面來看下exec函數族:
#include <uniSTd.h>
int execl(cONst char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);
exec函數族裝入並運行程序pathname,並將參數arg0(arg1,arg2,argv[],envp[])傳遞給子程序,出錯返回-1。在exec函數族中,后綴l、v、p、e添加到exec后,所指定的函數將具有某種操作能力有后綴:
execl("/bin/ls","ls","-a",NULL)
execv("/bin/ls",arg)
execlp("ls","ls","-a",NULL)
execvp("ls",arg)
execle("/bin/ls","ls","-a",NULL,envp)
execve("/bin/ls",arg,envp)
assert()使用
assert()是一個調試程序時經常使用的宏,在程序運行時它計算括號內的表達式,如果表達式為FALSE (0), 程序將報告錯誤,並終止執行。如果表達式不為0,則繼續執行后面的語句,它的作用是終止程序以免導致嚴重后果,同時也便於查找錯誤。
linux編程之dup與dup2
在linux下,通過open打開以文件后,會返回一個文件描述符,文件描述符會指向一個文件表,文件表中的節點指針會指向節點表。看下圖:
打開文件的內核數據結構
dup和dup2兩個函數都可以用來復制打開的文件描述符,復制成功后和復制源共享同一個文件表。看下表
執行dup后的內核數據結構
dup函數
fd1=dup(fd)
fd1和fd共享一個文件表(對df進行什么操作,fd1也會有相應的操作,fd和fd1是同步的)
具體解釋:
#inclue<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
int main()
{
char buf[6]={0};
char buf1[6]={0};
int fd = open("file",O_RDWR|O_CREAT,0644);
if(fd < 0)
printf("open error");
printf("fd:%d\n",fd);
//輸出fd=3;
write(fd,"hello,world",12);
lseek(fd,0,SEEK_SET); //將文件偏移量置為0,就是從第一個字符開始讀(h開始)
read(fd,buf,5);
printf("fd:%s",buf);//輸出hello
int fd1 = dup(fd);
read(fd1,buf1,5); //之前做的是對fd的讀寫操作,並沒有對fd1做任何操作。但在這對fd1進行了讀,如果輸出數據。說明fd和fd1是同步的(fd做了什么相當於fd1也做了什么)
printf("fd1:%s\n",buf1); //輸出,worl
//既然輸出的是fd中的內容,說明fd和fd1共用一個文件表,讀到的是,worl,而不是hello(我們在上面將偏移量從第一個字符開始,輸出hello之后,fd的偏移量距離開始有5個字符當我們再次讀fd的時候,它是從第6個字符開始讀的,很明顯,第6個是逗號,往后讀5個,就是,worl),說明偏移量是一致的。(其實不用寫偏移量,因為共用文件表就意味着文件偏移量也共用)
printf("fd1:%d\n",fd1);//輸出fd1 = 4
//fd=3不等於fd1說明不共用同一個文件描述符。這也是dup和dup2的區別。
close(fd);
close(fd1);
return 0;
}
(2)dup2函數
fd2 = dup2(fd,fd1);
fd2用的fd1(第二個參數)的描述符,用的fd(第一個參數)的文件(和fd共享一個文件表,當然也共享文件偏移量)
強調第幾個參數是因為如果你寫成fd2=dup2(fd1,fd);那么fd2 =fd,和fd1共享同一個文件表。
#inclue<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
int main()
{
int fd = open("file",O_RDWR|O_CREAT,0644);
if(fd < 0)
printf("open error");
printf("fd:%d\n",fd);
//輸出fd=3;
int fd1 =open("text",,O_RDWR|O_CREAT,0644);
if(fd1 < 0)
printf("open error");
printf("fd1:%d\n",fd1);
//輸出fd1=4;
int fd2 = dup2(fd,fd1);
printf("fd2:%d\n",fd2);
//輸出fd2=4;
//fd1 =fd2=4;說明fd2使用了fd1的文件描述符。
char buf[12]="hello,world";
write(fd,buf,12); //我們對fd進行了寫,並沒有對fd2進行寫
read(fd2,buf,12);//但是我們對fd2讀的時候,如果沒有寫,怎么可能讀出來呢
printf("fd2:%s\n",buf);//事實是讀出來了
//輸出fd2:hello,world //說明fd和fd2共用一個文件表。
lseek(fd,5,SEEK_SET);//距離開始偏移5位,說明下次讀的時候是從第6個開始,注意我們是對fd進行偏移,沒有對fd2偏移
read(fd2,buf,5); //但是如果讀fd2結果是從第6個字符開始的
buf[5]=0; //如果不寫這句,輸出的buf是按照12個字符輸出的。因為定義buf的時候數組中可以放12個字符。
printf("fd2:%s\n",buf);//輸出fd2:,worl //說明fd2和fd共享文件偏移量。
close(fd);
close(fd2);
return 0;
}
dup和dup2的區別
dup:fd1= dup(fd);目標描述符使用了fd的文件表
dup2:fd2 = dup2(fd1,fd)目標描述符使用了fd1的描述符,使用了fd的文件表
linux編程之pipe()函數
管道是一種把兩個進程之間的標准輸入和標准輸出連接起來的機制,從而提供一種讓多個進程間通信的方法,當進程創建管道時,每次都需要提供兩個文件描述符來操作管道。其中一個對管道進行寫操作,另一個對管道進行讀操作。對管道的讀寫與一般的IO系統函數一致,使用write()函數寫入數據,使用read()讀出數據。
#include<unistd.h>
int pipe(int filedes[2])
返回值:成功,返回0,否則返回-1。參數數組包含pipe使用的兩個文件的描述符。fd[0]:讀管道,fd[1]寫管道。
必須在fork()中調用pipe(),否則子進程不會繼承文件描述符。兩個進程不共享祖先進程,就不能使用pipe。但是可以使用命名管道。
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4 #include<unistd.h>
5 #include<sys/types.h>
6 int main(void){
7 int result=-1;
8 int fd[2],nbytes;
9 pid_t pid;
10 char string[]="hell world, my pipe!";
11 char readbuffer[100];
12 int *write_fd=&fd[1];
13 int *read_fd=&fd[0];
14 result=pipe(fd);;
15 if(-1==result){
16 printf("fail to create pipe\n");
17 return -1;
18 }
19 pid=fork();
20 if(-1==pid){
21 printf("fail to fork\n");
22 return -1;
23 }
24 if(0==pid){
25 close(*read_fd);
26 result=write(*write_fd,string,strlen(string));
27 return 0;
28 }else{
29 close(*write_fd);
30 nbytes=read(*read_fd,readbuffer,sizeof(readbuffer));
31 printf("the parent receive %d bytes data: %s \n",nbytes,readbuffer);
32 }
33 return 0;
34 }
the parent receive 20 bytes data: hell world, my pipe!
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define COUNT (10)
int main(int argc, char *argv[])
{
int pipefd[2];
int read_count = 0;
char buf[COUNT] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};;
if (pipe(pipefd) == -1) {
perror("call pipe failed \n");
exit(EXIT_FAILURE);
}
printf("write %d chars to pipe1 \n", COUNT);
write(pipefd[1], buf, COUNT);
while (read(pipefd[0], &buf, 1) > 0)
{
printf("read %c from pipe0\n", buf[0]);
read_count++;
if(read_count == COUNT)
{
printf("total read %d chars \n", read_count);
break;
}
}
close(pipefd[0]);
close(pipefd[1]);
}
編譯及執行結果:
[root@alexs-centos core_dump]# gcc pipe.c
[root@alexs-centos core_dump]# ./a.out
write 10 chars to pipe1
read 0 from pipe0
read 1 from pipe0
read 2 from pipe0
read 3 from pipe0
read 4 from pipe0
read 5 from pipe0
read 6 from pipe0
read 7 from pipe0
read 8 from pipe0
read 9 from pipe0
total read 10 chars
從shell中運行一個進程,默認會有3個文件描述符存在(0、1、2), 0與進程的標准輸入相關聯,1與進程的標准輸出相關聯,2與進程的標准錯誤輸出相關聯,一個進程當前有哪些打開的文件描述符可以通過/proc/進程ID/fd目錄查看
C語言提供了幾個標准庫函數,可以將任意類型(整型、長整型、浮點型等)的數字轉換為字符串。
1.int/float to string/array:
C語言提供了幾個標准庫函數,可以將任意類型(整型、長整型、浮點型等)的數字轉換為字符串,下面列舉了各函數的方法及其說明。
● itoa():將整型值轉換為字符串。
● ltoa():將長整型值轉換為字符串。
● ultoa():將無符號長整型值轉換為字符串。
● gcvt():將浮點型數轉換為字符串,取四舍五入。
● ecvt():將雙精度浮點型值轉換為字符串,轉換結果中不包含十進制小數點。
● fcvt():指定位數為轉換精度,其余同ecvt()。
除此外,還可以使用sprintf系列函數把數字轉換成字符串,其比itoa()系列函數運行速度慢
2. string/array to int/float
C/C++語言提供了幾個標准庫函數,可以將字符串轉換為任意類型(整型、長整型、浮點型等)。
● atof():將字符串轉換為雙精度浮點型值。
● atoi():將字符串轉換為整型值。
● atol():將字符串轉換為長整型值。
● strtod():將字符串轉換為雙精度浮點型值,並報告不能被轉換的所有剩余數字。
● strtol():將字符串轉換為長整值,並報告不能被轉換的所有剩余數字。
● strtoul():將字符串轉換為無符號長整型值,並報告不能被轉換的所有剩余數字。
以下是用itoa()函數將整數轉換為字符串的一個例子:
# include <stdio.h>
# include <stdlib.h>
void main (void)
{
int num = 100;
char str[25];
itoa(num, str, 10);
printf("The number 'num' is %d and the string 'str' is %s. \n" ,
num, str);
}
itoa()函數有3個參數:第一個參數是要轉換的數字,第二個參數是要寫入轉換結果的目標字符串,第三個參數是轉移數字時所用 的基數。在上例中,轉換基數為10。10:十進制;2:二進制...
C語言pthread_create傳遞帶多個參數的函數& pthread_join
pthread_create是類Unix操作系統(Unix、Linux、Mac OS X等)的創建線程的函數,頭文件在pthread.h中。函數的聲明如下:
int pthread_create(pthread_t *tidp,const pthread_attr_t *attr,
(void*)(*start_rtn)(void*),void *arg);
//返回值:若成功則返回0,否則返回錯誤編號
參數
第一個參數為指向線程標識符的指針。
第二個參數用來設置線程屬性。
第三個參數是線程運行函數的起始地址。
最后一個參數是運行函數的參數。
從第三個函數可以看到,傳入的函數參數需要為void*類型。但是很多情況下需要線程處理的函數是多參數的。可以通過把參數封裝成結構體的方式來實現傳遞帶多個參數的函數。
struct fun_para
{
var para1;//參數1
var para2;//參數2
.......
}
將這個結構體指針,作為void *形參的實際參數傳遞
struct fun_para para;
pthread_create(&ntid, NULL, thr_fn,¶);
接着在線程的調用函數thr_fn中可以通過下面的方式使用通過para傳入的參數。
void *thr_fn(void *arg)
{
fun_para *para;
para = (fun_para *) arg;
para->para1;//參數1
para->para2;//參數2
......
//pthread_exit(0);
return ((void *)0);
}
Additional Mark: 代碼中如果沒有pthread_join,主線程會很快結束從而使整個進程結束,從而使創建的線程沒有機會開始執行就結束了。加入pthread_join后,主線程會一直等待直到等待的線程結束自己才結束,使創建的線程有機會執行。
函數定義:
int pthread_join(pthread_t thread, void **retval);
- 1
描述 : pthread_join()函數,以阻塞的方式等待thread指定的線程結束。當函數返回時,被等待線程的資源被收回。如果線程已經結束,那么該函數會立即返回。並且thread指定的線程必須是joinable的。
參數: thread: 線程標識符,即線程ID,標識唯一線程。retval: 用戶定義的指針,用來存儲被等待線程的返回值。
返回值 : 0代表成功。 失敗,返回的則是錯誤號。
tmp1 = pthread_join(tid, &retval);
if (tmp1 != 0)
{
printf("cannot join with thread1\n");
}
多線程下變量-原子操作 __sync_fetch_and_add等等
當然我們知道,count++這種操作不是原子的。一個自加操作,本質是分成三步的:
1 從緩存取到寄存器
2 在寄存器加1
3 存入緩存。
由於時序的因素,多個線程操作同一個全局變量,會出現問題。這也是並發編程的難點。在目前多核條件下,這種困境會越來越彰顯出來。
最簡單的處理辦法就是加鎖保護,這也是我最初的解決方案。看下面的代碼:
pthread_mutex_t count_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&count_lock);
global_int++;
pthread_mutex_unlock(&count_lock);
后來在網上查找資料,找到了__sync_fetch_and_add系列的命令,發現這個系列命令講的最好的一篇文章,英文好的同學可以直接去看原文。Multithreaded simple data type access and atomic variables
__sync_fetch_and_add系列一共有十二個函數,有加/減/與/或/異或/等函數的原子性操作函數,__sync_fetch_and_add,顧名思義,現fetch,然后自加,返回的是自加以前的值。以count = 4為例,調用__sync_fetch_and_add(&count,1),之后,返回值是4,然后,count變成了5.
有__sync_fetch_and_add,自然也就有__sync_add_and_fetch,呵呵這個的意思就很清楚了,先自加,在返回。他們哥倆的關系與i++和++i的關系是一樣的。被譚浩強他老人家收過保護費的都會清楚了。
有了這個寶貝函數,我們就有新的解決辦法了。對於多線程對全局變量進行自加,我們就再也不用理線程鎖了。下面這行代碼,和上面被pthread_mutex保護的那行代碼作用是一樣的,而且也是線程安全的。
__sync_fetch_and_add( &global_int, 1 );
下面是這群函數的全家福,大家看名字就知道是這些函數是干啥的了。
在用gcc編譯的時候要加上選項 -march=i686
type __sync_fetch_and_add (type *ptr, type value);
type __sync_fetch_and_sub (type *ptr, type value);
type __sync_fetch_and_or (type *ptr, type value);
type __sync_fetch_and_and (type *ptr, type value);
type __sync_fetch_and_xor (type *ptr, type value);
type __sync_fetch_and_nand (type *ptr, type value);
type __sync_add_and_fetch (type *ptr, type value);
type __sync_sub_and_fetch (type *ptr, type value);
type __sync_or_and_fetch (type *ptr, type value);
type __sync_and_and_fetch (type *ptr, type value);
type __sync_xor_and_fetch (type *ptr, type value);
type __sync_nand_and_fetch (type *ptr, type value);